diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 0d98884c..132cc1b4 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -73,6 +73,7 @@ jobs:
run: npm run test:playwright
env:
WORDPRESS_PORT: 80${{ matrix.wp }}
+ PHP_VERSION: ${{ matrix.php }}
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
diff --git a/readme.txt b/readme.txt
index 3926b455..565e7c67 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,7 +4,7 @@ Donate link: https://tinypng.com/
Tags: compress images, compression, image size, page speed, performance
Requires at least: 4.0
Tested up to: 6.9
-Stable tag: 3.6.8
+Stable tag: 3.6.9
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -174,6 +174,9 @@ A: You can upgrade to a paid account by adding your *Payment details* on your [a
A: When the conversion feature is enabled (to convert images to AVIF or WebP), each image will use double the number of credits: one for compression and one for format conversion.
== Changelog ==
+= 3.6.9 =
+* fix: prevent picture element on product pages (WooCommerce)
+
= 3.6.8 =
* feat: download logs and diagnostics
* chore: updated phpcs rules and formatting
diff --git a/src/class-tiny-picture.php b/src/class-tiny-picture.php
index 1c9eaf9e..4806a715 100644
--- a/src/class-tiny-picture.php
+++ b/src/class-tiny-picture.php
@@ -38,13 +38,17 @@ class Tiny_Picture extends Tiny_WP_Base {
/** @var array */
private $allowed_domains = array();
+ /** @var Tiny_Settings */
+ private $settings;
+
/**
* Initialize the plugin.
*
* @param string $base_dir Absolute path (e.g. ABSPATH)
* @param array $domains List of allowed domain URLs
*/
- public function __construct( $base_dir = ABSPATH, $domains = array() ) {
+ public function __construct( $settings, $base_dir = ABSPATH, $domains = array() ) {
+ $this->settings = $settings;
$this->base_dir = $base_dir;
$this->allowed_domains = $domains;
@@ -64,12 +68,20 @@ public function __construct( $base_dir = ABSPATH, $domains = array() ) {
return;
}
- add_action(
- 'template_redirect',
- function () {
- ob_start( array( $this, 'replace_sources' ), 1000 );
- }
- );
+ add_action( 'template_redirect', array( $this, 'on_template_redirect' ) );
+ }
+
+ public function on_template_redirect() {
+ $conversion_enabled = $this->settings->get_conversion_enabled();
+ if ( apply_filters( 'tiny_replace_with_picture', $conversion_enabled ) ) {
+ /**
+ * Controls wether the page should replace
with elements
+ * converted sources.
+ *
+ * @since 3.6.9
+ */
+ ob_start( array( $this, 'replace_sources' ), 1000 );
+ }
}
public function replace_sources( $content ) {
diff --git a/src/class-tiny-plugin.php b/src/class-tiny-plugin.php
index 0beec978..1fc976c8 100644
--- a/src/class-tiny-plugin.php
+++ b/src/class-tiny-plugin.php
@@ -72,18 +72,8 @@ public function init() {
dirname( plugin_basename( __FILE__ ) ) . '/languages'
);
- if ( $this->settings->get_conversion_enabled() ) {
- /**
- * Controls wether the page should replace
with elements
- * converted sources.
- *
- * @since 3.7.0
- */
- $should_replace = apply_filters( 'tiny_replace_with_picture', true );
- if ( $should_replace ) {
- new Tiny_Picture( ABSPATH, array( get_site_url() ) );
- }
- }
+ new Tiny_Picture( $this->settings, ABSPATH, array( get_site_url() ) );
+ $this->tiny_compatibility();
}
public function cli_init() {
@@ -236,12 +226,14 @@ public function add_plugin_links( $current_links ) {
public function tiny_compatibility() {
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
- $tiny_wpml_compatibility = new Tiny_WPML();
+ new Tiny_WPML();
}
if ( Tiny_AS3CF::is_active() ) {
- $tiny_as3cf = new Tiny_AS3CF( $this->settings );
+ new Tiny_AS3CF( $this->settings );
}
+
+ new Tiny_WooCommerce();
}
public function compress_original_retina_image( $attachment_id, $path ) {
diff --git a/src/compatibility/as3cf/class-tiny-as3cf.php b/src/compatibility/as3cf/class-tiny-as3cf.php
index 6ad7ca26..314f65e3 100644
--- a/src/compatibility/as3cf/class-tiny-as3cf.php
+++ b/src/compatibility/as3cf/class-tiny-as3cf.php
@@ -25,18 +25,14 @@ class Tiny_AS3CF {
* Checks wether the lite version is active
*/
public static function lite_is_active() {
- $lite_name = 'amazon-s3-and-cloudfront/wordpress-s3.php';
-
- return is_plugin_active( $lite_name );
+ return class_exists( 'Amazon_S3_And_CloudFront' );
}
/**
* Checks wether the pro version is active
*/
public static function pro_is_active() {
- $pro_name = 'amazon-s3-and-cloudfront-pro/amazon-s3-and-cloudfront-pro.php';
-
- return is_plugin_active( $pro_name );
+ return class_exists( 'Amazon_S3_And_CloudFront_Pro' );
}
public function __construct( $settings ) {
diff --git a/src/compatibility/woocommerce/class-tiny-woocommerce.php b/src/compatibility/woocommerce/class-tiny-woocommerce.php
new file mode 100644
index 00000000..3f0f7a66
--- /dev/null
+++ b/src/compatibility/woocommerce/class-tiny-woocommerce.php
@@ -0,0 +1,58 @@
+add_hooks();
+ }
+
+ private function add_hooks() {
+ add_filter( 'tiny_replace_with_picture', array( $this, 'skip_on_product_pages' ), 10, 1 );
+ }
+
+ /**
+ * We are skipping single product pages for now.
+ * Variation images in the product gallery are injected through JavaScript but might never
+ * display because the sourceset takes priority over the root img. The replacement is on the image and not
+ * on the srcset.
+ *
+ * @since 3.6.9
+ *
+ * @param bool $should_replace Whether to replace images with picture elements.
+ * @return bool False on product pages, otherwise unchanged.
+ */
+ public function skip_on_product_pages( $should_replace ) {
+ if ( is_product() ) {
+ return false;
+ }
+
+ return $should_replace;
+ }
+}
diff --git a/test/integration/compatibility.spec.ts b/test/integration/compatibility.spec.ts
index 935be5f5..17f015c0 100644
--- a/test/integration/compatibility.spec.ts
+++ b/test/integration/compatibility.spec.ts
@@ -1,5 +1,5 @@
import { Page, expect, test } from '@playwright/test';
-import { activatePlugin, clearMediaLibrary, deactivatePlugin, enableCompressionSizes, getWPVersion, setAPIKey, setCompressionTiming, uploadMedia } from './utils';
+import { activatePlugin, clearMediaLibrary, deactivatePlugin, enableCompressionSizes, getPHPVersion, getWPVersion, setAPIKey, setCompressionTiming, uploadMedia } from './utils';
test.describe.configure({ mode: 'serial' });
@@ -36,9 +36,16 @@ test.describe('as3cf', () => {
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
WPVersion = await getWPVersion(page);
+ const phpVersion = getPHPVersion();
- if (WPVersion < 5.5) {
- // Skipping test as it WP Offload does not support WordPress < 5.5
+ if (WPVersion < 5.9) {
+ // Skipping test as it WP Offload does not support WordPress < 5.9
+ test.skip();
+ return;
+ }
+
+ if (phpVersion < 81) {
+ // Skipping test as WP Offload Media requires PHP 8.1+
test.skip();
return;
}
diff --git a/test/integration/utils.ts b/test/integration/utils.ts
index 5bcfbc7c..1208da7a 100644
--- a/test/integration/utils.ts
+++ b/test/integration/utils.ts
@@ -160,6 +160,18 @@ export async function getWPVersion(page: Page): Promise {
return parsedText;
}
+/**
+ * @returns {number} retrieves the current PHP version from environment variable
+ */
+export function getPHPVersion(): number {
+ const { PHP_VERSION } = process.env;
+ if (!PHP_VERSION) {
+ throw Error('PHP_VERSION is not set');
+ }
+
+ return +PHP_VERSION;
+}
+
/**
* @param {Page} page context
* @param {string} pluginSlug slug of the plugin, ex 'tiny-compress-images'
@@ -167,7 +179,7 @@ export async function getWPVersion(page: Page): Promise {
export async function activatePlugin(page: Page, pluginSlug: string) {
await page.goto('/wp-admin/plugins.php');
- const plugin = await page.locator('tr[data-slug="' + pluginSlug + '"]');
+ const plugin = page.locator('tr[data-slug="' + pluginSlug + '"]').first();
if (!plugin) {
throw Error(`Plug-in ${pluginSlug} not found. Are you sure it is installed?`);
}
@@ -192,7 +204,7 @@ export async function deactivatePlugin(page: Page, pluginSlug: string) {
return;
}
- const plugin = await page.locator('tr[data-slug="' + pluginSlug + '"]');
+ const plugin = await page.locator('tr[data-slug="' + pluginSlug + '"]').first();
const className = await plugin.getAttribute('class');
const pluginActivated = className === 'active';
if (!pluginActivated) {
diff --git a/test/unit/TinyPictureTest.php b/test/unit/TinyPictureTest.php
index 60713120..79ee7fda 100644
--- a/test/unit/TinyPictureTest.php
+++ b/test/unit/TinyPictureTest.php
@@ -12,7 +12,8 @@ public function set_up()
{
parent::set_up();
- $this->tiny_picture = new Tiny_Picture($this->vfs->url(), array('https://www.tinifytest.com'));
+ $settings = new Tiny_Settings();
+ $this->tiny_picture = new Tiny_Picture($settings, $this->vfs->url(), array('https://www.tinifytest.com'));
}
/**
@@ -335,7 +336,8 @@ public function test_does_not_register_hooks_when_pagebuilder_request()
return false;
});
- $tiny_picture = new Tiny_Picture($this->vfs->url(), array('https://www.tinifytest.com'));
+ $settings = new Tiny_Settings();
+ $tiny_picture = new Tiny_Picture($settings, $this->vfs->url(), array('https://www.tinifytest.com'));
$template_redirect_registered = false;
foreach ($this->wp->getCalls('add_action') as $call) {
diff --git a/test/unit/TinyTestCase.php b/test/unit/TinyTestCase.php
index dab0ea46..4838a404 100644
--- a/test/unit/TinyTestCase.php
+++ b/test/unit/TinyTestCase.php
@@ -5,6 +5,9 @@
require_once dirname( __FILE__ ) . '/../helpers/wordpress.php';
require_once dirname( __FILE__ ) . '/../helpers/wordpress-cli.php';
require_once dirname( __FILE__ ) . '/../../src/config/class-tiny-config.php';
+require_once dirname( __FILE__ ) . '/../../src/compatibility/wpml/class-tiny-wpml.php';
+require_once dirname( __FILE__ ) . '/../../src/compatibility/as3cf/class-tiny-as3cf.php';
+require_once dirname( __FILE__ ) . '/../../src/compatibility/woocommerce/class-tiny-woocommerce.php';
require_once 'vendor/autoload.php';
use org\bovigo\vfs\vfsStream;
diff --git a/test/unit/compatibility/TinyAC3SFTest.php b/test/unit/compatibility/TinyAC3SFTest.php
index 42d6ce20..9fb95a7e 100644
--- a/test/unit/compatibility/TinyAC3SFTest.php
+++ b/test/unit/compatibility/TinyAC3SFTest.php
@@ -3,6 +3,9 @@
require_once dirname(__FILE__) . '/../TinyTestCase.php';
require_once dirname(__FILE__) . '/../../../src/compatibility/as3cf/class-tiny-as3cf.php';
+class Amazon_S3_And_CloudFront_Pro {}
+class Amazon_S3_And_CloudFront {}
+
class Tiny_AC3SF_Test extends Tiny_TestCase
{
public function set_up()
@@ -10,84 +13,39 @@ public function set_up()
parent::set_up();
}
+
/**
- * Stub is_plugin_active to return true for the pro plugin
- * is_plugin_active will only work in admin area (after admin_init);
+ * Will check if AS3CF exists (pro version)
*/
- public function test_is_active_when_pro_is_enabled()
+ public function test_is_active_when_pro_is_available()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return $plugin_name === 'amazon-s3-and-cloudfront-pro/amazon-s3-and-cloudfront-pro.php';
- }
- );
-
- $tiny_settings = new Tiny_Settings();
- $tiny_ac3sf = new Tiny_AS3CF($tiny_settings);
-
+ $this->assertTrue(Tiny_AS3CF::pro_is_active());
$this->assertTrue(Tiny_AS3CF::is_active());
}
+ /**
+ * Will check if AS3CF exists (lite version)
+ * @see https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/blob/master/wordpress-s3.php
+ */
public function test_is_active_when_lite_is_enabled()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return $plugin_name === 'amazon-s3-and-cloudfront/wordpress-s3.php';
- }
- );
-
-
+ // Amazon_S3_And_CloudFront stub class is defined at top of file
+ $this->assertTrue(Tiny_AS3CF::lite_is_active());
$this->assertTrue(Tiny_AS3CF::is_active());
}
- public function test_is_not_active()
- {
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return false;
- }
- );
-
-
- $this->assertFalse(Tiny_AS3CF::is_active());
- }
-
public function test_remove_local_enabled_is_false_when_plugin_inactive()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return false;
- }
- );
-
$this->assertFalse(Tiny_AS3CF::remove_local_files_setting_enabled());
}
public function test_remove_local_false_when_option_not_exists()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return true;
- }
- );
-
$this->assertFalse(Tiny_AS3CF::remove_local_files_setting_enabled());
}
public function test_remove_local_false_when_option_is_false()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return true;
- }
- );
-
$this->wp->addOption('tantan_wordpress_s3', array(
'remove-local-file' => false,
));
@@ -97,13 +55,6 @@ function ($plugin_name) {
public function test_remove_local_true_when_option_is_true()
{
- $this->wp->stub(
- 'is_plugin_active',
- function ($plugin_name) {
- return true;
- }
- );
-
$this->wp->addOption('tantan_wordpress_s3', array(
'remove-local-file' => true,
));
diff --git a/tiny-compress-images.php b/tiny-compress-images.php
index 2e56b0c8..57b90c17 100644
--- a/tiny-compress-images.php
+++ b/tiny-compress-images.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: TinyPNG - JPEG, PNG & WebP image compression
* Description: Speed up your website. Optimize your JPEG, PNG, and WebP images automatically with TinyPNG.
- * Version: 3.6.8
+ * Version: 3.6.9
* Author: TinyPNG
* Author URI: https://tinypng.com
* Text Domain: tiny-compress-images
@@ -27,6 +27,7 @@
require dirname( __FILE__ ) . '/src/class-tiny-picture.php';
require dirname( __FILE__ ) . '/src/compatibility/wpml/class-tiny-wpml.php';
require dirname( __FILE__ ) . '/src/compatibility/as3cf/class-tiny-as3cf.php';
+require dirname( __FILE__ ) . '/src/compatibility/woocommerce/class-tiny-woocommerce.php';
if ( Tiny_PHP::client_supported() ) {
require dirname( __FILE__ ) . '/src/class-tiny-compress-client.php';