I have a multilingual website (english/hebrew).
The pages with an english url are cached but the utf-8 ones are not.

I disabled "Only allow ASCII characters in path" but still it doesn't work.

One thing I noticed is that on some computers the url addresses show up in hebrew characters and on others they show up as www.example.com/%D7%A2%D7%9E%D7

Could this be a part of the problem?
Any ideas what may be causing the problem?

Thanks!
Nimi

Comments

mikeytown2’s picture

related but not the solution
#782274: Allow % when using the "Only allow ASCII characters in path" setting - this would allow % to pass the ascii check

Are the files for the hebrew pages being created?

nimi’s picture

I just checked, the files are being created but they have very weird names:

סגל קבוע_.html
סגל קבוע_.html.gz

I remember this happening with uploaded files that have hebrew names. I had to install the transliteration module to make sure that all uploaded files in Hebrew are saved with english chars otherwise they won't work.

Perhaps Boost can save its utf8 cached files using different file names?

nimi’s picture

Please, is there anything I can try to fix this?

nimi’s picture

I tried renaming the cached file:
סגל קבוע_.html

to its proper name (as seen in the url):
צור_קשר_.html

And it works! Boost served the cached file.

How can I get Boost to generate utf8 files with proper names?

nimi’s picture

Would it be possible to have the cached filenames transliterated?

mikeytown2’s picture

Here's how I get the filename and write it

function boost_init() {
...
  //set variables
  if (isset($_REQUEST['q'])) {
    $GLOBALS['_boost_path'] = $_REQUEST['q'];
  }
  else {
    //front page
    $GLOBALS['_boost_path'] = '';
  }
...
}

function _boost_ob_handler() {
...
  boost_cache_set($GLOBALS['_boost_path'], $decompressed_buffer, BOOST_FILE_EXTENSION);
...
}

function boost_cache_set($path, $data, $extension = BOOST_FILE_EXTENSION) {
...
  $filename = boost_file_path($path, TRUE, $extension);
  boost_cache_write($filename, $data);
...
}

/**
 * Returns the static file path for a Drupal page.
 *
 * @param $path
 *   path to convert to boost's file naming convention
 * @param $query
 *   add query to path
 * @param $extension
 *   add extension to end of filename
 * @param $file_path
 *   Optional: BOOST_FILE_PATH
 *
 * $path = $GLOBALS['_boost_path'] most of the time
 */
function boost_file_path($path, $query = TRUE, $extension = BOOST_FILE_EXTENSION, $file_path = NULL) {
  //handling of url variables
  if ($GLOBALS['_boost_query'] != BOOST_CHAR) {
    if ($query) {
      $path .= $GLOBALS['_boost_query'];
    }
    else {
      $path .= BOOST_CHAR;
    }
  }
  else {
    $path .= $GLOBALS['_boost_query'];
  }

  // Under no circumstances should the incoming path contain '..' or null
  // bytes; we also limit the maximum directory nesting depth of the path
  if (   strpos($path, '..') !== FALSE
      || strpos($path, "\0") !== FALSE
      || count(explode('/', $path)) > BOOST_MAX_PATH_DEPTH
      ) {
    return FALSE;
  }
  $file_path = is_null($file_path) ? BOOST_FILE_PATH : $file_path;

  return implode('/', array($file_path, $path . (is_null($extension) ? '' : $extension)));
}


/**
 * Writes data to filename in an atomic operation thats compatible with older
 * versions of php (php < 5.2.4 file_put_contents() doesn't lock correctly).
 *
 * @param $filename
 *   Name of file to be written
 * @param $buffer
 *   Contents of file
 */
function boost_cache_write($filename, $buffer) {
  $filenames = boost_get_all_filenames($filename);
  foreach ($filenames as $key => $values) {
    if ($key == 'gzip') {
      $data = gzencode($buffer, 9);
    }
    else {
      $data = $buffer;
    }
    foreach ($values as $filename) {
      if (!_boost_mkdir_p(dirname($filename))) {
        if (BOOST_VERBOSE >= 3) {
          watchdog('boost', 'Unable to create directory: %dir<br /> Group ID: %gid<br /> User ID: %uid<br /> Current script owner: %user<br />', array('%dir' => dirname($filename), '%gid' => getmygid(), '%uid' => getmyuid(), '%user' => get_current_user()), WATCHDOG_WARNING);
        }
      }
      $tempfile = $filename . getmypid();
      if (@file_put_contents($tempfile, $data) === FALSE) {
        if (BOOST_VERBOSE >= 3) {
          watchdog('boost', 'Unable to write temp file: %file<br /> Group ID: %gid<br /> User ID: %uid<br /> Current script owner: %user<br />', array('%file' => $tempfile, '%gid' => getmygid(), '%uid' => getmyuid(), '%user' => get_current_user()), WATCHDOG_WARNING);
        }
        return FALSE;
      }
      else {
        if (is_numeric(BOOST_PERMISSIONS_FILE)) {
          @chmod($tempfile, octdec(BOOST_PERMISSIONS_FILE));
        }
        // Erase old file
        if (BOOST_OVERWRITE_FILE) {
          @unlink($filename);
        }
        // Put temp file in its final location
        if (@rename($tempfile, $filename) === FALSE) {
          if (BOOST_VERBOSE >= 5) {
            watchdog('boost', 'Unable to rename file: %temp  to  %file<br /> Group ID: %gid<br /> User ID: %uid<br /> Current script owner: %user<br />', array('%temp' => $tempfile, '%file' => $filename, '%gid' => getmygid(), '%uid' => getmyuid(), '%user' => get_current_user()), WATCHDOG_WARNING);
          }
          @unlink($tempfile);
          return FALSE;
        }
      }
    }
  }
  return TRUE;
}

/**
 * Returns all possible filenames given the input and current settings
 *
 * @param $filename
 *   Name of file
 * @param $base_dir
 *   Value from base_dir column in database
 * @return
 *   returns a 2 dimensional array
 *    1st dimension key is either gzip or normal
 *    2nd dimension contains all the filenames
 */
function boost_get_all_filenames($filename, $base_dir = NULL) {
  $namesA = array();
  $namesB = array();
  $filenames = array();
  $base_dir = is_null($base_dir) ? BOOST_FILE_PATH : $base_dir;

  if (stristr($filename, '[')) {
    $paths = explode('/', $filename);
    $end = array_pop($paths);
    $end = preg_replace("(\x5B[0-9]\x5D)", ']', $end);
    $paths[] = $end;
    $namesA[] = implode('/', $paths);
  }
  $namesA[] = $filename;

  foreach ($namesA as $filename) {
    $namesB[] = $filename;
    // Generate urlencoded filename, if name contains decoded characters
    $paths = explode('/', $filename);
    $end = array_pop($paths);
    //$end = str_replace('[', '%5B', $end);
    //$end = str_replace(']', '%5D', $end);
    $end = str_replace(',', '%2C', $end);
    $end = str_replace(' ', '%20', $end);
    $paths[] = $end;
    $namesB[] = implode('/', $paths);
  }
  $namesB = array_unique($namesB);

  // Generate gzip filenames
  foreach ($namesB as $name) {
    $filenames['normal'][] = $name;
    if (BOOST_GZIP) {
      // Replace the correct dir with the gzip version for the given base dir.
      $gzip_base_path = implode('/', array_filter(explode('/', str_replace(BOOST_ROOT_CACHE_DIR . '/' . BOOST_NORMAL_DIR, BOOST_ROOT_CACHE_DIR . '/' . BOOST_GZIP_DIR . '/', $base_dir))));
      $filenames['gzip'][] = str_replace($base_dir, $gzip_base_path, $name) . BOOST_GZIP_EXTENSION;
    }
  }
  return $filenames;
}

If you know of someway to write files in php with multi byte names, let me know http://php.net/mbstring.
A quick test to see if its possible is run this code and see what filename it generates.

  file_put_contents('cache/' . $GLOBALS['_boost_path'], $_REQUEST['q']);

output should be in the cache directory. This will let me know if I'm converting the input string or something else.

http://php.net/iconv appears to be the function I want if the filename doesn't come out right.

nimi’s picture

Thanks a lot for your reply Mikeytown2.

I tried doing the following:

file_put_contents('cache/test.txt', 'some_Hebrew_text');
Result: a file was created with Hebrew text inside.

However, when I did:

file_put_contents('cache/some_Hebrew_text', 'sometext');
Result: a weird file was created:
אפריקאי

Finaly when I did:

file_put_contents('cache/' . iconv("UTF-8", "CP1255", 'some_Hebrew_text'), 'sometext');
Result: a valid filename but with a wrong encoding was created:
àôøé÷àé

Wouldn't it be best to implement the Transliteration module (http://drupal.org/project/transliteration) for non-english pages?
Since this is a common problem when saving attachments in utf8 languages, the Transliteration module solves this.

Any idea how to implement the Transliteration module into Boost?

nimi’s picture

Title: utf-8 urls like www.example.com/%D7%A2%D7%9E%D7 not cached » utf-8 urls are not cached: filenames encoded wrongly
mikeytown2’s picture

The Transliteration module goes from utf-8 to acsii. You want to from utf-8 to your specific language CP1255 (Hebrew). Let me know if the Transliteration module does this but I don't think it does. I believe this will have to be a semi-complex patch to get this working. Do you know if PHP can auto detect the language used?

nimi’s picture

The Translation module translates into random English characters. It doesn't really "transliterate", but that is good enough to avoid encoding errors.

I don't think php can detect a language but there is an interesting solution here:
http://code.google.com/intl/en-US/apis/ajaxlanguage/documentation/#Detect
This may be a bit complex to implement though.

Perhaps it would be easiest to have Boost save utf8 chars in ascii? Is this complicated to do?

mikeytown2’s picture

I'm pretty sure apache won't serve those the transliterated filenames; or did you test it and it indeed does?

nimi’s picture

Here's what happens when I insert an attachment with the Transliteration module installed:

I edit a node. In the attachments, I browse and add a Hebrew file:
שלום.pdf

Once the file is uploaded it gets automatcally a new name consisting of some English characters:
kjd_jif_s.pdf

Although there is no real transliteration here because the new filename is meaningless, it works when I try to download it.

mikeytown2’s picture

look at the link in the html, it points to kjd_jif_s.pdf

If the link points to שלום.pdf and apache looks for, and gives you kjd_jif_s.pdf then the Transliteration module idea would work for boost. I'm pretty certain it won't work this way, but I do not do much with i18n so I could be wrong.

If you use the Transliteration module with path auto then you will get the desired effect; no need to do anything with boost.
http://drupal.org/project/pathauto

nimi’s picture

I checked again, when I upload a Hebrew filename it just gets renamed to an English filename.
The final link points to the English filename.

Thus, No Hebrew is being processed by Apache in this case. Is Apache/PHP even able to save files in utf8 languages?

The Pathauto model saves the Hebrew links in Ascii. So when I enter a Hebrew webaddress it is converted to ascii and then loaded.

The problem is that Boost is trying to save files with utf8 filenames.
Maybe it should save them in Ascii, just like Pathauto does?

mikeytown2’s picture

Boost saves the filename as the pathname; so if you use pathauto to rename all your paths the ascii then apache will be able to serve them

nimi’s picture

Finally found a solution!

This is how you can create files with Hebrew characters in their filenames (Make sure your system LOCALE is set to Hebrew):


<?php

// Use iconv to encode into Hebrew the filename

$file =  iconv("UTF-8", "CP1255", "שלום"); //text here is in Hebrew
$content = "some content...";

file_put_contents($file, $content);

?>

Now the only thing left to do is to fix in the Boost module the places where file_put_contents is used.

How do you suggest to doing this?

mikeytown2’s picture

Status: Active » Needs review
StatusFileSize
new956 bytes

For this change its used in 1 place. Here's a patch just for Hebrew; Let me know it works & I will be making a setting for this.

nimi’s picture

Status: Needs review » Active

Fixed! Here's how:

Scroll down boost.module until you see:

function boost_cache_write($filename, $buffer) {
...

add the following line right below the function:

function boost_cache_write($filename, $buffer) {
$filename = iconv("UTF-8", "CP1255", $filename);     /*this line here fixes encoding for Hebrew! */
...

And it works for Hebrew urls!

nimi’s picture

Mikey, just saw your post. Yep it works :)

Thanks for all your help!

mikeytown2’s picture

Status: Active » Reviewed & tested by the community
mikeytown2’s picture

meatbag’s picture

subscribing...

mikeytown2’s picture

Version: 6.x-1.18 » 6.x-1.x-dev
Status: Reviewed & tested by the community » Needs review
StatusFileSize
new3 KB
ukrdrupal’s picture

...subscribing...

mikeytown2’s picture

@ukrdrupal
try the patch

mikeytown2’s picture

Status: Needs review » Fixed

committed

nimi’s picture

Status: Fixed » Active

Sorry to re-open this issue but there is a problem which I think is related here:

Boost is showing expired pages for pages with Hebrew urls.
Only urls that are in English are cleared when expired.

If I clear the cache manually then everything works.
I tried to do a manual Cron but that didn't help.

Any idea why this is happening?

Thanks,
Nimi

mikeytown2’s picture

Status: Active » Needs review
StatusFileSize
new1.36 KB

put it in the wrong location; try this patch

mikeytown2’s picture

Status: Needs review » Fixed

committed

nimi’s picture

Worked like a charm! Thanks a lot mikeytown2.

Status: Fixed » Closed (fixed)

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

xandeadx’s picture

Version: 6.x-1.x-dev » 7.x-1.x-dev
Status: Closed (fixed) » Active

please add fix in 7.x version

LiorFil’s picture

I updated the module to latest dev to overcome the hebrew problem.
It is now showing me the cache status block, which wasn't there before.
it even says that it will expire in 58 minutes and 19 seconds, like it should.
but it also says that: cache/normal/www.mysite.co.il/חדשות_.html Does not exist.
there is no comment at the bottom of the source that says that the page is cached by boost.

any workaround for that?

bgm’s picture

Version: 7.x-1.x-dev » 6.x-1.x-dev
Status: Active » Closed (fixed)
Issue tags: +i18n, +utf-8

Since Boost 6.x and 7.x are pretty different, let's keep the issues separate.

Here is a 7.x-1.x issue for this: #1386166: PDOException with UTF-8 aliases / boost does not cache pages with UTF-8 aliases. Please review the patch.

Thanks