diff --git a/advagg_relocate/advagg_relocate.admin.inc b/advagg_relocate/advagg_relocate.admin.inc index 50e6683..3b5c8fd 100644 --- a/advagg_relocate/advagg_relocate.admin.inc +++ b/advagg_relocate/advagg_relocate.admin.inc @@ -61,6 +61,14 @@ function advagg_relocate_admin_settings_form() { ), ), ); + $form['css']['per_file_settings']['advagg_relocate_css_file_domains'] = array( + '#type' => 'textarea', + '#title' => t('What external domains are supported'), + '#default_value' => variable_get('advagg_relocate_css_file_domains', ADVAGG_RELOCATE_CSS_FILE_DOMAINS), + '#description' => t('Currently Only fonts.googleapis.com has been tested. Please open an issue if you want to inline import statements for other domains.', array('@url' => 'https://www.drupal.org/project/issues/advagg')), + '#disabled' => TRUE, + ); + $advagg_relocate_css_file_settings = variable_get('advagg_relocate_css_file_settings', array()); foreach ($files_with_import as $filename => $references) { $form_api_filename = str_replace(array('/', '.'), array('__', '--'), $filename); @@ -103,13 +111,11 @@ function advagg_relocate_admin_settings_form() { return system_settings_form($form); } -function _advagg_relocate_admin_is_file_settings($key) { - return strpos($key, 'advagg_relocate_css_file_settings') === 0; -} - // Submit callback. /** * Clear out the advagg cache bin when the save configuration button is pressed. + * + * Also process the advagg_relocate_css_file_settings variable. */ function advagg_relocate_admin_settings_form_submit($form, &$form_state) { // Get settings that start with advagg_relocate_css_file_settings. @@ -147,6 +153,29 @@ function advagg_relocate_admin_settings_form_submit($form, &$form_state) { advagg_cache_clear_admin_submit(); } +// Helper functions. +/** + * See if the key starts with 'advagg_relocate_css_file_settings'. + * + * @param string $key + * They array key as a string. + * + * @return bool + * TRUE if the input string starts with 'advagg_relocate_css_file_settings'. + */ +function _advagg_relocate_admin_is_file_settings($key) { + return strpos($key, 'advagg_relocate_css_file_settings') === 0; +} + +/** + * Check all CSS files, see if any contains an @import that is external. + * + * @param array $files + * An array of filenames to check. + * + * @return array + * An array of filenames that contain an @import that is external. + */ function advagg_relocate_admin_list_files_with_import(array $files = array()) { if (empty($files)) { // Get filename. diff --git a/advagg_relocate/advagg_relocate.advagg.inc b/advagg_relocate/advagg_relocate.advagg.inc index 3b0404a..4c35ea9 100644 --- a/advagg_relocate/advagg_relocate.advagg.inc +++ b/advagg_relocate/advagg_relocate.advagg.inc @@ -20,19 +20,28 @@ function advagg_relocate_advagg_get_css_aggregate_contents_alter(&$data, $files, } if (strpos($data, '@import') !== FALSE) { + // Set values that will be used when preg_replace_callback is ran. _advagg_relocate_callback(array(), $files, $aggregate_settings); + + // Replace external import statements with the contents of them. $data = preg_replace_callback('%@import\s*+(?:url\(\s*+)?+[\'"]?+((?:http:\/\/|https:\/\/|\/\/)(?:[^\'"()\s]++))[\'"]?+\s*+\)?+\s*+;%i', '_advagg_relocate_callback', $data); } } /** - * Implements hook_advagg_get_info_on_files_alter(). + * Gets external CSS files and puts the contents of it in the aggregate. + * + * @param array $matches + * Array of matched items from preg_replace_callback(). + * @param array $files + * List of files with the media type. + * @param array $aggregate_settings + * Array of settings. + * + * @return string + * Contents of the import statement. */ -function advagg_relocate_advagg_get_info_on_files_alter(&$return, $cached_data, $bypass_cache) { - -} - -function _advagg_relocate_callback(array $matches, $files = array(), $aggregate_settings = array()) { +function _advagg_relocate_callback(array $matches = array(), array $files = array(), array $aggregate_settings = array()) { // Store values for preg_replace_callback callback. $_args = &drupal_static(__FUNCTION__, array()); if (!empty($files)) { @@ -60,7 +69,15 @@ function _advagg_relocate_callback(array $matches, $files = array(), $aggregate_ return $matches[0]; } - // Check settings. + // Bail if the host doesn't match one of the listed domains. + if (!isset($_args['aggregate_settings']['variables']['advagg_relocate_css_file_domains'])) { + $_args['aggregate_settings']['variables']['advagg_relocate_css_file_domains'] = variable_get('advagg_relocate_css_file_domains', ADVAGG_RELOCATE_CSS_FILE_DOMAINS); + } + if (strpos($_args['aggregate_settings']['variables']['advagg_relocate_css_file_domains'], $parse['host']) === FALSE) { + return $matches[0]; + } + + // Check per file settings. if (!isset($_args['aggregate_settings']['variables']['advagg_relocate_css_file_settings'])) { $_args['aggregate_settings']['variables']['advagg_relocate_css_file_settings'] = variable_get('advagg_relocate_css_file_settings', array()); } @@ -111,11 +128,21 @@ function _advagg_relocate_callback(array $matches, $files = array(), $aggregate_ $new_css .= "@font-face {\n$output\n}\n"; } - return $new_css; } -function advagg_relocate_get_remote_data($url, $aggregate_settings) { +/** + * Gets external CSS files and puts the contents of it in the aggregate. + * + * @param string $url + * URL of the CSS file to import. + * @param array $aggregate_settings + * Array of settings. + * + * @return array + * Array of font data. + */ +function advagg_relocate_get_remote_data($url, array $aggregate_settings) { // Set settings if needed. if (!isset($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers'])) { $defaults = array( @@ -140,39 +167,56 @@ function advagg_relocate_get_remote_data($url, $aggregate_settings) { $options['headers']['Accept-Encoding'] .= ', br'; } - //$options['headers']['If-None-Match'] = $response->headers['etag']; + // Build CID. + $aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers'] = array_filter($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']); + $fonts = implode(',', $aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']); + $cid = "advagg_relocate_css_inline_import:$fonts:$url"; + // Try local cache. + $cached_data = cache_get($cid, 'cache_advagg_info'); + if (!empty($cached_data->data[0])) { + if ($cached_data->expire >= REQUEST_TIME) { + return $cached_data->data[0]; + } + else { + // Set header for 304 response. + //$options['headers']['If-None-Match'] = $response->headers['etag']; + $options['headers']['If-Modified-Since'] = gmdate('D, d M Y H:i:s T', $cached_data->created); + } + } + + // Get external data. $responses = array(); if (module_exists('httprl')) { // Get ttf. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['ttf']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['ttf'])) { $options['#font-type'] = 'ttf'; httprl_request($url . '#ttf', $options); } // Get eot. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['eot']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['eot'])) { $options['#font-type'] = 'eot'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)'; httprl_request($url . '#eot', $options); } // Get svg. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['svg']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['svg'])) { $options['#font-type'] = 'svg'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (iPad; U; CPU OS 3_2_2 like Mac OS X; nl-nl) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B500 Safari/531.21.10'; httprl_request($url . '#svg', $options); } // Get woff. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff'])) { $options['#font-type'] = 'woff'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)'; httprl_request($url . '#woff', $options); } // Get woff2. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff2']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff2'])) { $options['#font-type'] = 'woff2'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'; httprl_request($url . '#woff2', $options); @@ -182,44 +226,48 @@ function advagg_relocate_get_remote_data($url, $aggregate_settings) { } else { // Get ttf. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['ttf']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['ttf'])) { $options['#font-type'] = 'ttf'; $responses[] = drupal_http_request($url . '#ttf', $options); } // Get eot. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['eot']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['eot'])) { $options['#font-type'] = 'eot'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)'; $responses[] = drupal_http_request($url . '#eot', $options); } // Get svg. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['svg']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['svg'])) { $options['#font-type'] = 'svg'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (iPad; U; CPU OS 3_2_2 like Mac OS X; nl-nl) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B500 Safari/531.21.10'; $responses[] = drupal_http_request($url . '#svg', $options); } // Get woff. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff'])) { $options['#font-type'] = 'woff'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)'; $responses[] = drupal_http_request($url . '#woff', $options); } // Get woff2. - if ($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff2']) { + if (!empty($aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers']['woff2'])) { $options['#font-type'] = 'woff2'; $options['headers']['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'; $responses[] = drupal_http_request($url . '#woff2', $options); } } + // Parse data. $font_faces = array(); + $ttl = 0; + $now = REQUEST_TIME; foreach ($responses as $response) { - if ($response->code == 304) { - + if ($response->code == 304 && !empty($cached_data->data[0])) { + // This might need to be better handled in the future. + return $cached_data->data[0]; } if ($response->code != 200 @@ -254,14 +302,12 @@ function advagg_relocate_get_remote_data($url, $aggregate_settings) { } // Get ttl. - $ttl = 0; if (isset($response->headers['expires'])) { - $ttl = strtotime($response->headers['expires']); - $now = REQUEST_TIME; + $expires = strtotime($response->headers['expires']); if (isset($response->headers['date'])) { - $now = strtotime($response->headers['date']); + $now = max($now, strtotime($response->headers['date'])); } - $ttl = $ttl - $now; + $ttl = max($ttl, $expires - $now); } if (isset($response->headers['cache-control'])) { $cache_control_array = advagg_relocate_parse_cache_control($response->headers['cache-control']); @@ -299,6 +345,9 @@ function advagg_relocate_get_remote_data($url, $aggregate_settings) { } } } + + // Save data to the cache. + cache_set($cid, array($font_faces, $responses), 'cache_advagg_info', REQUEST_TIME + $ttl); return $font_faces; } @@ -328,7 +377,20 @@ function advagg_relocate_parse_cache_control($cache_control) { return $cache_control_parsed; } -function advagg_relocate_parse_css_font_face($css_string, $properties, $type) { +/** + * Parse the font family string into a structured array. + * + * @param string $css_string + * The raw css string. + * @param array $properties + * The css properties to get. + * @param string $type + * The type of font file. + * + * @return array + * Returns a key value array. + */ +function advagg_relocate_parse_css_font_face($css_string, array $properties, $type) { // Get the CSS that contains a font-family rule. $length = strlen($css_string); $property_position = 0; @@ -401,14 +463,12 @@ function advagg_relocate_parse_css_font_face($css_string, $properties, $type) { } } - - // Make sure src is the last one. $temp = $attributes['src']; unset($attributes['src']); $attributes['src'] = $temp; - + // Parse attributes into an output array. $temp = array(); $output = array(); foreach ($attributes as $property => $values) { diff --git a/advagg_relocate/advagg_relocate.module b/advagg_relocate/advagg_relocate.module index 2fdf1e1..9be950c 100644 --- a/advagg_relocate/advagg_relocate.module +++ b/advagg_relocate/advagg_relocate.module @@ -10,6 +10,11 @@ */ define('ADVAGG_RELOCATE_CSS_INLINE_IMPORT', TRUE); +/** + * Default value for supported domains. + */ +define('ADVAGG_RELOCATE_CSS_FILE_DOMAINS', 'fonts.googleapis.com'); + // Core hook implementations. /** * Implements hook_menu(). @@ -33,25 +38,6 @@ function advagg_relocate_menu() { return $items; } -/** - * Implements hook_js_alter(). - */ -function advagg_relocate_js_alter(&$js) { -} - - -/** - * Implements hook_css_alter(). - */ -function advagg_relocate_css_alter(&$css) { -} - -/** - * Implements hook_cron(). - */ -function advagg_relocate_cron() { -} - // Contrib hook implementations. /** * Implements hook_advagg_current_hooks_hash_array_alter(). @@ -67,6 +53,6 @@ function advagg_relocate_advagg_current_hooks_hash_array_alter(&$aggregate_setti $aggregate_settings['variables']['advagg_relocate_css_inline_import_browsers'] = variable_get('advagg_relocate_css_inline_import_browsers', $defaults); $aggregate_settings['variables']['advagg_relocate_css_file_settings'] = variable_get('advagg_relocate_css_file_settings', array()); -} -// Helper functions. + $aggregate_settings['variables']['advagg_relocate_css_file_domains'] = variable_get('advagg_relocate_css_file_domains', ADVAGG_RELOCATE_CSS_FILE_DOMAINS); +}