Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3,748 changes: 1,979 additions & 1,769 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@
},
"dependencies": {
"@mediabunny/mp3-encoder": "^1.25.0",
"browser-image-compression": "^2.0.2",
"classnames": "^2.5.1",
"heic2any": "^0.0.4",
"mediabunny": "^1.25.0",
"svgo": "^4.0.0",
"heic2any": "^0.0.4"
"mime": "^4.1.0",
"resemblejs": "^5.0.0",
"svgo": "^4.0.0"
}
}
2 changes: 1 addition & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Contributors: bfintal, gambitph
Tags: image, optimization, compress, convert, webp
Requires at least: 6.7.4
Tested up to: 6.9
Tested up to: 6.9.1
Requires PHP: 8.0
Stable tag: 1.2.2
License: GPLv2 or later
Expand Down
2 changes: 2 additions & 0 deletions src/admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ public function enqueue_admin_scripts( $hook ) {
'stats' => $stats,
'imageSizes' => $formatted_sizes,
'ratingDismissed' => '1' === get_option( 'cimo_rating_dismissed', '0' ) ? '1' : '0',
'isPremium' => CIMO_BUILD === 'premium',
'uploadsUrl' => wp_upload_dir()['baseurl'],
] );
}

Expand Down
94 changes: 82 additions & 12 deletions src/admin/class-meta-box.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,35 @@ function cimo_get_media_type_label( $mimetype ) {
$optimization_savings = $compression_savings !== null ? number_format( 100 - ( $compression_savings * 100 ), 2 ) : null;
$original_filesize = isset( $cimo['originalFilesize'] ) ? floatval( $cimo['originalFilesize'] ) : 0;
$converted_filesize = isset( $cimo['convertedFilesize'] ) ? floatval( $cimo['convertedFilesize'] ) : 0;

$is_bulk_optimized = isset( $cimo['bulk_optimization'] ) &&
is_array( $cimo['bulk_optimization'] ) &&
! empty( $cimo['bulk_optimization'] );

// For bulk optimization, we need to recalculate the optimization savings percentage
// based on the original and converted file sizes for all the thumbnails
if ( $is_bulk_optimized ) {
$bulk_optimization_count = count( $cimo['bulk_optimization'] );
$bulk_optimization_sizes = array_keys( $cimo['bulk_optimization'] );

// Gather all original and converted file sizes for bulk_optimization
$bulk_original_filesize = 0;
$bulk_converted_filesize = 0;
if ( $is_bulk_optimized ) {
foreach ( $cimo['bulk_optimization'] as $size_key => $bulk_data ) {
$orig = isset( $bulk_data['originalFilesize'] ) ? floatval( $bulk_data['originalFilesize'] ) : 0;
$conv = isset( $bulk_data['convertedFilesize'] ) ? floatval( $bulk_data['convertedFilesize'] ) : 0;
$bulk_original_filesize += $orig;
$bulk_converted_filesize += $conv;
}
}

$original_filesize = $bulk_original_filesize;
$converted_filesize = $bulk_converted_filesize;

$optimization_savings = number_format( 100 * ( $original_filesize - $converted_filesize ) / $original_filesize, 2 );
}
Comment on lines +94 to +120
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Short-circuit the meta box when there are no usable optimization stats.

sidebar-info.js now returns early when compressionSavings is missing and bulk_optimization is absent/empty. This path still renders the full box for that state, which leaves blank/zeroed stats, and the bulk percentage calculation still divides by $original_filesize unconditionally.

Suggested guard
 						$is_bulk_optimized = isset( $cimo['bulk_optimization'] ) &&
 							is_array( $cimo['bulk_optimization'] ) &&
 							! empty( $cimo['bulk_optimization'] );
+
+						if ( $compression_savings === null && ! $is_bulk_optimized ) {
+							echo '<p>' . esc_html__( 'Cimo did not optimize this attachment.', 'cimo-image-optimizer' ) . '</p>';
+							return;
+						}
@@
-							$optimization_savings = number_format( 100 * ( $original_filesize - $converted_filesize ) / $original_filesize, 2 );
+							$optimization_savings = $original_filesize > 0
+								? number_format( 100 * ( $original_filesize - $converted_filesize ) / $original_filesize, 2 )
+								: '0.00';
 						}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$is_bulk_optimized = isset( $cimo['bulk_optimization'] ) &&
is_array( $cimo['bulk_optimization'] ) &&
! empty( $cimo['bulk_optimization'] );
// For bulk optimization, we need to recalculate the optimization savings percentage
// based on the original and converted file sizes for all the thumbnails
if ( $is_bulk_optimized ) {
$bulk_optimization_count = count( $cimo['bulk_optimization'] );
$bulk_optimization_sizes = array_keys( $cimo['bulk_optimization'] );
// Gather all original and converted file sizes for bulk_optimization
$bulk_original_filesize = 0;
$bulk_converted_filesize = 0;
if ( $is_bulk_optimized ) {
foreach ( $cimo['bulk_optimization'] as $size_key => $bulk_data ) {
$orig = isset( $bulk_data['originalFilesize'] ) ? floatval( $bulk_data['originalFilesize'] ) : 0;
$conv = isset( $bulk_data['convertedFilesize'] ) ? floatval( $bulk_data['convertedFilesize'] ) : 0;
$bulk_original_filesize += $orig;
$bulk_converted_filesize += $conv;
}
}
$original_filesize = $bulk_original_filesize;
$converted_filesize = $bulk_converted_filesize;
$optimization_savings = number_format( 100 * ( $original_filesize - $converted_filesize ) / $original_filesize, 2 );
}
$is_bulk_optimized = isset( $cimo['bulk_optimization'] ) &&
is_array( $cimo['bulk_optimization'] ) &&
! empty( $cimo['bulk_optimization'] );
if ( $compression_savings === null && ! $is_bulk_optimized ) {
echo '<p>' . esc_html__( 'Cimo did not optimize this attachment.', 'cimo-image-optimizer' ) . '</p>';
return;
}
// For bulk optimization, we need to recalculate the optimization savings percentage
// based on the original and converted file sizes for all the thumbnails
if ( $is_bulk_optimized ) {
$bulk_optimization_count = count( $cimo['bulk_optimization'] );
$bulk_optimization_sizes = array_keys( $cimo['bulk_optimization'] );
// Gather all original and converted file sizes for bulk_optimization
$bulk_original_filesize = 0;
$bulk_converted_filesize = 0;
if ( $is_bulk_optimized ) {
foreach ( $cimo['bulk_optimization'] as $size_key => $bulk_data ) {
$orig = isset( $bulk_data['originalFilesize'] ) ? floatval( $bulk_data['originalFilesize'] ) : 0;
$conv = isset( $bulk_data['convertedFilesize'] ) ? floatval( $bulk_data['convertedFilesize'] ) : 0;
$bulk_original_filesize += $orig;
$bulk_converted_filesize += $conv;
}
}
$original_filesize = $bulk_original_filesize;
$converted_filesize = $bulk_converted_filesize;
$optimization_savings = $original_filesize > 0
? number_format( 100 * ( $original_filesize - $converted_filesize ) / $original_filesize, 2 )
: '0.00';
}
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 102-102: Avoid unused local variables such as '$bulk_optimization_sizes'. (undefined)

(UnusedLocalVariable)


[warning] 108-108: Avoid unused local variables such as '$size_key'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/admin/class-meta-box.php` around lines 94 - 120, Short-circuit rendering
and avoid dividing by zero: detect when the per-image stat compressionSavings is
missing and $is_bulk_optimized is false/empty and return/skip rendering the meta
box (or the stats block) early; when handling $cimo['bulk_optimization'] in the
block around $is_bulk_optimized and the loop that computes
$bulk_original_filesize/$bulk_converted_filesize, add a guard before computing
$optimization_savings to ensure $original_filesize > 0 (otherwise set
$optimization_savings to 0 or skip showing it) and remove the unconditional
division; update logic that sets $original_filesize/$converted_filesize and
$optimization_savings so it only runs when there are usable stats (use
$is_bulk_optimized and nonzero $bulk_original_filesize as conditions).


$kb_saved = cimo_format_filesize( $original_filesize - $converted_filesize, 1, true );
$optimization_savings_class = ( $optimization_savings > 0 ) ? 'cimo-optimization-savings-up' : 'cimo-optimization-savings-down';

Expand Down Expand Up @@ -121,33 +150,74 @@ function cimo_get_media_type_label( $mimetype ) {

// Optimization savings
echo '<li class="cimo-compression-savings ' . esc_attr( $optimization_savings_class ) . '">';
echo 'Saved ' . esc_html( $optimization_savings ) . '% <span class="cimo-compression-savings-bytes">(' . esc_html( $kb_saved ) . ')</span>';
echo sprintf(
/* translators: %s: percentage saved */
esc_html__( 'Saved %s%%', 'cimo-image-optimizer' ),
esc_html( $optimization_savings )
);
echo ' <span class="cimo-compression-savings-bytes">(' . esc_html( $kb_saved ) . ')</span>';
echo '</li>';

// Filesize original
echo '<li class="cimo-filesize-original">';
echo 'Original: <span class="cimo-value">' . esc_html( $original_size ) . '</span>';
echo sprintf(
/* translators: %s: original file size */
esc_html__( 'Original: %s', 'cimo-image-optimizer' ),
'<span class="cimo-value">' . esc_html( $original_size ) . '</span>'
);
echo '</li>';

// Filesize optimized
echo '<li class="cimo-filesize-optimized">';
echo 'Optimized: <span class="cimo-value">' . esc_html( $converted_size ) . '</span>';
$arrow = $converted_filesize < $original_filesize ? '↓' : ( $converted_filesize > $original_filesize ? '↑' : '' );
echo sprintf(
/* translators: %s: optimized file size */
esc_html__( 'Optimized: %s', 'cimo-image-optimizer' ),
'<span class="cimo-value">' . esc_html( $arrow ) . ' ' . esc_html( $converted_size ) . '</span>'
);
echo '</li>';

// Converted format
echo '<li class="cimo-converted">';
echo '🏞️ Converted to <span class="cimo-value">' . esc_html( $converted_format ) . '</span>';
echo '</li>';
if ( ! $is_bulk_optimized ) {

// Conversion time
echo '<li class="cimo-time">';
echo '⚡️ Done in <span class="cimo-value">' . esc_html( $converttime_str ) . '</span>';
echo '</li>';
// Converted format
echo '<li class="cimo-converted">';
echo '🏞️ ' . sprintf(
/* translators: %s: converted format */
esc_html__( 'Converted to %s', 'cimo-image-optimizer' ),
'<span class="cimo-value">' . esc_html( $converted_format ) . '</span>'
);
echo '</li>';

// Conversion time
echo '<li class="cimo-time">';
echo '⚡️ ' . sprintf(
/* translators: %s: conversion time */
esc_html__( 'Done in %s', 'cimo-image-optimizer' ),
'<span class="cimo-value">' . esc_html( $converttime_str ) . '</span>'
);
echo '</li>';

} else {

// Bulk optimization number
echo '<li class="cimo-bulk-optimization-number">';
echo '🏞️ ' . sprintf(
/* translators: %s: bulk optimization count */
esc_html__( '%s thumbnail(s) processed', 'cimo-image-optimizer' ),
'<span class="cimo-value">' . esc_html( $bulk_optimization_count ) . '</span>'
);
echo '</li>';

// Bulk optimization note
echo '<li class="cimo-bulk-optimization-note">';
echo '⚡️ ' . esc_html__( 'Bulk optimized', 'cimo-image-optimizer' );
echo '</li>';
}

echo '</ul>';
// echo '</div>';
} else {
echo '<p>Cimo did not optimize this attachment.</p>';
echo '<p>' . esc_html__( 'Cimo did not optimize this attachment.', 'cimo-image-optimizer' ) . '</p>';
}
},
'attachment',
Expand Down
8 changes: 8 additions & 0 deletions src/admin/class-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ public function save_metadata( $request ) {
}
}

// Add our metadata key to note that this was optimized during upload.
$sanitized_metadata['optimized_during_upload'] = true;

Comment on lines +162 to +164
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Data structure inconsistency: mixing non-numeric key into a numerically-indexed array.

$sanitized_metadata is a numerically-indexed array of metadata entries. Adding 'optimized_during_upload' => true as an associative key creates a mixed array that will cause issues downstream:

  1. In add_attachment_metadata() (line 215), the foreach iterates expecting each $item to be an array with a 'filename' key. When it encounters the boolean true value, isset($item['filename']) will fail or produce unexpected behavior.

  2. This could also affect consumers in class-meta-box.php and class-stats.php that expect consistent data structures.

Consider storing this flag separately or restructuring to avoid mixing numeric and associative keys:

Proposed fix
-		// Add our metadata key to note that this was optimized during upload.
-		$sanitized_metadata['optimized_during_upload'] = true;
-
 		// Save to a transient queue for metadata waiting for attachment creation
 		$transient_key = 'cimo_metadata_queue';
 		$queue = get_transient( $transient_key );
 		if ( ! is_array( $queue ) ) {
 			$queue = [];
 		}

 		// Append new metadata to the queue
-		$queue = array_merge( $queue, $sanitized_metadata );
+		// Mark each entry as optimized during upload
+		foreach ( $sanitized_metadata as &$entry ) {
+			$entry['optimized_during_upload'] = true;
+		}
+		$queue = array_merge( $queue, $sanitized_metadata );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/admin/class-metadata.php` around lines 162 - 164, You added an
associative key 'optimized_during_upload' => true into $sanitized_metadata (a
numerically-indexed array), which mixes array types and breaks consumers like
add_attachment_metadata() that expect each item to be an array with a 'filename'
key; instead, either (A) push a properly structured metadata entry into
$sanitized_metadata (e.g., an array with 'filename' and a flag field) so
iteration still yields arrays, or (B) keep the boolean flag separate (e.g., set
it on the attachment post meta or a separate variable/property) and do not
inject an associative key into $sanitized_metadata; update any usages in
add_attachment_metadata(), class-meta-box.php, and class-stats.php to read the
flag from the new location.

// Save to a transient queue for metadata waiting for attachment creation
$transient_key = 'cimo_metadata_queue';
$queue = get_transient( $transient_key );
Expand Down Expand Up @@ -285,6 +288,11 @@ public function prepare_attachment_for_js( $response, $attachment, $meta ) {
* @return array The updated metadata.
*/
public function preserve_cimo_metadata( $metadata, $attachment_id ) {
// If there's a metadata for cimo incoming, then normal operation.
if ( isset( $metadata['cimo'] ) ) {
return $metadata;
}

// Get the existing Cimo metadata from the attachment metadata.
$existing_metadata = wp_get_attachment_metadata( $attachment_id );
if ( $existing_metadata && isset( $existing_metadata['cimo'] ) ) {
Expand Down
49 changes: 49 additions & 0 deletions src/admin/class-stats.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ private static function compute_stats( $existing_stats = null ) {
}

$cimo_data = $metadata['cimo'];

/**
* This data comes from bulk optimizing media in the Media Library:
*/

if ( isset( $cimo_data['bulk_optimization'] ) ) {
foreach ( $cimo_data['bulk_optimization'] as $size => $data ) {
$updated_stats['media_optimized_num']++;
$original_size_b = isset( $data['originalFilesize'] ) ? (int) $data['originalFilesize'] : 0;
$converted_size_b = isset( $data['convertedFilesize'] ) ? (int) $data['convertedFilesize'] : 0;
$updated_stats['total_original_size'] += $original_size_b / 1024;
$updated_stats['total_optimized_size'] += $converted_size_b / 1024;
}
}

/**
* This data comes from individually optimized images from the user uploading media:
*/

$updated_stats['media_optimized_num']++;
Comment on lines +87 to 106
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Double-counting issue: media_optimized_num is incremented both inside and outside the bulk optimization loop.

When bulk_optimization data exists, the code increments media_optimized_num for each entry inside the loop (line 94), but then also unconditionally increments it again at line 106. This results in double-counting.

Proposed fix: only count individually optimized images when bulk_optimization is absent
 				if ( isset( $cimo_data['bulk_optimization'] ) ) {
 					foreach ( $cimo_data['bulk_optimization'] as $size => $data ) {
 						$updated_stats['media_optimized_num']++;
 						$original_size_b = isset( $data['originalFilesize'] ) ? (int) $data['originalFilesize'] : 0;
 						$converted_size_b = isset( $data['convertedFilesize'] ) ? (int) $data['convertedFilesize'] : 0;
 						$updated_stats['total_original_size'] += $original_size_b / 1024;
 						$updated_stats['total_optimized_size'] += $converted_size_b / 1024;
 					}
-				}
-
-				/**
-				 * This data comes from individually optimized images from the user uploading media:
-				 */
-
-				$updated_stats['media_optimized_num']++;
-
-				// Extract file sizes (bytes) and add to KB totals
-				$original_size_b = isset( $cimo_data['originalFilesize'] ) ? (int) $cimo_data['originalFilesize'] : 0;
-				$converted_size_b = isset( $cimo_data['convertedFilesize'] ) ? (int) $cimo_data['convertedFilesize'] : 0;
-
-				$updated_stats['total_original_size'] += $original_size_b / 1024;
-				$updated_stats['total_optimized_size'] += $converted_size_b / 1024;
+				} else {
+					/**
+					 * This data comes from individually optimized images from the user uploading media:
+					 */
+					$updated_stats['media_optimized_num']++;
+
+					// Extract file sizes (bytes) and add to KB totals
+					$original_size_b = isset( $cimo_data['originalFilesize'] ) ? (int) $cimo_data['originalFilesize'] : 0;
+					$converted_size_b = isset( $cimo_data['convertedFilesize'] ) ? (int) $cimo_data['convertedFilesize'] : 0;
+
+					$updated_stats['total_original_size'] += $original_size_b / 1024;
+					$updated_stats['total_optimized_size'] += $converted_size_b / 1024;
+				}
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 93-93: Avoid unused local variables such as '$size'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/admin/class-stats.php` around lines 87 - 106, The code double-counts
media_optimized_num because it increments inside the foreach over
$cimo_data['bulk_optimization'] and then unconditionally again after that block;
update the logic around $cimo_data and $updated_stats so the final
$updated_stats['media_optimized_num']++ only runs for individually optimized
images (i.e., when bulk_optimization is not present or empty). Concretely, use
an if/else or guard that checks isset($cimo_data['bulk_optimization']) (and
non-empty if appropriate) and only perform the standalone increment when no
bulk_optimization entries were processed, leaving the increments inside the
foreach unchanged.


// Extract file sizes (bytes) and add to KB totals
Expand Down Expand Up @@ -153,5 +172,35 @@ private static function format_bytes( $bytes, $decimals = 2 ) {

return $value . ' ' . $sizes[ $i ];
}

/**
* Update stats for when an attachment has been bulk optimized.
*
* @param int $attachment_id The attachment ID.
* @param int $original_size The original file size in bytes.
* @param int $optimized_size The optimized file size in bytes.
*/
public static function update_stats_bulk_optimized( $attachment_id, $original_size, $optimized_size ) {
$stats = self::get_stats();
$stats['media_optimized_num']++;
$stats['total_original_size'] += $original_size / 1024;
$stats['total_optimized_size'] += $optimized_size / 1024;
update_option( self::OPTION_KEY, $stats, false );
}

/**
* Update stats for when an attachment has been restored from bulk optimization.
*
* @param int $attachment_id The attachment ID.
* @param int $optimized_size The optimized file size in bytes.
* @param int $restored_size The restored file size in bytes.
*/
public static function update_stats_bulk_restored( $attachment_id, $optimized_size, $restored_size ) {
$stats = self::get_stats();
$stats['media_optimized_num']--;
$stats['total_original_size'] -= $optimized_size / 1024;
$stats['total_optimized_size'] -= $restored_size / 1024;
update_option( self::OPTION_KEY, $stats, false );
}
Comment on lines +198 to +204
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Verify the restore stats logic: parameter usage appears inverted.

The subtraction logic seems backwards:

  • total_original_size -= $optimized_size — but when restoring, the original size should remain unchanged or increase
  • total_optimized_size -= $restored_size — this should subtract the optimized size being removed

Also, $attachment_id is unused per static analysis. If it's reserved for future use, consider adding a // phpcs:ignore comment or prefixing with underscore.

Suggested fix (verify intended behavior)
-	public static function update_stats_bulk_restored( $attachment_id, $optimized_size, $restored_size ) {
+	public static function update_stats_bulk_restored( $_attachment_id, $optimized_size, $restored_size ) {
 		$stats = self::get_stats();
 		$stats['media_optimized_num']--;
-		$stats['total_original_size'] -= $optimized_size / 1024;
-		$stats['total_optimized_size'] -= $restored_size / 1024;
+		$stats['total_original_size'] -= $restored_size / 1024;
+		$stats['total_optimized_size'] -= $optimized_size / 1024;
 		update_option( self::OPTION_KEY, $stats, false );
 	}
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 198-198: Avoid unused parameters such as '$attachment_id'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/admin/class-stats.php` around lines 198 - 204, In
update_stats_bulk_restored, the two size fields are using the wrong parameters:
swap the size arithmetic so total_optimized_size subtracts $optimized_size/1024
and total_original_size is adjusted only according to how originals were tracked
during optimization (e.g. subtract $restored_size/1024 if originals were
previously added, otherwise leave unchanged) — update the lines in
update_stats_bulk_restored to use $optimized_size and $restored_size in the
correct places (reference function update_stats_bulk_restored and variables
$optimized_size, $restored_size, $stats['total_original_size'],
$stats['total_optimized_size']); also address the unused $attachment_id by
either prefixing it with an underscore or adding a // phpcs:ignore comment.

}
}
Loading
Loading