This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,272 @@
<?php
namespace Smush\Core\Png2Jpg;
use Smush\Core\Controller;
use Smush\Core\File_System;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item;
use Smush\Core\Media\Media_Item_Cache;
use Smush\Core\Settings;
use Smush\Core\Stats\Global_Stats;
use Smush\Core\Stats\Media_Item_Optimization_Global_Stats_Persistable;
use Smush\Core\Upload_Dir;
use WDEV_Logger;
use WP;
class Png2Jpg_Controller extends Controller {
const GLOBAL_STATS_OPTION_ID = 'wp-smush-png2jpg-global-stats';
const REWRITE_RULES_FLUSHED_OPTION = 'wp-smush-png2jpg-rewrite-rules-flushed';
const PNG2JPG_OPTIMIZATION_ORDER = 0;
/**
* @var WDEV_Logger
*/
private $logger;
/**
* @var Global_Stats
*/
private $global_stats;
/**
* @var Media_Item_Cache
*/
private $media_item_cache;
/**
* @var Png2Jpg_Helper
*/
private $helper;
/**
* Static instance
*
* @var self
*/
private static $instance;
/**
* @var File_System
*/
private $fs;
/**
* @var Settings
*/
private $settings;
public static function get_instance() {
if ( empty( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->logger = Helper::logger()->png2jpg();
$this->global_stats = Global_Stats::get();
$this->media_item_cache = Media_Item_Cache::get_instance();
$this->helper = new Png2Jpg_Helper();
$this->fs = new File_System();
$this->settings = Settings::get_instance();
$this->register_filter( 'wp_smush_optimizations', array(
$this,
'add_png2jpg_optimization',
), self::PNG2JPG_OPTIMIZATION_ORDER, 2 );
$this->register_filter( 'wp_smush_global_optimization_stats', array( $this, 'add_png2jpg_global_stats' ) );
$this->register_action( 'wp_smush_settings_updated', array(
$this,
'maybe_mark_global_stats_as_outdated',
), 10, 2 );
$this->register_filter( 'wp_smush_scan_library_slice_handle_attachment', array(
$this,
'maybe_update_transparent_status_during_scan',
), 10, 2 );
$this->register_action( 'wp_smush_after_attachment_upload', array(
$this,
'maybe_update_transparent_status_on_upload',
) );
$this->register_action( 'wp_smush_before_smush_file', array(
$this,
'maybe_update_transparent_status_before_optimization',
) );
if ( ! $this->settings->is_png2jpg_module_active() ) {
return;
}
$this->register_action( 'init', array( $this, 'add_fallback_png_rewrite_rules' ) );
$this->register_action( 'wp', array( $this, 'serve_fallback_png' ) );
}
public function add_fallback_png_rewrite_rules() {
/**
* @var $wp WP
*/
global $wp;
$wp->add_query_var( 'smush_load_fallback_png' );
$upload_dir = new Upload_Dir();
$upload_rel_path = ltrim( $upload_dir->get_upload_rel_path(), '/' );
$this->logger->info( "Added rewrite rule [$upload_rel_path/(.*\.(?:png))$] so fallback PNGs can be served" );
add_rewrite_rule( "$upload_rel_path/(.*\.(?:png))$", 'index.php?smush_load_fallback_png=$matches[1]', 'top' );
$this->maybe_flush_rewrite_rules();
}
public function serve_fallback_png() {
$png_relative_path = get_query_var( 'smush_load_fallback_png' );
if ( ! $png_relative_path ) {
return;
}
$this->logger->info( 'Attempting to serve a fallback PNG.' );
if ( headers_sent() ) {
$this->logger->info( 'Attempted to serve a fallback PNG but headers have already been sent.' );
return;
}
$fallback_jpg_path = $this->get_fallback_jpg_path( $png_relative_path );
if ( ! $fallback_jpg_path ) {
$this->logger->info( 'Attempted to serve a fallback PNG but no JPG was found for fallback.' );
return;
}
$extension = pathinfo( $fallback_jpg_path, PATHINFO_EXTENSION );
$mime_type = $extension === 'png' ? 'image/png' : 'image/jpeg';
status_header( 200 );
header( "Content-Type: $mime_type" );
readfile( $fallback_jpg_path );
exit;
}
public function get_fallback_jpg_path( $png_relative_path ) {
global $wpdb;
$wild = '%';
$png_relative_path = ltrim( $png_relative_path, '/' );
$path_like = $wild . $wpdb->esc_like( $png_relative_path ) . $wild;
$row = $wpdb->get_row( $wpdb->prepare( "SELECT post_id, meta_key FROM {$wpdb->postmeta} WHERE meta_value LIKE %s LIMIT 1", $path_like ) );
if ( empty( $row ) ) {
return false;
}
$media_item = Media_Item_Cache::get_instance()->get( $row->post_id );
if ( ! $media_item->is_image() ) {
return false;
}
$optimization = new Png2Jpg_Optimization( $media_item );
$converted_png_files = $optimization->get_converted_png_files();
$size_key = array_search( $png_relative_path, $converted_png_files );
if ( ! empty( $size_key ) && $media_item->has_size( $size_key ) ) {
$file_path = $media_item->get_size( $size_key )->get_file_path();
} else {
$file_path = $media_item->get_main_size()->get_file_path();
}
if ( ! $this->fs->file_exists( $file_path ) ) {
return false;
}
return $file_path;
}
/**
* @param $optimizations array
* @param $media_item Media_Item
*
* @return array
*/
public function add_png2jpg_optimization( $optimizations, $media_item ) {
$optimization = new Png2Jpg_Optimization( $media_item );
$optimizations[ $optimization->get_key() ] = $optimization;
return $optimizations;
}
private function maybe_flush_rewrite_rules() {
$flushed = get_option( self::REWRITE_RULES_FLUSHED_OPTION, false );
if ( WP_SMUSH_VERSION !== $flushed ) {
$this->logger->info( "Flushing rewrite rules so fallback PNGs can be served" );
flush_rewrite_rules();
update_option( self::REWRITE_RULES_FLUSHED_OPTION, WP_SMUSH_VERSION );
}
}
public function add_png2jpg_global_stats( $stats ) {
$stats[ Png2Jpg_Optimization::KEY ] = new Media_Item_Optimization_Global_Stats_Persistable( self::GLOBAL_STATS_OPTION_ID );
return $stats;
}
public function maybe_mark_global_stats_as_outdated( $old_settings, $settings ) {
$png_to_jpg_old = ! empty( $old_settings['png_to_jpg'] );
$png_to_jpg_new = ! empty( $settings['png_to_jpg'] );
if ( $png_to_jpg_old !== $png_to_jpg_new ) {
$this->global_stats->mark_as_outdated();
}
}
public function maybe_update_transparent_status_during_scan( $slice_data, $attachment_id ) {
$this->maybe_update_transparent_status( $attachment_id );
return $slice_data;
}
public function maybe_update_transparent_status_on_upload( $attachment_id ) {
$this->maybe_update_transparent_status( $attachment_id );
}
/**
* TODO: add test
*
* @param $attachment_id
*
* @return void
*/
public function maybe_update_transparent_status_before_optimization( $attachment_id ) {
$this->maybe_update_transparent_status( $attachment_id );
}
private function maybe_update_transparent_status( $attachment_id ) {
// We are checking the status of the resize module here because the resize module destroys transparency information.
$is_resize_module_active = $this->settings->is_resize_module_active();
$is_png2jpg_module_active = $this->settings->is_png2jpg_module_active();
$transparency_check_required = $is_png2jpg_module_active || $is_resize_module_active;
if ( ! $transparency_check_required ) {
return;
}
$media_item = $this->media_item_cache->get( $attachment_id );
if ( ! $media_item->is_valid() ) {
$this->logger->error( 'Tried to check transparent value but encountered a problem with the media item' );
return;
}
if ( apply_filters( 'wp_smush_skip_image_transparency_check', false, $attachment_id ) ) {
// The image is explicitly excluded from the transparency check
return;
}
if ( ! $media_item->is_png() ) {
// The media item is not even a png so no need to check.
return;
}
if ( $media_item->transparent_meta_exists() ) {
// Already checked, no need to check again.
return;
}
$this->logger->log( 'Setting transparent meta value' );
$full_size = $media_item->get_full_or_scaled_size();
$is_transparent = $this->helper->is_transparent( $full_size->get_file_path(), $full_size->get_width(), $full_size->get_height() );
$set_transparent = $media_item->set_transparent( $is_transparent );
if ( $set_transparent ) {
$media_item->save();
}
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace Smush\Core\Png2Jpg;
use Exception;
use Imagick;
use Smush\Core\File_System;
use Smush\Core\Helper;
class Png2Jpg_Helper {
const LARGE_PNG_SIZE = 3840;//4k.
private $logger;
/**
* @var File_System
*/
private $fs;
public function __construct() {
$this->logger = Helper::logger()->png2jpg();
$this->fs = new File_System();
}
/**
* @param $file_path string
* @param $width int
* @param $height int
*
* @return bool
*/
public function is_transparent( $file_path, $width, $height ) {
if ( $this->supports_imagick() && $this->use_editor_for_transparency_check( $width, $height ) ) {
try {
return ( new Imagick( $file_path ) )->getImageAlphaChannel();
} catch ( Exception $exception ) {
$this->logger->error( 'Imagick: Error in checking PNG transparency ' . $exception->getMessage() );
return false;
}
}
// TODO: we removed GD transparency code because it didn't work, we should add an alternative that works
return $this->file_contents_have_transparency( $file_path );
}
private function use_editor_for_transparency_check( $width, $height ) {
return $width <= self::LARGE_PNG_SIZE && $height <= self::LARGE_PNG_SIZE;
}
private function file_contents_have_transparency( $file_path ) {
// Simple check.
// Src: http://camendesign.com/code/uth1_is-png-32bit.
if ( ord( $this->fs->file_get_contents( $file_path, false, null, 25, 1 ) ) & 4 ) {
$this->logger->info( sprintf( 'File [%s] is a PNG 32-bit.', $file_path ) );
return true;
}
// Check for a transparent pixel line by line
// Src: https://stackoverflow.com/a/43996262
$handle = @fopen( $file_path, 'r' );
if ( ! $handle ) {
return false;
}
$contents = '';
$contain_plte = false;
$contain_trns = false;
while ( ! feof( $handle ) ) {
$new_line = fread( $handle, 8192 );
// Added previous line to avoid split a string while chunking.
$contents .= $new_line;
$contain_plte = $contain_plte || stripos( $contents, 'PLTE' ) !== false;
$contain_trns = $contain_trns || stripos( $contents, 'tRNS' ) !== false;
if ( $contain_plte && $contain_trns ) {
$this->logger->info( sprintf( 'File [%s] is an PNG 8-bit.', $file_path ) );
return true;
}
// Reset the content to save memory.
$contents = $new_line;
}
return false;
}
/**
* Check if Imagick is available or not
*
* @return bool True/False Whether Imagick is available or not
*/
public function supports_imagick() {
if ( ! class_exists( '\Imagick' ) ) {
return false;
}
return true;
}
/**
* Check if GD is loaded
*
* @return bool True/False Whether GD is available or not
*/
public function supports_gd() {
if ( ! function_exists( 'gd_info' ) ) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,680 @@
<?php
namespace Smush\Core\Png2Jpg;
use Smush\Core\Backups\Backups;
use Smush\Core\File_System;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item;
use Smush\Core\Media\Media_Item_Optimization;
use Smush\Core\Media\Media_Item_Size;
use Smush\Core\Media\Media_Item_Stats;
use Smush\Core\Settings;
use Smush\Core\Upload_Dir;
use WP_Error;
class Png2Jpg_Optimization extends Media_Item_Optimization {
const KEY = 'png2jpg_optimization';
const PNG2JPG_SAVINGS_KEY = 'wp-smush-pngjpg_savings';
const CONVERTED_PNG_FILES_META = 'converted_png_files';
/**
* @var Media_Item
*/
private $media_item;
/**
* @var array
*/
private $meta;
/**
* @var Media_Item_Stats[]
*/
private $size_stats = array();
private $converted_png_files;
private $reset_properties = array(
'meta',
'size_stats',
'converted_png_files',
);
private $logger;
private $backups;
/**
* @var WP_Error
*/
private $errors;
/**
* @var Settings
*/
private $settings;
/**
* @var Png2Jpg_Helper
*/
private $helper;
/**
* @var File_System
*/
private $fs;
/**
* @var Upload_Dir
*/
private $upload_dir;
public function __construct( $media_item ) {
$this->media_item = $media_item;
$this->logger = Helper::logger()->png2jpg();
$this->backups = new Backups();
$this->settings = Settings::get_instance();
$this->errors = new WP_Error();
$this->helper = new Png2Jpg_Helper();
$this->fs = new File_System();
$this->upload_dir = new Upload_Dir();
}
public function get_key() {
return self::KEY;
}
public function get_stats() {
$stats = new Media_Item_Stats();
$size_before = 0;
$size_after = 0;
foreach ( $this->get_sizes_to_convert( $this->media_item ) as $size_key => $size ) {
$size_stats = $this->get_size_stats( $size_key );
$size_before += $size_stats->get_size_before();
$size_after += $size_stats->get_size_after();
}
if ( empty( $size_before ) || empty( $size_after ) ) {
return $stats;
}
$stats->set_size_before( $size_before );
$stats->set_size_after( $size_after );
return $stats;
}
public function get_size_stats( $size_key ) {
if ( empty( $this->size_stats[ $size_key ] ) ) {
$this->size_stats[ $size_key ] = $this->prepare_size_stats( $size_key );
}
return $this->size_stats[ $size_key ];
}
public function save() {
$meta = $this->make_meta();
if ( ! empty( $meta ) ) {
update_post_meta( $this->media_item->get_id(), self::PNG2JPG_SAVINGS_KEY, $meta );
$this->reset();
}
}
/**
* @return array
*/
private function make_meta() {
$meta = array();
foreach ( $this->get_sizes_to_convert( $this->media_item ) as $size_key => $size ) {
$size_stats = $this->get_size_stats( $size_key );
if ( ! $size_stats->is_empty() ) {
$meta[ $size_key ] = $size_stats->to_array();
}
}
if ( ! empty( $this->get_converted_png_files() ) ) {
$meta[ self::CONVERTED_PNG_FILES_META ] = $this->get_converted_png_files();
}
return $meta;
}
public function is_optimized() {
return ! $this->get_stats()->is_empty();
}
public function should_optimize() {
if (
$this->media_item->is_skipped()
|| $this->media_item->has_errors()
|| ! $this->settings->is_png2jpg_module_active()
) {
return false;
}
return $this->can_be_converted( $this->media_item );
}
public function should_reoptimize() {
// PNG 2 JPG conversion happens only once so this is the same as should_optimize
return $this->should_optimize();
}
public function optimize() {
if ( ! $this->should_optimize() ) {
return false;
}
return $this->convert_media_item( $this->media_item );
}
private function get_meta() {
if ( is_null( $this->meta ) ) {
$this->meta = $this->fetch_meta();
}
return $this->meta;
}
private function fetch_meta() {
$post_meta = get_post_meta( $this->media_item->get_id(), self::PNG2JPG_SAVINGS_KEY, true );
return empty( $post_meta ) || ! is_array( $post_meta )
? array()
: $post_meta;
}
private function get_size_meta( $size_key ) {
$meta = $this->get_meta();
$size = empty( $meta[ $size_key ] )
? array()
: $meta[ $size_key ];
return empty( $size ) || ! is_array( $size )
? array()
: $size;
}
private function prepare_size_stats( $size_key ) {
$stats = new Media_Item_Stats();
$stats->from_array( $this->get_size_meta( $size_key ) );
return $stats;
}
/**
* @param $media_item Media_Item
*
* @return boolean
*/
private function can_be_converted( $media_item ) {
$id = $media_item->get_id();
$file = $media_item->get_full_or_scaled_size()->get_file_path();
if ( ! $media_item->is_png() ) {
$this->logger->info( sprintf( 'File [%s(%d)] does not have the PNG mime-type.', $file, $id ) );
return false;
}
if ( ! $this->helper->supports_imagick() && ! $this->helper->supports_gd() ) {
$this->logger->warning( 'The site does not support Imagick or GD.' );
return false;
}
$is_optimized = $this->is_optimized();
if ( $is_optimized ) {
$this->logger->info( sprintf( 'File [%s(%d)] already tried the conversion.', $file, $id ) );
return false;
}
/**
* Filter whether to convert the PNG to JPG or not
*
* @param bool $should_convert Current choice for image conversion
* @param int $id Attachment id
* @param string $file File path for the image
* @param string $size Image size being converted
*
* @since 2.4
*/
return apply_filters( 'wp_smush_convert_to_jpg', ! $media_item->is_transparent(), $id, $file, 'full' );
}
/**
* @param $media_item Media_Item
*
* @return boolean
*/
private function convert_media_item( $media_item ) {
$old_urls = $media_item->get_size_urls();
$converted_source_files = array();
$converted_sizes = array();
$sizes = $this->get_sizes_to_convert( $media_item ); // We must convert all sizes, regardless of which sizes the user has selected for smushing
$old_main_file_name = $media_item->get_full_or_scaled_size()->get_file_name();
$new_main_file_name = $this->get_main_jpg_file_name();
foreach ( $sizes as $size_key => $size ) {
$size_file_path = $size->get_file_path();
$file_already_converted = ! empty( $converted_source_files[ $size_file_path ] );
$new_size_file_name = $this->get_size_jpg_file_name( $size, $old_main_file_name, $new_main_file_name );
if ( $file_already_converted ) {
// The file for the current size was already converted under a different size, just copy the stats.
$this->copy_size(
$media_item,
$converted_source_files[ $size_file_path ],
$size_key
);
$converted_sizes[] = $size_key;
} else {
// Convert the file for the current size.
$converted_source_files[ $size_file_path ] = $size_key;
$size_converted = $this->convert_size( $media_item, $size, $new_size_file_name );
if ( $size_converted ) {
$converted_sizes[] = $size_key;
}
}
}
if ( count( $converted_sizes ) === count( $sizes ) ) {
$png_file_paths = array_flip( $converted_source_files );
$this->delete_files( $png_file_paths );
// All sizes successful, save media item.
$media_item->set_mime_type( 'image/jpeg' );
$media_item->save();
// Save optimization data.
$this->set_converted_png_files( $this->relative_paths( $png_file_paths ) );
$this->save();
$media_item_stats = $this->get_stats();
do_action(
'wp_smush_png_jpg_converted',
$media_item->get_id(),
$media_item->get_wp_metadata(),
$media_item_stats ? $media_item_stats->to_array() : array(),
$png_file_paths
);
$this->replace_urls_in_content( $old_urls, $media_item->get_size_urls() );
return true;
} else {
// We can't have some sizes as pngs and others as jpgs, if this is the case then we must delete the successfully converted files
$converted_files_to_delete = array_map( function ( $converted_size ) use ( $media_item ) {
return $media_item->get_size( $converted_size )->get_file_path();
}, $converted_sizes );
$this->delete_files( $converted_files_to_delete );
// Reset all the changes made so far.
$media_item->reset();
$this->reset();
return false;
}
}
/**
* @param $media_item Media_Item
* @param $media_item_size Media_Item_Size
* @param $new_file_name
*
* @return boolean
*/
private function convert_size( $media_item, $media_item_size, $new_file_name ) {
$new_file_path = $media_item->get_dir() . $new_file_name;
$result = $this->write_file_for_size( $media_item, $media_item_size, $new_file_path );
if ( ! $result || empty( $result['filesize'] ) ) {
return false;
}
$size_before = $media_item_size->get_filesize();
$size_after = $result['filesize'];
if ( $size_after > $size_before ) {
$this->fs->unlink( $new_file_path );
$this->add_error(
$media_item_size->get_key(),
'converted_image_larger',
__( 'Skipped: Smushed file is larger than the original file.', 'wp-smushit' )
);
$this->logger->error(
sprintf(
/* translators: 1: Converted path, 2: Converted file size, 3: Original path, 4: Original file size */
__( 'The new file [%1$s](%2$s) is larger than the original file [%3$s](%4$s).', 'wp-smushit' ),
$this->upload_dir->get_human_readable_path( $new_file_path ),
size_format( $size_after ),
$this->upload_dir->get_human_readable_path( $media_item_size->get_file_path() ),
size_format( $size_before )
)
);
return false;
}
$media_item_size->set_file_name( $new_file_name );
$media_item_size->set_mime_type( 'image/jpeg' );
$media_item_size->set_filesize( $size_after );
$size_stats = $this->get_size_stats( $media_item_size->get_key() );
$size_stats->set_size_before( $size_before );
$size_stats->set_size_after( $size_after );
$this->logger->info( sprintf( 'Image [%s] converted from PNG with size [%d] to JPG with size [%d].', $new_file_name, $size_before, $size_after ) );
do_action(
'wp_smush_png_jpg_size_converted',
$media_item->get_id(),
$media_item_size->get_key(),
$size_stats->to_array()
);
return true;
}
/**
* @param $media_item Media_Item
* @param $media_item_size Media_Item_Size
* @param $new_file_path string
*
* @return array|false
*/
private function write_file_for_size( $media_item, $media_item_size, $new_file_path ) {
$editor = wp_get_image_editor( $media_item_size->get_file_path() );
$this->logger->info( sprintf( 'Image editor [%s] selected for PNG 2 JPG conversion.', get_class( $editor ) ) );
if ( is_wp_error( $editor ) ) {
$this->add_error(
$media_item_size->get_key(),
'image_load_error',
sprintf(
/* translators: 1: Image path, 2: Image id, 3: Error message. */
__( 'Image Editor cannot load file [%1$s(%2$d)]: %3$s.', 'wp-smushit' ),
$this->upload_dir->get_human_readable_path( $media_item_size->get_file_path() ),
$media_item->get_id(),
$editor->get_error_message()
)
);
return false;
}
$new_image_data = $editor->save( $new_file_path, 'image/jpeg' );
if ( is_wp_error( $new_image_data ) ) {
$this->add_error(
$media_item_size->get_key(),
'image_save_error',
/* translators: %s: Error message. */
sprintf( __( 'The image editor was unable to save the image: %s', 'wp-smushit' ), $new_image_data->get_error_message() )
);
return false;
}
return $new_image_data;
}
private function delete_files( $files ) {
foreach ( $files as $file ) {
if ( $this->fs->file_exists( $file ) ) {
// TODO: create an S3 compatible method for this
$this->fs->unlink( $file );
}
}
}
/**
* TODO: add the action wp_smush_image_url_updated
*
* @param $old_url
* @param $new_url
*
* @return bool
*/
private function replace_url_in_content( $old_url, $new_url ) {
global $wpdb;
$wild = '%';
$old_url_like = $wild . $wpdb->esc_like( $old_url ) . $wild;
$query = $wpdb->prepare( "SELECT ID, post_content FROM $wpdb->posts WHERE post_content LIKE %s", $old_url_like );
$rows = $wpdb->get_results( $query );
if ( empty( $rows ) || ! is_array( $rows ) ) {
return true;
}
$update_count = 0;
foreach ( $rows as $row ) {
// Replace old URLs with new URLs.
$post_content = $row->post_content;
$post_content = str_replace( $old_url, $new_url, $post_content );
// Update Post content.
$updated = $wpdb->update(
$wpdb->posts,
array( 'post_content' => $post_content ),
array( 'ID' => $row->ID )
);
if ( $updated ) {
$update_count ++;
}
clean_post_cache( $row->ID );
}
// TODO: do something with this return value, see what we were doing in the legacy code
return $update_count === count( $rows );
}
public function reset() {
foreach ( $this->reset_properties as $property ) {
$this->$property = null;
}
}
private function copy_size( $media_item, $source_size_key, $destination_size_key ) {
$source_size = $media_item->get_size( $source_size_key );
$source_stats = $this->get_size_stats( $source_size_key );
$destination_size = $media_item->get_size( $destination_size_key );
$destination_stats = $this->get_size_stats( $destination_size_key );
$destination_size->set_file_name( $source_size->get_file_name() );
$destination_size->set_mime_type( $source_size->get_mime_type() );
$destination_size->set_filesize( $source_stats->get_size_after() );
$destination_stats->from_array( $source_stats->to_array() );
}
/**
* @param array $old_urls
* @param array $new_urls
*
* @return void
*/
private function replace_urls_in_content( $old_urls, $new_urls ) {
foreach ( $old_urls as $size_key => $old_size_url ) {
if ( empty( $new_urls[ $size_key ] ) ) {
continue;
}
$new_size_url = $new_urls[ $size_key ];
$this->replace_url_in_content( $old_size_url, $new_size_url );
}
}
public function delete_data() {
delete_post_meta( $this->media_item->get_id(), self::PNG2JPG_SAVINGS_KEY );
$this->reset();
}
public function should_optimize_size( $size ) {
if ( ! $this->should_optimize() ) {
return false;
}
return array_key_exists(
$size->get_key(),
$this->get_sizes_to_convert( $this->media_item )
);
}
/**
* @param Media_Item $media_item
*
* @return array|Media_Item_Size[]
*/
private function get_sizes_to_convert( $media_item ) {
return $media_item->get_sizes();
}
public function get_converted_png_files() {
if ( is_null( $this->converted_png_files ) ) {
$this->converted_png_files = $this->prepare_converted_png_files();
}
return $this->converted_png_files;
}
private function prepare_converted_png_files() {
$meta = $this->get_meta();
return empty( $meta[ self::CONVERTED_PNG_FILES_META ] )
? array()
: $meta[ self::CONVERTED_PNG_FILES_META ];
}
public function set_converted_png_files( $converted_png_files ) {
$this->converted_png_files = $converted_png_files;
}
public function can_restore() {
// If it is optimized then we can restore it
return $this->is_optimized();
}
public function restore() {
$media_item = $this->media_item;
$jpg_urls = $media_item->get_size_urls();
$jpg_paths = $media_item->get_size_paths();
$restore_file_path = $this->get_restore_file_path();
$after_restore = function ( $restored ) use ( $media_item, $jpg_urls, $jpg_paths, $restore_file_path ) {
// We are doing these changes in a callback so that the other callbacks hooked to wp_smush_after_restore_backup will receive the most up-to-date state of the media item
if ( $restored ) {
// Undo all the changes we did during the conversion
$media_item->get_main_size()->set_file_name( basename( $restore_file_path ) );
$media_item->set_mime_type( 'image/png' );
$media_item->save();
$this->replace_urls_in_content( $jpg_urls, $media_item->get_size_urls() );
$this->delete_files( $jpg_paths );
/**
* The DB data will be deleted by {@see delete_data}
*/
}
};
add_action( 'wp_smush_after_restore_backup', $after_restore, - 10 );
$restored = $this->backups->restore_backup_to_file_path( $media_item, $restore_file_path );
remove_action( 'wp_smush_after_restore_backup', $after_restore );
return $restored;
}
private function get_restore_file_path() {
$media_item = $this->media_item;
$converted_png_files = $this->get_converted_png_files();
$default_backup_size = $media_item->get_default_backup_size();
$check_file_exists = true;
if ( ! empty( $converted_png_files['full'] ) ) {
// First try to get the original file name from converted file meta
$file_name = basename( $converted_png_files['full'] );
} elseif ( $default_backup_size ) {
// If converted meta didn't work out then
$backup_file_path = $default_backup_size->get_file_path();
if ( strpos( $backup_file_path, '.bak' ) ) {
$backup_file_path = str_replace( '.bak', '', $backup_file_path );
} else {
// If the default backup path does not have .bak extension then we don't need to do wp_unique_filename because we know that the file at the restore path is our file
$check_file_exists = false;
}
$file_name = basename( $backup_file_path );
} else {
$full_size = $media_item->get_full_size();
$file_name = $full_size->get_file_name_without_extension() . '.png';
}
$restore_file_path = path_join( $media_item->get_dir(), $file_name );
if ( $check_file_exists && $this->fs->file_exists( $restore_file_path ) ) {
$unique_filename = wp_unique_filename( $media_item->get_dir(), $file_name );
return path_join( $media_item->get_dir(), $unique_filename );
} else {
return $restore_file_path;
}
}
private function add_error( $size_key, $code, $message ) {
// Log the error
$this->logger->error( $message );
// Add the error
if ( $size_key ) {
$message = "[$size_key] $message";
}
$this->errors->add( $code, $message );
}
public function get_errors() {
return $this->errors;
}
private function relative_paths( $absolute_paths ) {
$relative_paths = array();
foreach ( $absolute_paths as $key => $absolute_path ) {
$dir = $this->media_item->get_relative_file_dir();
$file = wp_basename( $absolute_path );
$relative_paths[ $key ] = "$dir/$file";
}
return $relative_paths;
}
private function get_main_jpg_file_name() {
$media_item = $this->media_item;
$full_size = $media_item->get_full_or_scaled_size();
$png_extension = '.' . pathinfo( $full_size->get_file_path(), PATHINFO_EXTENSION );
$full_file_name = str_replace( $png_extension, '.jpg', $full_size->get_file_name() );
return $this->fs->file_exists( $media_item->get_dir() . $full_file_name )
? wp_unique_filename( $media_item->get_dir(), $full_file_name )
: $full_file_name;
}
/**
* @param $size Media_Item_Size
* @param $main_png_file_name string
* @param $main_jpg_file_name string
*
* @return string
*/
private function get_size_jpg_file_name( $size, $main_png_file_name, $main_jpg_file_name ) {
$png_extension = '.' . pathinfo( $main_png_file_name, PATHINFO_EXTENSION );
$png_without_ext = str_replace( $png_extension, '', $main_png_file_name );
$jpg_without_ext = str_replace( '.jpg', '', $main_jpg_file_name );
$size_file_name = str_replace( $png_without_ext, $jpg_without_ext, $size->get_file_name() );
$size_file_name = str_replace( $png_extension, '.jpg', $size_file_name );
return $this->fs->file_exists( $size->get_dir() . $size_file_name )
? wp_unique_filename( $size->get_dir(), $size_file_name )
: $size_file_name;
}
public function get_optimized_sizes_count() {
$count = 0;
$sizes = $this->get_sizes_to_convert( $this->media_item );
foreach ( $sizes as $size_key => $size ) {
$size_stats = $this->get_size_stats( $size_key );
if ( $size_stats && ! $size_stats->is_empty() ) {
$count ++;
}
}
return $count;
}
}