Thursday, 28 April 2011

Chrome image bug and file headers

There is a bug in Chrome which means that, under some circumstances, an image can be displayed on the page for a moment and then disappear.

http://www.google.com/support/forum/p/Chrome/thread?tid=1d825178248ab136&hl=en

The fix is to remove the Content-Length header. Unfortunately that is virtually impossible to do in Drupal without hacking core.

Going into this problem revealed an interesting omission: the file_download() function collects headers from interested modules, but if two modules offer the same header the earlier one gets overwritten. Heavier modules can overwrite the headers of lighter modules.

So making my module heavy would mean that I could overwrite Content-Length with NULL.

But this is not a good solution. What's missing is that, after collecting the headers, file_download() does not use drupal_alter() to allow modules to change the headers.

So the correct solution is a patch which inserts drupal_alter('file_download_headers', $headers, $uri) into the file_download() function at the correct location. And you can find the patch here:

http://drupal.org/node/1137534

All I had to do then was create a function that implemented hook_file_download_headers() which took a look at the $uri and, if it's a graphic, remove the Content-Length header, which is perfectly safe because it's not a critical header.

Tuesday, 19 April 2011

Conditions for using where

Just to help you avoid spending as many hours as I have trying to force a nasty little query with a subquery to do what I wanted...

If you want to have a WHERE clause that contains something like "fss.entity_id = n.nid", you can't use the condition function. In other words, you can't do this:

$query->condition('fss.entity_id', 'n.nid');

You have to use this:

$query->where('fss.entity_id = n.nid');

Because the first version gets translated into fss.entity_id = 'n.nid' in the SQL, which will never be true.

In debugging this it slowly became obvious that something fundamental was wrong - because when I output the $query string and then replaced the placeholders with the right arguments, and shoved it straight into the MySQL server - it worked.

Eventually it dawned on me to use the Devel query output to see what was actually being called and found the error - though by that time I was looking for it, having exhausted every other option.

Here's a quick tip for outputting the entire contents of an object which contains public and private properties:

dpm(print_r($object, TRUE));

I could have done without that today.

Tuesday, 12 April 2011

CSS and Overlays

This might seem obvious but if you need to modify the CSS for your admin pages which appear in overlays, you will be needing to create a subtheme.

Assuming you use Seven for your admin theme, create a subtheme based on Seven, let's say you call it SevenUp, and add your own CSS overrides, then change the default admin theme to SevenUp.

You'll get all the Seven settings and your overrides.

However when you enable your theme you'll have to go to the Block admin page and remove all the blocks which have now added themselves.

Friday, 1 April 2011

drupal_get_destination and overlays

There may be a better way of doing this, but...

Had the situation where I had an admin page (set up to appear in an overlay) on which there were other links to forms. The problem was that when I clicked "Submit" on one of these forms it went back to the original page and not the previous overlay page.

If there is a destination in drupal_get_destination() this takes precedence over any form setting. So I needed to somehow force this to be what I wanted. The solution is relatively easy though might be considered a bit hacky.

What you need to do is this:

$_GET['destination'] = $_GET['q'];

which forces the current page to be the one you go back to. But this won;t always work because if drupal_get_destination() has already been called the destination will now be held in drupal_static(). So what we actually need is this:

drupal_static('drupal_get_destination', NULL, TRUE);
$_GET['destination'] = $_GET['q'];

This wipes out any older version and ensures you get what you want.