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,79 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Controller;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item_Query;
class Ajax_Media_Library_Scanner extends Controller {
const PARALLEL_REQUESTS = 5;
/**
* @var Media_Library_Scanner
*/
private $scanner;
public function __construct() {
$this->scanner = new Media_Library_Scanner();
$this->register_action( 'wp_ajax_wp_smush_before_scan_library', array( $this, 'before_scan_library' ) );
$this->register_action( 'wp_ajax_wp_smush_scan_library_slice', array( $this, 'scan_library_slice' ) );
$this->register_action( 'wp_ajax_wp_smush_after_scan_library', array( $this, 'after_scan_library' ) );
}
public function before_scan_library() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
$this->scanner->before_scan_library();
$slice_size = $this->scanner->get_slice_size();
$parallel_requests = $this->get_parallel_requests();
$query = new Media_Item_Query();
$image_attachment_count = $query->get_image_attachment_count();
$slice_count = $query->get_slice_count( $slice_size );
wp_send_json_success( array(
'image_attachment_count' => $image_attachment_count,
'slice_count' => $slice_count,
'slice_size' => $slice_size,
'parallel_requests' => $parallel_requests,
) );
}
public function scan_library_slice() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
$data = stripslashes_deep( $_POST );
if ( ! isset( $data['slice'] ) ) {
wp_send_json_error();
}
$slice = (int) $data['slice'];
wp_send_json_success( $this->scanner->scan_library_slice( $slice ) );
}
public function after_scan_library() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
$this->scanner->after_scan_library();
wp_send_json_success();
}
public function get_parallel_requests() {
return self::PARALLEL_REQUESTS;
}
}

View File

@@ -0,0 +1,201 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Controller;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item_Query;
use Smush\Core\Stats\Global_Stats;
use WP_Smush;
class Background_Media_Library_Scanner extends Controller {
const OPTIMIZE_ON_COMPLETED_OPTION_KEY = 'wp_smush_run_optimize_on_scan_completed';
/**
* @var Media_Library_Scanner
*/
private $scanner;
/**
* @var Media_Library_Scan_Background_Process
*/
private $background_process;
private $logger;
/**
* @var bool
*/
private $optimize_on_scan_completed;
/**
* @var Global_Stats
*/
private $global_stats;
/**
* Static instance
*
* @var self
*/
private static $instance;
public static function get_instance() {
if ( empty( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->scanner = new Media_Library_Scanner();
$this->logger = Helper::logger();
$this->global_stats = Global_Stats::get();
$identifier = $this->make_identifier();
$this->background_process = new Media_Library_Scan_Background_Process( $identifier, $this->scanner );
$this->background_process->set_logger( Helper::logger() );
$this->register_action( 'wp_ajax_wp_smush_start_background_scan', array( $this, 'start_background_scan' ) );
$this->register_action( 'wp_ajax_wp_smush_cancel_background_scan', array( $this, 'cancel_background_scan' ) );
$this->register_action( 'wp_ajax_wp_smush_get_background_scan_status', array( $this, 'send_status' ) );
$this->register_action( "{$identifier}_completed", array( $this, 'background_process_completed' ) );
$this->register_action( "{$identifier}_dead", array( $this, 'background_process_dead' ) );
add_filter( 'wp_smush_script_data', array( $this, 'localize_media_library_scan_script_data' ) );
}
public function start_background_scan() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
$in_processing = $this->background_process->get_status()->is_in_processing();
if ( $in_processing ) {
// Already in progress
wp_send_json_error( array( 'message' => __( 'Background scan is already in processing.', 'wp-smushit' ) ) );
}
if ( ! Helper::loopback_supported() ) {
$this->logger->error( 'Loopback check failed. Not starting a new background process.' );
wp_send_json_error( array(
'message' => sprintf(
esc_html__( 'Your site seems to have an issue with loopback requests. Please try again and if the problem persists find out more %s.', 'wp-smushit' ),
sprintf( '<a target="_blank" href="https://wpmudev.com/docs/wpmu-dev-plugins/smush/#background-processing">%s</a>', esc_html__( 'here', 'wp-smushit' ) )
),
) );
} else {
$this->logger->notice( 'Loopback check successful.' );
}
$this->set_optimize_on_scan_completed( ! empty( $_REQUEST['optimize_on_scan_completed'] ) );
if ( $this->background_process->get_status()->is_dead() ) {
$this->scanner->reduce_slice_size_option();
}
$this->scanner->before_scan_library();
$slice_size = $this->scanner->get_slice_size();
$query = new Media_Item_Query();
$slice_count = $query->get_slice_count( $slice_size );
$tasks = range( 1, $slice_count );
$this->background_process->start( $tasks );
wp_send_json_success( $this->get_scan_status() );
}
public function cancel_background_scan() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
$this->background_process->cancel();
$this->set_optimize_on_scan_completed( false );
wp_send_json_success( $this->get_scan_status() );
}
public function send_status() {
check_ajax_referer( 'wp_smush_media_library_scanner' );
if ( ! Helper::is_user_allowed() ) {
wp_send_json_error();
}
wp_send_json_success( $this->get_scan_status() );
}
public function background_process_completed() {
$this->scanner->after_scan_library();
if ( $this->enabled_optimize_on_scan_completed() ) {
$bg_optimization = WP_Smush::get_instance()->core()->mod->bg_optimization;
$bg_optimization->start_bulk_smush_direct();
}
}
public function background_process_dead() {
$this->global_stats->mark_as_outdated();
}
private function make_identifier() {
$identifier = 'wp_smush_background_scan_process';
if ( is_multisite() ) {
$post_fix = "_" . get_current_blog_id();
$identifier .= $post_fix;
}
return $identifier;
}
public function localize_media_library_scan_script_data( $script_data ) {
$scan_script_data = $this->background_process->get_status()->to_array();
$scan_script_data['nonce'] = wp_create_nonce( 'wp_smush_media_library_scanner' );
$script_data['media_library_scan'] = $scan_script_data;
return $script_data;
}
private function set_optimize_on_scan_completed( $status ) {
$this->optimize_on_scan_completed = $status;
if ( $this->optimize_on_scan_completed ) {
update_option( self::OPTIMIZE_ON_COMPLETED_OPTION_KEY, 1, false );
} else {
delete_option( self::OPTIMIZE_ON_COMPLETED_OPTION_KEY );
}
}
private function enabled_optimize_on_scan_completed() {
if ( null === $this->optimize_on_scan_completed ) {
$this->optimize_on_scan_completed = get_option( self::OPTIMIZE_ON_COMPLETED_OPTION_KEY );
}
return ! empty( $this->optimize_on_scan_completed );
}
private function get_scan_status() {
$is_completed = $this->background_process->get_status()->is_completed();
$is_cancelled = $this->background_process->get_status()->is_cancelled();
$status = $this->background_process->get_status()->to_array();
$status['optimize_on_scan_completed'] = $this->enabled_optimize_on_scan_completed();
// Add global stats on completed/cancelled.
if ( $is_completed || $is_cancelled ) {
$status['global_stats'] = WP_Smush::get_instance()->admin()->get_global_stats_with_bulk_smush_content_and_notice();
}
if ( $is_completed ) {
$bg_optimization = WP_Smush::get_instance()->core()->mod->bg_optimization;
$status['enabled_background_process'] = $bg_optimization->should_use_background();
}
return $status;
}
public function get_background_process() {
return $this->background_process;
}
}

View File

@@ -0,0 +1,629 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item;
use Smush\Core\Media\Media_Item_Cache;
use Smush\Core\Media\Media_Item_Optimizer;
use Smush\Core\Media\Media_Item_Stats;
use Smush\Core\Settings;
use Smush\Core\Smush\Smush_Optimization;
use Smush\Core\Stats\Global_Stats;
use WP_Error;
use WP_Smush;
class Media_Library_Row {
/**
* @var int
*/
private $attachment_id;
/**
* @var WP_Error
*/
private $errors;
/**
* @var Media_Item_Optimizer
*/
private $optimizer;
/**
* @var Media_Item
*/
private $media_item;
/**
* @var Global_Stats
*/
private $global_stats;
/**
* @var Settings
*/
private $settings;
public function __construct( $attachment_id ) {
$this->attachment_id = $attachment_id;
$this->media_item = Media_Item_Cache::get_instance()->get( $this->attachment_id );
$this->global_stats = Global_Stats::get();
$this->optimizer = new Media_Item_Optimizer( $this->media_item );
$this->errors = $this->prepare_errors();
$this->settings = Settings::get_instance();
}
private function prepare_errors() {
$error_list = $this->global_stats->get_error_list();
if (
$error_list->has_id( $this->attachment_id )
|| ( ! $this->media_item->has_wp_metadata() && $this->media_item->is_mime_type_supported() )
) {
return $this->media_item->get_errors();
}
if ( $this->optimizer->has_errors() ) {
return $this->optimizer->get_errors();
}
return new WP_Error();
}
/**
* @return string
*/
public function generate_markup() {
if ( ! $this->media_item->is_image() || ! $this->media_item->is_mime_type_supported() ) {
return esc_html__( 'Not processed', 'wp-smushit' );
}
if ( $this->optimizer->in_progress() || $this->optimizer->restore_in_progress() ) {
return esc_html__( 'File processing is in progress.', 'wp-smushit' );
}
if ( $this->media_item->is_animated() ) {
return $this->generate_markup_for_animated_item();
}
$has_error = $this->errors->has_errors();
if ( $has_error && $this->media_item->size_limit_exceeded() ) {
return $this->generate_markup_for_size_limited_item();
}
// Render ignored after animated/size limited to show upsell even ignored the image.
// And render ignored before media item failed to show Ignored message when the image is ignored.
if ( $this->media_item->is_ignored() ) {
return $this->generate_markup_for_ignored_item();
}
if ( $has_error && $this->media_item->has_errors() ) {
return $this->generate_markup_for_failed_item();
}
if ( $this->is_first_optimization_required() && ! $has_error ) {
return $this->generate_markup_for_unsmushed_item();
}
return $this->generate_markup_for_smushed_item( $this->optimizer->get_total_stats() );
}
private function is_first_optimization_required() {
return ! $this->optimizer->is_optimized() && $this->optimizer->should_optimize();
}
private function generate_markup_for_animated_item() {
$error_message = esc_html__( 'Skipped animated file.', 'wp-smushit' );
$utm_link = $this->get_animated_html_utm_link();
return $this->get_html_markup_for_failed_item_with_utm_link( $error_message, $utm_link );
}
private function get_animated_html_utm_link() {
if ( WP_Smush::is_pro() ) {
return $this->get_animated_cdn_notice_with_config_link();
}
return $this->get_html_utm_link(
__( 'Upgrade to Serve GIFs faster with CDN.', 'wp-smushit' ),
'smush_bulksmush_library_gif_cdn'
);
}
private function get_animated_cdn_notice_with_config_link() {
$cdn = WP_Smush::get_instance()->core()->mod->cdn;
if ( $cdn->get_status() ) {
return '<span class="smush-cdn-notice">' . esc_html__( 'GIFs are serving from global CDN', 'wp-smushit' ) . '</span>';
}
$cdn_link = Helper::get_page_url( 'smush-cdn' );
return '<span class="smush-cdn-notice">' . sprintf(
/* translators: %1$s : Open a link %2$s Close the link */
esc_html__( '%1$sEnable CDN%2$s to serve GIFs closer and faster to visitors', 'wp-smushit' ),
'<a href="' . esc_url( $cdn_link ) . '" target="_blank">',
'</a>'
) . '</span>';
}
private function get_html_utm_link( $utm_message, $utm_campain ) {
$upgrade_url = 'https://wpmudev.com/project/wp-smush-pro/';
$args = array(
'utm_source' => 'smush',
'utm_medium' => 'plugin',
'utm_campaign' => $utm_campain,
);
$utm_link = add_query_arg( $args, $upgrade_url );
return sprintf( '<a class="smush-upgrade-link" href="%1$s" target="_blank">%2$s</a>', esc_url( $utm_link ), esc_html( $utm_message ) );
}
private function get_html_markup_for_failed_item_with_utm_link( $error_message, $utm_link = '' ) {
if ( $this->media_item->is_ignored() ) {
$links = $this->get_revert_with_utm_link( $utm_link );
} else {
$links = $this->get_ignore_with_utm_link( $utm_link );
}
return $this->get_html_markup_for_failed_item( $error_message, $links );
}
private function get_revert_with_utm_link( $utm_link = '' ) {
$class_names = array();
$links = $utm_link;
if ( ! empty( $utm_link ) ) {
$class_names[] = 'smush-revert-utm';
}
$links .= $this->get_revert_link( $class_names );
return $links;
}
private function get_revert_link( $class_names = array() ) {
$nonce = wp_create_nonce( 'wp-smush-remove-skipped' );
$class_names[] = 'wp-smush-remove-skipped'; // smush-revert-utm
return sprintf(
'<a href="#" class="%1$s" data-id="%2$d" data-nonce="%3$s">%4$s</a>',
esc_attr( join( ' ', $class_names ) ),
$this->attachment_id,
$nonce,
esc_html__( 'Revert back to previous state', 'wp-smushit' ) . '</a>'
);
}
private function get_ignore_with_utm_link( $utm_link = '' ) {
$class_names = array();
$links = $utm_link;
if ( ! empty( $utm_link ) ) {
$class_names[] = ' smush-ignore-utm';
}
$links .= $this->get_ignore_link( $class_names );
return $links;
}
private function get_ignore_link( $class_names = array() ) {
$class_names[] = 'smush-ignore-image';
return sprintf(
'<a href="#" class="%s" data-id="%d">%s</a>',
esc_attr( join( ' ', $class_names ) ),
$this->attachment_id,
esc_html__( 'Ignore', 'wp-smushit' )
);
}
private function get_html_markup_for_failed_item( $error_message, $links ) {
$html = $this->get_html_markup_optimization_status_for_failed_item( $error_message );
$html .= $this->get_html_markup_action_links( $links );
return $html;
}
private function get_html_markup_optimization_status_for_failed_item( $error_message ) {
if ( $this->media_item->is_ignored() ) {
$class_name = 'smush-ignored';
} else {
$class_name = 'smush-warning';
}
return $this->get_html_markup_optimization_status( $error_message, $class_name );
}
private function get_html_markup_optimization_status( $message, $class_names = array() ) {
return sprintf( '<p class="smush-status %s">%s</p>', join( ' ', (array) $class_names ), $message );
}
private function get_html_markup_action_links( $links, $separator = ' | ' ) {
$links = (array) $links;
$max_links = 4;
if ( count( $links ) > $max_links ) {
$links = array_splice( $links, count( $links ) - $max_links );
}
return sprintf( '<div class="sui-smush-media smush-status-links">%s</div>', join( $separator, $links ) );
}
private function generate_markup_for_size_limited_item() {
$utm_link = '';
if ( ! WP_Smush::is_pro() ) {
$utm_link = $this->get_html_utm_link(
__( 'Upgrade to Pro to Smush larger images.', 'wp-smushit' ),
'smush_bulksmush_library_filesizelimit'
);
}
if ( $this->media_item->is_ignored() ) {
$error_message = esc_html__( 'Ignored.', 'wp-smushit' );
} else {
$error_message = $this->errors->get_error_message();
}
return $this->get_html_markup_for_failed_item_with_utm_link( $error_message, $utm_link );
}
private function generate_markup_for_ignored_item() {
return $this->get_html_markup_for_failed_item_with_suggestion_link( esc_html__( 'Ignored.', 'wp-smushit' ) );
}
private function generate_markup_for_failed_item() {
$error_suggestion = $this->get_error_suggestion();
$suggestion_link = $this->get_array_value( $error_suggestion, 'link' );
$suggestion_message = $this->get_array_value( $error_suggestion, 'message' );
$error_message = $this->errors->get_error_message();
if ( $suggestion_message ) {
$error_message = sprintf(
'%s. %s',
rtrim( $error_message, '.' ),
$suggestion_message
);
}
return $this->get_html_markup_for_failed_item_with_suggestion_link( $error_message, $suggestion_link );
}
private function get_error_suggestion() {
$error_suggestion = array(
'message' => '',
'link' => '',
);
if ( ! $this->errors->has_errors() ) {
return $error_suggestion;
}
switch ( $this->errors->get_error_code() ) {
case 'file_not_found':
case 'no_file_meta':
if ( $this->media_item->backup_file_exists() ) {
$error_suggestion['message'] = esc_html__( 'We recommend using the restore image function to regenerate the thumbnails.', 'wp-smushit' );
} else {
$error_suggestion['message'] = esc_html__( 'We recommend regenerating the thumbnails.', 'wp-smushit' );
$error_suggestion['link'] = $this->get_html_markup_for_regenerate_doc_link();
}
break;
}
return $error_suggestion;
}
private function get_html_markup_for_regenerate_doc_link() {
return sprintf(
'<a target="_blank" href="%s" class="wp-smush-learnmore" data-id="%d">%s</a>',
esc_url( $this->get_regenerate_doc_link() ),
$this->attachment_id,
esc_html__( 'Learn more', 'wp-smushit' )
);
}
private function get_regenerate_doc_link() {
$doc = 'https://wpmudev.com/docs/wpmu-dev-plugins/smush/';
if ( ! WP_Smush::is_pro() ) {
$doc = 'https://wpmudev.com/docs/wpmu-dev-plugins/smush/?utm_source=smush&utm_medium=plugin&utm_campaign=smush_pluginlist_docs';
}
$doc .= '#restoring-images';
return $doc;
}
private function get_html_markup_for_failed_item_with_suggestion_link( $error_message, $suggestion_link = '' ) {
$links = array();
if ( $suggestion_link ) {
$links[] = $suggestion_link;
}
if ( $this->media_item->is_ignored() ) {
$links[] = $this->get_revert_link();
} else {
$resmush_link = $this->get_resmush_link();
if ( $resmush_link ) {
$links[] = $resmush_link;
}
$restore_link = $this->get_restore_link();
if ( $restore_link ) {
$links[] = $restore_link;
}
$links[] = $this->get_ignore_link();
}
return $this->get_html_markup_for_failed_item( $error_message, $links );
}
private function generate_markup_for_unsmushed_item() {
$action_links = array(
$this->get_smush_link(),
$this->get_ignore_link(),
);
$html = $this->get_html_markup_optimization_status( esc_html__( 'Not processed', 'wp-smushit' ) );
$html .= $this->get_html_markup_action_links( $action_links );
return $html;
}
private function generate_markup_for_smushed_item( Media_Item_Stats $total_stats ) {
$error_class = $this->errors->has_errors() ? 'smush-warning' : '';
$html = $this->get_html_markup_optimization_status( $this->get_optimization_status( $total_stats ), $error_class );
$html .= $this->get_html_markup_action_links( $this->get_action_links( $total_stats ) );
$html .= $this->get_html_markup_detailed_stats( $total_stats );
return $html;
}
private function get_optimization_status( Media_Item_Stats $total_stats ) {
$error_message = $this->errors->get_error_message();
if ( $error_message ) {
return $error_message;
}
$no_savings = $total_stats->get_size_after() >= $total_stats->get_size_before();
if ( $no_savings ) {
return esc_html__( 'Skipped: Image is already optimized.', 'wp-smushit' );
}
return $this->get_savings_status_text( $total_stats );
}
private function get_savings_status_text( $total_stats ) {
$count_images = $this->optimizer->get_optimized_sizes_count();
if ( 1 < $count_images ) {
$status_text = sprintf( /* translators: %1$s: bytes savings, %2$s: percentage savings, %3$d: number of images */
esc_html__( '%3$d images reduced by %1$s (%2$s)', 'wp-smushit' ),
$total_stats->get_human_bytes(),
sprintf( '%01.1f%%', $total_stats->get_percent() ),
$count_images
);
} else {
$status_text = sprintf( /* translators: %1$s: bytes savings, %2$s: percentage savings */
esc_html__( 'Reduced by %1$s (%2$s)', 'wp-smushit' ),
$total_stats->get_human_bytes(),
sprintf( '%01.1f%%', $total_stats->get_percent() )
);
}
// Do we need to show the main image size?
$status_text .= sprintf(
/* translators: 1: <br/> tag, 2: Image file size */
esc_html__( '%1$sMain Image size: %2$s', 'wp-smushit' ),
'<br />',
size_format( $this->media_item->get_scaled_or_full_size()->get_filesize(), 2 )
);
return $status_text;
}
/**
* @return array
*/
private function get_action_links( Media_Item_Stats $total_stats ) {
if ( $this->is_first_optimization_required() ) {
return array( $this->get_smush_link(), $this->get_ignore_link() );
}
$links = array();
$resmush_link = $this->get_resmush_link();
if ( $resmush_link ) {
$links[] = $resmush_link;
// Add ignore button while showing resmush button.
$links[] = $this->get_ignore_link();
}
$no_savings = $total_stats->get_size_after() >= $total_stats->get_size_before();
if ( $no_savings ) {
return $links;
}
$restore_link = $this->get_restore_link();
if ( $restore_link ) {
$links[] = $restore_link;
}
$links[] = $this->get_view_stats_link();
return $links;
}
/**
* @return string|void
*/
private function get_html_markup_detailed_stats( Media_Item_Stats $total_stats ) {
$no_savings = $total_stats->get_size_after() >= $total_stats->get_size_before();
if ( $no_savings ) {
return;
}
return sprintf(
'<div id="smush-stats-%d" class="sui-smush-media smush-stats-wrapper hidden">
<table class="wp-smush-stats-holder">
<thead>
<tr>
<th class="smush-stats-header">%s</th>
<th class="smush-stats-header">%s</th>
</tr>
</thead>
<tbody>%s</tbody>
</table>
</div>',
$this->attachment_id,
esc_html__( 'Image size', 'wp-smushit' ),
esc_html__( 'Savings', 'wp-smushit' ),
$this->get_detailed_stats_content()
);
}
private function get_detailed_stats_content() {
$stats_rows = array();
$savings_sizes = array();
// Show Sizes and their compression.
foreach ( $this->media_item->get_sizes() as $size_key => $size ) {
$total_size_stats = $this->optimizer->get_total_size_stats( $size_key );
if ( $total_size_stats->is_empty() ) {
continue;
}
$dimensions = "{$size->get_width()}x{$size->get_height()}";
$stats_rows[ $size_key ] = sprintf(
'<tr>
<td>%s<br/>(%s)</td>
<td>%s ( %s%% )</td>
</tr>',
strtoupper( $size_key ),
$dimensions,
$total_size_stats->get_human_bytes(),
$total_size_stats->get_percent()
);
$savings_sizes[ $size_key ] = $total_size_stats->get_bytes();
}
uksort(
$stats_rows,
function( $size_key1, $size_key2 ) use ( $savings_sizes ) {
return $savings_sizes[ $size_key2 ] - $savings_sizes[ $size_key1 ];
}
);
return join( '', $stats_rows );
}
private function get_smush_link() {
return sprintf(
'<a href="#" class="wp-smush-send" data-id="%d">%s</a>',
$this->attachment_id,
esc_html__( 'Smush', 'wp-smushit' )
);
}
private function should_reoptimize() {
$reoptimize_list = $this->global_stats->get_reoptimize_list();
$error_list = $this->global_stats->get_error_list();
return $reoptimize_list->has_id( $this->attachment_id ) || $error_list->has_id( $this->attachment_id );
}
/**
* @return string|void
*/
private function get_resmush_link() {
if ( ! $this->should_reoptimize() || ! $this->media_item->has_wp_metadata() ) {
return;
}
$next_level_smush_link = $this->get_next_level_smush_link();
if ( ! empty( $next_level_smush_link ) ) {
return $next_level_smush_link;
}
return sprintf(
'<a href="#" data-tooltip="%s" data-id="%d" data-nonce="%s" class="wp-smush-action wp-smush-title sui-tooltip sui-tooltip-constrained wp-smush-resmush">%s</a>',
esc_html__( 'Smush image including original file', 'wp-smushit' ),
$this->attachment_id,
wp_create_nonce( 'wp-smush-resmush-' . $this->attachment_id ),
esc_html__( 'Resmush', 'wp-smushit' )
);
}
/**
* @return string|void
*/
private function get_next_level_smush_link() {
if (
$this->errors->has_errors()
|| $this->is_first_optimization_required()
|| ! $this->is_next_level_smush_required()
) {
return;
}
$anchor_text = $this->get_next_level_smush_anchor_text();
if ( ! $anchor_text ) {
return;
}
return sprintf(
'<a href="#" class="wp-smush-send" data-id="%d">%s</a>',
$this->attachment_id,
$anchor_text
);
}
/**
* @return bool
*/
private function is_next_level_smush_required() {
$smush_optimization = $this->get_smush_optimization();
return $smush_optimization && $smush_optimization->is_next_level_available();
}
private function get_next_level_smush_anchor_text() {
$required_level = $this->settings->get_lossy_level_setting();
switch ( $required_level ) {
case Settings::LEVEL_ULTRA_LOSSY:
return esc_html__( 'Ultra Smush', 'wp-smushit' );
case Settings::LEVEL_SUPER_LOSSY:
return esc_html__( 'Super Smush', 'wp-smushit' );
default:
return false;
}
}
/**
* @return Smush_Optimization|null
*/
private function get_smush_optimization() {
/**
* @var $smush_optimization Smush_Optimization|null
*/
$smush_optimization = $this->optimizer->get_optimization( Smush_Optimization::KEY );
return $smush_optimization;
}
/**
* @return string|void
*/
private function get_restore_link() {
if ( ! $this->media_item->backup_file_exists() ) {
return;
}
return sprintf(
'<a href="#" data-tooltip="%s" data-id="%d" data-nonce="%s" class="wp-smush-action wp-smush-title sui-tooltip wp-smush-restore">%s</a>',
esc_html__( 'Restore original image', 'wp-smushit' ),
$this->attachment_id,
wp_create_nonce( 'wp-smush-restore-' . $this->attachment_id ),
esc_html__( 'Restore', 'wp-smushit' )
);
}
private function get_view_stats_link() {
return sprintf(
'<a href="#" class="wp-smush-action smush-stats-details wp-smush-title sui-tooltip sui-tooltip-top-right" data-tooltip="%s">%s</a>',
esc_html__( 'Detailed stats for all the image sizes', 'wp-smushit' ),
esc_html__( 'View Stats', 'wp-smushit' )
);
}
private function get_array_value( $array, $key ) {
return isset( $array[ $key ] ) ? $array[ $key ] : null;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Modules\Background\Background_Process;
class Media_Library_Scan_Background_Process extends Background_Process {
/**
* @var Media_Library_Scanner
*/
private $scanner;
public function __construct( $identifier, $scanner ) {
parent::__construct( $identifier );
$this->scanner = $scanner;
}
protected function task( $slice_id ) {
$this->scanner->scan_library_slice( $slice_id );
return true;
}
protected function attempt_restart_during_health_check() {
return false;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Controller;
use Smush\Core\Media\Media_Item_Query;
/**
* An un-opinionated scanner.
* All it does is traverse attachments, the real work is supposed to be done by other controllers through actions and filters.
* Supposed to handle parallel requests, each request handling a 'slice' of the total media items.
*/
class Media_Library_Scanner {
const SLICE_SIZE_DEFAULT = 2500;
const SLICE_SIZE_MIN = 500;
const SLICE_SIZE_OPTION_ID = 'wp_smush_scan_slice_size';
public function before_scan_library() {
do_action( 'wp_smush_before_scan_library' );
}
public function scan_library_slice( $slice ) {
$slice_size = $this->get_slice_size();
$query = new Media_Item_Query();
$attachment_ids = $query->fetch_slice_ids( $slice, $slice_size );
$slice_data = apply_filters( 'wp_smush_before_scan_library_slice', array(), $slice, $slice_size );
foreach ( $attachment_ids as $attachment_id ) {
$slice_data = apply_filters( 'wp_smush_scan_library_slice_handle_attachment', $slice_data, $attachment_id, $slice, $slice_size );
}
return apply_filters( 'wp_smush_after_scan_library_slice', $slice_data, $slice, $slice_size );
}
public function after_scan_library() {
do_action( 'wp_smush_after_scan_library' );
}
public function get_slice_size() {
$constant_value = $this->get_slice_size_constant();
if ( $constant_value ) {
return $constant_value;
}
$option_value = $this->get_slice_size_option();
if ( $option_value ) {
return $option_value;
}
return self::SLICE_SIZE_DEFAULT;
}
public function reduce_slice_size_option() {
update_option( self::SLICE_SIZE_OPTION_ID, self::SLICE_SIZE_MIN );
}
private function get_slice_size_option() {
$option_value = (int) get_option( self::SLICE_SIZE_OPTION_ID, 0 );
return max( $option_value, 0 );
}
private function get_slice_size_constant() {
if ( ! defined( 'WP_SMUSH_SCAN_SLICE_SIZE' ) ) {
return 0;
}
$constant_value = (int) WP_SMUSH_SCAN_SLICE_SIZE;
return max( $constant_value, 0 );
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Array_Utils;
use Smush\Core\Controller;
use Smush\Core\Helper;
use Smush\Core\Media\Media_Item_Query;
class Media_Library_Slice_Data_Fetcher extends Controller {
private $slice_post_meta = array();
private $slice_post_ids = array();
private $query;
/**
* @var \WDEV_Logger|null
*/
private $logger;
private $is_multisite;
private $current_site_id;
/**
* @var Array_Utils
*/
private $array_utils;
public function __construct( $is_multisite = false, $current_site_id = 0 ) {
$this->is_multisite = $is_multisite;
$this->current_site_id = $current_site_id;
$this->query = new Media_Item_Query();
$this->logger = Helper::logger();
$this->array_utils = new Array_Utils();
$this->register_filter( 'wp_smush_before_scan_library_slice', array( $this, 'prefetch_slice_data' ), 10, 3 );
$this->register_filter( 'wp_smush_before_scan_library_slice', array( $this, 'hook_meta_filters' ), 20, 3 );
$this->register_filter( 'wp_smush_after_scan_library_slice', array( $this, 'unhook_meta_filters' ) );
$this->register_filter( 'wp_smush_after_scan_library_slice', array( $this, 'reset_slice_data' ) );
}
public function hook_meta_filters() {
add_filter( 'get_post_metadata', array( $this, 'maybe_serve_post_meta' ), 10, 3 );
add_filter( 'add_post_meta', array( $this, 'update_post_meta_on_add' ), 10, 3 );
add_filter( 'update_post_meta', array( $this, 'update_post_meta_on_update' ), 10, 4 );
add_action( 'delete_post_meta', array( $this, 'purge_post_meta_on_delete' ), 10, 3 );
}
public function unhook_meta_filters() {
remove_filter( 'get_post_metadata', array( $this, 'maybe_serve_post_meta' ) );
remove_filter( 'add_post_meta', array( $this, 'update_post_meta_on_add' ) );
remove_filter( 'update_post_meta', array( $this, 'update_post_meta_on_update' ) );
remove_action( 'delete_post_meta', array( $this, 'purge_post_meta_on_delete' ) );
}
public function prefetch_slice_data( $slice_data, $slice, $slice_size ) {
$this->prefetch_slice_post_meta( $slice, $slice_size );
$this->prefetch_slice_posts( $slice, $slice_size );
return $slice_data;
}
public function maybe_serve_post_meta( $meta_value, $attachment_id, $meta_key ) {
$slice_post_meta = $this->get_slice_post_meta();
if ( empty( $slice_post_meta ) ) {
return $meta_value;
}
$cache_key = $this->get_post_meta_cache_key( $attachment_id, $meta_key );
$cached_value = '';
if ( isset( $slice_post_meta[ $cache_key ]->meta_value ) ) {
$cached_value = maybe_unserialize( $slice_post_meta[ $cache_key ]->meta_value );
}
return array( $cached_value );
}
public function update_post_meta_on_add( $attachment_id, $meta_key, $meta_value ) {
$this->update_post_meta( $attachment_id, $meta_key, $meta_value );
}
public function update_post_meta_on_update( $meta_id, $attachment_id, $meta_key, $meta_value ) {
$this->update_post_meta( $attachment_id, $meta_key, $meta_value );
}
public function purge_post_meta_on_delete( $meta_ids, $attachment_id, $meta_key ) {
$cache_key = $this->get_post_meta_cache_key( $attachment_id, $meta_key );
$slice_post_meta = $this->get_slice_post_meta();
if ( isset( $slice_post_meta[ $cache_key ] ) ) {
unset( $slice_post_meta[ $cache_key ] );
$this->set_slice_post_meta( $slice_post_meta );
}
}
public function reset_slice_data( $slice_data ) {
$this->set_slice_post_meta( array() );
$this->reset_slice_posts();
return $slice_data;
}
private function prefetch_slice_post_meta( $slice, $slice_size ) {
$fetched_post_meta = $this->query->fetch_slice_post_meta( $slice, $slice_size );
$fetched_post_meta = $this->array_utils->ensure_array( $fetched_post_meta );
$this->set_slice_post_meta( $fetched_post_meta );
}
private function prefetch_slice_posts( $slice, $slice_size ) {
$slice_posts = $this->query->fetch_slice_posts( $slice, $slice_size );
if ( ! empty( $slice_posts ) && is_array( $slice_posts ) ) {
$slice_post_ids = array();
foreach ( $slice_posts as $slice_post_key => $slice_post ) {
$slice_post_ids[] = $slice_post_key;
// Sanitize before adding to cache otherwise the post is going to be sanitized every time it is fetched from the cache
$sanitized_post = sanitize_post( $slice_post, 'raw' );
wp_cache_add( $slice_post_key, $sanitized_post, 'posts' );
}
$this->set_slice_post_ids( $slice_post_ids );
}
}
private function reset_slice_posts() {
foreach ( $this->get_slice_post_ids() as $slice_post_id ) {
wp_cache_delete( $slice_post_id, 'posts' );
}
$this->set_slice_post_ids( array() );
}
/**
* @param $attachment_id
* @param $meta_key
*
* @return string
*/
private function get_post_meta_cache_key( $attachment_id, $meta_key ) {
return "$attachment_id-$meta_key";
}
private function get_slice_post_meta() {
$slice_post_meta = $this->slice_post_meta;
if ( $this->is_multisite ) {
$slice_post_meta = $this->array_utils->get_array_value( $slice_post_meta, $this->current_site_id );
}
return $this->array_utils->ensure_array( $slice_post_meta );
}
private function set_slice_post_meta( $slice_post_meta ) {
if ( $this->is_multisite ) {
$this->slice_post_meta[ $this->current_site_id ] = $slice_post_meta;
} else {
$this->slice_post_meta = $slice_post_meta;
}
}
private function get_slice_post_ids() {
$slice_post_ids = $this->slice_post_ids;
if ( $this->is_multisite ) {
$slice_post_ids = $this->array_utils->get_array_value( $slice_post_ids, $this->current_site_id );
}
return $this->array_utils->ensure_array( $slice_post_ids );
}
private function set_slice_post_ids( $slice_post_ids ) {
if ( $this->is_multisite ) {
$this->slice_post_ids[ $this->current_site_id ] = $slice_post_ids;
} else {
$this->slice_post_ids = $slice_post_ids;
}
}
/**
* @param $attachment_id
* @param $meta_key
* @param $meta_value
*
* @return void
*/
private function update_post_meta( $attachment_id, $meta_key, $meta_value ) {
$cache_key = $this->get_post_meta_cache_key( $attachment_id, $meta_key );
$slice_post_meta = $this->get_slice_post_meta();
if ( empty( $slice_post_meta[ $cache_key ] ) ) {
$slice_post_meta[ $cache_key ] = new \stdClass();
}
$slice_post_meta[ $cache_key ]->meta_value = $meta_value;
$this->set_slice_post_meta( $slice_post_meta );
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Smush\Core\Media_Library;
use Smush\Core\Array_Utils;
use Smush\Core\Controller;
use Smush\Core\Helper;
class Media_Library_Watcher extends Controller {
const WP_SMUSH_IMAGE_SIZES_STATE = 'wp_smush_image_sizes_state';
/**
* @var Array_Utils
*/
private $array_utils;
public function __construct() {
$this->array_utils = new Array_Utils();
}
public function init() {
parent::init();
add_action( 'add_attachment', array( $this, 'wait_for_generate_metadata' ) );
add_action( 'admin_init', array( $this, 'watch_image_sizes' ), PHP_INT_MAX );
}
public function wait_for_generate_metadata() {
add_action( 'wp_generate_attachment_metadata', array( $this, 'trigger_custom_add_attachment' ), 10, 2 );
}
public function trigger_custom_add_attachment( $metadata, $attachment_id ) {
do_action( 'wp_smush_after_attachment_upload', $attachment_id );
remove_action( 'wp_generate_attachment_metadata', array( $this, 'trigger_custom_add_attachment' ) );
return $metadata;
}
public function watch_image_sizes() {
$skip = get_transient( 'wp_smush_skip_image_sizes_recheck' );
if ( $skip ) {
return;
}
$new_sizes = Helper::fetch_image_sizes();
$new_hash = $this->array_utils->array_hash( $new_sizes );
$old_state = $this->get_image_sizes_state();
$old_sizes = $old_state['sizes'];
$old_hash = $old_state['hash'];
if ( $new_hash !== $old_hash ) {
do_action( 'wp_smush_image_sizes_changed', $old_sizes, $new_sizes );
$this->update_image_sizes_state( $new_sizes, $new_hash );
}
set_transient( 'wp_smush_skip_image_sizes_recheck', true, HOUR_IN_SECONDS );
}
private function get_image_sizes_state() {
$state = get_option( self::WP_SMUSH_IMAGE_SIZES_STATE );
if ( empty( $state ) ) {
$state = array();
}
if ( empty( $state['sizes'] ) || ! is_array( $state['sizes'] ) ) {
$state['sizes'] = array();
}
if ( empty( $state['hash'] ) ) {
$state['hash'] = '';
}
return $state;
}
private function update_image_sizes_state( $sizes, $hash ) {
update_option( self::WP_SMUSH_IMAGE_SIZES_STATE, array(
'sizes' => empty( $sizes ) || ! is_array( $sizes ) ? array() : $sizes,
'hash' => empty( $hash ) ? '' : $hash,
) );
}
}