? .DS_Store
? .cache
? .git
? .project
? .settings
? empty
? hook_file_14.patch
? logs
? test.php
? sites/all/modules
? sites/default/files
? sites/default/settings.php
? sites/default/test
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.799
diff -u -p -r1.799 common.inc
--- includes/common.inc 20 Sep 2008 20:22:23 -0000 1.799
+++ includes/common.inc 30 Sep 2008 22:06:06 -0000
@@ -648,7 +648,7 @@ function _drupal_get_last_caller($backtr
// The first trace is the call itself.
// It gives us the line and the file of the last call.
$call = $backtrace[0];
-
+
// The second call give us the function where the call originated.
if (isset($backtrace[1])) {
if (isset($backtrace[1]['class'])) {
@@ -1851,7 +1851,7 @@ function drupal_build_css_cache($types,
$data = implode('', $matches[0]) . $data;
// Create the CSS file.
- file_save_data($data, $csspath . '/' . $filename, FILE_EXISTS_REPLACE);
+ file_unmanaged_save_data($data, $csspath . '/' . $filename, FILE_EXISTS_REPLACE);
}
return $csspath . '/' . $filename;
}
@@ -1952,7 +1952,7 @@ function _drupal_load_stylesheet($matche
* 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_unmanaged_delete', TRUE);
}
/**
@@ -2315,7 +2315,7 @@ function drupal_build_js_cache($files, $
}
// Create the JS file.
- file_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
+ file_unmanaged_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
}
return $jspath . '/' . $filename;
@@ -2325,7 +2325,7 @@ function drupal_build_js_cache($files, $
* 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_unmanaged_delete', TRUE);
variable_set('javascript_parsed', array());
}
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.135
diff -u -p -r1.135 file.inc
--- includes/file.inc 20 Sep 2008 03:49:23 -0000 1.135
+++ includes/file.inc 30 Sep 2008 22:06:06 -0000
@@ -254,7 +254,137 @@ function file_check_location($source, $d
}
/**
- * Copy a file to a new location.
+ * Load a file object from the database.
+ *
+ * @param $param
+ * Either the id of a file or an array of conditions to match against in the
+ * database query.
+ * @param $reset
+ * Whether to reset the internal file_load cache.
+ * @return
+ * A file object.
+ */
+function file_load($param, $reset = NULL) {
+ static $files = array();
+
+ if ($reset) {
+ $files = array();
+ }
+
+ $arguments = array();
+ if (is_numeric($param)) {
+ if (isset($files[(string) $param])) {
+ return is_object($files[$param]) ? clone $files[$param] : $files[$param];
+ }
+ $cond = 'f.fid = %d';
+ $arguments[] = $param;
+ }
+ elseif (is_array($param)) {
+ // Turn the conditions into a query.
+ $cond = array();
+ foreach ($param as $key => $value) {
+ $cond[] = 'f.' . db_escape_table($key) . " = '%s'";
+ $arguments[] = $value;
+ }
+ $cond = implode(' AND ', $cond);
+ }
+ else {
+ return FALSE;
+ }
+ $file = db_fetch_object(db_query('SELECT f.* FROM {files} f WHERE ' . $cond, $arguments));
+
+ if ($file && $file->fid) {
+ // Allow modules to add or change the file object.
+ module_invoke_all('file_load', $file);
+
+ // Cache the fully loaded value.
+ $files[(string) $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 returned by file_load().
+ * @return
+ * The updated file object.
+ */
+function file_save($file) {
+ $file = (object)$file;
+ $file->timestamp = REQUEST_TIME;
+ $file->filesize = filesize($file->filepath);
+
+ if (empty($file->fid)) {
+ drupal_write_record('files', $file);
+ // Inform modules about the newly added file.
+ module_invoke_all('file_insert', $file);
+ }
+ else {
+ drupal_write_record('files', $file, 'fid');
+ // Inform modules that the file has been updated.
+ module_invoke_all('file_update', $file);
+ }
+
+ return $file;
+}
+
+/**
+ * Copy a file to a new location and adds a file record to the database.
+ *
+ * This function should be used when manipulating files that have records
+ * stored in the database. This is a powerful function that in many ways
+ * performs like an advanced version of copy().
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Checks that $source is not equal to $destination; if they are an error
+ * is reported.
+ * - If file already exists in $destination 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. If the source file is a
+ * temporary file, the resulting file will also be a temporary file.
+ * @see file_save_upload about temporary files.
+ *
+ * @param $source
+ * A file object.
+ * @param $destination
+ * 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.
+ *
+ * @see file_unmanaged_copy()
+ */
+function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+ $source = (object)$source;
+
+ if ($filepath = file_unmanaged_copy($source->filepath, $destination, $replace)) {
+ $file = clone $source;
+ $file->fid = NULL;
+ $file->filename = basename($filepath);
+ $file->filepath = $filepath;
+ if ($file = file_save($file)) {
+ // Inform modules that the file has been copied.
+ module_invoke_all('file_copy', $file, $source);
+ return $file;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Copy a file to a new location without calling any hooks or making any
+ * changes to the database.
*
* This is a powerful function that in many ways performs like an advanced
* version of copy().
@@ -277,8 +407,10 @@ function file_check_location($source, $d
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
* @return
* The path to the new file, or FALSE in the event of an error.
+ *
+ * @see file_copy()
*/
-function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
$source = realpath($source);
if (!file_exists($source)) {
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' => $source)), 'error');
@@ -360,7 +492,52 @@ function file_destination($destination,
}
/**
- * Move a file to a new location.
+ * Move a file to a new location and update the file's database entry.
+ *
+ * Moving a file is performed by copying the file to the new location and then
+ * deleting the original.
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Performs a file move if $source is not equal to $destination.
+ * - If file already exists in $destination 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 file object.
+ * @param $destination
+ * 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
+ * Resulting file object for success, or FALSE in the event of an error.
+ *
+ * @see file_unmanaged_move()
+ */
+function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+ $source = (object)$source;
+
+ if ($filepath = file_unmanaged_move($source->filepath, $destination, $replace)) {
+ $file = clone $source;
+ $file->filename = basename($filepath);
+ $file->filepath = $filepath;
+ if ($file = file_save($file)) {
+ // Inform modules that the file has been moved.
+ module_invoke_all('file_move', $file, $source);
+ return $file;
+ }
+ drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $source->filepath)), 'error');
+ }
+ return FALSE;
+}
+
+/**
+ * Move a file to a new location without calling any hooks or making any
+ * changes to the database.
*
* @param $source
* A string specifying the file location of the original file.
@@ -375,10 +552,12 @@ function file_destination($destination,
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
* @return
* The filepath of the moved file, or FALSE in the event of an error.
+ *
+ * @see file_move()
*/
-function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
- $filepath = file_copy($source, $destination, $replace);
- if ($filepath == FALSE || file_delete($source) == FALSE) {
+function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+ $filepath = file_unmanaged_copy($source, $destination, $replace);
+ if ($filepath == FALSE || file_unmanaged_delete($source) == FALSE) {
return FALSE;
}
return $filepath;
@@ -470,17 +649,64 @@ function file_create_filename($basename,
}
/**
- * Delete a file.
+ * Delete a file and its database record.
+ *
+ * If the $force parameter is not TRUE hook_file_references() will be called
+ * to determine if the file is being used by any modules. If the file is being
+ * used is the delete will be canceled.
+ *
+ * @param $path
+ * A file object.
+ * @param $force
+ * Boolean indicating that the file should be deleted even if
+ * hook_file_references() reports that the file is in use.
+ * @return mixed
+ * TRUE for success, Array for reference count block, or FALSE in the event
+ * of an error.
+ *
+ * @see hook_file_references()
+ * @see file_unmanaged_delete()
+ */
+function file_delete($file, $force = FALSE) {
+ $file = (object)$file;
+
+ // 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 any references to the deleted file.
+ module_invoke_all('file_delete', $file);
+
+ // 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_query('DELETE FROM {files} WHERE fid = %d', array($file->fid));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Delete a file without calling any hooks or making any changes to the
+ * database.
+ *
+ * This function should be used when the file to be deleted does not have an
+ * entry recorded in the files table.
*
* @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.
+ *
+ * @see file_delete()
*/
-function file_delete($path) {
+function file_unmanaged_delete($path) {
if (is_dir($path)) {
- watchdog('file', t('%path is a directory and cannot be removed using file_delete().', array('%path' => $path)), WATCHDOG_ERROR);
+ watchdog('file', '%path is a directory and cannot be removed using file_unmanaged_delete().', array('%path' => $path), WATCHDOG_ERROR);
return FALSE;
}
if (is_file($path)) {
@@ -489,7 +715,7 @@ function file_delete($path) {
// Return TRUE for non-existant file, but log that nothing was actually
// deleted, as the current state is the indended result.
if (!file_exists($path)) {
- watchdog('file', t('The file %path was not deleted, because it does not exist.', array('%path' => $path)), WATCHDOG_NOTICE);
+ watchdog('file', 'The file %path was not deleted, because it does not exist.', array('%path' => $path), WATCHDOG_NOTICE);
return TRUE;
}
// Catch all for everything else: sockets, symbolic links, etc.
@@ -642,14 +868,11 @@ function file_save_upload($source, $vali
}
// If we made it this far it's safe to record this file in the database.
- $file->status = FILE_STATUS_TEMPORARY;
- $file->timestamp = REQUEST_TIME;
- drupal_write_record('files', $file);
-
- // Add file to the cache.
- $upload_cache[$source] = $file;
- return $file;
-
+ if ($file = file_save($file)) {
+ // Add file to the cache.
+ $upload_cache[$source] = $file;
+ return $file;
+ }
}
return FALSE;
}
@@ -658,6 +881,9 @@ function file_save_upload($source, $vali
/**
* Check that a file meets the criteria specified by the validators.
*
+ * After executing the validator callbacks specified hook_file_validate() will
+ * also be called to allow other modules to report errors about the file.
+ *
* @param $file
* A Drupal file object.
* @param $validators
@@ -678,7 +904,8 @@ function file_validate(&$file, $validato
$errors = array_merge($errors, call_user_func_array($function, $args));
}
- return $errors;
+ // Let other modules perform validation on the new file.
+ return array_merge($errors, module_invoke_all('file_validate', $file));
}
/**
@@ -833,7 +1060,7 @@ function file_validate_image_resolution(
}
/**
- * Save a string to the specified destination.
+ * Save a string to the specified destination and create a database file entry.
*
* @param $data
* A string containing the contents of the file.
@@ -848,9 +1075,51 @@ function file_validate_image_resolution(
* unique.
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
* @return
- * A string with the path of the resulting file, or FALSE on error.
+ * A file object, or FALSE on error.
+ *
+ * @see file_unmanaged_save_data()
*/
function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+ global $user;
+
+ if ($filepath = file_unmanaged_save_data($data, $destination, $replace)) {
+ // Create a file object.
+ $file = new stdClass();
+ $file->filepath = $filepath;
+ $file->filename = basename($file->filepath);
+ $file->filemime = file_get_mimetype($file->filepath);
+ $file->uid = $user->uid;
+ $file->status = FILE_STATUS_PERMANENT;
+ return file_save($file);
+ }
+ return FALSE;
+}
+
+/**
+ * Save a string to the specified destination without calling any hooks or
+ * 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.
+ *
+ * @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.
+ * @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 with the path of the resulting file, or FALSE on error.
+ *
+ * @see file_save_data()
+ */
+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');
if (file_put_contents($temp_name, $data) === FALSE) {
@@ -859,7 +1128,7 @@ function file_save_data($data, $destinat
}
// Move the file to its final destination.
- return file_move($temp_name, $destination, $replace);
+ return file_unmanaged_move($temp_name, $destination, $replace);
}
/**
@@ -878,9 +1147,14 @@ function file_save_data($data, $destinat
* error.
*/
function file_set_status($file, $status = FILE_STATUS_PERMANENT) {
- if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', array($status, $file->fid))) {
+ $file = (object)$file;
+
+ db_query('UPDATE {files} SET status = %d WHERE fid = %d', array($status, $file->fid));
+ if (db_affected_rows()) {
$file->status = $status;
- return TRUE;
+ // Notify other modules that the file's status has changed.
+ module_invoke_all('file_status', $file);
+ return $file;
}
return FALSE;
}
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.187
diff -u -p -r1.187 locale.inc
--- includes/locale.inc 27 Sep 2008 20:23:30 -0000 1.187
+++ includes/locale.inc 30 Sep 2008 22:06:07 -0000
@@ -2165,7 +2165,7 @@ function _locale_rebuild_js($langcode =
// Save the file.
$dest = $dir . '/' . $language->language . '_' . $data_hash . '.js';
- if (file_save_data($data, $dest)) {
+ if (file_unmanaged_save_data($data, $dest)) {
$language->javascript = $data_hash;
$status = ($status == 'deleted') ? 'updated' : 'created';
}
Index: modules/aggregator/aggregator.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.test,v
retrieving revision 1.8
diff -u -p -r1.8 aggregator.test
--- modules/aggregator/aggregator.test 15 Sep 2008 21:06:06 -0000 1.8
+++ modules/aggregator/aggregator.test 30 Sep 2008 22:06:07 -0000
@@ -151,7 +151,7 @@ class AggregatorTestCase extends DrupalW
EOF;
$path = file_directory_path() . '/valid-opml.xml';
- return file_save_data($opml, $path);
+ return file_unmanaged_save_data($opml, $path);
}
/**
@@ -168,7 +168,7 @@ EOF;
EOF;
$path = file_directory_path() . '/invalid-opml.xml';
- return file_save_data($opml, $path);
+ return file_unmanaged_save_data($opml, $path);
}
/**
@@ -190,7 +190,7 @@ EOF;
EOF;
$path = file_directory_path() . '/empty-opml.xml';
- return file_save_data($opml, $path);
+ return file_unmanaged_save_data($opml, $path);
}
function getRSS091Sample() {
@@ -223,7 +223,7 @@ EOF;
EOT;
$path = file_directory_path() . '/rss091.xml';
- return file_save_data($feed, $path);
+ return file_unmanaged_save_data($feed, $path);
}
}
Index: modules/blogapi/blogapi.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blogapi/blogapi.module,v
retrieving revision 1.126
diff -u -p -r1.126 blogapi.module
--- modules/blogapi/blogapi.module 17 Sep 2008 21:07:47 -0000 1.126
+++ modules/blogapi/blogapi.module 30 Sep 2008 22:06:07 -0000
@@ -385,7 +385,7 @@ function blogapi_metaweblog_new_media_ob
return blogapi_error(t('No file sent.'));
}
- if (!$filepath = file_save_data($data, $name)) {
+ if (!$filepath = file_unmanaged_save_data($data, $name)) {
return blogapi_error(t('Error storing file.'));
}
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.47
diff -u -p -r1.47 color.module
--- modules/color/color.module 20 Sep 2008 20:22:24 -0000 1.47
+++ modules/color/color.module 30 Sep 2008 22:06:07 -0000
@@ -308,7 +308,7 @@ function color_scheme_form_submit($form,
foreach ($info['copy'] as $file) {
$base = basename($file);
$source = $paths['source'] . $file;
- $filepath = file_copy($source, $paths['target'] . $base);
+ $filepath = file_unmanaged_copy($source, $paths['target'] . $base);
$paths['map'][$file] = $base;
$paths['files'][] = $filepath;
}
@@ -435,7 +435,7 @@ function _color_rewrite_stylesheet($them
* Save the rewritten stylesheet to disk.
*/
function _color_save_stylesheet($file, $style, &$paths) {
- $filepath = file_save_data($style, $file, FILE_EXISTS_REPLACE);
+ $filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE);
$paths['files'][] = $filepath;
// Set standard file permissions for webserver-generated files.
Index: modules/simpletest/simpletest.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.install,v
retrieving revision 1.10
diff -u -p -r1.10 simpletest.install
--- modules/simpletest/simpletest.install 20 Sep 2008 03:49:23 -0000 1.10
+++ modules/simpletest/simpletest.install 30 Sep 2008 22:06:07 -0000
@@ -33,7 +33,7 @@ function simpletest_install() {
$original = drupal_get_path('module', 'simpletest') . '/files';
$files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/');
foreach ($files as $file) {
- file_copy($file->filename, $path . '/' . $file->basename);
+ file_unmanaged_copy($file->filename, $path . '/' . $file->basename);
}
$generated = TRUE;
}
Index: modules/simpletest/simpletest.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v
retrieving revision 1.15
diff -u -p -r1.15 simpletest.module
--- modules/simpletest/simpletest.module 20 Sep 2008 20:22:24 -0000 1.15
+++ modules/simpletest/simpletest.module 30 Sep 2008 22:06:07 -0000
@@ -571,7 +571,7 @@ function simpletest_clean_temporary_dire
simpletest_clean_temporary_directory($file_path);
}
else {
- file_delete($file_path);
+ file_unmanaged_delete($file_path);
}
}
}
Index: modules/simpletest/tests/file.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v
retrieving revision 1.5
diff -u -p -r1.5 file.test
--- modules/simpletest/tests/file.test 20 Sep 2008 07:35:53 -0000 1.5
+++ modules/simpletest/tests/file.test 30 Sep 2008 22:06:07 -0000
@@ -76,7 +76,7 @@ class FileTestCase extends DrupalWebTest
$filepath = file_directory_path() . '/' . $this->randomName();
}
- file_put_contents($filepath, '');
+ file_put_contents($filepath, 'asdf');
$this->assertTrue(is_file($filepath), t('The test file exists on the disk.'));
$file = new stdClass();
@@ -85,7 +85,8 @@ class FileTestCase extends DrupalWebTest
$file->filemime = 'text/plain';
$file->uid = 1;
$file->timestamp = REQUEST_TIME;
- $file->filesize = 0;
+ $file->filesize = filesize($file->filepath);
+ $file->status = FILE_STATUS_TEMPORARY;
$this->assertNotIdentical(drupal_write_record('files', $file), FALSE, t('The file was added to the database.'));
return $file;
@@ -93,15 +94,55 @@ class FileTestCase extends DrupalWebTest
}
/**
+ * Base class for file tests that use the file_test module to test uploads and
+ * hooks.
+ */
+class FileHookTestCase extends FileTestCase {
+ /**
+ * Implementation of setUp().
+ */
+ function setUp() {
+ // Install file_test module
+ parent::setUp('file_test');
+ // Clear out any hook calls.
+ file_test_reset();
+ }
+
+ /**
+ * Assert that a hook_file_* hook was called a certain number of times.
+ *
+ * @param $hook
+ * String with the hook name, e.g. 'load', 'save', 'insert', etc.
+ * @param $expected_count
+ * Optional integer count.
+ * @param $message
+ * Optional translated string message.
+ */
+ function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) {
+ $actual_count = count(file_test_get_calls($hook));
+
+ if (is_null($message)) {
+ if ($actual_count == $expected_count) {
+ $message = t('hook_file_@name was called correctly.', array('@name' => $hook));
+ }
+ else {
+ $message = t('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count));
+ }
+ }
+ $this->assertEqual($actual_count, $expected_count, $message);
+ }
+}
+
+/**
* This will run tests against the file validation functions (file_validate_*).
*/
-class FileValidateTest extends DrupalWebTestCase {
+class FileValidatorTest extends DrupalWebTestCase {
/**
* Implementation of getInfo().
*/
function getInfo() {
return array(
- 'name' => t('File validation'),
+ 'name' => t('File validator tests'),
'description' => t('Tests the functions used to validate uploaded files.'),
'group' => t('File'),
);
@@ -123,22 +164,6 @@ class FileValidateTest extends DrupalWeb
}
/**
- * Test the file_validate() function.
- */
- function testFileValidate() {
- // Empty validators.
- $this->assertEqual(file_validate($this->image, array()), array(), t('Validating an empty array works succesfully.'));
-
- // Use the file_test.module's test validator to ensure that passing tests
- // return correctly.
- $passing = array('file_test_validator' => array(array()));
- $this->assertEqual(file_validate($this->image, $passing), array(), t('Validating passes.'));
-
- $failing = array('file_test_validator' => array(array('Failed', 'Badly')));
- $this->assertEqual(file_validate($this->image, $failing), array('Failed', 'Badly'), t('Validating returns errors.'));
- }
-
- /**
* Test the file_validate_extensions() function.
*/
function testFileValidateExtensions() {
@@ -270,35 +295,37 @@ class FileValidateTest extends DrupalWeb
}
}
+
+
/**
- * Tests the file_save_data() function.
+ * Tests the file_unmanaged_save_data() function.
*/
-class FileSaveDataTest extends FileTestCase {
+class FileUnmanagedSaveDataTest extends FileTestCase {
/**
* Implementation of getInfo().
*/
function getInfo() {
return array(
- 'name' => t('File save'),
- 'description' => t('Tests the file save data function.'),
+ 'name' => t('Unmanaged file save data'),
+ 'description' => t('Tests the unmanaged file save data function.'),
'group' => t('File'),
);
}
/**
- * Test the file_save_data() function.
+ * Test the file_unmanaged_save_data() function.
*/
function testFileSaveData() {
$contents = $this->randomName(8);
// No filename.
- $filepath = file_save_data($contents);
+ $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.'));
// Provide a filename.
- $filepath = file_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE);
+ $filepath = file_unmanaged_save_data($contents, '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('asdf.txt', basename($filepath), t('File was named correctly.'));
@@ -310,7 +337,7 @@ class FileSaveDataTest extends FileTestC
/**
* Test the file_save_upload() function.
*/
-class FileSaveUploadTest extends FileTestCase {
+class FileSaveUploadTest extends FileHookTestCase {
/**
* Implementation of getInfo().
*/
@@ -323,19 +350,10 @@ class FileSaveUploadTest extends FileTes
}
/**
- * Implementation of setUp().
- */
- function setUp() {
- // Need to enable the test module for an upload target.
- parent::setUp('file_test');
- }
-
- /**
* Test the file_save_upload() function.
*/
function testFileSaveUpload() {
$max_fid_before = db_result(db_query('SELECT MAX(fid) AS fid FROM {files}'));
-
$upload_user = $this->drupalCreateUser(array('access content'));
$this->drupalLogin($upload_user);
@@ -345,12 +363,13 @@ class FileSaveUploadTest extends FileTes
$this->drupalPost('file-test/upload', $edit, t('Submit'));
$this->assertResponse(200, t('Received a 200 response for posted test file.'));
+ // We can't easily check that the hooks were called easily but since
+ // file_save_upload() calles file_save() we can rely on file_save()'s
+ // test to catch problems invoking the hooks.
+
$max_fid_after = db_result(db_query('SELECT MAX(fid) AS fid FROM {files}'));
$this->assertTrue($max_fid_after > $max_fid_before, t('A new file was created.'));
-
- // FIXME: Replace with file_load() once the hook_file patch gets committed.
- $file = db_fetch_object(db_query('SELECT f.* FROM {files} f WHERE f.fid = %d', array($max_fid_after)));
- $this->assertTrue($file, t('Loaded the file.'));
+ $this->assertTrue(file_load($max_fid_after), t('Loaded the file.'));
}
}
@@ -524,14 +543,14 @@ class FileDirectoryTest extends FileTest
/**
* Deletion related tests.
*/
-class FileDeleteTest extends FileTestCase {
+class FileUnmanagedDeleteTest extends FileTestCase {
/**
* Implementation of getInfo().
*/
function getInfo() {
return array(
- 'name' => t('File delete'),
- 'description' => t('Tests the file delete function.'),
+ 'name' => t('Unmanaged file delete'),
+ 'description' => t('Tests the unmanaged file delete function.'),
'group' => t('File'),
);
}
@@ -544,7 +563,7 @@ class FileDeleteTest extends FileTestCas
$file = $this->createFile();
// Delete a regular file
- $this->assertTrue(file_delete($file->filepath), t('Deleted worked.'));
+ $this->assertTrue(file_unmanaged_delete($file->filepath), t('Deleted worked.'));
$this->assertFalse(file_exists($file->filepath), t('Test file has actually been deleted.'));
}
@@ -553,7 +572,7 @@ class FileDeleteTest extends FileTestCas
*/
function testMissing() {
// Try to delete a non-existing file
- $this->assertTrue(file_delete(file_directory_path() . '/' . $this->randomName()), t('Returns true when deleting a non-existant file.'));
+ $this->assertTrue(file_unmanaged_delete(file_directory_path() . '/' . $this->randomName()), t('Returns true when deleting a non-existant file.'));
}
/**
@@ -561,26 +580,26 @@ class FileDeleteTest extends FileTestCas
*/
function testDirectory() {
// A directory to operate on.
- $this->dirname = $this->createDirectory();
+ $directory = $this->createDirectory();
// Try to delete a directory
- $this->assertFalse(file_delete($this->dirname), t('Could not delete the delete directory.'));
- $this->assertTrue(file_exists($this->dirname), t('Directory has not been deleted.'));
+ $this->assertFalse(file_unmanaged_delete($directory), t('Could not delete the delete directory.'));
+ $this->assertTrue(file_exists($directory), t('Directory has not been deleted.'));
}
}
/**
- * Move related tests
+ * Unmanaged move related tests
*/
-class FileMoveTest extends FileTestCase {
+class FileUnmanagedMoveTest extends FileTestCase {
/**
* Implementation of getInfo().
*/
function getInfo() {
return array(
- 'name' => t('File moving'),
- 'description' => t('Tests the file move function.'),
+ 'name' => t('Unmanaged file moving'),
+ 'description' => t('Tests the unmanaged file move function.'),
'group' => t('File'),
);
}
@@ -594,7 +613,7 @@ class FileMoveTest extends FileTestCase
// Moving to a new name.
$desired_filepath = file_directory_path() . '/' . $this->randomName();
- $new_filepath = file_move($file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
+ $new_filepath = file_unmanaged_move($file->filepath, $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.'));
@@ -605,7 +624,7 @@ class FileMoveTest extends FileTestCase
$desired_filepath = file_directory_path() . '/' . $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_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+ $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
$this->assertTrue($newer_filepath, t('Move was successful.'));
$this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.'));
$this->assertTrue(file_exists($newer_filepath), t('File exists at the new location.'));
@@ -620,7 +639,7 @@ class FileMoveTest extends FileTestCase
*/
function testMissing() {
// Move non-existant file.
- $new_filepath = file_move($this->randomName(), $this->randomName());
+ $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName());
$this->assertFalse($new_filepath, t('Moving a missing file fails.'));
}
@@ -632,12 +651,12 @@ class FileMoveTest extends FileTestCase
$file = $this->createFile();
// Move the file onto itself without renaming shouldn't make changes.
- $new_filepath = file_move($file->filepath, $file->filepath, FILE_EXISTS_REPLACE);
+ $new_filepath = file_unmanaged_move($file->filepath, $file->filepath, 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.'));
// Move the file onto itself with renaming will result in a new filename.
- $new_filepath = file_move($file->filepath, $file->filepath, FILE_EXISTS_RENAME);
+ $new_filepath = file_unmanaged_move($file->filepath, $file->filepath, 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->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.'));
@@ -646,16 +665,16 @@ class FileMoveTest extends FileTestCase
/**
- * Copy related tests.
+ * Unmanaged copy related tests.
*/
-class FileCopyTest extends FileTestCase {
+class FileUnmanagedCopyTest extends FileTestCase {
/**
* Implementation of getInfo().
*/
function getInfo() {
return array(
- 'name' => t('File copying'),
- 'description' => t('Tests the file copy function.'),
+ 'name' => t('Unmanaged file copying'),
+ 'description' => t('Tests the unmanaged file copy function.'),
'group' => t('File'),
);
}
@@ -669,7 +688,7 @@ class FileCopyTest extends FileTestCase
// Copying to a new name.
$desired_filepath = file_directory_path() . '/' . $this->randomName();
- $new_filepath = file_copy($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
+ $new_filepath = file_unmanaged_copy($this->file->filepath, $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($this->file->filepath), t('Original file remains.'));
@@ -679,7 +698,7 @@ class FileCopyTest extends FileTestCase
// Copying with rename.
$desired_filepath = file_directory_path() . '/' . $this->randomName();
$this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.'));
- $newer_filepath = file_copy($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+ $newer_filepath = file_unmanaged_copy($new_filepath, $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($this->file->filepath), t('Original file remains.'));
@@ -696,7 +715,7 @@ class FileCopyTest extends FileTestCase
// Copy non-existant file
$desired_filepath = $this->randomName();
$this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists."));
- $new_filepath = file_copy($desired_filepath, $this->randomName());
+ $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName());
$this->assertFalse($new_filepath, t('Copying a missing file fails.'));
}
@@ -708,24 +727,24 @@ class FileCopyTest extends FileTestCase
$this->file = $this->createFile();
// Copy the file onto itself with renaming works.
- $new_filepath = file_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
+ $new_filepath = file_unmanaged_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
$this->assertTrue($new_filepath, t('Copying onto itself with renaming works.'));
$this->assertNotEqual($new_filepath, $this->file->filepath, t('Copied file has a new name.'));
$this->assertTrue(file_exists($this->file->filepath), t('Original file exists after copying onto itself.'));
$this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.'));
// Copy the file onto itself without renaming fails.
- $new_filepath = file_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_ERROR);
+ $new_filepath = file_unmanaged_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_ERROR);
$this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.'));
$this->assertTrue(file_exists($this->file->filepath), t('File exists after copying onto itself.'));
// Copy the file into same directory without renaming fails.
- $new_filepath = file_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_ERROR);
+ $new_filepath = file_unmanaged_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_ERROR);
$this->assertFalse($new_filepath, t('Copying onto itself fails.'));
$this->assertTrue(file_exists($this->file->filepath), t('File exists after copying onto itself.'));
// Copy the file into same directory with renaming works.
- $new_filepath = file_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_RENAME);
+ $new_filepath = file_unmanaged_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_RENAME);
$this->assertTrue($new_filepath, t('Copying into same directory works.'));
$this->assertNotEqual($new_filepath, $this->file->filepath, t('Copied file has a new name.'));
$this->assertTrue(file_exists($this->file->filepath), t('Original file exists after copying onto itself.'));
@@ -733,3 +752,331 @@ class FileCopyTest extends FileTestCase
}
}
+
+
+/**
+ * Deletion related tests.
+ */
+class FileDeleteTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File delete'),
+ 'description' => t('Tests the file delete function.'),
+ 'group' => t('File'),
+ );
+ }
+
+ function testNormal() {
+ $file = $this->createFile();
+
+ // Check that deletion removes the file and database record.
+ $this->assertTrue(is_file($file->filepath), t("File exists."));
+ $this->assertIdentical(file_delete($file), TRUE, t("Delete worked."));
+ $this->assertFileHookCalled('references');
+ $this->assertFileHookCalled('delete');
+ $this->assertFalse(file_exists($file->filepath), t("Test file has actually been deleted."));
+ $this->assertFalse(file_load(array('filepath' => $file->filepath)), t("File was removed from the database."));
+
+ // TODO: implement hook_file_references() in file_test.module and report a
+ // file in use and test the $force parameter.
+ }
+}
+
+
+/**
+ * Move related tests
+ */
+class FileMoveTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File moving'),
+ 'description' => t('Tests the file move function.'),
+ 'group' => t('File'),
+ );
+ }
+
+ function testNormal() {
+ $file = $this->createFile();
+ $desired_filepath = file_directory_path() . '/' . $this->randomName();
+
+ $file = file_move(clone $file, $desired_filepath, FILE_EXISTS_ERROR);
+ $this->assertTrue($file, t("File moved sucessfully."));
+ $this->assertFileHookCalled('move');
+ $this->assertFileHookCalled('update');
+ $this->assertEqual($file->fid, $file->fid, t("File id $file->fid is unchanged after move."));
+
+ $loaded_file = file_load($file->fid, TRUE);
+ $this->assertTrue($loaded_file, t("File can be loaded from the database."));
+ $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database."));
+ $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database."));
+ }
+}
+
+
+/**
+ * Copy related tests.
+ */
+class FileCopyTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File copying'),
+ 'description' => t('Tests the file copy function.'),
+ 'group' => t('File'),
+ );
+ }
+
+ function testNormal() {
+ $source_file = $this->createFile();
+ $desired_filepath = file_directory_path() . '/' . $this->randomName();
+
+ $file = file_copy(clone $source_file, $desired_filepath, FILE_EXISTS_ERROR);
+ $this->assertTrue($file, t("File copied sucessfully."));
+ $this->assertFileHookCalled('copy');
+ $this->assertFileHookCalled('insert');
+ $this->assertNotEqual($source_file->fid, $file->fid, t("A new file id was created."));
+ $this->assertNotEqual($source_file->filepath, $file->filepath, t("A new filepath was created."));
+ $this->assertEqual($file->filepath, $desired_filepath, t('The copied file object has the desired filepath.'));
+ $this->assertTrue(file_exists($source_file->filepath), t('The original file still exists.'));
+ $this->assertTrue(file_exists($file->filepath), t('The copied file exists.'));
+
+ // Check that the changes were actually saved to the database.
+ $loaded_file = file_load($file->fid, TRUE);
+ $this->assertTrue($loaded_file, t("File can be loaded from the database."));
+ $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database."));
+ $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database."));
+ }
+}
+
+
+/**
+ * Tests the file_load() and file_save() functions.
+ *
+ */
+class FileLoadSaveTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File loading and saving'),
+ 'description' => t('Tests the file loading and saving functions.'),
+ 'group' => t('File'),
+ );
+ }
+
+ /**
+ * This will test saving file data to the database.
+ */
+ function testFileLoadSave() {
+ // 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,
+ );
+ $this->file = (object) $file;
+
+ // Try to load bogus stuff.
+ file_test_reset();
+ $this->assertFalse(file_load(-1), t("Try to load an invalid fid."));
+ $this->assertFileHookCalled('load', 0);
+
+ file_test_reset();
+ $this->assertFalse(file_load(array('filepath' => $this->file->filepath)), t("Try to load a file that doesn't exist in the database."));
+ $this->assertFileHookCalled('load', 0);
+
+ // Save it, inserting a new record.
+ file_test_reset();
+ $saved_file = file_save($this->file);
+ $this->assertFileHookCalled('insert');
+ $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');
+ $this->assertEqual(db_result(db_query('SELECT COUNT(*) FROM {files} f WHERE f.fid = %d', array($saved_file->fid))), 1, t("Record exists in the database."));
+ $this->assertEqual($saved_file->filesize, filesize($this->file->filepath), t("File size was set correctly."), 'File');
+ $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File');
+
+ // Load by path.
+ file_test_reset();
+ $by_path_file = file_load(array('filepath' => $this->file->filepath));
+ $this->assertFileHookCalled('load');
+ $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+ $this->assertEqual($by_path_file->fid, $this->file->fid, t("Loading by filepath got the correct fid."), 'File');
+
+ // Load by fid.
+ file_test_reset();
+ $by_fid_file = file_load($this->file->fid);
+ $this->assertFileHookCalled('load', 0);
+ $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, $this->file->filepath, t("Loading by fid got the correct filepath."), 'File');
+
+ // Load again by fid but use the reset param to reload.
+ file_test_reset();
+ $by_fid_file = file_load($this->file->fid, TRUE);
+ $this->assertFileHookCalled('load', 1);
+ $this->assertEqual($by_fid_file->filepath, $this->file->filepath, t("Loading by fid got the correct filepath."), 'File');
+
+ // Resave the file, updating the existing record.
+ file_test_reset();
+ $resaved_file = file_save($saved_file);
+ $this->assertFileHookCalled('update');
+ $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');
+ $count = db_result(db_query('SELECT COUNT(*) FROM {files} f WHERE f.fid = %d', array($saved_file->fid)));
+ $this->assertEqual($count, 1, t("Record still exists in the database."), 'File');
+ }
+}
+
+
+/**
+ * Tests the file_validate() function..
+ */
+class FileValidateTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File validate'),
+ 'description' => t('Tests the file_validate() function.'),
+ 'group' => t('File'),
+ );
+ }
+
+ /**
+ * Test that the validators passed into are checked.
+ */
+ function testCallerValidation() {
+ $file = $this->createFile();
+
+ // Empty validators.
+ $this->assertEqual(file_validate($file, array()), array(), t('Validating an empty array works succesfully.'));
+ $this->assertFileHookCalled('validate', 1);
+
+ // Use the file_test.module's test validator to ensure that passing tests
+ // return correctly.
+ file_test_reset();
+ $GLOBALS['file_test_hook_return']['validate'] = array();
+ $passing = array('file_test_validator' => array(array()));
+ $this->assertEqual(file_validate($file, $passing), array(), t('Validating passes.'));
+ $this->assertFileHookCalled('validate', 1);
+
+ // Now test for failures in validators passed in and by hook_validate.
+ file_test_reset();
+ $GLOBALS['file_test_hook_return']['validate'] = array('Epic fail');
+ $failing = array('file_test_validator' => array(array('Failed', 'Badly')));
+ $this->assertEqual(file_validate($file, $failing), array('Failed', 'Badly', 'Epic fail'), t('Validating returns errors.'));
+ $this->assertFileHookCalled('validate', 1);
+ }
+}
+
+/**
+ * Tests the file_set_status() function.
+ */
+class FileSetStatusTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File set status'),
+ 'description' => t('Tests the file set status functions.'),
+ 'group' => t('File'),
+ );
+ }
+
+ function testFileSetStatus() {
+ // Create a new file object.
+ $file = array(
+ 'uid' => 1,
+ 'filename' => 'druplicon.png',
+ 'filepath' => 'misc/druplicon.png',
+ 'filemime' => 'image/png',
+ 'timestamp' => 1,
+ 'status' => FILE_STATUS_TEMPORARY,
+ );
+ $file = file_save($file);
+ // Just a couple of sanity checks before we start the real testing.
+ $this->assertTrue($file->fid, t("Make sure the file saved correctly."));
+ $this->assertEqual($file->status, FILE_STATUS_TEMPORARY, t("Status was set during save."));
+
+ // Change the status and make sure everything works
+ file_test_reset();
+ $returned = file_set_status($file);
+ $this->assertEqual(count(file_test_get_calls('status')), 1, t('hook_file_status was called.'));
+ $this->assertNotIdentical($returned, FALSE, t("file_set_status() worked and returned a non-false value."));
+ $this->assertEqual($returned->fid, $file->fid, t("Returned the correct file."));
+ $this->assertEqual($returned->status, FILE_STATUS_PERMANENT, t("File's status was changed."));
+
+ // Try it resetting it to the same value.
+ file_test_reset();
+ $returned = file_set_status($file, FILE_STATUS_PERMANENT);
+ $this->assertEqual(count(file_test_get_calls('status')), 0, t('hook_file_status was not called.'));
+ $this->assertIdentical($returned, FALSE, t("file_set_status() failed since there was no change."));
+ $test_file = file_load($file->fid);
+ $this->assertEqual($test_file->fid, $file->fid , t("Loaded the correct file."));
+ $this->assertEqual($test_file->status, FILE_STATUS_PERMANENT, t("File's status is correct."));
+
+ // Now switch it.
+ file_test_reset();
+ $returned = file_set_status($file, FILE_STATUS_TEMPORARY);
+ $this->assertEqual(count(file_test_get_calls('status')), 1, t('hook_file_status was called.'));
+ $this->assertNotIdentical($returned, FALSE, t("file_set_status() worked and returned a non-false value."));
+ $this->assertEqual($returned->fid, $file->fid , t("Returned the correct file."));
+ $this->assertEqual($returned->status, FILE_STATUS_TEMPORARY, t("File's status is correct."));
+ }
+}
+
+/**
+ * Tests the file_save_data() function.
+ */
+class FileSaveDataTest extends FileHookTestCase {
+ /**
+ * Implementation of getInfo().
+ */
+ function getInfo() {
+ return array(
+ 'name' => t('File save data'),
+ 'description' => t('Tests the file save data function.'),
+ 'group' => t('File'),
+ );
+ }
+
+ function testFileSaveData() {
+ $contents = $this->randomName(8);
+
+ // No filename.
+ $file = file_save_data($contents);
+ $this->assertTrue($file, t("Unnamed file saved correctly."));
+ $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory."));
+ $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct."));
+ $this->assertEqual($file->filemime, 'application/octet-stream', t("A MIME type was set."));
+ $this->assertEqual($file->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+ // Try loading the file.
+ $loaded_file = file_load($file->fid);
+ $this->assertTrue($loaded_file, t("File loaded from database."));
+
+ // Provide a filename.
+ $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE);
+ $this->assertTrue($file, t("Unnamed file saved correctly."));
+ $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory."));
+ $this->assertEqual('asdf.txt', basename($file->filepath), t("File was named correctly."));
+ $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct."));
+
+ // Check the overwrite error.
+ $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_ERROR);
+ $this->assertFalse($file, t("Overwriting a file fails when FILE_EXISTS_ERROR is specified."));
+ }
+}
\ No newline at end of file
Index: modules/simpletest/tests/file_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file_test.module,v
retrieving revision 1.1
diff -u -p -r1.1 file_test.module
--- modules/simpletest/tests/file_test.module 20 Sep 2008 07:35:53 -0000 1.1
+++ modules/simpletest/tests/file_test.module 30 Sep 2008 22:06:07 -0000
@@ -50,3 +50,117 @@ function _file_test_form_submit(&$form,
drupal_set_message(t('Epic upload FAIL!'), 'error');
}
}
+
+
+/**
+ * Reset/initialize the history of calls to the file_* hooks.
+ */
+function file_test_reset() {
+ // Keep track of calls to these hooks
+ $GLOBALS['file_test_results'] = array(
+ 'load' => array(),
+ 'validate' => array(),
+ 'download' => array(),
+ 'references' => array(),
+ 'status' => array(),
+ 'insert' => array(),
+ 'update' => array(),
+ 'copy' => array(),
+ 'move' => array(),
+ 'delete' => array(),
+ );
+
+ // These hooks will return these values.
+ $GLOBALS['file_test_hook_return'] = array(
+ 'validate' => NULL,
+ 'download' => NULL,
+ 'references' => NULL,
+ );
+}
+
+/**
+ * Get the values passed to a the hook calls for a given operation.
+ *
+ * @param $op One of the hook_file_* operations.
+ * @returns Array of the parameters passed to each call.
+ */
+function file_test_get_calls($op) {
+ return $GLOBALS['file_test_results'][$op];
+}
+
+/**
+ * Implementation of hook_file_load().
+ */
+function file_test_file_load(&$file) {
+ $GLOBALS['file_test_results']['load'][] = func_get_args();
+ // Assign a value on the object so that we can test that the $file is passed
+ // by reference.
+ $file->file_test['loaded'] = TRUE;
+}
+
+/**
+ * Implementation of hook_file_validate().
+ */
+function file_test_file_validate(&$file) {
+ $GLOBALS['file_test_results']['validate'][] = func_get_args();
+ return $GLOBALS['file_test_hook_return']['validate'];
+}
+
+/**
+ * Implementation of hook_file_status().
+ */
+function file_test_file_status(&$file) {
+ $GLOBALS['file_test_results']['status'][] = func_get_args();
+}
+
+/**
+ * Implementation of hook_file_download().
+ */
+function file_test_file_download(&$file) {
+ $GLOBALS['file_test_results']['download'][] = func_get_args();
+ return $GLOBALS['file_test_hook_return']['download'];
+}
+
+/**
+ * Implementation of hook_file_references().
+ */
+function file_test_file_references(&$file) {
+ $GLOBALS['file_test_results']['references'][] = func_get_args();
+ return $GLOBALS['file_test_hook_return']['references'];
+}
+
+/**
+ * Implementation of hook_file_insert().
+ */
+function file_test_file_insert(&$file) {
+ $GLOBALS['file_test_results']['insert'][] = func_get_args();
+}
+
+/**
+ * Implementation of hook_file_update().
+ */
+function file_test_file_update(&$file) {
+ $GLOBALS['file_test_results']['update'][] = func_get_args();
+}
+
+/**
+ * Implemenation of hook_file_copy().
+ */
+function file_test_file_copy(&$file, &$source) {
+ $GLOBALS['file_test_results']['copy'][] = func_get_args();
+}
+
+/**
+ * Implemenation of hook_file_move().
+ */
+function file_test_file_move(&$file, &$source) {
+ $GLOBALS['file_test_results']['move'][] = func_get_args();
+}
+
+/**
+ * Implementation of hook_file_delete().
+ */
+function file_test_file_delete(&$file) {
+ $GLOBALS['file_test_results']['delete'][] = func_get_args();
+}
+
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.94
diff -u -p -r1.94 system.admin.inc
--- modules/system/system.admin.inc 27 Sep 2008 19:03:30 -0000 1.94
+++ modules/system/system.admin.inc 30 Sep 2008 22:06:07 -0000
@@ -338,7 +338,7 @@ function system_theme_settings(&$form_st
// 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_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
+ if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
$_POST['default_logo'] = 0;
$_POST['logo_path'] = $filepath;
$_POST['toggle_logo'] = 1;
@@ -353,7 +353,7 @@ function system_theme_settings(&$form_st
// 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_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
+ if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
$_POST['default_favicon'] = 0;
$_POST['favicon_path'] = $filepath;
$_POST['toggle_favicon'] = 1;
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.624
diff -u -p -r1.624 system.module
--- modules/system/system.module 27 Sep 2008 19:47:43 -0000 1.624
+++ modules/system/system.module 30 Sep 2008 22:06:07 -0000
@@ -1407,7 +1407,7 @@ function system_cron() {
if (file_exists($file->filepath)) {
// If files that exist cannot be deleted, continue so the database remains
// consistent.
- if (!file_delete($file->filepath)) {
+ if (!file_delete($file)) {
watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath), WATCHDOG_ERROR);
continue;
}
Index: modules/upload/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v
retrieving revision 1.207
diff -u -p -r1.207 upload.module
--- modules/upload/upload.module 27 Sep 2008 20:37:01 -0000 1.207
+++ modules/upload/upload.module 30 Sep 2008 22:06:07 -0000
@@ -263,6 +263,38 @@ function upload_form_alter(&$form, $form
}
/**
+ * Implementation of hook_file_load().
+ */
+function upload_file_load(&$file, $source = NULL) {
+ // 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;
+ }
+}
+
+/**
+ * Implementation of hook_file_references().
+ */
+function upload_file_references(&$file, $source = NULL) {
+ // 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);
+ }
+}
+
+/**
+ * Implementation of hook_file_delete().
+ */
+function upload_file_delete(&$file, $source = NULL) {
+ // Delete all information associated with the file.
+ db_query('DELETE FROM {upload} WHERE fid = %d', $file->fid);
+}
+
+
+/**
* Implementation of hook_nodeapi().
*/
function upload_nodeapi(&$node, $op, $teaser) {
@@ -406,13 +438,15 @@ function upload_save(&$node) {
// 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;
}
@@ -433,38 +467,23 @@ function upload_save(&$node) {
}
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;
+ db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
+ if (!is_array($node->files)) {
+ return;
}
-
- 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);
+ foreach($node->files as $file) {
+ file_delete($file);
}
-
- // Delete all file revision information associated with the node
- db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
}
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) {
@@ -478,11 +497,11 @@ function _upload_form($node) {
if (!empty($node->files) && is_array($node->files)) {
$form['files']['#theme'] = 'upload_form_current';
$form['files']['#tree'] = TRUE;
- foreach ($node->files as $key => $file) {
+ foreach ($node->files as $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 );
+ $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('#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);
@@ -497,12 +516,26 @@ function _upload_form($node) {
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',
@@ -564,9 +597,9 @@ function upload_load($node) {
$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 u.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);
}
}
Index: modules/upload/upload.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.test,v
retrieving revision 1.4
diff -u -p -r1.4 upload.test
--- modules/upload/upload.test 15 Sep 2008 09:28:50 -0000 1.4
+++ modules/upload/upload.test 30 Sep 2008 22:06:07 -0000
@@ -100,16 +100,25 @@ class UploadTestCase extends DrupalWebTe
$this->drupalLogin($web_user);
$node = $this->drupalCreateNode();
- $text_files = $this->drupalGetTestFiles('text');
- $html_files = $this->drupalGetTestFiles('html');
- $files = array(current($text_files)->filename, current($html_files)->filename);
-
- // Attempt to upload .txt file when .test is only extension allowed.
- $this->uploadFile($node, $files[0], FALSE);
- $this->assertRaw(t('The specified file %name could not be uploaded. Only files with the following extensions are allowed: %files-allowed.', array('%name' => basename($files[0]), '%files-allowed' => $settings['upload_extensions'])), 'File '. $files[0] . ' was not allowed to be uploaded');
- // Attempt to upload .test file when .test is only extension allowed.
- $this->uploadFile($node, $files[1]);
+ // Attempt to upload .txt file when .html is only extension allowed.
+ $text_files = array_values($this->drupalGetTestFiles('text'));
+ // Select a file that's less than the 1MB upload limit so we only test one
+ // limit at a time.
+ $text_file = $text_files[2]->filename;
+ $this->uploadFile($node, $text_file, 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' => basename($text_file))), t('File %filename was not allowed to be uploaded', array('%filename' => $text_file)));
+ $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]->filename;
+ $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'));
}
/**
@@ -143,7 +152,10 @@ class UploadTestCase extends DrupalWebTe
$filename = basename($file);
$filesize = format_size($info['size']);
$maxsize = format_size(parse_size(($settings['upload_uploadsize'] * 1024) . 'KB')); // Won't parse decimals.
- $this->assertRaw(t('The specified file %name could not be uploaded. The file is %filesize exceeding the maximum file size of %maxsize.', array('%name' => $filename, '%filesize' => $filesize, '%maxsize' => $maxsize)), t('File upload was blocked since it was larger than maxsize.'));
+ // 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' => $filename)), t('File upload was blocked'));
+ $this->assertRaw(t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => $filesize, '%maxsize' => $maxsize)), t('File size cited as problem with upload'));
}
function setUploadSettings($settings, $rid = NULL) {
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.924
diff -u -p -r1.924 user.module
--- modules/user/user.module 21 Sep 2008 15:08:39 -0000 1.924
+++ modules/user/user.module 30 Sep 2008 22:06:07 -0000
@@ -407,7 +407,7 @@ function user_validate_picture(&$form, &
if ($file = file_save_upload('picture_upload', $validators)) {
// Remove the old picture.
if (isset($form_state['values']['_account']->picture) && file_exists($form_state['values']['_account']->picture)) {
- file_delete($form_state['values']['_account']->picture);
+ file_unmanaged_delete($form_state['values']['_account']->picture);
}
// The image was saved using file_save_upload() and was added to the
@@ -415,7 +415,7 @@ function user_validate_picture(&$form, &
// collector delete the original upload.
$info = image_get_info($file->filepath);
$destination = file_create_path(variable_get('user_picture_path', 'pictures') . '/picture-' . $form['#uid'] . '.' . $info['extension']);
- if ($filepath = file_copy($file->filepath, $destination, FILE_EXISTS_REPLACE)) {
+ if ($filepath = file_unmanaged_copy($file->filepath, $destination, FILE_EXISTS_REPLACE)) {
$form_state['values']['picture'] = $filepath;
}
else {
@@ -1541,7 +1541,7 @@ function _user_edit_submit($uid, &$edit)
// 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);
+ file_unmanaged_delete($user->picture);
}
$edit['picture'] = '';
}