diff --git a/file_entity.api.php b/file_entity.api.php index 9fb7457..d90d3ad 100644 --- a/file_entity.api.php +++ b/file_entity.api.php @@ -367,3 +367,11 @@ function hook_file_type_alter(&$types, $file) { // Choose a specific, non-first, file type. $types = array($types[4]); } + +function hook_file_metadata_info() { + +} + +function hook_file_metadata_info_alter() { + +} diff --git a/file_entity.file.inc b/file_entity.file.inc index 829df96..f80ea1c 100644 --- a/file_entity.file.inc +++ b/file_entity.file.inc @@ -31,6 +31,9 @@ function file_entity_file_presave($file) { } field_attach_presave('file', $file); + + // Fetch image dimensions. + file_entity_metadata_fetch_image_dimensions($file); } /** @@ -54,13 +57,23 @@ function file_entity_file_insert($file) { // Ensure field data is saved since file_save() does not in Drupal 7. field_attach_insert('file', $file); + // Save file metadata. + if (!empty($file->metadata)) { + foreach ($file->metadata as $name => $value) { + db_insert('file_metadata') + ->fields(array( + 'value' => serialize($value), + )) + ->key(array( + 'fid' => $file->fid, + 'name' => $name, + )) + ->execute(); + } + } + // Clear any related field caches. file_entity_invalidate_field_caches($file); - - // Get and store image dimensions. - // @todo We should fetch image dimensions in file_entity_file_presave and - // then save them in this hook. - file_entity_image_dimensions($file, TRUE); } /** @@ -70,26 +83,35 @@ function file_entity_file_update($file) { // Ensure field data is saved since file_save() does not in Drupal 7. field_attach_update('file', $file); - // Clear any related field caches. - file_entity_invalidate_field_caches($file); + // Save file metadata. + db_delete('file_metadata')->condition('fid', $file->fid); + if (!empty($file->metadata)) { + foreach ($file->metadata as $name => $value) { + db_insert('file_metadata') + ->fields(array( + 'value' => serialize($value), + )) + ->key(array( + 'fid' => $file->fid, + 'name' => $name, + )) + ->execute(); + } + } - // Reset the image dimensions for a file. - // @todo We should fetch image dimensions in file_entity_file_presave and - // then save them in this hook. - file_entity_image_dimensions($file, TRUE); - - if ($file->type == 'image' && module_exists('image')) { + if (file_entity_file_get_mimetype_type($file) == 'image' && module_exists('image')) { // If the image dimensions have changed, update any image field references // to this file and flush image style derivatives. - if (isset($file->image_dimensions) && isset($file->original->image_dimensions)) { - if ($file->image_dimensions != $file->original->image_dimensions) { - _file_entity_update_image_field_dimensions($file); - } + if ($file->image_dimensions != $file->original->image_dimensions) { + _file_entity_update_image_field_dimensions($file); } // Flush image style derivatives whenever an image is updated. image_path_flush($file->uri); } + + // Clear any related field caches. + file_entity_invalidate_field_caches($file); } /** @@ -102,8 +124,8 @@ function file_entity_file_delete($file) { // not yet been deleted. file_entity_invalidate_field_caches($file); - // Delete image dimensions from the {image_dimensions} table - db_query('DELETE FROM {image_dimensions} WHERE fid = :fid', array(':fid' => $file->fid)); + // Remove file metadata. + db_delete('file_metadata')->condition('fid', $file->fid)->execute(); // Remove this file from the search index if needed. // This code is implemented in file entity module rather than in search module, @@ -153,38 +175,24 @@ function file_entity_file_mimetype_mapping_alter(&$mapping) { * Implements hook_file_load(). */ function file_entity_file_load($files) { - // Load images dimensions already in the {image_dimensions} table. - $result = db_query('SELECT * FROM {image_dimensions} id WHERE id.fid IN (:fids)', array(':fids' => array_keys($files))); - foreach ($result as $record) { - $files[$record->fid]->image_dimensions = array( - 'width' => $record->width, - 'height' => $record->height, - ); - } - // Retrieve any missing images dimensions. foreach ($files as $file) { - file_entity_image_dimensions($file, FALSE); + $file->metadata = array(); + } + + // Load and unserialize metadata. + $results = db_query("SELECT * FROM {file_metadata} WHERE fid IN (:fids)", array(':fids' => array_keys($files))); + foreach ($results as $result) { + $files[$result->fid]->metadata[$result->name] = unserialize($result->value); } } /** - * Retrieve the dimensions of an image file and store them in the - * {image dimensions} table. + * Fetch the dimensions of an image and store them in the file metadata array. * * @param $file * A file object. - * - * @param $force - * TRUE if the image dimensions should always be loaded from the actual file - * even if $file->image_dimensions is already set. - * - * @return - * The image dimensions as an array with the 'width' and 'height' properties. - * The array is also added to $file as its image_dimensions property. If the - * image dimensions cannot be read, the 'width' and 'height' properties will - * be NULL. If $file is either empty or not an image file, FALSE is returned. */ -function file_entity_image_dimensions($file, $force = FALSE) { +function file_entity_metadata_fetch_image_dimensions($file) { // Prevent PHP notices when trying to read empty files. // @see http://drupal.org/node/681042 if (!$file->filesize) { @@ -192,36 +200,17 @@ function file_entity_image_dimensions($file, $force = FALSE) { } // Do not bother proceeding if this file does not have an image mime type. - if (strpos($file->filemime, 'image/') !== 0) { + if (file_entity_file_get_mimetype_type($file) != 'image') { return; } - // Return the existing $file->image_dimensions unless a reload is forced. - if (!$force && isset($file->image_dimensions)) { - return $file->image_dimensions; - } - // We have a non-empty image file. $image_info = image_get_info($file->uri); if ($image_info) { - $file->image_dimensions = array( - 'width' => $image_info['width'], - 'height' => $image_info['height'], - ); - db_merge('image_dimensions') - ->key(array('fid' => $file->fid)) - ->fields(array( - 'width' => $file->image_dimensions['width'], - 'height' => $file->image_dimensions['height'], - )) - ->execute(); + $file->metadata += array_intersect_key($image_info, drupal_map_assoc(array('width', 'height'))); } else { - // Fallback to NULL values. - $file->image_dimensions = array( - 'width' => NULL, - 'height' => NULL, - ); + $file->metadata = array_diff_key($image_info, drupal_map_assoc(array('width', 'height'))); } } @@ -234,6 +223,11 @@ function file_entity_image_dimensions($file, $force = FALSE) { * @see http://drupal.org/node/1448124 */ function _file_entity_update_image_field_dimensions($file) { + // Do not bother proceeding if this file does not have an image mime type. + if (file_entity_file_get_mimetype_type($file) != 'image') { + return; + } + // Find all image field enabled on the site. $image_fields = array(); foreach (field_info_fields() as $field) { @@ -290,3 +284,12 @@ function _file_entity_entity_fields_update($entity_type, $entity) { // Clear the cache for this entity now. entity_get_controller($entity_type)->resetCache(array($id)); } + +/** + * Implements hook_file_metadata_info(). + */ +function file_entity_file_metadata_info() { + $info['width'] = array('label' => t('Width'), 'type' => 'integer'); + $info['height'] = array('label' => t('Height'), 'type' => 'integer'); + return $info; +} diff --git a/file_entity.install b/file_entity.install index a1f86c2..b11e1f1 100644 --- a/file_entity.install +++ b/file_entity.install @@ -131,31 +131,31 @@ function file_entity_schema() { ), ), ); - $schema['image_dimensions'] = array( + + $schema['file_metadata'] = array( 'description' => 'Cache images dimensions.', 'fields' => array( 'fid' => array( - 'description' => 'File ID.', + 'description' => 'The file ID.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, ), - 'height' => array( - 'description' => 'The height of the image in pixels.', - 'type' => 'int', - 'unsigned' => TRUE, + 'name' => array( + 'description' => "The name of the metadata (e.g. 'width').", + 'type' => 'varchar', + 'length' => '255', 'not null' => TRUE, - 'default' => 0, ), - 'width' => array( - 'description' => 'The width of the image in pixels..', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, + 'value' => array( + 'description' => "The value of the metadata (e.g. '200px').", + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, ), ), - 'primary key' => array('fid'), + 'primary key' => array('fid', 'name'), 'foreign keys' => array( 'file_managed' => array( 'table' => 'file_managed', @@ -163,6 +163,7 @@ function file_entity_schema() { ), ), ); + return $schema; } @@ -859,3 +860,52 @@ function file_entity_update_7209() { db_drop_table('file_type_streams'); } } + +/** + * Migrate the {image_dimensions} table to {file_metadata}. + */ +function file_entity_update_7210() { + $schema = array( + 'description' => 'Stores file metadata in a key/value store.', + 'fields' => array( + 'fid' => array( + 'description' => 'The file ID.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'description' => "The name of the metadata (e.g. 'width').", + 'type' => 'varchar', + 'length' => '255', + 'not null' => TRUE, + ), + 'value' => array( + 'description' => "The value of the metadata (e.g. '200px').", + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('fid', 'name'), + 'foreign keys' => array( + 'file_managed' => array( + 'table' => 'file_managed', + 'columns' => array('fid' => 'fid'), + ), + ), + ); + db_create_table('file_metadata', $schema); + + if (db_table_exists('image_dimensions')) { + foreach (array('width', 'height') as $name) { + $query = db_select('image_dimensions', 'id'); + $query->addField('id', 'fid'); + $query->addExpression(':name', 'name', array(':name' => $name)); + $query->addExpression("CONCAT('i:', $name, ';')", 'value'); + db_insert('file_metadata')->from($query)->execute(); + } + db_drop_table('image_dimensions'); + } +} diff --git a/file_entity.module b/file_entity.module index 5531873..0b4b07d 100644 --- a/file_entity.module +++ b/file_entity.module @@ -1131,25 +1131,26 @@ function file_entity_file_formatter_file_image_view($file, $display, $langcode) } // Do not bother proceeding if this file does not have an image mime type. - if (strpos($file->filemime, 'image/') !== 0) { + if (file_entity_file_get_mimetype_type($file) != 'image') { return; } - if (file_entity_file_is_readable($file) && isset($file->image_dimensions)) { + if (file_entity_file_is_readable($file)) { // We don't sanitize here. // @see http://drupal.org/node/1553094#comment-6257382 // Theme function will take care of escaping. + $file->metadata += array('width' => NULL, 'height' => NULL); $replace_options = array( - 'clear' => 1, - 'sanitize' => 0, + 'clear' => TRUE, + 'sanitize' => FALSE, ); if (!empty($display['settings']['image_style'])) { $element = array( '#theme' => 'image_style', '#style_name' => $display['settings']['image_style'], '#path' => $file->uri, - '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->image_dimensions['width'], - '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->image_dimensions['height'], + '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->metadata['width'], + '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->metadata['height'], '#alt' => token_replace($display['settings']['alt'], array('file' => $file), $replace_options), '#title' => token_replace($display['settings']['title'], array('file' => $file), $replace_options), ); @@ -1158,8 +1159,8 @@ function file_entity_file_formatter_file_image_view($file, $display, $langcode) $element = array( '#theme' => 'image', '#path' => $file->uri, - '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->image_dimensions['width'], - '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->image_dimensions['height'], + '#width' => isset($file->override['attributes']['width']) ? $file->override['attributes']['width'] : $file->metadata['width'], + '#height' => isset($file->override['attributes']['height']) ? $file->override['attributes']['height'] : $file->metadata['height'], '#alt' => token_replace($display['settings']['alt'], array('file' => $file), $replace_options), '#title' => token_replace($display['settings']['title'], array('file' => $file), $replace_options), ); @@ -2106,7 +2107,7 @@ function path_file_insert($file) { // Only save a non-empty alias. if (!empty($path['alias'])) { // Ensure fields for programmatic executions. - $path['source'] = 'file/' . $file->fid; + $path['source'] = 'file/' . $file->tid; // Core does not provide a way to store the file language but contrib // modules can do it so we need to take this into account. $langcode = entity_language('file', $file); @@ -2189,3 +2190,8 @@ function file_entity_download_uri($file) { } return $uri; } + +function file_entity_file_get_mimetype_type($file) { + list($type, $subtype) = explode('/', $file->filemime, 2); + return $type; +}