Skip to content

Commit 5a5bbb0

Browse files
authored
Merge pull request #16 from gumlet/new-version
New version release
2 parents e29252d + f4e6296 commit 5a5bbb0

7 files changed

Lines changed: 112 additions & 23 deletions

File tree

changelog.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
== Changelog ==
22

3-
= 1.3.20 =
4-
* Added control to disable resizing images.
3+
= 1.4.0 =
4+
* Advanced setting: Auto Resize (auto_resize). When disabled, img src is set directly to the Gumlet URL instead of the placeholder pixel (no viewport-based resize via gumlet.js for those tags).
5+
* Fix DOM parsing for img/source tags: do not entity-encode the whole tag before loadHTML (was preventing any img from being found).
56
* Support SVG URLs in replace_image_url.
7+
* Normalize JSON-style escapes in fragments (wp_unslash, \/) before parsing.
68

79
= 1.3.19 =
810
* Fix fatal errors related to PHP 8.4 compatibility.

gumlet.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* Plugin Name: Gumlet
1212
* Plugin URI: https://github.com/gumlet/wordpress-plugin
1313
* Description: A WordPress plugin to automatically load all your existing (and future) WordPress images via the <a href="http://www.gumlet.com" target="_blank">Gumlet</a> service for smaller, faster, and better looking images.
14-
* Version: 1.3.20
14+
* Version: 1.4.0
1515
* Author: Gumlet
1616
* Text Domain: gumlet
1717
* Author URI: https://www.gumlet.com
@@ -30,6 +30,7 @@
3030

3131
include('includes/compability.php');
3232
include('includes/logger.php');
33+
include('includes/gumlet-html-fragment.php');
3334
include('includes/class-gumlet.php');
3435
include('includes/options-page.php');
3536

@@ -51,7 +52,7 @@ function gumlet_plugin_activate()
5152
{
5253
// plugin activation code here...
5354
if (!get_option('gumlet_settings')) {
54-
update_option('gumlet_settings', ["lazy_load" => 1, "original_images" => 1, "auto_compress"=> 1, "server_webp"=> 0]);
55+
update_option('gumlet_settings', ["lazy_load" => 1, "original_images" => 1, "auto_compress"=> 1, "server_webp"=> 0, "auto_resize" => 1]);
5556
}
5657
}
5758

includes/class-gumlet.php

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ protected function isWelcome()
161161

162162
public function enqueue_script()
163163
{
164-
if (!empty($this->options['cdn_link']) && $this->isWelcome()) {
164+
if (!empty($this->options['cdn_link']) && $this->isWelcome() && $this->is_auto_resize_enabled()) {
165165
if (isset($this->options['external_cdn_link'])) {
166166
$external_cdn_host = parse_url($this->options['external_cdn_link'], PHP_URL_HOST);
167167
}
@@ -247,6 +247,16 @@ public function get_option($key, $default = null)
247247
return array_key_exists($key, $this->options) ? $this->options[$key] : $default;
248248
}
249249

250+
/**
251+
* Auto Resize: placeholder + gumlet.js viewport sizing. When false, src is set to the Gumlet URL and gumlet.js is not loaded.
252+
*
253+
* @return bool
254+
*/
255+
protected function is_auto_resize_enabled()
256+
{
257+
return (int) $this->get_option('auto_resize', 1) === 1;
258+
}
259+
250260
/**
251261
* Override options from settings.
252262
* Used in unit tests.
@@ -275,7 +285,7 @@ public function replace_image_url($url)
275285
&& ($parsed_url['host'] === parse_url(home_url('/'), PHP_URL_HOST)
276286
|| (isset($this->options['external_cdn_link']) && ! empty($this->options['external_cdn_link'])
277287
&& strpos($this->options['external_cdn_link'], $parsed_url['host']) !== false))
278-
&& preg_match('/\.(jpg|jpeg|png|gif|svg)$/i', $parsed_url['path'])
288+
&& preg_match('/\.(jpg|jpeg|png|gif|svg|webp)$/i', $parsed_url['path'])
279289
)
280290
{
281291
$cdn = parse_url($this->options['cdn_link']);
@@ -296,7 +306,6 @@ public function replace_image_url($url)
296306
}
297307
$url = http_build_url($parsed_url);
298308
$url = add_query_arg($this->get_global_params(), $url);
299-
$this->logger->log("URL:", $url);
300309
}
301310
}
302311
return $url;
@@ -546,7 +555,7 @@ public function replace_src_in_imgtag($matches, $content, $gumlet_host, $going_t
546555
{
547556
$this->logger->log("Matched regex:", $matches);
548557
foreach ($matches[0] as $unconverted_img_tag) {
549-
$this->logger->log("Processing img:", $unconverted_img_tag);
558+
$this->logger->log('Processing img: ' . $unconverted_img_tag);
550559

551560
try {
552561
$doc = new DOMDocument();
@@ -616,20 +625,28 @@ public function replace_src_in_imgtag($matches, $content, $gumlet_host, $going_t
616625
}
617626

618627
if (parse_url($src, PHP_URL_HOST) == $going_to_be_replaced_host || parse_url($src, PHP_URL_HOST) == $gumlet_host || !parse_url($src, PHP_URL_HOST)) {
619-
$imageTag->setAttribute("data-gmsrc", $src);
620-
$imageTag->setAttribute("src", plugins_url('assets/images/pixel.png', __DIR__));
628+
$gumlet_url = $this->replace_image_url($src);
629+
$auto_resize_on = $this->is_auto_resize_enabled();
630+
631+
if ($auto_resize_on) {
632+
$imageTag->setAttribute("data-gmsrc", $gumlet_url);
633+
$imageTag->setAttribute("src", plugins_url('assets/images/pixel.png', __DIR__));
634+
} else {
635+
$imageTag->setAttribute("src", $gumlet_url);
636+
$imageTag->removeAttribute("data-gmsrc");
637+
}
621638
$imageTag->removeAttribute("srcset");
622639
$imageTag->removeAttribute("data-src");
623640
$imageTag->removeAttribute("data-srcset");
624641
$imageTag->removeAttribute("data-lazy-srcset");
625642
$imageTag->removeAttribute("data-lazy-src");
626643

627-
if (strpos($imageTag->getAttribute("class"), "wp-post-image") !== false && $imageTag->getAttribute("data-large_image_width") != '') {
628-
$imageTag->setAttribute("data-src", $src);
644+
if ($auto_resize_on && strpos($imageTag->getAttribute("class"), "wp-post-image") !== false && $imageTag->getAttribute("data-large_image_width") != '') {
645+
$imageTag->setAttribute("data-src", $gumlet_url);
629646
}
630647

631648
$new_img_tag = $doc->saveHTML($imageTag);
632-
$this->logger->log("New img tag:", $new_img_tag);
649+
$this->logger->log('New img tag: ' . $new_img_tag);
633650
$content = str_replace($unconverted_img_tag, $new_img_tag, $content);
634651
} else {
635652
$this->logger->log("Skipping due to mismatched host to be replaced.");
@@ -741,15 +758,16 @@ public function replace_src_in_css($matches,$content,$going_to_be_replaced_host,
741758
}
742759

743760
/**
744-
* Convert img tag to UTF-8 encoding.
761+
* Prepare a matched tag for DOMDocument::loadHTML.
762+
*
763+
* Do not entity-encode the whole fragment: htmlspecialchars turns `<img` into `&lt;img`,
764+
* so libxml never produces an img node (getElementsByTagName('img') stays empty).
745765
*
746766
* @param string $unconverted_img_tag
747767
* @return string
748768
*/
749769
public function convert_to_utf($unconverted_img_tag) {
750-
// Use htmlspecialchars with ENT_QUOTES to handle both single and double quotes
751-
// and ENT_HTML5 for better HTML5 compatibility
752-
return htmlspecialchars($unconverted_img_tag, ENT_QUOTES | ENT_HTML5, 'UTF-8');
770+
return gumlet_normalize_html_fragment_for_dom($unconverted_img_tag);
753771
}
754772

755773
/**

includes/gumlet-html-fragment.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Normalize HTML snippets before DOMDocument::loadHTML (JSON-escaped quotes/slashes).
4+
*
5+
* @package gumlet-wordpress
6+
* @param string $fragment Raw matched tag from output buffer.
7+
* @return string
8+
*/
9+
function gumlet_normalize_html_fragment_for_dom($fragment)
10+
{
11+
if (!is_string($fragment) || $fragment === '') {
12+
return $fragment;
13+
}
14+
if (function_exists('wp_unslash')) {
15+
$fragment = wp_unslash($fragment);
16+
} else {
17+
$fragment = stripslashes($fragment);
18+
}
19+
// JSON encodes forward slashes in URLs as \/ — libxml needs real slashes.
20+
$fragment = str_replace('\\/', '/', $fragment);
21+
return $fragment;
22+
}

includes/options-page.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@ class="regular-text code" />
150150
<div class="tab">
151151
<table class="form-table">
152152
<tbody>
153+
<tr>
154+
<th>
155+
<label class="description" for="gumlet_settings_auto_resize">
156+
<?php esc_html_e('Auto Resize', 'gumlet'); ?>
157+
</label>
158+
</th>
159+
<td>
160+
<input type="hidden" name="gumlet_settings[auto_resize]" value="0" />
161+
<input id="gumlet_settings_auto_resize" type="checkbox"
162+
name="gumlet_settings[auto_resize]" value="1" <?php
163+
checked($this->get_option('auto_resize', true)) ?> />
164+
<p style="color: #666"><?php esc_html_e('When enabled, images use a placeholder and Gumlet.js resizes per viewport (including lazy loading, if enabled). When disabled, Gumlet.js is not loaded: the image src is set directly to the Gumlet URL (no placeholder and no lazy loading).', 'gumlet'); ?></p>
165+
</td>
166+
</tr>
153167
<tr>
154168
<th>
155169
<label class="description" for="gumlet_settings[original_images]">
@@ -314,12 +328,13 @@ public function gumlet_register_settings()
314328
* Get option and handle if option is not set
315329
*
316330
* @param string $key
317-
*
331+
* @param mixed $default Returned when the key is not present in the array.
318332
* @return mixed
319333
*/
320-
protected function get_option($key)
334+
protected function get_option($key, $default = '')
321335
{
322-
return isset($this->options[ $key ]) ? $this->options[ $key ] : '';
336+
// array_key_exists: key exists but value is null → still use null (isset() would treat as missing).
337+
return array_key_exists($key, $this->options) ? $this->options[ $key ] : $default;
323338
}
324339
}
325340

readme.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Author URI: https://www.gumlet.com
88
Requires at least: 3.3
99
Requires PHP: 5.6.3
1010
Tested up to: 6.8
11-
Stable tag: 1.3.20
11+
Stable tag: 1.4.0
1212
License: BSD-2
1313
License URI: http://opensource.org/licenses/BSD-2-Clause
1414

@@ -141,9 +141,11 @@ If you need any help, you can reach out to us at support@gumlet.com.
141141

142142
== Changelog ==
143143

144-
= 1.3.20 =
145-
* Added control to disable resizing images.
144+
= 1.4.0 =
145+
* Advanced setting: Auto Resize (auto_resize). When disabled, img src is set directly to the Gumlet URL instead of the placeholder pixel.
146+
* Fix DOM parsing for img/source tags: do not entity-encode the whole tag before loadHTML (was preventing any img from being found).
146147
* Support SVG URLs in replace_image_url.
148+
* Normalize JSON-style escapes in fragments (wp_unslash, \/) before parsing.
147149

148150

149151
= 1.3.19 =

tests/fragment-normalize-test.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
/**
3+
* CLI regression: gumlet_normalize_html_fragment_for_dom (no WordPress).
4+
* Run: php tests/fragment-normalize-test.php
5+
*/
6+
7+
if (!function_exists('wp_unslash')) {
8+
function wp_unslash($value)
9+
{
10+
return stripslashes($value);
11+
}
12+
}
13+
14+
require_once dirname(__DIR__) . '/includes/gumlet-html-fragment.php';
15+
16+
$escaped = '<img fetchpriority="high" src="http:\/\/wordpress.turingiq.com\/wp-content\/uploads\/2025\/03\/screenshot-7-1024x545.png" srcset="https:\/\/wordpress.turingiq.com\/wp-content\/uploads\/2025\/03\/screenshot-7.png 1440w" />';
17+
18+
$normalized = gumlet_normalize_html_fragment_for_dom($escaped);
19+
20+
$ok = strpos($normalized, 'http://wordpress.turingiq.com/wp-content') !== false
21+
&& strpos($normalized, 'https://wordpress.turingiq.com/wp-content') !== false
22+
&& strpos($normalized, '\\/') === false;
23+
24+
if (!$ok) {
25+
fwrite(STDERR, "FAIL: expected unescaped URLs in fragment.\nGot: $normalized\n");
26+
exit(1);
27+
}
28+
29+
echo "OK: fragment normalize\n";

0 commit comments

Comments
 (0)