OMGF | GDPR/DSVGO Compliant, Faster Google Fonts. Easy. - Version 5.0.5

Version Description

  • Added: Compatibility with Mesmerize Pro theme; this theme loads Google Fonts asynchronously, which causes CLS.
  • Added: UNIX timestamp cached stylesheets to make sure browser cache of visitors is busted, upon cache refresh.
  • Fixed: Running Save & Optimize a 2nd time could trigger some firewall rules, due to the serialized array being passed along with the settings form's POST action. This serialized array is now stored in the form using base64_encode() and decoded before being saved to the database.
  • Fixed: Since the Google Fonts API has removed the subsets paramater and returns all subsets by default, OMGF now does the same. Unlike the Google Fonts API, OMGF does still respect and apply the parameter if it set, because it is still used by many themes and plugins.
    • Re-worded Force Subsets (Pro) featured to clarify this behavior.
  • Fixed: Some resource hints that were added using unconventional methods (i.e. not using wp_resource_hints()) weren't removed.
  • Fixed: If no regular Google Fonts stylesheets were present, the omgf_processed_html filter would never be triggered.
  • Fixed: Stylesheets on AMP pages would be rewritten to local stylesheets, while this is not supported by AMP.
  • Removed: AMP handling (Pro) option from Advanced Settings, because it's no longer supported by AMP.
Download this release

Release Info

Developer DaanvandenBergh
Plugin Icon 128x128 OMGF | GDPR/DSVGO Compliant, Faster Google Fonts. Easy.
Version 5.0.5
Comparing to
See all releases

Code changes from version 5.0.4 to 5.0.5

host-webfonts-local.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin Name: OMGF
5
  * Plugin URI: https://ffw.press/wordpress/omgf/
6
  * Description: Increase GDPR compliance, reduce DNS requests and leverage browser cache by automatically downloading Google Fonts to your server.
7
- * Version: 5.0.4
8
  * Author: Daan from FFW.Press
9
  * Author URI: https://ffw.press
10
  * License: GPL2v2 or later
4
  * Plugin Name: OMGF
5
  * Plugin URI: https://ffw.press/wordpress/omgf/
6
  * Description: Increase GDPR compliance, reduce DNS requests and leverage browser cache by automatically downloading Google Fonts to your server.
7
+ * Version: 5.0.5
8
  * Author: Daan from FFW.Press
9
  * Author URI: https://ffw.press
10
  * License: GPL2v2 or later
includes/admin/class-settings.php CHANGED
@@ -23,9 +23,10 @@ class OMGF_Admin_Settings extends OMGF_Admin
23
  /**
24
  * Transients
25
  */
26
- const OMGF_NEWS_REEL = 'omgf_news_reel';
27
- const OMGF_CACHE_IS_STALE = 'omgf_cache_is_stale';
28
- const OMGF_CURRENT_DB_VERSION = 'omgf_current_db_version';
 
29
 
30
  /**
31
  * Settings Fields
@@ -115,10 +116,6 @@ class OMGF_Admin_Settings extends OMGF_Admin
115
  'trebuchet-ms' => 'Trebuchet MS',
116
  'verdana' => 'Verdana'
117
  ];
118
- const OMGF_AMP_HANDLING_OPTIONS = [
119
- 'fallback' => 'Fallback (default)',
120
- 'disable' => 'Disable'
121
- ];
122
 
123
  /**
124
  * Optimize Fonts
23
  /**
24
  * Transients
25
  */
26
+ const OMGF_NEWS_REEL = 'omgf_news_reel';
27
+ const OMGF_CACHE_IS_STALE = 'omgf_cache_is_stale';
28
+ const OMGF_CURRENT_DB_VERSION = 'omgf_current_db_version';
29
+ const OMGF_CACHE_TIMESTAMP = 'omgf_cache_timestamp';
30
 
31
  /**
32
  * Settings Fields
116
  'trebuchet-ms' => 'Trebuchet MS',
117
  'verdana' => 'Verdana'
118
  ];
 
 
 
 
119
 
120
  /**
121
  * Optimize Fonts
includes/admin/settings/class-advanced.php CHANGED
@@ -33,7 +33,6 @@ class OMGF_Admin_Settings_Advanced extends OMGF_Admin_Settings_Builder
33
  add_filter('omgf_advanced_settings_content', [$this, 'do_before'], 20);
34
 
35
  // Settings
36
- add_filter('omgf_advanced_settings_content', [$this, 'do_promo_amp_handling'], 40);
37
  add_filter('omgf_advanced_settings_content', [$this, 'do_cache_dir'], 70);
38
  add_filter('omgf_advanced_settings_content', [$this, 'do_promo_fonts_source_url'], 80);
39
  add_filter('omgf_advanced_settings_content', [$this, 'do_uninstall'], 110);
@@ -54,19 +53,6 @@ class OMGF_Admin_Settings_Advanced extends OMGF_Admin_Settings_Builder
54
  <?php
55
  }
56
 
57
- public function do_promo_amp_handling()
58
- {
59
- $this->do_select(
60
- __('AMP handling (Pro)', $this->plugin_text_domain),
61
- 'omgf_pro_amp_handling',
62
- OMGF_Admin_Settings::OMGF_AMP_HANDLING_OPTIONS,
63
- defined('OMGF_PRO_AMP_HANDLING') ? OMGF_PRO_AMP_HANDLING : '',
64
- sprintf(__("Decide how OMGF Pro should behave on AMP pages. Only select <strong>enable</strong> if the custom CSS limit of 75kb is not already reached by your theme and/or other plugins and no other <code>amp-custom</code> tag is present on your pages.", $this->plugin_text_domain), OMGF_Admin_Settings::FFWP_WORDPRESS_PLUGINS_OMGF_PRO) . ' ' . $this->promo,
65
- false,
66
- true
67
- );
68
- }
69
-
70
  /**
71
  *
72
  */
33
  add_filter('omgf_advanced_settings_content', [$this, 'do_before'], 20);
34
 
35
  // Settings
 
36
  add_filter('omgf_advanced_settings_content', [$this, 'do_cache_dir'], 70);
37
  add_filter('omgf_advanced_settings_content', [$this, 'do_promo_fonts_source_url'], 80);
38
  add_filter('omgf_advanced_settings_content', [$this, 'do_uninstall'], 110);
53
  <?php
54
  }
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  /**
57
  *
58
  */
includes/admin/settings/class-builder.php CHANGED
@@ -96,7 +96,7 @@ class OMGF_Admin_Settings_Builder
96
  <td>
97
  <?php foreach ($inputs as $option => $option_label) : ?>
98
  <label>
99
- <input type="radio" <?= strpos($option_label, '(Pro)') !== false ? apply_filters($name . '_' . $option . '_setting_disabled', 'disabled') : ''; ?> class="<?= str_replace('_', '-', $name . '_' . $option); ?>" name="<?= $name; ?>" value="<?= $option; ?>" <?= $option == $checked ? 'checked="checked"' : ''; ?> />
100
  <?= $option_label; ?>
101
  </label>
102
  <br />
@@ -139,7 +139,7 @@ class OMGF_Admin_Settings_Builder
139
  $is_selected = $selected == $option;
140
  }
141
  ?>
142
- <option value="<?= $option; ?>" <?= $is_selected ? 'selected="selected"' : ''; ?>><?= $option_label; ?></option>
143
  <?php endforeach; ?>
144
  </select>
145
  <p class="description">
@@ -164,7 +164,7 @@ class OMGF_Admin_Settings_Builder
164
  <tr valign="top" <?= $visible ? '' : 'style="display: none;"'; ?>>
165
  <th scope="row"><?= apply_filters($name . '_setting_label', $label); ?></th>
166
  <td>
167
- <input class="<?= str_replace('_', '-', $name); ?>" type="number" name="<?= $name; ?>" min="<?= $min; ?>" value="<?= $value; ?>" />
168
  <p class="description">
169
  <?= apply_filters($name . '_setting_description', $description); ?>
170
  </p>
@@ -189,7 +189,7 @@ class OMGF_Admin_Settings_Builder
189
  <tr class="<?= str_replace('_', '-', $name); ?>-row">
190
  <th scope="row"><?= apply_filters($name . '_setting_label', $label); ?></th>
191
  <td>
192
- <input <?= apply_filters($name . '_setting_disabled', $disabled) ? 'disabled' : ''; ?> class="<?= str_replace('_', '-', $name); ?>" type="text" name="<?= $name; ?>" placeholder="<?= $placeholder; ?>" value="<?= $value; ?>" />
193
  <p class="description">
194
  <?= apply_filters($name . 'setting_description', $description); ?>
195
  </p>
96
  <td>
97
  <?php foreach ($inputs as $option => $option_label) : ?>
98
  <label>
99
+ <input type="radio" <?= strpos($option_label, '(Pro)') !== false ? apply_filters($name . '_' . $option . '_setting_disabled', 'disabled') : ''; ?> class="<?= str_replace('_', '-', $name . '_' . $option); ?>" name="<?= $name; ?>" value="<?= esc_attr($option); ?>" <?= $option == $checked ? 'checked="checked"' : ''; ?> />
100
  <?= $option_label; ?>
101
  </label>
102
  <br />
139
  $is_selected = $selected == $option;
140
  }
141
  ?>
142
+ <option value="<?= esc_attr($option); ?>" <?= $is_selected ? 'selected="selected"' : ''; ?>><?= $option_label; ?></option>
143
  <?php endforeach; ?>
144
  </select>
145
  <p class="description">
164
  <tr valign="top" <?= $visible ? '' : 'style="display: none;"'; ?>>
165
  <th scope="row"><?= apply_filters($name . '_setting_label', $label); ?></th>
166
  <td>
167
+ <input class="<?= str_replace('_', '-', $name); ?>" type="number" name="<?= $name; ?>" min="<?= $min; ?>" value="<?= esc_attr($value); ?>" />
168
  <p class="description">
169
  <?= apply_filters($name . '_setting_description', $description); ?>
170
  </p>
189
  <tr class="<?= str_replace('_', '-', $name); ?>-row">
190
  <th scope="row"><?= apply_filters($name . '_setting_label', $label); ?></th>
191
  <td>
192
+ <input <?= apply_filters($name . '_setting_disabled', $disabled) ? 'disabled' : ''; ?> class="<?= str_replace('_', '-', $name); ?>" type="text" name="<?= $name; ?>" placeholder="<?= $placeholder; ?>" value="<?= esc_attr($value); ?>" />
193
  <p class="description">
194
  <?= apply_filters($name . 'setting_description', $description); ?>
195
  </p>
includes/admin/settings/class-optimize.php CHANGED
@@ -217,7 +217,7 @@ class OMGF_Admin_Settings_Optimize extends OMGF_Admin_Settings_Builder
217
  'omgf_pro_force_subsets',
218
  OMGF_Admin_Settings::OMGF_FORCE_SUBSETS_OPTIONS,
219
  defined('OMGF_PRO_FORCE_SUBSETS') ? OMGF_PRO_FORCE_SUBSETS : [],
220
- __('If a theme or plugin loads subsets you don\'t need, use this option to force all Google Fonts to be loaded in the selected subsets. You can also use this option to force the loading of additional subsets, if a theme/plugin doesn\'t allow you to configure the loaded subsets. <em>Use CTRL + click to select multiple values</em>.', $this->plugin_text_domain) . ' ' . $this->promo,
221
  true,
222
  true
223
  );
@@ -343,9 +343,9 @@ class OMGF_Admin_Settings_Optimize extends OMGF_Admin_Settings_Builder
343
  <em><?= sprintf(__("This list is populated with all Google Fonts stylesheets captured and downloaded throughout your site. It will grow organically if other Google Fonts stylesheets are discovered throughout your site.", $this->plugin_text_domain), get_site_url()); ?></em>
344
  </p>
345
  </div>
346
- <input type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS; ?>" value='<?= serialize($this->optimized_fonts); ?>' />
347
- <input id="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" value="<?= OMGF_UNLOAD_STYLESHEETS; ?>" />
348
- <input id="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS; ?>" type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS; ?>" value="<?= implode(',', $cache_handles); ?>" />
349
  <?php echo apply_filters('omgf_optimize_fonts_hidden_fields', ''); ?>
350
  </div>
351
  <?php
217
  'omgf_pro_force_subsets',
218
  OMGF_Admin_Settings::OMGF_FORCE_SUBSETS_OPTIONS,
219
  defined('OMGF_PRO_FORCE_SUBSETS') ? OMGF_PRO_FORCE_SUBSETS : [],
220
+ __('By default, Google Fonts are loaded in all subsets, which results in unnecessarily large stylesheets. Use this option to force all Google Fonts to be loaded in the selected subset(s). <em>Use CTRL + click to select multiple values</em>.', $this->plugin_text_domain) . ' ' . $this->promo,
221
  true,
222
  true
223
  );
343
  <em><?= sprintf(__("This list is populated with all Google Fonts stylesheets captured and downloaded throughout your site. It will grow organically if other Google Fonts stylesheets are discovered throughout your site.", $this->plugin_text_domain), get_site_url()); ?></em>
344
  </p>
345
  </div>
346
+ <input type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS; ?>" value="<?= base64_encode(serialize($this->optimized_fonts)); ?>" />
347
+ <input id="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_STYLESHEETS; ?>" value="<?= esc_attr(OMGF_UNLOAD_STYLESHEETS); ?>" />
348
+ <input id="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS; ?>" type="hidden" name="<?= OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS; ?>" value="<?= esc_attr(implode(',', $cache_handles)); ?>" />
349
  <?php echo apply_filters('omgf_optimize_fonts_hidden_fields', ''); ?>
350
  </div>
351
  <?php
includes/class-admin.php CHANGED
@@ -51,10 +51,7 @@ class OMGF_Admin
51
  $this->do_help();
52
  $this->maybe_do_after_update_notice();
53
 
54
- /**
55
- * @since v4.7.0 Fixes a bug where the Optimized Fonts wouldn't be shown after page reload.
56
- */
57
- add_filter('pre_update_option_omgf_optimized_fonts', [$this, 'update_optimized_fonts'], 10, 2);
58
  add_filter('pre_update_option_omgf_cache_keys', [$this, 'clean_up_cache'], 10, 3);
59
  add_action('pre_update_option_omgf_cache_dir', [$this, 'validate_cache_dir'], 10, 2);
60
  add_filter('pre_update_option', [$this, 'settings_changed'], 10, 3);
@@ -136,17 +133,25 @@ class OMGF_Admin
136
  }
137
 
138
  /**
139
- * This fixes a bug where the admin screen wouldn't properly be updated after omgf_optimized_fonts
140
- * was updated by the API.
141
  *
142
- * @param $old_value
143
- * @param $value
144
- *
145
- * @return bool|array
 
146
  */
147
- public function update_optimized_fonts($value, $old_value)
148
  {
149
- return $old_value;
 
 
 
 
 
 
 
150
  }
151
 
152
  /**
51
  $this->do_help();
52
  $this->maybe_do_after_update_notice();
53
 
54
+ add_filter('alloptions', [$this, 'force_optimized_fonts_from_db']);
 
 
 
55
  add_filter('pre_update_option_omgf_cache_keys', [$this, 'clean_up_cache'], 10, 3);
56
  add_action('pre_update_option_omgf_cache_dir', [$this, 'validate_cache_dir'], 10, 2);
57
  add_filter('pre_update_option', [$this, 'settings_changed'], 10, 3);
133
  }
134
 
135
  /**
136
+ * @since v5.0.5 Forces get_option() to fetch a fresh copy of omgf_optimized_fonts from the database,
137
+ * we're doing plenty to limit reads from the DB already. So, this is warranted.
138
  *
139
+ * @see OMGF::optimized_fonts()
140
+ *
141
+ * @param array $alloptions
142
+ *
143
+ * @return array
144
  */
145
+ public function force_optimized_fonts_from_db($alloptions)
146
  {
147
+ if (
148
+ isset($alloptions[OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS])
149
+ && $alloptions[OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS] == false
150
+ ) {
151
+ unset($alloptions[OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS]);
152
+ }
153
+
154
+ return $alloptions;
155
  }
156
 
157
  /**
includes/class-ajax.php CHANGED
@@ -162,6 +162,7 @@ class OMGF_AJAX
162
  'exclude' => [],
163
  'queue' => [
164
  OMGF_Admin_Settings::OMGF_CACHE_IS_STALE,
 
165
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS,
166
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS,
167
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_FONTS,
162
  'exclude' => [],
163
  'queue' => [
164
  OMGF_Admin_Settings::OMGF_CACHE_IS_STALE,
165
+ OMGF_Admin_Settings::OMGF_CACHE_TIMESTAMP,
166
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_CACHE_KEYS,
167
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_OPTIMIZED_FONTS,
168
  OMGF_Admin_Settings::OMGF_OPTIMIZE_SETTING_UNLOAD_FONTS,
includes/class-omgf.php CHANGED
@@ -36,6 +36,7 @@ class OMGF
36
  }
37
 
38
  add_action('admin_init', [$this, 'do_optimize']);
 
39
  add_filter('content_url', [$this, 'force_ssl'], 1000, 2);
40
 
41
  /**
@@ -99,6 +100,24 @@ class OMGF
99
  return new OMGF_Admin_Optimize();
100
  }
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  /**
103
  * content_url() uses is_ssl() to detect whether SSL is used. This fails for servers behind
104
  * load balancers and/or reverse proxies. So, we double check with this filter.
36
  }
37
 
38
  add_action('admin_init', [$this, 'do_optimize']);
39
+ add_filter('pre_update_option_omgf_optimized_fonts', [$this, 'base64_decode_optimized_fonts']);
40
  add_filter('content_url', [$this, 'force_ssl'], 1000, 2);
41
 
42
  /**
100
  return new OMGF_Admin_Optimize();
101
  }
102
 
103
+ /**
104
+ * @since v5.0.5 omgf_optimized_fonts is base64_encoded in the frontend, to bypass firewall restrictions on
105
+ * some servers.
106
+ *
107
+ * @param $old_value
108
+ * @param $value
109
+ *
110
+ * @return bool|array
111
+ */
112
+ public function base64_decode_optimized_fonts($value)
113
+ {
114
+ if (is_string($value) && base64_decode($value, true)) {
115
+ return base64_decode($value);
116
+ }
117
+
118
+ return $value;
119
+ }
120
+
121
  /**
122
  * content_url() uses is_ssl() to detect whether SSL is used. This fails for servers behind
123
  * load balancers and/or reverse proxies. So, we double check with this filter.
includes/class-optimize.php CHANGED
@@ -43,7 +43,7 @@ class OMGF_Optimize
43
  private $original_handle = '';
44
 
45
  /** @var string $subset */
46
- private $subset = 'latin';
47
 
48
  /** @var string $return */
49
  private $return = 'url';
@@ -58,7 +58,7 @@ class OMGF_Optimize
58
  * @param string $family Contents of "family" parameters in Google Fonts API URL, e.g. "?family="Lato:100,200,300,etc."
59
  * @param string $handle The cache handle, generated using $handle + 5 random chars. Used for storing the fonts and stylesheet.
60
  * @param string $original_handle The stylesheet handle, present in the ID attribute.
61
- * @param string $subset Contents of "subset" parameter, defaults to 'latin', because most fonts are available in this subset.
62
  * @param string $return Valid values: 'url' | 'path' | 'object'.
63
  *
64
  * @return string Local URL of generated stylesheet.
@@ -79,7 +79,7 @@ class OMGF_Optimize
79
  $this->family = $family;
80
  $this->handle = sanitize_title_with_dashes($handle);
81
  $this->original_handle = sanitize_title_with_dashes($original_handle);
82
- $this->subset = $subset ?: 'latin';
83
  $this->path = OMGF_CACHE_PATH . '/' . $this->handle;
84
  $this->return = $return;
85
  }
@@ -101,9 +101,13 @@ class OMGF_Optimize
101
  return '';
102
  }
103
 
104
- $font_families = explode('|', $this->family);
105
- $query['subsets'] = $this->subset;
106
- $fonts = [];
 
 
 
 
107
 
108
  foreach ($font_families as $font_family) {
109
  if (empty($font_family)) {
@@ -271,19 +275,15 @@ class OMGF_Optimize
271
  * an alternate API.
272
  */
273
  $alternate_fonts = apply_filters('omgf_alternate_fonts', []);
274
- $alternate_url = false;
275
- $url = '';
276
 
277
  if (in_array($family, array_keys($alternate_fonts))) {
278
- $alternate_url = true;
279
-
280
- $url = apply_filters('omgf_alternate_api_url', '', $family);
281
  unset($query);
282
  }
283
 
284
- $query_string = '';
285
-
286
- if (isset($query)) {
287
  $query_string = '?' . http_build_query($query);
288
  }
289
 
@@ -295,19 +295,10 @@ class OMGF_Optimize
295
  }
296
 
297
  if (!$alternate_url) {
298
- $response = wp_remote_get(
299
- sprintf(self::OMGF_GOOGLE_FONTS_API_URL . '%s', $family) . $query_string
300
- );
301
-
302
- // Try with mirror, if first request failed.
303
- if (is_wp_error($response) && $response->get_error_code() == 'http_request_failed') {
304
- $response = wp_remote_get(
305
- sprintf(self::OMGF_GOOGLE_FONTS_API_FALLBACK . '%s', $family) . $query_string
306
- );
307
- }
308
  } else {
309
  $response = wp_remote_get(
310
- sprintf($url . '%s', $family) . $query_string
311
  );
312
  }
313
 
@@ -315,6 +306,16 @@ class OMGF_Optimize
315
  OMGF_Admin_Notice::set_notice(sprintf(__('OMGF encountered an error while trying to fetch fonts: %s', $this->plugin_text_domain), $response->get_error_message()), $response->get_error_code(), 'error', 408);
316
  }
317
 
 
 
 
 
 
 
 
 
 
 
318
  $response_code = wp_remote_retrieve_response_code($response);
319
 
320
  if ($response_code != 200) {
@@ -349,6 +350,30 @@ class OMGF_Optimize
349
  return json_decode(wp_remote_retrieve_body($response));
350
  }
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  /**
353
  * @param $font_families
354
  * @param $font
43
  private $original_handle = '';
44
 
45
  /** @var string $subset */
46
+ private $subset = '';
47
 
48
  /** @var string $return */
49
  private $return = 'url';
58
  * @param string $family Contents of "family" parameters in Google Fonts API URL, e.g. "?family="Lato:100,200,300,etc."
59
  * @param string $handle The cache handle, generated using $handle + 5 random chars. Used for storing the fonts and stylesheet.
60
  * @param string $original_handle The stylesheet handle, present in the ID attribute.
61
+ * @param string $subset Contents of "subset" parameter. If left empty, the downloaded files will support all subsets.
62
  * @param string $return Valid values: 'url' | 'path' | 'object'.
63
  *
64
  * @return string Local URL of generated stylesheet.
79
  $this->family = $family;
80
  $this->handle = sanitize_title_with_dashes($handle);
81
  $this->original_handle = sanitize_title_with_dashes($original_handle);
82
+ $this->subset = $subset;
83
  $this->path = OMGF_CACHE_PATH . '/' . $this->handle;
84
  $this->return = $return;
85
  }
101
  return '';
102
  }
103
 
104
+ $font_families = explode('|', $this->family);
105
+ $query = [];
106
+ $fonts = [];
107
+
108
+ if ($this->subset) {
109
+ $query['subsets'] = $this->subset;
110
+ }
111
 
112
  foreach ($font_families as $font_family) {
113
  if (empty($font_family)) {
275
  * an alternate API.
276
  */
277
  $alternate_fonts = apply_filters('omgf_alternate_fonts', []);
278
+ $alternate_url = '';
279
+ $query_string = '';
280
 
281
  if (in_array($family, array_keys($alternate_fonts))) {
282
+ $alternate_url = apply_filters('omgf_alternate_api_url', '', $family);
 
 
283
  unset($query);
284
  }
285
 
286
+ if (!empty($query)) {
 
 
287
  $query_string = '?' . http_build_query($query);
288
  }
289
 
295
  }
296
 
297
  if (!$alternate_url) {
298
+ $response = $this->remote_get($family, $query_string);
 
 
 
 
 
 
 
 
 
299
  } else {
300
  $response = wp_remote_get(
301
+ sprintf($alternate_url . '%s', $family) . $query_string
302
  );
303
  }
304
 
306
  OMGF_Admin_Notice::set_notice(sprintf(__('OMGF encountered an error while trying to fetch fonts: %s', $this->plugin_text_domain), $response->get_error_message()), $response->get_error_code(), 'error', 408);
307
  }
308
 
309
+ /**
310
+ * If no subset was set, do a quick refresh to make sure all available subsets are included.
311
+ */
312
+ if (!$query_string && !$alternate_url) {
313
+ $response_body = wp_remote_retrieve_body($response);
314
+ $body = json_decode($response_body);
315
+ $query_string = '?subsets=' . (isset($body->subsets) ? implode(',', $body->subsets) : 'latin,latin-ext');
316
+ $response = $this->remote_get($family, $query_string);
317
+ }
318
+
319
  $response_code = wp_remote_retrieve_response_code($response);
320
 
321
  if ($response_code != 200) {
350
  return json_decode(wp_remote_retrieve_body($response));
351
  }
352
 
353
+ /**
354
+ * Wrapper for wp_remote_get() which tries a mirror API if the first request fails. (It tends to timeout sometimes)
355
+ *
356
+ * @param string $family
357
+ * @param string $query
358
+ *
359
+ * @return array|WP_Error
360
+ */
361
+ private function remote_get($family, $query)
362
+ {
363
+ $response = wp_remote_get(
364
+ sprintf(self::OMGF_GOOGLE_FONTS_API_URL . '%s', $family) . $query
365
+ );
366
+
367
+ // Try with mirror, if first request failed.
368
+ if (is_wp_error($response) && $response->get_error_code() == 'http_request_failed') {
369
+ $response = wp_remote_get(
370
+ sprintf(self::OMGF_GOOGLE_FONTS_API_FALLBACK . '%s', $family) . $query
371
+ );
372
+ }
373
+
374
+ return $response;
375
+ }
376
+
377
  /**
378
  * @param $font_families
379
  * @param $font
includes/frontend/class-process.php CHANGED
@@ -34,11 +34,22 @@ class OMGF_Frontend_Process
34
  'vc_action'
35
  ];
36
 
 
 
 
37
  /**
38
  * OMGF_Frontend_Functions constructor.
39
  */
40
  public function __construct()
41
  {
 
 
 
 
 
 
 
 
42
  $this->init();
43
  }
44
 
@@ -56,13 +67,16 @@ class OMGF_Frontend_Process
56
  return;
57
  }
58
 
 
59
  add_action('template_redirect', [$this, 'maybe_buffer_output'], 3);
 
60
  add_filter('omgf_buffer_output', [$this, 'parse']);
61
- add_action('wp_head', [$this, 'add_preloads'], 3);
62
- add_filter('wp_resource_hints', [$this, 'remove_resource_hints']);
63
 
64
  /** Smart Slider 3 compatibility */
65
  add_filter('wordpress_prepare_output', [$this, 'parse'], 11);
 
 
 
66
  }
67
 
68
  /**
@@ -113,31 +127,6 @@ class OMGF_Frontend_Process
113
  }
114
  }
115
 
116
- /**
117
- * We're downloading the fonts, so preconnecting to Google is a waste of time. Literally.
118
- *
119
- * @param array $urls
120
- * @return array
121
- */
122
- public function remove_resource_hints($urls)
123
- {
124
- foreach ($urls as $key => &$url) {
125
- if (is_array($url)) {
126
- $url = $this->remove_resource_hints($url);
127
-
128
- continue;
129
- }
130
-
131
- foreach (self::RESOURCE_HINTS as $hint) {
132
- if (strpos($url, $hint) !== false) {
133
- unset($urls[$key]);
134
- }
135
- }
136
- }
137
-
138
- return $urls;
139
- }
140
-
141
  /**
142
  * Start output buffer.
143
  *
@@ -148,7 +137,7 @@ class OMGF_Frontend_Process
148
  public function maybe_buffer_output()
149
  {
150
  /**
151
- * Always run, if the omgf_optimize (added by Save & Optimize) is set.
152
  */
153
  if (isset($_GET['omgf_optimize'])) {
154
  return ob_start([$this, 'return_buffer']);
@@ -231,6 +220,39 @@ class OMGF_Frontend_Process
231
  return apply_filters('omgf_buffer_output', $html);
232
  }
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  /**
235
  * This method uses Regular Expressions to parse the HTML. It's tested to be at least
236
  * twice as fast compared to using Xpath.
@@ -246,23 +268,27 @@ class OMGF_Frontend_Process
246
  *
247
  * Using Xpath proved to be untestable, because it varied anywhere between 38 seconds and, well, timeouts.
248
  *
249
- * @param string $html
250
  *
251
- * @return string
252
  */
253
  public function parse($html)
254
  {
 
 
 
 
255
  preg_match_all('/<link.*fonts\.googleapis\.com\/css.*?[\/]?>/', $html, $links);
256
 
257
  if (!isset($links[0]) || empty($links[0])) {
258
- return $html;
259
  }
260
 
261
  $google_fonts = $this->build_fonts_set($links[0]);
262
  $search_replace = $this->build_search_replace($google_fonts);
263
 
264
  if (empty($search_replace['search']) || empty($search_replace['replace'])) {
265
- return $html;
266
  }
267
 
268
  $html = str_replace($search_replace['search'], $search_replace['replace'], $html);
@@ -270,6 +296,17 @@ class OMGF_Frontend_Process
270
  return apply_filters('omgf_processed_html', $html, $this);
271
  }
272
 
 
 
 
 
 
 
 
 
 
 
 
273
  /**
274
  * Builds a processable array of Google Fonts' ID and (external) URL.
275
  *
@@ -396,7 +433,7 @@ class OMGF_Frontend_Process
396
  */
397
  if (!isset($_GET['omgf_optimize']) && file_exists(OMGF_CACHE_PATH . "/$handle/$handle.css")) {
398
  $search[$key] = $stack['href'];
399
- $replace[$key] = content_url(OMGF_CACHE_DIR . "/$handle/$handle.css");
400
 
401
  continue;
402
  }
@@ -418,7 +455,7 @@ class OMGF_Frontend_Process
418
  }
419
 
420
  $search[$key] = $stack['href'];
421
- $replace[$key] = $cached_url;
422
  }
423
 
424
  return ['search' => $search, 'replace' => $replace];
@@ -523,4 +560,20 @@ class OMGF_Frontend_Process
523
 
524
  return ['family' => implode('|', $fonts)];
525
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  }
34
  'vc_action'
35
  ];
36
 
37
+ /** @var string $timestamp */
38
+ private $timestamp = '';
39
+
40
  /**
41
  * OMGF_Frontend_Functions constructor.
42
  */
43
  public function __construct()
44
  {
45
+ $this->timestamp = get_option(OMGF_Admin_Settings::OMGF_CACHE_TIMESTAMP, '');
46
+
47
+ if (!$this->timestamp) {
48
+ $this->timestamp = time();
49
+
50
+ update_option(OMGF_Admin_settings::OMGF_CACHE_TIMESTAMP, $this->timestamp);
51
+ }
52
+
53
  $this->init();
54
  }
55
 
67
  return;
68
  }
69
 
70
+ add_action('wp_head', [$this, 'add_preloads'], 3);
71
  add_action('template_redirect', [$this, 'maybe_buffer_output'], 3);
72
+ add_filter('omgf_buffer_output', [$this, 'remove_resource_hints'], 9);
73
  add_filter('omgf_buffer_output', [$this, 'parse']);
 
 
74
 
75
  /** Smart Slider 3 compatibility */
76
  add_filter('wordpress_prepare_output', [$this, 'parse'], 11);
77
+
78
+ /** Mesmerize Pro theme compatibility */
79
+ add_filter('style_loader_tag', [$this, 'remove_mesmerize_filter'], 12, 1);
80
  }
81
 
82
  /**
127
  }
128
  }
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  /**
131
  * Start output buffer.
132
  *
137
  public function maybe_buffer_output()
138
  {
139
  /**
140
+ * Always run, if the omgf_optimize parameter (added by Save & Optimize) is set.
141
  */
142
  if (isset($_GET['omgf_optimize'])) {
143
  return ob_start([$this, 'return_buffer']);
220
  return apply_filters('omgf_buffer_output', $html);
221
  }
222
 
223
+ /**
224
+ * We're downloading the fonts, so preconnecting to Google is a waste of time. Literally.
225
+ *
226
+ * @since v5.0.5 Use a regular expression to match all resource hints.
227
+ *
228
+ * @param string $html Valid HTML.
229
+ *
230
+ * @return string Valid HTML.
231
+ */
232
+ public function remove_resource_hints($html)
233
+ {
234
+ preg_match_all('/<link.*?(dns-prefetch|preconnect).*?>/', $html, $resource_hints);
235
+
236
+ if (!isset($resource_hints[0]) || empty($resource_hints[0])) {
237
+ return $html;
238
+ }
239
+
240
+ $search = [];
241
+
242
+ foreach ($resource_hints[0] as $key => $match) {
243
+ if (
244
+ strpos($match, 'fonts.googleapis.com') === false
245
+ && strpos($match, 'fonts.gstatic.com') === false
246
+ ) {
247
+ continue;
248
+ }
249
+
250
+ $search[] = $match;
251
+ }
252
+
253
+ return str_replace($search, '', $html);
254
+ }
255
+
256
  /**
257
  * This method uses Regular Expressions to parse the HTML. It's tested to be at least
258
  * twice as fast compared to using Xpath.
268
  *
269
  * Using Xpath proved to be untestable, because it varied anywhere between 38 seconds and, well, timeouts.
270
  *
271
+ * @param string $html Valid HTML.
272
  *
273
+ * @return string Valid HTML, filtered by @filter omgf_processed_html.
274
  */
275
  public function parse($html)
276
  {
277
+ if ($this->is_amp()) {
278
+ return apply_filters('omgf_processed_html', $html, $this);
279
+ }
280
+
281
  preg_match_all('/<link.*fonts\.googleapis\.com\/css.*?[\/]?>/', $html, $links);
282
 
283
  if (!isset($links[0]) || empty($links[0])) {
284
+ return apply_filters('omgf_processed_html', $html, $this);
285
  }
286
 
287
  $google_fonts = $this->build_fonts_set($links[0]);
288
  $search_replace = $this->build_search_replace($google_fonts);
289
 
290
  if (empty($search_replace['search']) || empty($search_replace['replace'])) {
291
+ return apply_filters('omgf_processed_html', $html, $this);
292
  }
293
 
294
  $html = str_replace($search_replace['search'], $search_replace['replace'], $html);
296
  return apply_filters('omgf_processed_html', $html, $this);
297
  }
298
 
299
+ /**
300
+ * @since v5.0.5 Check if current page is AMP page.
301
+ *
302
+ * @return bool
303
+ */
304
+ private function is_amp()
305
+ {
306
+ return (function_exists('is_amp_endpoint') && is_amp_endpoint())
307
+ || (function_exists('ampforwp_is_amp_endpoint') && ampforwp_is_amp_endpoint());
308
+ }
309
+
310
  /**
311
  * Builds a processable array of Google Fonts' ID and (external) URL.
312
  *
433
  */
434
  if (!isset($_GET['omgf_optimize']) && file_exists(OMGF_CACHE_PATH . "/$handle/$handle.css")) {
435
  $search[$key] = $stack['href'];
436
+ $replace[$key] = content_url(OMGF_CACHE_DIR . "/$handle/$handle.css") . '?ver=' . $this->timestamp;
437
 
438
  continue;
439
  }
455
  }
456
 
457
  $search[$key] = $stack['href'];
458
+ $replace[$key] = $cached_url . '?ver=' . $this->timestamp;
459
  }
460
 
461
  return ['search' => $search, 'replace' => $replace];
560
 
561
  return ['family' => implode('|', $fonts)];
562
  }
563
+
564
+ /**
565
+ * Because all great themes come packed with extra Cumulative Layout Shifting.
566
+ *
567
+ * @param string $tag
568
+ *
569
+ * @return string
570
+ */
571
+ public function remove_mesmerize_filter($tag)
572
+ {
573
+ if (wp_get_theme()->template == 'mesmerize-pro' && strpos($tag, 'fonts.googleapis.com') !== false) {
574
+ return str_replace('href="" data-href', 'href', $tag);
575
+ }
576
+
577
+ return $tag;
578
+ }
579
  }
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: DaanvandenBergh
3
  Tags: google, fonts, gdpr, cache, speed, preload, font-display, webfonts, subsets, remove, minimize, external, requests
4
  Requires at least: 4.6
5
  Tested up to: 5.9
6
- Stable tag: 5.0.4
7
  Requires PHP: 7.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -75,6 +75,17 @@ For the FAQ, [click here](https://docs.ffw.press/category/76-omgf-pro---faq).
75
 
76
  == Changelog ==
77
 
 
 
 
 
 
 
 
 
 
 
 
78
  = 5.0.4 =
79
  * Fixed: don't allow starting buffer twice.
80
 
3
  Tags: google, fonts, gdpr, cache, speed, preload, font-display, webfonts, subsets, remove, minimize, external, requests
4
  Requires at least: 4.6
5
  Tested up to: 5.9
6
+ Stable tag: 5.0.5
7
  Requires PHP: 7.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
75
 
76
  == Changelog ==
77
 
78
+ = 5.0.5 =
79
+ * Added: Compatibility with Mesmerize Pro theme; this theme loads Google Fonts asynchronously, which causes CLS.
80
+ * Added: UNIX timestamp cached stylesheets to make sure browser cache of visitors is busted, upon cache refresh.
81
+ * Fixed: Running Save & Optimize a 2nd time could trigger some firewall rules, due to the serialized array being passed along with the settings form's POST action. This serialized array is now stored in the form using base64_encode() and decoded before being saved to the database.
82
+ * Fixed: Since the Google Fonts API has removed the `subsets` paramater and returns all subsets by default, OMGF now does the same. Unlike the Google Fonts API, OMGF does still respect and apply the parameter if it set, because it is still used by many themes and plugins.
83
+ * Re-worded Force Subsets (Pro) featured to clarify this behavior.
84
+ * Fixed: Some resource hints that were added using unconventional methods (i.e. *not* using `wp_resource_hints()`) weren't removed.
85
+ * Fixed: If no regular Google Fonts stylesheets were present, the `omgf_processed_html` filter would never be triggered.
86
+ * Fixed: Stylesheets on AMP pages would be rewritten to local stylesheets, while this is not supported by AMP.
87
+ * Removed: AMP handling (Pro) option from Advanced Settings, because it's no longer supported by AMP.
88
+
89
  = 5.0.4 =
90
  * Fixed: don't allow starting buffer twice.
91