diff --git a/css_emimage.install b/css_emimage.install index ba91abf..15f2c60 100755 --- a/css_emimage.install +++ b/css_emimage.install @@ -9,6 +9,8 @@ * Implementation of hook_install(). */ function css_emimage_install() { + drupal_install_schema('css_emimage'); + // Set the module's weight high so that it runs after other modules. db_query("UPDATE {system} SET weight = 9999 WHERE name = 'css_emimage' AND type = 'module'"); cache_clear_all(); @@ -22,4 +24,17 @@ function css_emimage_uninstall() { variable_del('css_emimage_force_inline'); variable_del('css_emimage_duplicate_embed_limit'); variable_del('css_emimage_inline_datauri_limit'); + + cache_clear_all('*', 'cache_css_emimage_advagg', TRUE); + drupal_uninstall_schema('css_emimage'); +} + +/** + * Implementation of hook_schema. + */ +function css_emimage_schema() { + $schema['cache_css_emimage_advagg'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_css_emimage_advagg']['description'] = t('Cache table for CSS Embedded Image Advagg integration. Used to keep track of what kind of file the md5 is'); + + return $schema; } diff --git a/css_emimage.module b/css_emimage.module old mode 100755 new mode 100644 index 4c1f392..89dbd3d --- a/css_emimage.module +++ b/css_emimage.module @@ -63,13 +63,374 @@ function css_emimage_form_alter(&$form, &$form_state, $form_id) { } /** + * Implementation of hook_advagg_filenames_alter(). + * + * See if any CSS file in the desired bundle contains a local image in it. + */ +function css_emimage_advagg_filenames_alter(&$filenames) { + global $base_path; + $output = array(); + foreach ($filenames as $values) { + // Set values. + $filetype = $values['filetype']; + $files = $values['files']; + $counter = $values['counter']; + $bundle_md5 = $values['bundle_md5']; + + + // Only operate on CSS files. + if ($filetype != 'css') { + $output[] = $values; + continue; + } + + $cached_data_key = 'css_emimage_' . $bundle_md5; + // Try cache first; cache table is cache_advagg_bundle_reuse. + $cached_data = advagg_cached_bundle_get($cached_data_key, 'css_emimage_filenames_alter'); + if (!empty($cached_data)) { + // verify cache_css_emimage_advagg has the data we need. + $good = TRUE; + foreach ($cached_data as $info) { + $saved = cache_get($info['bundle_md5'], 'cache_css_emimage_advagg'); + if (empty($saved->data)) { + $good = FALSE; + } + } + if ($good) { + $output = array_merge($output, $cached_data); + continue; + } + } + $cached_data = array(); + + // Load up each CSS file. + $has_image = FALSE; + foreach ($files as $filename) { + $filename_md5 = md5($filename); + $data = advagg_get_file_data($filename_md5); + if (!empty($data['css_emimage'])) { + $has_image = TRUE; + break; + } + + if (!isset($data['css_emimage'])) { + // Search css file to see if it contains an image. + if (!advagg_file_exists($filename)) { + continue; + } + $css = file_get_contents($filename); + + // See if CSS file contains an image. + $pattern = '/(background(?:-image)?|list-style(?:-image)?):[^{};)]*?((?:none|url\([\'"]?(.+?)[\'"]?\)))([^{};]*)/i'; + if (preg_match_all($pattern, $css, $matches) > 0) { + $images = $matches[3]; + foreach ($matches[3] as $imagename) { + if (empty($imagename)) { + continue; + } + + // Strip base path from filename. + $imagename = preg_replace('/^' . preg_quote($base_path, '/') .'/i', '', $imagename); + // If not a local file, continue the search. + if (file_exists($filename) == FALSE) { + continue; + } + + // Break on the first local image we hit. + $has_image = TRUE; + break; + } + } + + if (!$has_image) { + $data['css_emimage'] = 0; + advagg_set_file_data($filename_md5, $data); + } + } + + // Jump out of loop if we found a local image. + if ($has_image) { + break; + } + } + + if ($has_image) { + // See if this has already been "processed". + $saved = cache_get($bundle_md5, 'cache_css_emimage_advagg'); + if (!empty($saved->data)) { + if ($saved->data != 'none') { + $output[] = $values; + $cached_data = array($values); + cache_set($cached_data_key, $cached_data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY); + continue; + } + else { + // css file used to not have any images, now it does. + cache_clear_all($bundle_md5, 'cache_advagg'); + } + } + + // Insert base css md5 into cache_css_emimage_advagg. + $values['bundle_md5'] = md5($bundle_md5 . 'base'); + $saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg'); + if (empty($saved->data)) { + cache_set($values['bundle_md5'], 'base', 'cache_css_emimage_advagg'); + } + $output[] = $values; + $cached_data[] = $values; + + // Insert emimage css md5 into cache_css_emimage_advagg. + $values['bundle_md5'] = md5($bundle_md5 . 'emimage'); + $saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg'); + if (empty($saved->data)) { + cache_set($values['bundle_md5'], 'emimage', 'cache_css_emimage_advagg'); + } + $output[] = $values; + $cached_data[] = $values; + + // Insert mhtml css md5 into cache_css_emimage_advagg. + $values['bundle_md5'] = md5($bundle_md5 . 'mhtml'); + $saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg'); + if (empty($saved->data)) { + cache_set($values['bundle_md5'], 'mhtml', 'cache_css_emimage_advagg'); + } + $output[] = $values; + $cached_data[] = $values; + cache_set($cached_data_key, $cached_data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY); + } + else { + // Insert none css md5 into cache_css_emimage_advagg; no processing needed. + $saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg'); + if (empty($saved->data)) { + cache_set($values['bundle_md5'], 'none', 'cache_css_emimage_advagg'); + } + $output[] = $values; + $data = array($values); + cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY); + + // Remove bundle_md5s if file use to have an image and now does not. + $key = md5($bundle_md5 . 'base'); + $saved = cache_get($key, 'cache_css_emimage_advagg'); + if (!empty($saved->data)) { + cache_clear_all($key, 'cache_advagg'); + } + $key = md5($bundle_md5 . 'emimage'); + $saved = cache_get($key, 'cache_css_emimage_advagg'); + if (!empty($saved->data)) { + cache_clear_all($key, 'cache_advagg'); + } + $key = md5($bundle_md5 . 'mhtml'); + $saved = cache_get($key, 'cache_css_emimage_advagg'); + if (!empty($saved->data)) { + cache_clear_all($key, 'cache_advagg'); + } + } + } + + $filenames = $output; +} + +/** + * Implementation of hook_advagg_css_extra_alter(). + * + * Set the CSS prefix and suffix. + */ +function css_emimage_advagg_css_extra_alter(&$values) { + list($filename, $bundle_md5, $prefix, $suffix) = $values; + $type = cache_get($bundle_md5, 'cache_css_emimage_advagg'); + if (!empty($type->data)) { + $type = $type->data; + } + + if ($type == 'base') { + $prefix = ""; + $suffix = ""; + } + elseif ($type == 'emimage') { + $prefix = ""; + $suffix = ""; + } + elseif ($type == 'mhtml') { + $prefix = ""; + } + $values = array($filename, $bundle_md5, $prefix, $suffix); +} + +/** + * Implementation of hook_advagg_css_alter(). + * + * Given CSS data embed images into it. + */ +function css_emimage_advagg_css_alter($data, $files, $bundle_md5) { + $type = cache_get($bundle_md5, 'cache_css_emimage_advagg'); + if (!empty($type->data)) { + $type = $type->data; + } + + if (empty($type)) { + return; + } + if ($type == 'none') { + return; + } + + + // Do magic; code could be improved. + _css_emimage_collect_static(array(array(), array())); // Reset the processed declarations. + $contents = $data; + $datauri_css = ''; + + $pattern = '/([^{}]+){([^{}]*?(background(?:-image)?|list-style(?:-image)?):[^{};)]*?(?:none|url\([\'"]?.+?[\'"]?\))[^{}]*)}/i'; + $contents = preg_replace_callback($pattern, '_css_emimage_replace', $contents); + + if (!is_null($contents)) { + list($declarations, $file_stats) = _css_emimage_collect_static(); + + // Generate MHTML for IE. + // We do this before checking for duplicate images because MHTML can handle duplicates efficiently. + $mhtml_css = _css_emimage_build_mhtml_css("$base_url/$mhtml_file_path", $contents, $declarations, $file_stats); + + // Check for duplicate images and exclude those exceeding our duplication limit. + // Sum the amount of data being embedded. + $datauri_total_length = 0; + foreach ($file_stats as $fs) { + if (count($fs['indices']) > 1 && $fs['total_length'] > variable_get('css_emimage_duplicate_embed_limit', CSS_EMIMAGE_DUPLICATE_EMBED_LIMIT)) { + foreach ($fs['indices'] as $fsi) { + $declarations[$fsi]['base64'] = ''; + } + } + else { + $datauri_total_length += $fs['total_length']; + } + } + + list($ext_contents, $ext_data) = _css_emimage_build_external($contents, $declarations); + + // If the amount of data being embedded is within the inline limit, inline the data URIs; + // otherwise, store the data URIs in a separate CSS file. + if (variable_get('css_emimage_force_inline', 0) || ($datauri_total_length && $datauri_total_length <= variable_get('css_emimage_inline_datauri_limit', CSS_EMIMAGE_INLINE_DATAURI_LIMIT))) { + $inline = _css_emimage_build_inline($contents, $declarations); + if (strlen($inline) < (strlen($ext_contents) + strlen($ext_data))) { + $datauri_css = $inline; + } + else { + $datauri_css = "$ext_contents\n$ext_data"; + } + $contents = ''; + } + else { + $contents = $ext_contents; + $datauri_css = $ext_data; + } + } + else { + $error_code = preg_last_error(); + $error_messages = array(PREG_NO_ERROR => 'NO_ERROR', PREG_INTERNAL_ERROR => 'INTERNAL_ERROR', PREG_BACKTRACK_LIMIT_ERROR => 'BACKTRACK_LIMIT_ERROR', PREG_RECURSION_LIMIT_ERROR => 'RECURSION_LIMIT_ERROR', PREG_BAD_UTF8_ERROR => 'BAD_UTF8_ERROR', PREG_BAD_UTF8_OFFSET_ERROR => 'BAD_UTF8_OFFSET_ERROR'); + watchdog('css_emimage', 'Error while trying to embed images in your CSS, falling back to unmodified CSS. PCRE error was: !error.', + array('!error' => array_key_exists($error_code, $error_messages) ? $error_messages[$error_code] : $error_code), WATCHDOG_ERROR); + return; + } + + // Send data back. + if ($type == 'base') { + $data = $contents; + } + elseif ($type == 'emimage') { + $data = $datauri_css; + } + elseif ($type == 'mhtml') { + $data = $mhtml_css; + } +} + +/** + * Implementation of hook_advagg_files_table(). + * + * See if any images referenced in a CSS file has changed. + */ +function css_emimage_advagg_files_table($row, $checksum) { + global $base_path; + + // Only operate on CSS files. + if ($row['filetype'] != 'css') { + return; + } + + // Load the CSS file. + $images = array(); + $css = advagg_build_css_bundle(array($row['filename'])); + $data = unserialize($row['data']); + + // See if CSS file contains an image. + $pattern = '/(background(?:-image)?|list-style(?:-image)?):[^{};)]*?((?:none|url\([\'"]?(.+?)[\'"]?\)))([^{};]*)/i'; + if (preg_match_all($pattern, $css, $matches) > 0) { + $images = $matches[3]; + } + + // In no images bail out. + if (empty($images)) { + if (isset($data['css_emimage']) && $data['css_emimage'] == 0) { + return; + } + $data['css_emimage'] = 0; + advagg_set_file_data($row['filename_md5'], $data); + return; + } + + + $out = array(); + $rebuild = FALSE; + $save = FALSE; + foreach ($images as $filename) { + if (empty($filename)) { + continue; + } + // Strip base path from filename. + $filename = preg_replace('/^' . preg_quote($base_path, '/') .'/i', '', $filename); + if (file_exists($filename) == FALSE) { + continue; + } + + // Get the checksum of each image. + $checksum = advagg_checksum($filename); + if (!empty($data['css_emimage'][$filename])) { + if ($data['css_emimage'][$filename] != $checksum) { + $rebuild = TRUE; + $save = TRUE; + } + } + else { + $save = TRUE; + } + + if (!is_array($data['css_emimage'])) { + unset($data['css_emimage']); + } + $data['css_emimage'][$filename] = $checksum; + } + + if ($save) { + advagg_set_file_data($row['filename_md5'], $data); + } + return $rebuild; +} + +/** + * Implementation of hook_advagg_master_reset(). + */ +function css_emimage_advagg_master_reset() { + cache_clear_all('*', 'cache_css_emimage_advagg', TRUE); +} + +/** * Implementation of hook_theme_registry_alter(). * * Make css_emimage's page preprocess function run after everything else. * If the css_gzip module is installed, move it's preprocess function after ours. */ function css_emimage_theme_registry_alter(&$theme_registry) { - if (isset($theme_registry['page'])) { + if (isset($theme_registry['page']) && !module_exists('advagg')) { // Move our preprocess function after everything else. if (($key = array_search('css_emimage_preprocess_page', $theme_registry['page']['preprocess functions'])) !== FALSE) { unset($theme_registry['page']['preprocess functions'][$key]);