? .DS_Store ? .cache ? .cvsignore ? .git ? .project ? .settings ? modules/.DS_Store ? 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.793 diff -u -p -r1.793 common.inc --- includes/common.inc 14 Sep 2008 21:14:21 -0000 1.793 +++ includes/common.inc 15 Sep 2008 11:17:53 -0000 @@ -1850,7 +1850,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; } @@ -1951,7 +1951,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); } /** @@ -2314,7 +2314,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; @@ -2324,7 +2324,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.131 diff -u -p -r1.131 file.inc --- includes/file.inc 15 Sep 2008 09:28:49 -0000 1.131 +++ includes/file.inc 15 Sep 2008 11:17:53 -0000 @@ -254,7 +254,134 @@ 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->timestamp = $_SERVER['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) { + 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 +404,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 +489,50 @@ 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) { + 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 +547,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 +644,62 @@ 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 cancled. + * + * @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) { + // 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', t('%path is a directory and cannot be removed using file_unmanaged_delete().', array('%path' => $path)), WATCHDOG_ERROR); return FALSE; } if (is_file($path)) { @@ -641,15 +860,11 @@ function file_save_upload($source, $vali return FALSE; } - // If we made it this far it's safe to record this file in the database. - $file->status = FILE_STATUS_TEMPORARY; - $file->timestamp = $_SERVER['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 +873,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 +896,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)); } /** @@ -834,7 +1053,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. @@ -849,9 +1068,53 @@ 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. + * An 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) { + global $user; + // Write the data to a temporary file. $temp_name = tempnam(file_directory_temp(), 'file'); if (file_put_contents($temp_name, $data) === FALSE) { @@ -860,7 +1123,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); } /** @@ -881,7 +1144,9 @@ function file_save_data($data, $destinat 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->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.181 diff -u -p -r1.181 locale.inc --- includes/locale.inc 9 Sep 2008 13:43:40 -0000 1.181 +++ includes/locale.inc 15 Sep 2008 11:17:54 -0000 @@ -2159,7 +2159,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.7 diff -u -p -r1.7 aggregator.test --- modules/aggregator/aggregator.test 3 Sep 2008 19:25:08 -0000 1.7 +++ modules/aggregator/aggregator.test 15 Sep 2008 11:17:54 -0000 @@ -151,8 +151,7 @@ class AggregatorTestCase extends DrupalW EOF; $path = file_directory_path() . '/valid-opml.xml'; - file_save_data($opml, $path); - return $path; + return file_unmanaged_save_data($opml, $path); } /** @@ -169,8 +168,7 @@ EOF; EOF; $path = file_directory_path() . '/invalid-opml.xml'; - file_save_data($opml, $path); - return $path; + return file_unmanaged_save_data($opml, $path); } /** @@ -192,8 +190,7 @@ EOF; EOF; $path = file_directory_path() . '/empty-opml.xml'; - file_save_data($opml, $path); - return $path; + return file_unmanaged_save_data($opml, $path); } function getRSS091Sample() { @@ -226,8 +223,7 @@ EOF; EOT; $path = file_directory_path() . '/rss091.xml'; - file_save_data($feed, $path); - return $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.124 diff -u -p -r1.124 blogapi.module --- modules/blogapi/blogapi.module 15 Sep 2008 09:28:49 -0000 1.124 +++ modules/blogapi/blogapi.module 15 Sep 2008 11:17:54 -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.44 diff -u -p -r1.44 color.module --- modules/color/color.module 15 Sep 2008 09:28:49 -0000 1.44 +++ modules/color/color.module 15 Sep 2008 11:17:54 -0000 @@ -308,9 +308,9 @@ function color_scheme_form_submit($form, foreach ($info['copy'] as $file) { $base = basename($file); $source = $paths['source'] . $file; - file_copy($source, $paths['target'] . $base); + $filepath = file_unmanaged_copy($source, $paths['target'] . $base); $paths['map'][$file] = $base; - $paths['files'][] = $paths['target'] . $base; + $paths['files'][] = $filepath; } // Render new images, if image has been provided. @@ -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.8 diff -u -p -r1.8 simpletest.install --- modules/simpletest/simpletest.install 10 Sep 2008 04:13:01 -0000 1.8 +++ modules/simpletest/simpletest.install 15 Sep 2008 11:17:54 -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.12 diff -u -p -r1.12 simpletest.module --- modules/simpletest/simpletest.module 10 Sep 2008 04:13:01 -0000 1.12 +++ modules/simpletest/simpletest.module 15 Sep 2008 11:17:54 -0000 @@ -558,7 +558,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.1 diff -u -p -r1.1 file.test --- modules/simpletest/tests/file.test 15 Sep 2008 09:30:03 -0000 1.1 +++ modules/simpletest/tests/file.test 15 Sep 2008 11:17:54 -0000 @@ -33,7 +33,7 @@ class FileValidateTest extends DrupalWeb * Implementation of setUp(). */ function setUp() { - parent::setUp(); + parent::setUp('file_test'); $this->image = new stdClass(); $this->image->filepath = 'misc/druplicon.png'; @@ -210,22 +210,143 @@ class FileLoadSaveTest extends DrupalWeb ); } - function testFileSaveData() { + /** + * Implementation of setUp(). + */ + function setUp() { + parent::setUp('file_test'); + } + + /** + * 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_calls(); + $this->assertFalse(file_load(-1), t("Try to load an invalid fid")); + $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called.')); + file_test_reset_calls(); + $this->assertFalse(file_load(array('filepath' => $this->file->filepath)), t("Try to load a file that doesn't exist in the database.")); + $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called.')); + + // Save it, inserting a new record + file_test_reset_calls(); + $saved_file = file_save($this->file); + $this->assertEqual(count(file_test_get_calls('insert')), 1, t('hook_file_insert was called.')); + $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_calls(); + $by_path_file = file_load(array('filepath' => $this->file->filepath)); + $this->assertEqual(count(file_test_get_calls('load')), 1, t('hook_file_load was called.')); + $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_calls(); + $by_fid_file = file_load($this->file->fid); + $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called because the loaded file was cached.')); + $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 to make sure the caching doesn't screw anythign up + file_test_reset_calls(); + $by_fid_file = file_load($this->file->fid); + $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called since it loaded from cache.')); + $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_calls(); + $resaved_file = file_save($saved_file); + $this->assertEqual(count(file_test_get_calls('update')), 1, t('hook_file_update was called.')); + $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'); + + // TODO: test the reset parameter + } + + function testFileUnmanagedSaveData() { $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.")); $this->assertEqual($contents, file_get_contents(realpath($filepath)), t("Contents of the file are correct.")); } + + 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.")); + + // Clear out the error messages. +# drupal_get_messages(); + } + + 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); + + $image = current($this->drupalGetTestFiles('image')); + $this->assertTrue(is_file($image->filename), t("the file we're going to upload exists.")); + $edit = array('files[file_test_upload]' => realpath($image->filename)); + $this->drupalPost('file-test/upload', $edit, t('Submit')); + $this->assertResponse(200, t("Received a 200 response for posted test file.")); + $this->assertText(t('hook_file_validate() was called.'), t('hook_file_validate() was called.')); + $this->assertText(t('hook_file_insert() was called.'), t('hook_file_insert() was called.')); + + $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.")); + } } /** @@ -247,7 +368,7 @@ class FileDirectoryTest extends DrupalWe * Implementation of setUp(). */ function setUp() { - parent::setUp(); + parent::setUp('file_test'); // A directory to operate on. $this->directory = file_directory_path() . '/' . $this->randomName(); @@ -415,7 +536,7 @@ class FileCopyDeleteMoveTest extends Dru */ function setUp() { // Install file_test module - parent::setUp(); + parent::setUp('file_test'); // A directory to operate on. $this->dirname = file_directory_path() . '/' . $this->randomName(); @@ -428,36 +549,71 @@ class FileCopyDeleteMoveTest extends Dru touch($f->filepath); $f->filemime = 'text/plain'; $f->uid = 1; - $f->timestamp = $_SERVER['REQUEST_TIME']; - $f->filesize = 0; - drupal_write_record('files', $f); - $this->file = $f; + $this->file = file_save($f); } + function testFileDelete() { + file_test_reset_calls(); + + // Check that deletion removes the file and database record. + $this->assertTrue(is_file($this->file->filepath), t("File exists.")); + $this->assertTrue(file_delete($this->file), t("Delete worked.")); + $this->assertEqual(count(file_test_get_calls('references')), 1, t('hook_file_references was called.')); + $this->assertEqual(count(file_test_get_calls('delete')), 1, t('hook_file_delete was called.')); + $this->assertFalse(file_exists($this->file->filepath), t("Test file has actually been deleted.")); + $this->assertFalse(file_load(array('filepath' => $this->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. + } + + + function testFileUnmanagedDelete() { // Delete a regular file $this->assertTrue(is_file($this->file->filepath), t("File exists.")); - $this->assertTrue(file_delete($this->file->filepath), t("Deleted worked.")); + $this->assertTrue(file_unmanaged_delete($this->file->filepath), t("Deleted worked.")); $this->assertFalse(file_exists($this->file->filepath), t("Test file has actually been deleted.")); } - function testFileDelete_Missing() { + function testFileUnmanagedDelete_Missing() { // 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.")); } - function testFileDelete_Directory() { + function testFileUnmanagedDelete_Directory() { // Try to delete a directory $this->assertTrue(is_dir($this->dirname), t("Directory exists.")); - $this->assertFalse(file_delete($this->dirname), t("Could not delete the delete directory.")); + $this->assertFalse(file_unmanaged_delete($this->dirname), t("Could not delete the delete directory.")); $this->assertTrue(file_exists($this->dirname), t("Directory has not been deleted.")); } + function testFileMove() { + file_test_reset_calls(); + $desired_filepath = file_directory_path() . '/' . $this->randomName(); + + $file = file_move(clone $this->file, $desired_filepath, FILE_EXISTS_ERROR); + $this->assertTrue($file, t("File moved sucessfully.")); + $this->assertEqual(count(file_test_get_calls('move')), 1, t('hook_file_move was called.')); + $this->assertEqual(count(file_test_get_calls('update')), 1, t('hook_file_update was called.')); + $this->assertEqual($file->fid, $this->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.")); + + // Clean up the file so that the directory can be removed + @unlink($loaded_file->filepath); + } + + + function testFileUnmanagedMove() { // Moving to a new name. $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving.")); $desired_filepath = file_directory_path() . '/' . $this->randomName(); - $new_filepath = file_move($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR); + $new_filepath = file_unmanaged_move($this->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.")); @@ -467,7 +623,7 @@ class FileCopyDeleteMoveTest extends Dru $desired_filepath = file_directory_path() . '/' . $this->randomName(); $this->assertTrue(file_exists($new_filepath), t("File exists before moving.")); $this->assertTrue(touch($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.")); @@ -476,24 +632,24 @@ class FileCopyDeleteMoveTest extends Dru // TODO: test moving to a directory (rather than full directory/file path) } - function testFileMove_Missing() { + function testFileUnmanagedMove_Missing() { // 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")); drupal_get_messages(); } - function testFileMove_OverwriteSelf() { + function testFileUnmanagedMove_OverwriteSelf() { // Move the file onto itself without renaming shouldn't make changes. $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving.")); - $new_filepath = file_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_REPLACE); + $new_filepath = file_unmanaged_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_REPLACE); $this->assertFalse($new_filepath, t("Moving onto itself without renaming fails.")); $this->assertTrue(file_exists($this->file->filepath), t("File exists after moving onto itself.")); // Move the file onto itself with renaming will result in a new filename. $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving.")); - $new_filepath = file_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME); + $new_filepath = file_unmanaged_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME); $this->assertTrue($new_filepath, t("Moving onto itself with renaming works.")); $this->assertFalse(file_exists($this->file->filepath), t("Original file has been removed.")); $this->assertTrue(file_exists($new_filepath), t("File exists after moving onto itself.")); @@ -501,10 +657,36 @@ class FileCopyDeleteMoveTest extends Dru drupal_get_messages(); } + function testFileCopy() { + file_test_reset_calls(); + $desired_filepath = file_directory_path() . '/' . $this->randomName(); + + $file = file_copy(clone $this->file, $desired_filepath, FILE_EXISTS_ERROR); + $this->assertTrue($file, t("File copied sucessfully.")); + $this->assertEqual(count(file_test_get_calls('copy')), 1, t('hook_file_copy was called.')); + $this->assertEqual(count(file_test_get_calls('insert')), 1, t('hook_file_insert was called.')); + $this->assertNotEqual($file->fid, $this->file->fid, t("A new file id was created.")); + $this->assertNotEqual($file->filepath, $this->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($this->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.")); + + // Clean up the file so that the directory can be removed + @unlink($loaded_file->filepath); + } + + + function testFileUnmanagedCopy() { // 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.")); @@ -513,7 +695,7 @@ class FileCopyDeleteMoveTest extends Dru // Copying with rename. $desired_filepath = file_directory_path() . '/' . $this->randomName(); $this->assertTrue(touch($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.")); @@ -522,20 +704,20 @@ class FileCopyDeleteMoveTest extends Dru // TODO: test copying to a directory (rather than full directory/file path) } - function testFileCopy_NonExistant() { + function testFileUnmanagedCopy_NonExistant() { // 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")); drupal_get_messages(); } - function testFileCopy_OverwriteSelf() { + function testFileUnmanagedCopy_OverwriteSelf() { // Copy the file onto itself with renaming works. $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying.")); - $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.")); @@ -543,19 +725,19 @@ class FileCopyDeleteMoveTest extends Dru // Copy the file onto itself without renaming fails. $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying.")); - $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. $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying.")); - $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. $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying.")); - $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.")); Index: modules/simpletest/tests/file_test.info =================================================================== RCS file: modules/simpletest/tests/file_test.info diff -N modules/simpletest/tests/file_test.info --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/file_test.info 15 Sep 2008 11:17:54 -0000 @@ -0,0 +1,8 @@ +; $Id$ +name = "File test" +description = "Support module for file handling tests." +package = Testing +version = VERSION +core = 7.x +files[] = file_test.module +hidden = TRUE Index: modules/simpletest/tests/file_test.module =================================================================== RCS file: modules/simpletest/tests/file_test.module diff -N modules/simpletest/tests/file_test.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/file_test.module 15 Sep 2008 11:17:54 -0000 @@ -0,0 +1,185 @@ + t('Upload test'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('_file_test_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Reset/initialize the history of calls to the file_* hooks. + */ +function file_test_reset_calls() { + global $file_test_results; + + $file_test_results = array( + 'load' => array(), + 'validate' => array(), + 'download' => array(), + 'references' => array(), + 'status' => array(), + 'insert' => array(), + 'update' => array(), + 'copy' => array(), + 'move' => array(), + 'delete' => array(), + ); +} + +/** + * Log the invocation of a file hook. + * + * @param $op One of the hook_file_* operations. + * @param $args Arguments passed to the hook. + */ +function _file_test_add_call($op, $args) { + global $file_test_results; + $file_test_results[$op][] = $args; +} + +/** + * 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) { + global $file_test_results; + return $file_test_results[$op]; +} + +/** + * Implementation of hook_file_load(). + */ +function file_test_file_load(&$file) { + // Assign a value on the object so that we can test that the $file is passed + // by reference. + $file->file_test['loaded'] = TRUE; + $args = func_get_args(); + _file_test_add_call('load', $args); +} + +/** + * Implementation of hook_file_validate(). + */ +function file_test_file_validate(&$file) { + $args = func_get_args(); + _file_test_add_call('validate', $args); +} + +/** + * Implementation of hook_file_status(). + */ +function file_test_file_status(&$file) { + $args = func_get_args(); + _file_test_add_call('status', $args); +} + +/** + * Implementation of hook_file_download(). + */ +function file_test_file_download(&$file) { + $args = func_get_args(); + _file_test_add_call('download', $args); +} + +/** + * Implementation of hook_file_references(). + */ +function file_test_file_references(&$file) { + $args = func_get_args(); + _file_test_add_call('references', $args); +} + +/** + * Implementation of hook_file_insert(). + */ +function file_test_file_insert(&$file) { + $args = func_get_args(); + _file_test_add_call('insert', $args); +} + +/** + * Implementation of hook_file_update(). + */ +function file_test_file_update(&$file) { + $args = func_get_args(); + _file_test_add_call('update', $args); +} + +/** + * Implemenation of hook_file_copy(). + */ +function file_test_file_copy(&$file, &$source) { + $args = func_get_args(); + _file_test_add_call('copy', $args); +} + +/** + * Implemenation of hook_file_move(). + */ +function file_test_file_move(&$file, &$source) { + $args = func_get_args(); + _file_test_add_call('move', $args); +} + +/** + * Implementation of hook_file_delete(). + */ +function file_test_file_delete(&$file) { + $args = func_get_args(); + _file_test_add_call('delete', $args); +} + + +/** + * Form to test file uploads. + */ +function _file_test_form(&$form_state) { + $form['#validate'][] = '_file_test_validate_upload'; + $form['file_test_upload'] = array( + '#type' => 'file', + '#title' => t('Upload image file'), + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Submit'), + ); + return $form; +} + +/** + * Process the upload. + */ +function _file_test_validate_upload(&$form, &$form_state) { + // Validate the uploaded picture. + $validators = array( + 'file_validate_is_image' => array(), + ); + + if ($file = file_save_upload('file_test_upload', $validators)) { + $form_state['values']['file_test_upload'] = $file; + if (count(file_test_get_calls('validate'))) { + drupal_set_message(t('hook_file_validate() was called.')); + } + if (count(file_test_get_calls('insert'))) { + drupal_set_message(t('hook_file_insert() was called.')); + } + } +} Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.88 diff -u -p -r1.88 system.admin.inc --- modules/system/system.admin.inc 15 Sep 2008 09:28:50 -0000 1.88 +++ modules/system/system.admin.inc 15 Sep 2008 11:17:55 -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.619 diff -u -p -r1.619 system.module --- modules/system/system.module 15 Sep 2008 08:49:40 -0000 1.619 +++ modules/system/system.module 15 Sep 2008 11:17:56 -0000 @@ -1408,7 +1408,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.206 diff -u -p -r1.206 upload.module --- modules/upload/upload.module 15 Sep 2008 09:28:50 -0000 1.206 +++ modules/upload/upload.module 15 Sep 2008 11:17:56 -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/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.920 diff -u -p -r1.920 user.module --- modules/user/user.module 15 Sep 2008 09:28:50 -0000 1.920 +++ modules/user/user.module 15 Sep 2008 11:17:57 -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 { @@ -1538,7 +1538,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'] = ''; }