=== modified file 'includes/common.inc'
--- includes/common.inc 2008-02-18 23:28:58 +0000
+++ includes/common.inc 2008-02-18 23:39:15 +0000
@@ -1787,7 +1787,7 @@
$data = implode('', $matches[0]) . $data;
// Create the CSS file.
- file_save_data($data, $csspath .'/'. $filename, FILE_EXISTS_REPLACE);
+ _file_save_data($data, $csspath .'/'. $filename, FILE_EXISTS_REPLACE);
}
return $csspath .'/'. $filename;
}
@@ -1888,7 +1888,7 @@
* Delete all cached CSS files.
*/
function drupal_clear_css_cache() {
- file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
+ file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), '_file_delete', TRUE);
}
/**
@@ -2246,7 +2246,7 @@
}
// Create the JS file.
- file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE);
+ _file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE);
}
return $jspath .'/'. $filename;
@@ -2256,7 +2256,7 @@
* Delete all cached JS files.
*/
function drupal_clear_js_cache() {
- file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
+ file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), '_file_delete', TRUE);
variable_set('javascript_parsed', array());
}
=== modified file 'includes/file.inc'
--- includes/file.inc 2008-02-18 23:28:58 +0000
+++ includes/file.inc 2008-02-19 00:05:09 +0000
@@ -35,8 +35,10 @@
/**
* Create the download path to a file.
*
- * @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.
+ * @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.
@@ -55,10 +57,12 @@
* Make sure the destination is a complete path and resides in the file system
* directory, if it is not prepend the file system directory.
*
- * @param $dest A string containing the path to verify. If this value is
- * omitted, Drupal's 'files' directory will be used.
- * @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
+ * @param $dest
+ * A string containing the path to verify. If this value is omitted, Drupal's
+ * 'files' directory will be used.
+ * @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).
*/
function file_create_path($dest = 0) {
@@ -66,11 +70,13 @@
if (!$dest) {
return $file_path;
}
- // file_check_location() checks whether the destination is inside the Drupal files directory.
+ // file_check_location() checks whether the destination is inside the Drupal
+ // files directory.
if (file_check_location($dest, $file_path)) {
return $dest;
}
- // check if the destination is instead inside the Drupal temporary files directory.
+ // Check if the destination is instead inside the Drupal temporary files
+ // directory.
else if (file_check_location($dest, file_directory_temp())) {
return $dest;
}
@@ -86,14 +92,18 @@
* Check that the directory exists and is writable. 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 A Boolean value to indicate if the directory should be created
- * if it does not exist or made writable if it is read-only.
- * @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.
+ * @param $directory
+ * A string containing the name of a directory path.
+ * @param $mode
+ * A Boolean value to indicate if the directory should be created if it does
+ * not exist or made writable if it is read-only.
+ * @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.
*/
function file_check_directory(&$directory, $mode = 0, $form_item = NULL) {
$directory = rtrim($directory, '/\\');
@@ -143,10 +153,11 @@
/**
* Checks path to see if it is a directory, or a dir/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.
+ * @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.
*/
function file_check_path(&$path) {
// Check if path is a directory.
@@ -174,9 +185,12 @@
* 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 0 for invalid path or the real path of the source.
+ * @param $source
+ * A string set to the file to check.
+ * @param $directory
+ * A string where the file should be located.
+ * @return
+ * 0 for invalid path or the real path of the source.
*/
function file_check_location($source, $directory = '') {
$check = realpath($source);
@@ -195,25 +209,71 @@
}
/**
- * Copies a file to a new location. This is a powerful function that in many ways
- * performs like an advanced version of copy().
- * - Checks if $source and $dest are valid and readable/writable.
- * - Performs a file copy if $source is not equal to $dest.
- * - If file already exists in $dest either the call will error out, replace the
- * file or rename the file based on the $replace parameter.
- *
- * @param $source A string specifying the file location of the original file.
- * This parameter will contain the resulting destination filename in case of
- * success.
- * @param $dest A string containing the directory $source should be copied to.
- * If this value is omitted, Drupal's 'files' directory will be used.
- * @param $replace Replace behavior when the destination file already exists.
- * - FILE_EXISTS_REPLACE - Replace the existing file
- * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
- * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
- * @return True for success, FALSE for failure.
- */
-function file_copy(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
+ * Copies a file to a new location and adds a record for the new file to the
+ * database. This is a powerful function that in many ways performs like an
+ * advanced version of copy().
+ * - Checks if $source and $dest are valid and readable/writable.
+ * - Performs a file copy if $source is not equal to $dest.
+ * - If file already exists in $dest either the call will error out, replace the
+ * file or rename the file based on the $replace parameter.
+ * - Adds thew new file to the files database. If the source file is a
+ * temporary file, the resulting file will also be a temporary file.
+ *
+ * @param $source
+ * A file object location of the original file.
+ * @param $dest
+ * A string containing the directory $source should be copied to. If this
+ * value is omitted, Drupal's 'files' directory will be used.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
+ * - FILE_EXISTS_REPLACE - Replace the existing file
+ * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
+ * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ * @return
+ * File object if the copy is successful, or FALSE in the event of an error.
+ */
+function file_copy($source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
+ if ($result = _file_copy($source->filepath, $dest, $replace)) {
+ $file = clone($source);
+ $file->fid = NULL;
+ $file->filename = basename($result);
+ $file->filepath = $result;
+ if ($file = file_save($file)) {
+ module_invoke_all('file', 'copy', $file, $source);
+ return $file;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Copies a file to a new location. This is a powerful function that in many
+ * ways performs like an advanced version of copy().
+ * - Checks if $source and $dest are valid and readable/writable.
+ * - Performs a file copy if $source is not equal to $dest.
+ * - If file already exists in $dest either the call will error out, replace the
+ * file or rename the file based on the $replace parameter.
+ *
+ * @param $source
+ * A string specifying the file location of the original file.
+ * @param $dest
+ * A string containing the directory $source should be copied to. If this
+ * value is omitted, Drupal's 'files' directory will be used.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
+ * - FILE_EXISTS_REPLACE - Replace the existing file
+ * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
+ * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ * @return
+ * The path to the new file, or FALSE in the event of an error.
+ */
+function _file_copy($source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
+ $source = realpath($source);
+ if (!file_exists($source)) {
+ drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error');
+ return FALSE;
+ }
+
$dest = file_create_path($dest);
$directory = $dest;
@@ -221,25 +281,8 @@
// Make sure we at least have a valid directory.
if ($basename === FALSE) {
- $source = is_object($source) ? $source->filepath : $source;
- drupal_set_message(t('The selected file %file could not be uploaded, because the destination %directory is not properly configured.', array('%file' => $source, '%directory' => $dest)), 'error');
- watchdog('file system', 'The selected file %file could not be uploaded, because the destination %directory could not be found, or because its permissions do not allow the file to be written.', array('%file' => $source, '%directory' => $dest), WATCHDOG_ERROR);
- return 0;
- }
-
- // Process a file upload object.
- if (is_object($source)) {
- $file = $source;
- $source = $file->filepath;
- if (!$basename) {
- $basename = $file->filename;
- }
- }
-
- $source = realpath($source);
- if (!file_exists($source)) {
- drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error');
- return 0;
+ drupal_set_message(t('The selected file %file could not be copied, because the destination %directory is not properly configured.', array('%file' => $source, '%directory' => $dest)), 'error');
+ return FALSE;
}
// If the destination file is not specified then use the filename of the source file.
@@ -257,7 +300,7 @@
if (!@copy($source, $dest)) {
drupal_set_message(t('The selected file %file could not be copied.', array('%file' => $source)), 'error');
- return 0;
+ return FALSE;
}
// Give everyone read access so that FTP'd users or
@@ -267,29 +310,23 @@
@chmod($dest, 0664);
}
- if (isset($file) && is_object($file)) {
- $file->filename = $basename;
- $file->filepath = $dest;
- $source = $file;
- }
- else {
- $source = $dest;
- }
-
- return 1; // Everything went ok.
+ return $dest;
}
/**
* 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.
- * @param $replace Replace behavior when the destination file already exists.
+ * @param $destination
+ * A string specifying the desired path.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE - Replace the existing file
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
* unique
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
- * @return The destination file path or FALSE if the file already exists and
+ * @return
+ * The destination file path or FALSE if the file already exists and
* FILE_EXISTS_ERROR was specified.
*/
function file_destination($destination, $replace) {
@@ -310,47 +347,56 @@
}
/**
- * Moves a file to a new location.
+ * Moves a file to a new location and updates the file's database entry. The
+ * move is performed by copying the file to the new location and the deleting
+ * the original.
* - Checks if $source and $dest are valid and readable/writable.
* - Performs a file move if $source is not equal to $dest.
* - If file already exists in $dest either the call will error out, replace the
* file or rename the file based on the $replace parameter.
+ * - Adds the new file to the files database.
*
- * @param $source A string specifying the file location of the original file.
- * This parameter will contain the resulting destination filename in case of
- * success.
- * @param $dest A string containing the directory $source should be copied to.
- * If this value is omitted, Drupal's 'files' directory will be used.
- * @param $replace Replace behavior when the destination file already exists.
+ * @param $source
+ * A file object for the original file.
+ * @param $dest
+ * A string containing the directory $source should be copied to. If this
+ * value is omitted, Drupal's 'files' directory will be used.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE - Replace the existing file
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
- * @return True for success, FALSE for failure.
+ * @return
+ * Resulting file object for success, or FALSE in the event of an error.
*/
-function file_move(&$source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
- $path_original = is_object($source) ? $source->filepath : $source;
-
- if (file_copy($source, $dest, $replace)) {
- $path_current = is_object($source) ? $source->filepath : $source;
-
- if ($path_original == $path_current || file_delete($path_original)) {
- return 1;
+function file_move($source, $dest = 0, $replace = FILE_EXISTS_RENAME) {
+ if ($result = _file_copy($source->filepath, $dest, $replace)) {
+ if ($source->filepath == $result || _file_delete($source->filepath)) {
+ $file = clone($source);
+ $file->filename = basename($result);
+ $file->filepath = $result;
+ if ($file = file_save($file)) {
+ module_invoke_all('file', 'move', $file, $source);
+ return $file;
+ }
}
- drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $path_original)), 'error');
+ drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $source->filepath)), 'error');
}
- return 0;
+ return FALSE;
}
/**
* Munge the filename as needed for security purposes. For instance the file
* name "exploit.php.pps" would become "exploit.php_.pps".
*
- * @param $filename The name of a file to modify.
- * @param $extensions A space separated list of extensions that should not
- * be altered.
- * @param $alerts Whether alerts (watchdog, drupal_set_message()) should be
- * displayed.
- * @return $filename The potentially modified $filename.
+ * @param $filename
+ * The name of a file to modify.
+ * @param $extensions
+ * A space separated list of extensions that should not be altered.
+ * @param $alerts
+ * Whether alerts (watchdog, drupal_set_message()) should be displayed.
+ * @return $filename
+ * The potentially modified $filename.
*/
function file_munge_filename($filename, $extensions, $alerts = TRUE) {
$original = $filename;
@@ -387,7 +433,8 @@
/**
* Undo the effect of upload_munge_filename().
*
- * @param $filename string filename
+ * @param $filename
+ * A filename string.
* @return string
*/
function file_unmunge_filename($filename) {
@@ -398,8 +445,10 @@
* Create a full file path from a directory and filename. If a file with the
* specified name already exists, an alternative will be used.
*
- * @param $basename string filename
- * @param $directory string directory
+ * @param $basename
+ * A filename string.
+ * @param $directory
+ * A directory string.
* @return
*/
function file_create_filename($basename, $directory) {
@@ -425,29 +474,77 @@
}
/**
+ * Delete a file and its database record.
+ *
+ * @param $path
+ * A file object.
+ * @param $force
+ * Force File Deletion ignoring reference counting.
+ * @return mixed
+ * TRUE for success, Array for reference count block, or FALSE in the event of an error.
+ */
+function file_delete($file, $force = FALSE) {
+ // If any module returns a value from the reference hook, the
+ // file will not be deleted from drupal, but file_delete will
+ // return a populated array that tests as TRUE.
+ if (!$force && $references = module_invoke_all('file', 'references', $file)) {
+ return $references;
+ }
+
+ // Let other modules clean up on delete.
+ module_invoke_all('file', 'delete', $file);
+
+ // Make sure the file is deleted before removing it's row from the
+ // database, so UI's can still find the file in the database.
+ if (_file_delete($file->filepath)) {
+ db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
* Delete a file.
*
- * @param $path A string containing a file path.
- * @return TRUE for success, FALSE for failure.
+ * @param $path
+ * A string containing a file path.
+ * @return
+ * TRUE for success or path does not exist, or FALSE in the event of an error.
*/
-function file_delete($path) {
+function _file_delete($path) {
if (is_file($path)) {
return unlink($path);
}
+ if (is_dir($path)) {
+ watchdog('file', t('%path is a directory and cannot be removed with _file_delete.', array('%path' => $path)), WATCHDOG_ERROR);
+ return FALSE;
+ }
+ // Return true for non-existant file, but log that nothing was actually delete, as the intended
+ // result of _file_delete is in fact the current state.
+ if (!file_exists($path)) {
+ watchdog('file', t('The file %path was not deleted, because it does not exist.', array('%path' => $path)), WATCHDOG_NOTICE);
+ return TRUE;
+ }
+ return FALSE;
}
/**
* Determine total disk space used by a single user or the whole filesystem.
*
* @param $uid
- * An optional user id. A NULL value returns the total space used
- * by all files.
+ * An optional, user id. A NULL value returns the total space used by all
+ * non-temporary files.
+ *
+ * @param $status
+ * File Status to return. Combine with a bitwise OR(|) to return multiple statuses.
+ * The default status is FILE_STATUS_PERMANENT.
+ * @return int
*/
-function file_space_used($uid = NULL) {
+function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
if (isset($uid)) {
- return (int) db_result(db_query('SELECT SUM(filesize) FROM {files} WHERE uid = %d', $uid));
+ return db_result(db_query('SELECT SUM(filesize) FROM {files} WHERE uid = %d AND status & %d', $uid, $status));
}
- return (int) db_result(db_query('SELECT SUM(filesize) FROM {files}'));
+ return (int) db_result(db_query('SELECT SUM(filesize) FROM {files} WHERE status & %d', $status));
}
/**
@@ -475,7 +572,8 @@
* destination directory should overwritten. A false value will generate a
* new, unique filename in the destination directory.
* @return
- * An object containing the file information, or 0 in the event of an error.
+ * An object containing the file information, or FALSE in the event of an
+ * error.
*/
function file_save_upload($source, $validators = array(), $dest = FALSE, $replace = FILE_EXISTS_RENAME) {
global $user;
@@ -492,8 +590,8 @@
// If a file was uploaded, process it.
if (isset($_FILES['files']) && $_FILES['files']['name'][$source] && is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
- // Check for file upload errors and return FALSE if a
- // lower level system error occurred.
+ // Check for file upload errors and return FALSE if a lower level system
+ // error occurred.
switch ($_FILES['files']['error'][$source]) {
// @see http://php.net/manual/en/features.file-upload.errors.php
case UPLOAD_ERR_OK:
@@ -502,17 +600,17 @@
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size()))), 'error');
- return 0;
+ return FALSE;
case UPLOAD_ERR_PARTIAL:
case UPLOAD_ERR_NO_FILE:
drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source)), 'error');
- return 0;
+ return FALSE;
// Unknown error
default:
drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source)), 'error');
- return 0;
+ return FALSE;
}
// Build the list of non-munged extensions.
@@ -525,9 +623,12 @@
// Begin building file object.
$file = new stdClass();
+ $file->uid = $user->uid;
+ $file->status = FILE_STATUS_TEMPORARY;
$file->filename = file_munge_filename(trim(basename($_FILES['files']['name'][$source]), '.'), $extensions);
$file->filepath = $_FILES['files']['tmp_name'][$source];
$file->filemime = $_FILES['files']['type'][$source];
+ $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')) {
@@ -543,17 +644,19 @@
}
$file->source = $source;
- $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), $replace);
- $file->filesize = $_FILES['files']['size'][$source];
+ $file->destination = file_destination(file_create_path($dest .'/'. $file->filename), FILE_EXISTS_RENAME);
- // Call the validation functions.
+ // Call the validation functions specified by this function's caller.
$errors = array();
foreach ($validators as $function => $args) {
array_unshift($args, $file);
$errors = array_merge($errors, call_user_func_array($function, $args));
}
- // Check for validation errors.
+ // Let other modules perform validation on the new file.
+ $errors = array_merge($errors, module_invoke_all('file', 'validate', $file));
+
+ // Check for errors.
if (!empty($errors)) {
$message = t('The selected file %name could not be uploaded.', array('%name' => $file->filename));
if (count($errors) > 1) {
@@ -563,29 +666,26 @@
$message .= ' '. array_pop($errors);
}
form_set_error($source, $message);
- return 0;
+ return FALSE;
}
- // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary directory.
- // This overcomes open_basedir restrictions for future file operations.
+ // 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)) {
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));
- return 0;
- }
-
- // If we made it this far it's safe to record this file in the database.
- $file->uid = $user->uid;
- $file->status = FILE_STATUS_TEMPORARY;
- $file->timestamp = time();
- drupal_write_record('files', $file);
-
- // Add file to the cache.
- $upload_cache[$source] = $file;
- return $file;
+ watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination', $file->filepath));
+ return FALSE;
+ }
+
+ if ($file = file_save($file)) {
+ // Add file to the cache.
+ $upload_cache[$source] = $file;
+ return $file;
+ }
}
- return 0;
+ return FALSE;
}
/**
@@ -739,47 +839,93 @@
}
/**
+ * Save a string to the specified destination. The file will be added to the
+ * database as a permanent file.
+
+ *
+ * @param $data
+ * A string containing the contents of the file.
+ * @param $dest
+ * A string containing the destination location.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
+ * - FILE_EXISTS_REPLACE - Replace the existing file
+ * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
+ * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ * A string containing the resulting filename or 0 on error
+ */
+function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
+ global $user;
+
+ if ($filepath = _file_save_data($data, $dest, $replace)) {
+ // Create a file object.
+ $file = new stdClass();
+ $file->filepath = $filepath;
+ $file->filename = basename($file->filepath);
+ $file->filemime = 'text/plain';
+ $file->uid = $user->uid;
+ $file->status = FILE_STATUS_PERMANENT;
+ return file_save($file);
+ }
+ return FALSE;
+}
+
+/**
* Save a string to the specified destination.
*
- * @param $data A string containing the contents of the file.
- * @param $dest A string containing the destination location.
- * @param $replace Replace behavior when the destination file already exists.
+ * @param $data
+ * A string containing the contents of the file.
+ * @param $dest
+ * A string containing the destination location.
+ * @param $replace
+ * Replace behavior when the destination file already exists.
* - FILE_EXISTS_REPLACE - Replace the existing file
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
*
- * @return A string containing the resulting filename or 0 on error
+ * @return
+ * A string with the path of the resulting file, or FALSE in the event of an
+ * error.
*/
-function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
- $temp = file_directory_temp();
- // On Windows, tempnam() requires an absolute path, so we use realpath().
- $file = tempnam(realpath($temp), 'file');
- if (!$fp = fopen($file, 'wb')) {
+function _file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
+ global $user;
+
+ // Write the data to a temporary file.
+ $temp_name = tempnam(file_directory_temp(), 'file');
+ if (!$fp = fopen($temp_name, 'wb')) {
drupal_set_message(t('The file could not be created.'), 'error');
- return 0;
+ return FALSE;
}
fwrite($fp, $data);
fclose($fp);
- if (!file_move($file, $dest, $replace)) {
- return 0;
- }
-
- return $file;
+ // Move the file to its final destination.
+ return _file_move($temp_name, $dest, $replace);
}
+
+
/**
* Set the status of a file.
*
- * @param file A Drupal file object
- * @param status A status value to set the file to.
- * @return FALSE on failure, TRUE on success and $file->status will contain the
- * status.
+ * @param $file
+ * A Drupal file object
+ * @param $status
+ * A status value to set the file to.
+ * - FILE_STATUS_TEMPORARY - A temporary file that Drupal's garbage
+ * collection will remove.
+ * - FILE_STATUS_PERMANENT - A permanant file that will drupal's garbage
+ * collection will not remove.
+ * @return
+ * File object if the change is successful, or FALSE in the event of an error.
*/
-function file_set_status(&$file, $status) {
+function file_set_status($file, $status = FILE_STATUS_PERMANENT) {
if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', $status, $file->fid)) {
$file->status = $status;
- return TRUE;
+ module_invoke_all('file', 'status', $file);
+ return $file;
}
return FALSE;
}
@@ -788,8 +934,10 @@
* Transfer file using http to client. Pipes a file through Drupal to the
* client.
*
- * @param $source File to transfer.
- * @param $headers An array of http headers to send along with file.
+ * @param $source
+ * File to transfer.
+ * @param $headers
+ * An array of http headers to send along with file.
*/
function file_transfer($source, $headers) {
ob_end_clean();
@@ -915,7 +1063,8 @@
/**
* Determine the default temporary directory.
*
- * @return A string containing a temp directory.
+ * @return
+ * A string containing a temp directory.
*/
function file_directory_temp() {
$temporary_directory = variable_get('file_directory_temp', NULL);
@@ -956,7 +1105,8 @@
/**
* Determine the default 'files' directory.
*
- * @return A string containing the path to Drupal's 'files' directory.
+ * @return
+ * A string containing the path to Drupal's 'files' directory.
*/
function file_directory_path() {
return variable_get('file_directory_path', conf_path() .'/files');
@@ -980,5 +1130,62 @@
}
/**
+ * Load a file object from the database.
+ *
+ * @param $file_id
+ * A numeric file id or string containg the file path.
+ * @param $reset
+ * Whether to reset the internal file_load cache.
+ */
+function file_load($file_id, $reset = NULL) {
+ static $files = array();
+
+ if ($reset) {
+ $files = array();
+ }
+
+ if (is_numeric($file_id)) {
+ if (isset($files[$file_id])) {
+ return clone($files[$file_id]);
+ }
+ $file = db_fetch_object(db_query('SELECT f.* FROM {files} f WHERE f.fid = %d', $file_id));
+ }
+ else {
+ $file = db_fetch_object(db_query("SELECT f.* FROM {files} f WHERE f.filepath = '%s'", $file_id));
+ }
+
+ module_invoke_all('file', 'load', $file);
+
+ // Cache the fully loaded value.
+ $files[$file->fid] = clone($file);
+ return $file;
+}
+
+/**
+ * Save a file object to the database. If the $file->fid is not set a new record
+ * will be added. Re-saving an existing file will not change its status.
+ *
+ * @param $file
+ * A file object.
+ * @return
+ * The updated file object.
+ */
+function file_save($file) {
+ $file->timestamp = time();
+ $file->filesize = filesize($file->filepath);
+
+ if (empty($file->fid)) {
+ db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, '%s', '%s', '%s', %d, %d, %d)", $file->uid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $file->status, $file->timestamp);
+ $file->fid = db_last_insert_id('files', 'fid');
+ module_invoke_all('file', 'insert', $file);
+ }
+ else {
+ db_query("UPDATE {files} SET uid = %d, filename = '%s', filepath = '%s', filemime = '%s', filesize = %d, timestamp = %d WHERE fid = %d", $file->uid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $file->timestamp, $file->fid);
+ module_invoke_all('file', 'update', $file);
+ }
+ return $file;
+}
+
+/**
* @} End of "defgroup file".
*/
=== modified file 'modules/blogapi/blogapi.module'
--- modules/blogapi/blogapi.module 2008-02-18 23:29:04 +0000
+++ modules/blogapi/blogapi.module 2008-02-18 23:39:17 +0000
@@ -378,12 +378,12 @@
return blogapi_error(t('No file sent.'));
}
- if (!$file = file_save_data($data, $name)) {
+ if (!$filepath = _file_save_data($data, $name)) {
return blogapi_error(t('Error storing file.'));
}
// Return the successful result.
- return array('url' => file_create_url($file), 'struct');
+ return array('url' => _file_create_url($filepath), 'struct');
}
/**
* Blogging API callback. Returns a list of the taxonomy terms that can be
=== modified file 'modules/dblog/dblog.admin.inc'
--- modules/dblog/dblog.admin.inc 2008-01-22 22:20:38 +0000
+++ modules/dblog/dblog.admin.inc 2008-01-19 06:35:11 +0000
@@ -68,7 +68,7 @@
$icons[$dblog->severity],
t($dblog->type),
format_date($dblog->timestamp, 'small'),
- l(truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE), 'admin/reports/db_log/event/'. $dblog->wid, array('html' => TRUE)),
+ l(truncate_utf8(_dblog_format_message($dblog), 56, TRUE, TRUE), 'admin/reports/event/'. $dblog->wid, array('html' => TRUE)),
theme('username', $dblog),
$dblog->link,
),
=== modified file 'modules/dblog/dblog.module'
--- modules/dblog/dblog.module 2008-01-22 22:20:40 +0000
+++ modules/dblog/dblog.module 2008-01-19 06:35:11 +0000
@@ -71,7 +71,7 @@
'page arguments' => array('access denied'),
'file' => 'dblog.admin.inc',
);
- $items['admin/reports/db_log/event/%'] = array(
+ $items['admin/reports/event/%'] = array(
'title' => 'Details',
'page callback' => 'dblog_event',
'page arguments' => array(3),
=== modified file 'modules/system/system.admin.inc'
--- modules/system/system.admin.inc 2008-02-06 10:58:09 +0000
+++ modules/system/system.admin.inc 2008-02-18 23:39:18 +0000
@@ -350,9 +350,9 @@
// 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 (file_copy($file, $filename, FILE_EXISTS_REPLACE)) {
+ if ($filepath = _file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
$_POST['default_logo'] = 0;
- $_POST['logo_path'] = $file->filepath;
+ $_POST['logo_path'] = $filepath;
$_POST['toggle_logo'] = 1;
}
}
@@ -365,9 +365,9 @@
// 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 (file_copy($file, $filename)) {
+ if ($filepath = _file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
$_POST['default_favicon'] = 0;
- $_POST['favicon_path'] = $file->filepath;
+ $_POST['favicon_path'] = $filepath;
$_POST['toggle_favicon'] = 1;
}
}
=== modified file 'modules/system/system.module'
--- modules/system/system.module 2008-02-18 23:29:07 +0000
+++ modules/system/system.module 2008-02-18 23:39:18 +0000
@@ -1220,12 +1220,14 @@
if (file_exists($file->filepath)) {
// If files that exist cannot be deleted, continue so the database remains
// consistent.
- if (!file_delete($file->filepath)) {
- watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath), 'error');
+ if (!file_delete($file)) {
+ watchdog('file system', t('Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath)), 'error');
continue;
}
}
- db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
+ else {
+ db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
+ }
}
}
=== modified file 'modules/upload/upload.admin.inc'
--- modules/upload/upload.admin.inc 2008-01-19 06:15:48 +0000
+++ modules/upload/upload.admin.inc 2008-01-19 06:35:57 +0000
@@ -74,11 +74,11 @@
'#field_suffix' => ''. t('WIDTHxHEIGHT') .''
);
$form['settings_general']['upload_list_default'] = array(
- '#type' => 'select',
+ '#type' => 'checkbox',
'#title' => t('List files by default'),
'#default_value' => variable_get('upload_list_default', 1),
'#options' => array(0 => t('No'), 1 => t('Yes')),
- '#description' => t('Display attached files when viewing a post.'),
+ '#description' => t('Determines whether files attached to nodes are listed in the node view by default.'),
);
$form['settings_general']['upload_extensions_default'] = array(
=== modified file 'modules/upload/upload.module'
--- modules/upload/upload.module 2008-02-18 23:29:09 +0000
+++ modules/upload/upload.module 2008-02-19 00:09:41 +0000
@@ -144,12 +144,12 @@
/**
* Implementation of hook_file_download().
*/
-function upload_file_download($file) {
+function upload_file_download($filepath) {
if (!user_access('view uploaded files')) {
return -1;
}
- $file = file_create_path($file);
- $result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = '%s'", $file);
+ $filepath = file_create_path($file);
+ $result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = '%s'", $filepath);
if ($file = db_fetch_object($result)) {
return array(
'Content-Type: '. $file->filemime,
@@ -261,6 +261,36 @@
}
/**
+ * Implementation of hook_file().
+ */
+function upload_file($op, &$file, $source = NULL) {
+ switch ($op) {
+ case 'load':
+ // Add the upload specific data into the file object.
+ $values = db_fetch_array(db_query('SELECT * FROM {upload} u WHERE u.fid = %d', $file->fid));
+ foreach ((array) $values as $key => $value) {
+ $file->{$key} = $value;
+ }
+ break;
+
+ case 'references':
+ // If upload.module is still using a file, do not let other modules delete it.
+ $count = db_result(db_query('SELECT count(*) FROM {upload} WHERE fid = %d', $file->fid));
+ if ($count) {
+ // return the name of the module and how many references it has to the file.
+ return array('upload' => $count);
+ }
+ break;
+
+ case 'delete':
+ // Delete all information associated with the file.
+ db_query('DELETE FROM {upload} WHERE fid = %d', $file->fid);
+ break;
+ }
+}
+
+
+/**
* Implementation of hook_nodeapi().
*/
function upload_nodeapi(&$node, $op, $teaser) {
@@ -404,13 +434,15 @@
// If the file isn't used by any other revisions delete it.
$count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE fid = %d', $fid));
if ($count < 1) {
- file_delete($file->filepath);
+ file_delete($file);
db_query('DELETE FROM {files} WHERE fid = %d', $fid);
}
// Remove it from the session in the case of new uploads,
// that you want to disassociate before node submission.
unset($_SESSION['upload_files'][$fid]);
+ // Try to clean up a file that is no longer in use.
+ file_delete($file);
// Move on, so the removed file won't be added to new revisions.
continue;
}
@@ -418,13 +450,12 @@
// Create a new revision, or associate a new file needed.
if (!empty($node->old_vid) || isset($_SESSION['upload_files'][$fid])) {
db_query("INSERT INTO {upload} (fid, nid, vid, list, description, weight) VALUES (%d, %d, %d, %d, '%s', %d)", $file->fid, $node->nid, $node->vid, $file->list, $file->description, $file->weight);
- file_set_status($file, FILE_STATUS_PERMANENT);
}
// Update existing revision.
else {
db_query("UPDATE {upload} SET list = %d, description = '%s', weight = %d WHERE fid = %d AND vid = %d", $file->list, $file->description, $file->weight, $file->fid, $node->vid);
- file_set_status($file, FILE_STATUS_PERMANENT);
}
+ file_set_status($file, FILE_STATUS_PERMANENT);
}
// Empty the session storage after save. We use this variable to track files
// that haven't been related to the node yet.
@@ -432,38 +463,23 @@
}
function upload_delete($node) {
- $files = array();
- $result = db_query('SELECT DISTINCT f.* FROM {upload} u INNER JOIN {files} f ON u.fid = f.fid WHERE u.nid = %d', $node->nid);
- while ($file = db_fetch_object($result)) {
- $files[$file->fid] = $file;
- }
-
- foreach ($files as $fid => $file) {
- // Delete all files associated with the node
- db_query('DELETE FROM {files} WHERE fid = %d', $fid);
- file_delete($file->filepath);
- }
-
- // Delete all file revision information associated with the node
db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
+ if (!is_array($node->files)) {
+ return;
+ }
+ foreach($node->files as $file) {
+ file_delete($file);
+ }
}
function upload_delete_revision($node) {
- if (is_array($node->files)) {
- foreach ($node->files as $file) {
- // Check if the file will be used after this revision is deleted
- $count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE fid = %d', $file->fid));
-
- // if the file won't be used, delete it
- if ($count < 2) {
- db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
- file_delete($file->filepath);
- }
- }
- }
-
- // delete the revision
db_query('DELETE FROM {upload} WHERE vid = %d', $node->vid);
+ if (!is_array($node->files)) {
+ return;
+ }
+ foreach ($node->files as $file) {
+ file_delete($file);
+ }
}
function _upload_form($node) {
@@ -477,11 +493,17 @@
if (!empty($node->files) && is_array($node->files)) {
$form['files']['#theme'] = 'upload_form_current';
$form['files']['#tree'] = TRUE;
- foreach ($node->files as $key => $file) {
- $file = (object)$file;
- $description = file_create_url($file->filepath);
- $description = "". check_plain($description) ."";
- $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
+ foreach ($node->files as $file) {
+ $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]['size'] = array('#value' => 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);
@@ -496,12 +518,26 @@
if (user_access('upload files')) {
$limits = _upload_file_limits($user);
+
+ $limit_description = t('The maximum size of file uploads is %filesize. ', array('%filesize' => format_size($limits['file_size'])));
+ if (!empty($limits['resolution'])) {
+ if (image_get_toolkit()) {
+ $limit_description .= t('Images larger than %resolution will be resized. ', array('%resolution' => $limits['resolution']));
+ }
+ else {
+ $limit_description .= t('Images may not be larger than %resolution. ', array('%resolution' => $limits['resolution']));
+ }
+ }
+ if ($user->uid != 1) {
+ $limit_description .= t('Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions']));
+ }
+
$form['new']['#weight'] = 10;
$form['new']['upload'] = array(
'#type' => 'file',
'#title' => t('Attach new file'),
'#size' => 40,
- '#description' => ($limits['resolution'] ? t('Images are larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions'], '%filesize' => format_size($limits['file_size']))),
+ '#description' => $limit_description,
);
$form['new']['attach'] = array(
'#type' => 'submit',
@@ -563,9 +599,9 @@
$files = array();
if ($node->vid) {
- $result = db_query('SELECT * FROM {files} f INNER JOIN {upload} r ON f.fid = r.fid WHERE r.vid = %d ORDER BY r.weight, f.fid', $node->vid);
+ $result = db_query('SELECT fid FROM {upload} u WHERE u.vid = %d ORDER BY u.fid', $node->vid);
while ($file = db_fetch_object($result)) {
- $files[$file->fid] = $file;
+ $files[$file->fid] = file_load($file->fid);
}
}
=== modified file 'modules/user/user.module'
--- modules/user/user.module 2008-02-18 23:29:10 +0000
+++ modules/user/user.module 2008-02-18 23:39:20 +0000
@@ -389,7 +389,7 @@
// collector delete the original upload.
$info = image_get_info($file->filepath);
$destination = variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'];
- if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
+ if ($file = file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
$form_state['values']['picture'] = $file->filepath;
}
else {
@@ -1500,8 +1500,8 @@
$user = user_load(array('uid' => $uid));
// Delete picture if requested, and if no replacement picture was given.
if (!empty($edit['picture_delete'])) {
- if ($user->picture && file_exists($user->picture)) {
- file_delete($user->picture);
+ if ($user->picture && $file = file_load($user->picture)) {
+ file_delete($file);
}
$edit['picture'] = '';
}