Non-local Files on Drupal 6
Drupal 6 does not handle non-local files well. That means if you want to use something like Amazon's S3 file service you are pretty much out of luck. Media Mover got around this by overriding some templates but the implementation did not work well for extensibility and left most use cases in the dark.
Ultimately the problem with Drupal 6 is that core and contributed modules leave system level file functions in templates with no alter functions. Not having file load or file alter functions makes it almost impossible to override things without rewriting these templates. This is really not good but since Drupal 7 mostly solves this problem we can probably forgive ourselves. Between stream wrappers and hook_file_load() we've got two really good places where we can handle things like storing non-local files or altering existing ones.
The solution that I came up with for Drupal 6 was to override all the obvious theme functions and create a filepath alter hook so that other modules can jump into the mix and alter what filepath gets spit out to the display. This is not a great solution because it ends up happening on the template level. There maybe better solutions- this is just what I came up with to try to get S3 public and private files to work with filefield and Drupal's uploads.
In the case of Media Mover's S3 module, it stores the full URL to the file in the Drupal files table (minus the permissions- those have to come later because they are time and user dependent). This is different than normal file handling in Drupal. Yes, this can cause some nasty things to happen if any kind of system level file operations try to take place on this particular file. Let us ignore this rather large detail for the moment.
So here goes. The moral of the story is that hook_theme_registry_alter() is your friend. First we hijack upload and filefield theming:
function mm_node_theme_registry_alter(&$theme_registry) {
// We need to override all of the filefield theme functions
// so that a non-local URI can be used to display the file
$types = array(
'filefield_file',
'filefield_formatter_default',
'filefield_formatter_url_plain',
'filefield_formatter_path_plain',
'filefield_item',
'upload_attachments'
);
foreach ($types as $type) {
$theme_registry[$type]['function'] = str_replace('theme', 'theme_mm_node', $theme_registry[$type]['function']);
// This mimics the value that gets set by hook_theme()
$theme_registry[$type]['include files'][0] = './' . drupal_get_path('module', 'mm_node') . '/mm_node.formater.inc';
$theme_registry[$type]['file'] = 'mm_node.formater.inc';
}
}
Now that we have hijacked the filefield and upload theme functions, here's an example of one of how a filefield template gets altered to support the filefield hook. This is pretty much identical to the original with the exception of the mm_node_filefield_check() call:
function theme_mm_node_filefield_file($file) {
// Views may call this function with a NULL value, return an empty string.
if (empty($file['fid'])) {
return '';
}
$path = $file['filepath'];
// If this filepath has a URI in it, do not format it
$url = mm_node_filepath_check($file);
...
The mm_node_filepath_check() function provides the alter hook for other modules to take charge of the filepath. It is probably the case that the alter should be done for local and non-local files but I did not want to get into that mess. Remember that Media Mover is storing the full filepath to the S3 file- http://s3.amazonaws.com/bucket/file.mov- in the Drupal files table.
function mm_node_filepath_check($source_file) {
if (! is_object($source_file)) {
$file = (object) $source_file;
}
else {
$file = $source_file;
}
// If this filepath is not a URL, create a local path for it
if (! strstr($file->filepath, '://')) {
$file->filepath = file_create_url($file->filepath);
}
// We have a non-local URL- we can alter it
else {
drupal_alter('media_mover_filepath', $file);
}
return $file->filepath;
}
Now we can simply alter the filepath before it gets displayed. In this example, Media Mover's S3 module takes the passed in file, loads up the corresponding Media Mover file in order to get the settings which control private file settings on S3 (what Drupal user roles can access this file, how long this file URL will be good for, etc).
function mm_s3_media_mover_filepath_alter(&$file) {
// We need to load any media mover files related to this fid
if ($mm_files = media_mover_api_file_get_fid($file->fid, 'mm_s3')) {
// We only deal with one file for this example
$mm_file = array_shift($mm_files);
// Get the s3 data from the mmfile
if ($s3 = $mm_file->data_get('s3')) {
$path = mm_s3_create_uri($s3);
$file->filepath = $path;
}
}
}
The outcome is that the final filepath displayed by filefield and upload is the S3 one with minimal intrusion. The downside is if people have altered these templates in their themes and do not implement the alter hook- this is clearly why implementing the hook on the theme level is dangerous. However there seems to be little choice....
Categories: media moverPlanet DrupalDrupalfilesremoteCDNS3hacking for pleasure