I am working with last Pressflow 6 that includes the CDN patch with the new hook_file_url_alter, backported from the Drupal 7 file_create_url function.

I found a problem with the module taxonomy_image where it's using the file_create_url($path) having the $path a relative url from the files directory.
By default it would be category_pictures as directory and as example it could be category_pictures/icon.png

Reading the original api function: http://api.drupal.org/api/function/file_create_url/6
It seems that the use that taxonomy_image module is doing from that function it's correct.
It passing a relative path for a file. And it would work fine with the old D6 function.

But the CDN patch is adding the shipped file feature from D7, and then when the function arrives to:

  // Shipped files.
  if (strpos($path, file_directory_path() . '/') !== 0) {
    return base_path() . $path;
  }

It founds that $path=category_files/icon.png and file_directory_path = sites/all/default/files (for example) and then of course the $path doesn't include the file directory path and then it returns
the base path and path returning /category_files/icon.png. That is wrong reading the api definition.

I open the issue here because I don't find any bug in the taxonomy_image function, but so, in the backporting of a D7 feature that alters the original D6 definition.

The shipped feature works fine if you are using files from the misc/, modules/, themes/ or sites/ directory when it just send the full url,
but it fails for the case where you want to create a url from a relative path for a file that it is in your files directory, and you don't know if the files are private or public.

I know that the change in the taxonomy_images is tiny, but it could be affecting to more modules that are using the file_create_url function as it's defined in the api and thus this patch is breaking the D6 compatibility.

A possible solution could be checking for the shipped files that the url includes the misc/,modules/, sites/, themes/ (and possibly profiles/) paths, because on that case it wouldn't be a relative path for the files directory.

CommentFileSizeAuthor
#13 839282-cdn-file_create_url.patch765 bytesyang_yi_cn
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

joostvdl’s picture

IMCE module has the same problem.

Pressflow bug reported: https://bugs.launchpad.net/pressflow/+bug/597718

corbacho’s picture

Also Javascript Aggregator has the same problem with Pressflow.

It looks for relative paths, but CDN module patches have made that Pressflow builds absolute paths.

https://bugs.launchpad.net/pressflow/+bug/605867

melon’s picture

subscribing

jrowny’s picture

I also ran into the IMCE issue. Fortunately our site is not in production yet, I'll look for a fix.

brianV’s picture

Just for related info, I created a patch for taxonomy_image.module that allows it to work with both Pressflow's version of file_create_url() and stock D6. However, that doesn't lessen the fact that this patch introduces a departure from the stock API.

Patch at #877982: Fix pressflow incompatibility and standardize image object URL creation..

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Status: Active » Fixed

(This comment is cross-posted to http://drupal.org/node/3358288 and https://bugs.launchpad.net/pressflow/+bug/597718.)

Brian's analysis at https://bugs.launchpad.net/pressflow/+bug/597718/comments/8 is correct.

file_create_url() in Drupal 6 is broken. It was only used for creating URLs for files in the files directory (i.e. the files_directory_path() directory). But even that it did not do well, since it allows for 2 kinds of calls:
1) with just test.jpg
2) with files/test.jpg
Both would result in http://example.com/files/test.jpg being returned.

You can easily see this when looking at the code, it first strips the files directory and then adds it again:

/**
 * Create the download path to a file.
 *
 * @param $path A string containing the path of the file to generate URL for.
 * @return A string containing a URL that can be used to download the file.
 */
function file_create_url($path) {
  // Strip file_directory_path from $path. We only include relative paths in urls.
  if (strpos($path, file_directory_path() .'/') === 0) {
    $path = trim(substr($path, strlen(file_directory_path())), '\\/');
  }
  switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
    case FILE_DOWNLOADS_PUBLIC:
      return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace('\\', '/', $path);
    case FILE_DOWNLOADS_PRIVATE:
      return url('system/files/'. $path, array('absolute' => TRUE));
  }
}

My patch changes that: it makes it so that file_create_url() can be used for *any* file URL. That's necessary to create a single point where all file URLs can be altered, those in the files directory (i.e. "created files") and those that ship with Drupal and modules/themes (i.e. "shipped files"). I.e. to make it possible to alter file URLs so that we can use a CDN.
But, it did not take properly into account the fact that many modules assume that the files directory is automatically added into the URL.

So, to avoid patching Drupal modules to make them use the improved file_create_url() (and thus require them to include file_directory_path() in the file paths they're passing to it), I've revised the way that shipped files are being detected: it now uses regular expressions instead of a simple string comparison. I didn't do any performance testing, but we should be fine.

Basically it involves changing this line:
if (strpos($path, file_directory_path() . '/') !== 0) {
to:
if (preg_match("#(/?)(misc|modules|sites|themes)/(.*)#i", $path)) {

Committed (but first committed the wrong patch, hence 2 commits)!

http://drupal.org/cvs?commit=410718
http://drupal.org/cvs?commit=410722

jcmarco’s picture

Status: Fixed » Needs work

I was testing the last patch, and I found some issues.

First, I attach the last CDN patch for the Pressflow distro to test it.

It doesn't solve the problem with the 'broken' or 'not well dual defined usage' of file_create_url() in D6,
it still fails if you are using file_create_url() as it could be interpreted from the D6 api documentation and as it is used in some of the already reported failing modules.
It covers the case of using the shipped files, but the regular expression have some caveats:
- if you have inside you file directory a directory called misc|modules|sites|themes and your file directory is outside sites/ (a possible configuration),
- or your files directory is inside the sites/ directory (recommended setting) then
it considers that it is a shipped file, breaking the next downloadable file process.

You can test it with http://tools.netshiftmedia.com/regexlibrary/.

So it would be required to process the regular expression from the beginning of the path string only and avoid the shipped files directory format when using the defined file_directory_path().
Other consideration is that if we are not going to process the returned array we don't need the (.*) part.

The core patch would require this change:

if (preg_match("#(/?)(misc|modules|sites|themes)/(.*)#i", $path)) {
if (preg_match("#^(/?)(misc|modules|sites|themes)/#i", $path) && (strpos($path, file_directory_path() . '/') !== 0)) {

Cross-posted with pressflow:
https://bugs.launchpad.net/pressflow/+bug/597718/comments/11

Wim Leers’s picture

Title: Problems with shipped files and Drupal 6 file_create_url behavior » Problems with shipped files and Drupal 6 file_create_url() behavior
Status: Needs work » Fixed

Thanks jcmarco, committed: http://drupal.org/cvs?commit=416710

jcmarco’s picture

Status: Fixed » Needs work

Thank you Wim for your review, but the committed patch only consider half of the problem.
The problem with the file directory content still persists, if you have your file directory in sites/*/files (the most usual and recommend setting), the condition always consider any file with this beginning as a shipped file and it is never considered as a file in the file directory thus no executing the part of the file processing (Adding the right root directory or processing the private files).

Wim Leers’s picture

Status: Needs work » Fixed

You're right. That was in fact a copy/paste error. My apologies. Committed your entire suggested change, as posted in #7: http://drupal.org/cvs?commit=432432

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

yang_yi_cn’s picture

Status: Closed (fixed) » Active

the current version still breaks for

file_get_path(''); 

which is used in the IMCE file browser.

Without the core patch, it returns

$GLOBALS['base_url'] . '/' . file_directory_path() . '/' . str_replace('\\', '/', $path);

which is "http://mysite/sites/default/files/".

After the core path, in file_create_url(), the empty path after alter will become "http://my-cdn-site/", and not equal to the old path, so it will run this part:

  // Return path if it was altered by any module, or if it already is a
  // root-relative or a protocol-relative URL.
  if ($path != $old_path || drupal_substr($path, 0, 1) == '/' || drupal_substr($path, 0, 2) == '//') {
    return $path;
  }

and return "http://my-cdn-site/".

That breaks IMCE's file browsing functionality.

which is "http://mysite/" .

yang_yi_cn’s picture

Version: 6.x-2.x-dev » 6.x-2.1
FileSize
765 bytes

I'm providing a patch (against 6.x-2.1). It detects if the path is emty ('' or '/'), if so, the module must just want to get the file directory path, so I return it.

Might not be the best way, but fixes the conflicts with the IMCE module. Hope this could be committed.

yang_yi_cn’s picture

actually the latest version of IMCE doesn't not user file_create_url('') to get the file directory any more, it calls file_directory_path() directly now. So this issue might not be so important.

However, if there's any other modules still use file_create_url('') it could be a problem.

Wim Leers’s picture

Status: Active » Closed (fixed)

I highly doubt any other module used this hack. If so, they should simply also switch to calling file_directory_path() directly.