diff options
Diffstat (limited to 'includes')
-rw-r--r-- | includes/file.inc | 153 |
1 files changed, 139 insertions, 14 deletions
diff --git a/includes/file.inc b/includes/file.inc index 328cf08de..051b1210a 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -495,6 +495,7 @@ function file_load_multiple($fids = array(), $conditions = array()) { * * @param $fid * A file ID. + * * @return * A file object. * @@ -542,7 +543,130 @@ function file_save(stdClass $file) { } /** - * Copy a file to a new location and adds a file record to the database. + * Determines where a file is used. + * + * @param $file + * A file object. + * + * @return + * A nested array with usage data. The first level is keyed by module name, + * the second by object type, the third has 'id' and 'count' keys. + * + * @see file_usage_add() + * @see file_usage_delete() + */ +function file_usage_list(stdClass $file) { + $result = db_select('file_usage', 'f') + ->fields('f', array('module', 'type', 'id', 'count')) + ->condition('fid', $file->fid) + ->condition('count', 0, '>') + ->execute(); + $references = array(); + foreach ($result as $usage) { + $references[$usage->module][$usage->type] = array('id' => $usage->id, 'count' => $usage->count); + } + return $references; +} + +/** + * Records that a module is using a file. + * + * This usage information will be queried during file_delete() to ensure that + * a file is not in use before it is physically removed from disk. + * + * Examples: + * - A module that associates files with nodes, so $type would be + * 'node' and $id would be the node's nid. Files for all revisions are stored + * within a single nid. + * - The User module associates an image with a user, so $type would be 'user' + * and the $id would be the user's uid. + * + * @param $file + * A file object. + * @param $module + * The name of the module using the file. + * @param $type + * The type of the object that contains the referenced file. + * @param $id + * The unique, numeric ID of the object containing the referenced file. + * @param $count + * (optional) The number of references to add to the object. Defaults to 1. + * + * @see file_usage_list() + * @see file_usage_delete() + */ +function file_usage_add(stdClass $file, $module, $type, $id, $count = 1) { + db_merge('file_usage') + ->key(array( + 'fid' => $file->fid, + 'module' => $module, + 'type' => $type, + 'id' => $id, + )) + ->fields(array('count' => $count)) + ->expression('count', 'count + :count', array(':count' => $count)) + ->execute(); +} + +/** + * Removes a record to indicate that a module is no longer using a file. + * + * The file_delete() function is typically called after removing a file usage + * to remove the record from the file_managed table and delete the file itself. + * + * @param $file + * A file object. + * @param $module + * The name of the module using the file. + * @param $type + * (optional) The type of the object that contains the referenced file. May + * be omitted if all module references to a file are being deleted. + * @param $id + * (optional) The unique, numeric ID of the object containing the referenced + * file. May be omitted if all module references to a file are being deleted. + * @param $count + * (optional) The number of references to delete from the object. Defaults to + * 1. 0 may be specified to delete all references to the file within a + * specific object. + * + * @see file_usage_add() + * @see file_usage_list() + * @see file_delete() + */ +function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $count = 1) { + // Delete rows that have a exact or less value to prevent empty rows. + $query = db_delete('file_usage') + ->condition('module', $module) + ->condition('fid', $file->fid); + if ($type && $id) { + $query + ->condition('type', $type) + ->condition('id', $id); + } + if ($count) { + $query->condition('count', $count, '<='); + } + $result = $query->execute(); + + // If the row has more than the specified count decrement it by that number. + if (!$result) { + $query = db_update('file_usage') + ->condition('module', $module) + ->condition('fid', $file->fid); + if ($type && $id) { + $query + ->condition('type', $type) + ->condition('id', $id); + } + if ($count) { + $query->expression('count', 'count - :count', array(':count' => $count)); + } + $query->execute(); + } +} + +/** + * Copies 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 @@ -609,7 +733,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS } /** - * Copy a file to a new location without invoking the file API. + * Copies a file to a new location without invoking the file API. * * This is a powerful function that in many ways performs like an advanced * version of copy(). @@ -982,30 +1106,30 @@ function file_create_filename($basename, $directory) { /** * 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. + * If the $force parameter is not TRUE, file_usage_list() will be called to + * determine if the file is being used by any modules. If the file is being + * used the delete will be canceled. * * @param $file * 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. + * Boolean indicating that the file should be deleted even if the file is + * reported as in use by the file_usage table. * * @return mixed * TRUE for success, FALSE in the event of an error, or an array if the file - * is being used by another module. The array keys are the module's name and - * the values are the number of references. + * is being used by any modules. * * @see file_unmanaged_delete() - * @see hook_file_references() + * @see file_usage_list() + * @see file_usage_delete() * @see hook_file_delete() */ function file_delete(stdClass $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))) { + // If any module still has a usage entry in the file_usage table, the file + // will not be deleted, but file_delete() will return a populated array + // that tests as TRUE. + if (!$force && ($references = file_usage_list($file))) { return $references; } @@ -1016,6 +1140,7 @@ function file_delete(stdClass $file, $force = FALSE) { // database, so UIs can still find the file in the database. if (file_unmanaged_delete($file->uri)) { db_delete('file_managed')->condition('fid', $file->fid)->execute(); + db_delete('file_usage')->condition('fid', $file->fid)->execute(); return TRUE; } return FALSE; |