Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4680f5c
release: 3.6.10
Sreini Feb 16, 2026
e154eb9
Merge branch 'master' of https://github.com/tinify/wordpress-plugin
tijmenbruggeman Feb 16, 2026
f53f00b
Merge branch 'tinify:master' into master
tijmenbruggeman Feb 17, 2026
ba97e04
Merge branch 'master' of https://github.com/tinify/wordpress-plugin
tijmenbruggeman Mar 6, 2026
3933d3c
Merge branch 'master' of https://github.com/tinify/wordpress-plugin
tijmenbruggeman Mar 17, 2026
c91a855
Merge branch 'master' of https://github.com/tinify/wordpress-plugin
tijmenbruggeman Mar 18, 2026
7ea5ea3
add checkbox for backup
tijmenbruggeman Mar 18, 2026
2446827
seperate backup settings from preserve
tijmenbruggeman Mar 18, 2026
24c7c0a
seperate settings
tijmenbruggeman Mar 18, 2026
435823c
create backup of original image
tijmenbruggeman Mar 18, 2026
a522659
add docs for tests
tijmenbruggeman Mar 18, 2026
6882d97
add docs for stubs
tijmenbruggeman Mar 18, 2026
9c5b056
add test
tijmenbruggeman Mar 18, 2026
b765371
Add more tests
tijmenbruggeman Mar 18, 2026
1d86da6
ensure trailing slash on dir
tijmenbruggeman Mar 18, 2026
43991bf
format
tijmenbruggeman Mar 19, 2026
3a863f7
fix test: also has backup registered
tijmenbruggeman Mar 25, 2026
fd2bdc8
wait for healthcheck to resolve in docker compose
tijmenbruggeman Mar 25, 2026
295b1dc
Merge branch 'master' of https://github.com/tinify/wordpress-plugin
tijmenbruggeman Apr 7, 2026
45e082e
Merge branch 'master' of github.com:wcreateweb/wordpress-plugin into …
tijmenbruggeman Apr 7, 2026
2dc0ccd
use action to create filter
tijmenbruggeman Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/hooks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The plugin provides several hooks to let you extend or modify its behavior.

- [updated_tiny_postmeta](updated_tiny_postmeta.md) — Triggered when tinify meta data has been updated
- [tiny_image_after_compression](tiny_image_after_compression.md) — Triggered after successful optimization.
- [tiny_image_size_before_compression](tiny_image_size_before_compression.md) — Triggered before optimizing an image size.


## Filters

Expand Down
23 changes: 23 additions & 0 deletions docs/hooks/tiny_image_size_before_compression.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# tiny_image_size_before_compression

Action that is done before compressing an single image size.

**Location:** `src/class-tiny-image.php`
**Since:** 3.7.0

## Arguments

1. `int $attachment_id` - The attachment ID.
2. `int|string $size_name` - The image size name. 0 for the original.
3. `string $filepath` - The file path to the image being compressed.

## Example

```php
add_filter(
'tiny_image_size_before_compression',
function ( $attachment_id, $size_name, $filename ) {
// notify system of compression
}
);
```
2 changes: 1 addition & 1 deletion src/class-tiny-compress.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function get_status() {
/**
* Compresses a single file
*
* @param [type] $file
* @param string $file path to file
* @param array $resize_opts
* @param array $preserve_opts
* @param array{ string } conversion options
Expand Down
17 changes: 17 additions & 0 deletions src/class-tiny-image.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,23 @@ public function compress() {
if ( ! $size->is_duplicate() ) {
$size->add_tiny_meta_start();
$this->update_tiny_post_meta();

/**
* Fires before an image size is sent for compression.
*
* @since 3.6.8
*
* @param int $attachment_id The attachment ID.
* @param int|string $size_name The image size name. 0 for the original.
* @param string $filepath The file path to the image being compressed.
*/
do_action(
'tiny_image_size_before_compression',
$this->id,
$size_name,
$size->filename
);

$resize = $this->settings->get_resize_options( $size_name );
$preserve = $this->settings->get_preserve_options( $size_name );
Tiny_Logger::debug(
Expand Down
44 changes: 44 additions & 0 deletions src/class-tiny-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ public function init() {

add_action( 'delete_attachment', $this->get_method( 'clean_attachment' ), 10, 2 );

add_action(
'tiny_image_size_before_compression',
$this->get_method( 'backup_image_size' ),
10,
3
);

load_plugin_textdomain(
self::NAME,
false,
Expand Down Expand Up @@ -861,6 +868,43 @@ public function clean_attachment( $post_id ) {
$tiny_image->delete_converted_image();
}

/**
* Creates a backup of an image size before compression.
*
* Hooked to the `tiny_image_size_before_compression` action. Only creates
* a backup for the original image size when the backup setting is enabled.
* The backup is stored under {upload_dir}/tinify_backup/, preserving the
* original path structure relative to the uploads base directory.
*
* @since 3.6.8
*
* @param int $image_id The attachment ID.
* @param int|string $size_name The image size name. 0 for the original.
* @param string $filepath The file path to the image to be backed up.
* @return bool return true on backup created
*/
public function backup_image_size( $image_id, $size_name, $filepath ) {
if ( ! Tiny_Image::is_original( $size_name ) ) {
return false;
}

if ( ! $this->settings->get_backup_enabled() ) {
return false;
}

$upload_dir = wp_upload_dir();
$upload_base = trailingslashit( $upload_dir['basedir'] );
$relative_path = ltrim( str_replace( $upload_base, '', $filepath ), '/' );
$backup_file = $upload_base . 'tinify_backup/' . $relative_path;
$backup_dir = dirname( $backup_file );

if ( ! wp_mkdir_p( $backup_dir ) ) {
return false;
}

return copy( $filepath, $backup_file );
}

public static function request_review() {
$review_url =
'https://wordpress.org/support/plugin/tiny-compress-images/reviews/#new-post';
Expand Down
19 changes: 19 additions & 0 deletions src/class-tiny-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ public function admin_init() {
$field = self::get_prefixed_name( 'resize_original' );
register_setting( 'tinify', $field );

$field = self::get_prefixed_name( 'backup' );
register_setting( 'tinify', $field );

$field = self::get_prefixed_name( 'preserve_data' );
register_setting( 'tinify', $field );

Expand Down Expand Up @@ -305,6 +308,16 @@ public function new_plugin_install() {
return ! $compression_timing;
}

public function get_backup_enabled() {
$sizes = $this->get_sizes();
if ( ! $sizes[ Tiny_Image::ORIGINAL ]['tinify'] ) {
return false;
}

$setting = get_option( self::get_prefixed_name( 'backup' ) );
return isset( $setting['enabled'] ) && 'on' === $setting['enabled'];
}

public function get_resize_enabled() {
/* This only applies if the original is being resized. */
$sizes = $this->get_sizes();
Expand Down Expand Up @@ -343,6 +356,12 @@ public function get_preserve_enabled( $name ) {
return isset( $setting[ $name ] ) && 'on' === $setting[ $name ];
}

/**
* Retrieves the preserve options for the original image
*
* @param string - size name
* @return false|array<string> false if size is not original, otherwise array of preserved keys
Comment on lines +360 to +363
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docblock for get_preserve_options has an invalid @param tag (missing variable name) and the return type description is a bit inconsistent. Use a standard form like "@param string $size_name" and consider "array|false" for the return type to match PHPDoc conventions.

Suggested change
* Retrieves the preserve options for the original image
*
* @param string - size name
* @return false|array<string> false if size is not original, otherwise array of preserved keys
* Retrieves the preserve options for the original image.
*
* @param string $size_name Size name.
* @return array<string>|false Array of preserved keys if size is original, or false otherwise.

Copilot uses AI. Check for mistakes.
*/
public function get_preserve_options( $size_name ) {
if ( ! Tiny_Image::is_original( $size_name ) ) {
return false;
Expand Down
22 changes: 22 additions & 0 deletions src/views/settings-original-image.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@
</div>
</div>

<p class="tiny-preserve">
<?php
$backup_enabled_id = self::get_prefixed_name( 'backup' );
$backup_enabled_name = self::get_prefixed_name( 'backup[enabled]' );
$backup_enabled = $this->get_backup_enabled();
?>
<input
type="checkbox"
id="<?php echo esc_attr( $backup_enabled_id ); ?>"
name="<?php echo esc_attr( $backup_enabled_name ); ?>"
value="on"
<?php checked( $backup_enabled ); ?> />
<label for="<?php echo esc_attr( $backup_enabled_id ); ?>">
<?php
esc_html_e(
'Create a backup of the uncompressed image',
'tiny-compress-images'
);
?>
</label>
</p>

<?php
$this->render_preserve_input(
'creation',
Expand Down
19 changes: 17 additions & 2 deletions test/helpers/wordpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ public function createImage($file_size, $path, $name)
->at($dir);
}

/**
* Creates images on the virtual disk for testing
* @param null|array $sizes Array of size => bytes to create, file will be named $name-$size.png
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The createImages() docblock says generated files will be named "$name-$size.png", but the implementation uses the array key (size name) in the filename ("$name-$key.png"). Update the docblock wording so it matches what the helper actually writes.

Suggested change
* @param null|array $sizes Array of size => bytes to create, file will be named $name-$size.png
* @param null|array $sizes Array of size name (array key) => bytes to create; each file will be named "$name-<size name>.png"

Copilot uses AI. Check for mistakes.
* @param int $original_size Bytes of image
* @param string $path Path to image
* @param string $name Name of the image
* @return void
*/
public function createImages($sizes = null, $original_size = 12345, $path = '14/01', $name = 'test')
{
vfsStream::newDirectory(self::UPLOAD_DIR . "/$path")->at($this->vfs);
Expand Down Expand Up @@ -309,6 +317,13 @@ public function createImagesFromJSON($virtual_images)
}
}

/**
* creates image meta data for testing
*
* @param string $path directory of the file in UPLOAD_DIR
* @param string $name name of the file without extension
* @return array object containing metadata
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docblock says "@return array object containing metadata", but getTestMetadata() returns a plain array. Consider updating the @return description to avoid confusion for test authors.

Suggested change
* @return array object containing metadata
* @return array metadata array

Copilot uses AI. Check for mistakes.
*/
public function getTestMetadata($path = '14/01', $name = 'test')
{
$metadata = array(
Expand Down Expand Up @@ -338,7 +353,7 @@ public function getTestMetadata($path = '14/01', $name = 'test')
*/
public static function assertHook($hookname, $expected_args = null)
{
$hooks = array('add_action', 'add_filter');
$hooks = array('add_action', 'add_filter', 'do_action', 'apply_filters');
$found = false;

foreach ($hooks as $method) {
Expand Down Expand Up @@ -401,7 +416,7 @@ public function current_time()
*/
public function wp_mkdir_p($dir)
{
mkdir($dir, 0755, true);
return mkdir($dir, 0755, true) || is_dir($dir);
}

/**
Expand Down
1 change: 1 addition & 0 deletions test/unit/TinyImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,5 @@ public function test_conversion_same_mimetype()
// second call should be only with image/webp because first call was a image/webp
$this->assertEquals(array('image/webp'), $compress_calls[1]['convert_to']);
}

}
38 changes: 37 additions & 1 deletion test/unit/TinyPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
require_once dirname(__FILE__) . '/TinyTestCase.php';

use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\content\LargeFileContent;

use function PHPUnit\Framework\assertFalse;
use function PHPUnit\Framework\assertTrue;

class Tiny_Plugin_Test extends Tiny_TestCase
{
Expand Down Expand Up @@ -495,4 +497,38 @@ public function test_conversion_enabled_and_not_filtered()

WordPressStubs::assertHook('template_redirect', array($tiny_picture, 'on_template_redirect'));
}

public function test_init_adds_backup_image_size_action() {
$tiny_plugin = new Tiny_Plugin();
$tiny_plugin->init();

// assert that backup is hooked into `tiny_image_size_before_compression`
WordPressStubs::assertHook('tiny_image_size_before_compression', array($tiny_plugin, 'backup_image_size'));
}

public function test_will_copy_original_file_on_backup() {
$this->wp->createImage( 37857, '2026/04', 'testfile.png' );
$og_file_path = $this->vfs->url() . '/wp-content/uploads/2026/04/testfile.png';
$expected_backup = $this->vfs->url() . '/wp-content/uploads/tinify_backup/2026/04/testfile.png';

$tiny_plugin = new Tiny_Plugin();

$ref = new \ReflectionClass($tiny_plugin);
$settings_prop = $ref->getProperty('settings');
$settings_prop->setAccessible(true);
$mock_settings = $this->createMock(Tiny_Settings::class);
$mock_settings->method('get_backup_enabled')->willReturn(true);
$settings_prop->setValue($tiny_plugin, $mock_settings);

$tiny_plugin->backup_image_size(1, 0, $og_file_path);

assertTrue(file_exists($expected_backup), 'expected backup to be created');
}

public function test_when_not_original_will_not_backup() {
$tiny_plugin = new Tiny_Plugin();
$created = $tiny_plugin->backup_image_size(1, 'thumbnail', 'filepath');

assertFalse($created, 'expected backup not te be created');
}
}
1 change: 1 addition & 0 deletions test/unit/TinySettingsAdminTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function test_admin_init_should_register_keys() {
array( 'tinify', 'tinypng_compression_timing' ),
array( 'tinify', 'tinypng_sizes' ),
array( 'tinify', 'tinypng_resize_original' ),
array( 'tinify', 'tinypng_backup' ),
array( 'tinify', 'tinypng_preserve_data' ),
array( 'tinify', 'tinypng_convert_format' ),
array( 'tinify', 'tinypng_logging_enabled' ),
Expand Down
5 changes: 5 additions & 0 deletions test/unit/TinyTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public static function client_supported() {
}

abstract class Tiny_TestCase extends TestCase {
/**
* WordPress stubs
*
* @var \WordPressStubs
*/
protected $wp;
protected $vfs;

Expand Down
Loading