Doing CSS aggregation in your theme
Drupal's built-in CSS aggregation works by creating the aggregated files under the "files/css" folder, one could have any number of these as if one css file isn't needed on a particular page compared to another then a new file will be generated and printed appropriately by the $styles variable in page.tpl.php. However, this setup will not work for some people, an example being having drupal running on multiple front end servers with no shared space for "files" (they could be rsynced around from a main server to which editors are adding content) and with page caching turned on. What will happen in this case is there will be windows in which css does not appear on some front end servers because the cached page is being returned without checking to see if the css file actually exists or not.
- Visitor hits server 1 and generates a cached page and aggregated css
- Cached page is served to Visitor, this time from server 2
- CSS file referenced in the cached page does not exist on server 2
A solution to this is to move CSS aggregation duties to template.php and have css files cached under the theme directory (this directory should probably then be excluded from any rsyncing going on between servers, however it will still work even if you don't). Each time a user hits a page on one of the front end servers a string is created from the md5 of the file names and their last modified time. If a file already exists with this string as a name then it's served up, otherwise it's generated and saved in the 'cache' directory.
So firstly make a subdirectory of your theme named 'cache' and change the permissions appropriately so whichever user is executing the code can write to it.
If you're using the Zen theme add this to your sub-theme's template.php (check to see if you already have the function, just add this code into the bottom of it if you do, minus the function declaration) and change STARTERKIT to the name of your theme.
function STARTERKIT_preprocess_page(&$vars) {
$css = drupal_add_css();
$css_arr = array();
$modifiedDates = '';
$fileString = '';
foreach($css['all']['module'] as $css_module => $css_module_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module;
$fileString .= $css_module . ',';
}
}
foreach($css['all']['theme'] as $css_theme => $css_theme_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme;
$fileString .= $css_theme . ',';
}
}
$combinedContent = '';
$fileName = base_path() . path_to_subtheme() . '/cache/' . md5($fileString . $modifiedDates) . '.css';
$file = $_SERVER['DOCUMENT_ROOT'] . $fileName;
if(!file_exists($file)) {
foreach($css_arr as $css_file) {
$combinedContent .= PHP_EOL.PHP_EOL.file_get_contents($css_file);
}
$fh = fopen($file, 'w');
fwrite($fh, $combinedContent);
fclose($fh);
}
$vars['styles_aggregated'] = '<style type="text/css" media="all">@import "' . $fileName . '";</style>';
}If you're using an ordinary theme then you will need to add the following into your template.php (again, you might already have this function).
function _phptemplate_variables($hook, $vars) {
switch($hook) {
case 'page' :
$css = drupal_add_css();
$css_arr = array();
$modifiedDates = '';
$fileString = '';
foreach($css['all']['module'] as $css_module => $css_module_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module;
$fileString .= $css_module . ',';
}
}
foreach($css['all']['theme'] as $css_theme => $css_theme_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme;
$fileString .= $css_theme . ',';
}
}
$combinedContent = '';
$fileName = base_path() . path_to_theme() . '/cache/' . md5($fileString . $modifiedDates) . '.css';
$file = $_SERVER['DOCUMENT_ROOT'] . $fileName;
if(!file_exists($file)) {
foreach($css_arr as $css_file) {
$combinedContent .= PHP_EOL.PHP_EOL.file_get_contents($css_file);
}
$fh = fopen($file, 'w');
fwrite($fh, $combinedContent);
fclose($fh);
}
$vars['styles_aggregated'] = '<style type="text/css" media="all">@import "' . $fileName . '";</style>';
break;
}
return $vars;
}And finally, don't forget to replace the $styles variable in your page.tpl.php with $styles_aggregated
