rebase from live enviornment

This commit is contained in:
Rachit Bhargava
2024-01-09 22:14:20 -05:00
parent ff0b49a046
commit 3a22fcaa4a
15968 changed files with 2344674 additions and 45234 deletions

View File

@@ -0,0 +1,117 @@
<?php
namespace Imagify\Bulk;
use Imagify_Filesystem;
/**
* Abstract class to use for bulk.
*
* @since 1.9
*/
abstract class AbstractBulk implements BulkInterface {
/**
* Filesystem object.
*
* @var Imagify_Filesystem
* @since 1.9
*/
protected $filesystem;
/**
* The constructor.
*
* @since 1.9
*/
public function __construct() {
$this->filesystem = Imagify_Filesystem::get_instance();
}
/**
* Format context data (stats).
*
* @since 1.9
*
* @param array $data {
* The data to format.
*
* @type int $count-optimized Number of media optimized.
* @type int $count-errors Number of media having an optimization error.
* @type int $optimized-size Optimized filesize.
* @type int $original-size Original filesize.
* @type string $errors_url URL to the page listing the optimization errors.
* }
* @return array {
* The formated data.
*
* @type string $count-optimized Number of media optimized.
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
* @type string $optimized-size Optimized filesize.
* @type string $original-size Original filesize.
* }
*/
protected function format_context_data( $data ) {
$defaults = [
'count-optimized' => '',
'count-errors' => '',
'optimized-size' => '',
'original-size' => '',
];
$data = wp_parse_args( $data, $defaults );
$data = array_map( function( $item ) {
return empty( $item ) ? '' : $item;
}, $data );
if ( ! empty( $data['count-optimized'] ) ) {
// translators: %s is a formatted number, dont use %d.
$data['count-optimized'] = sprintf( _n( '%s Media File Optimized', '%s Media Files Optimized', $data['count-optimized'], 'imagify' ), '<span>' . number_format_i18n( $data['count-optimized'] ) . '</span>' );
}
if ( ! empty( $data['count-errors'] ) ) {
/* translators: %s is a formatted number, dont use %d. */
$data['count-errors'] = sprintf( _n( '%s Error', '%s Errors', $data['count-errors'], 'imagify' ), '<span>' . number_format_i18n( $data['count-errors'] ) . '</span>' );
$data['count-errors'] .= ' <a href="' . esc_url( $data['errors_url'] ) . '">' . __( 'View Errors', 'imagify' ) . '</a>';
}
if ( ! empty( $data['optimized-size'] ) ) {
$data['optimized-size'] = '<span class="imagify-cell-label">' . __( 'Optimized Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['optimized-size'], 2 );
}
if ( ! empty( $data['original-size'] ) ) {
$data['original-size'] = '<span class="imagify-cell-label">' . __( 'Original Filesize', 'imagify' ) . '</span> ' . imagify_size_format( $data['original-size'], 2 );
}
unset( $data['errors_url'] );
return $data;
}
/**
* Attempts to set no limit to the PHP timeout for time intensive processes.
*
* @return void
*/
protected function set_no_time_limit() {
if (
function_exists( 'set_time_limit' )
&&
false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' )
&& ! ini_get( 'safe_mode' ) // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
) {
@set_time_limit( 0 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}
}
/**
* Tell if there are optimized media without WebP versions.
*
* @since 1.9
*
* @return int The number of media.
*/
public function has_optimized_media_without_webp() {
return count( $this->get_optimized_media_ids_without_webp()['ids'] );
}
}

View File

@@ -0,0 +1,578 @@
<?php
namespace Imagify\Bulk;
use Exception;
use Imagify\Traits\InstanceGetterTrait;
/**
* Bulk optimization
*/
class Bulk {
use InstanceGetterTrait;
/**
* Class init: launch hooks.
*
* @since 2.1
*/
public function init() {
add_action( 'imagify_optimize_media', [ $this, 'optimize_media' ], 10, 3 );
add_action( 'imagify_convert_webp', [ $this, 'generate_webp_versions' ], 10, 2 );
add_action( 'imagify_convert_webp_finished', [ $this, 'clear_webp_transients' ], 10, 2 );
add_action( 'wp_ajax_imagify_bulk_optimize', [ $this, 'bulk_optimize_callback' ] );
add_action( 'wp_ajax_imagify_missing_webp_generation', [ $this, 'missing_webp_callback' ] );
add_action( 'wp_ajax_imagify_get_folder_type_data', [ $this, 'get_folder_type_data_callback' ] );
add_action( 'wp_ajax_imagify_bulk_info_seen', [ $this, 'bulk_info_seen_callback' ] );
add_action( 'wp_ajax_imagify_bulk_get_stats', [ $this, 'bulk_get_stats_callback' ] );
add_action( 'imagify_after_optimize', [ $this, 'check_optimization_status' ], 10, 2 );
add_action( 'imagify_deactivation', [ $this, 'delete_transients_data' ] );
}
/**
* Delete transients data on deactivation
*
* @return void
*/
public function delete_transients_data() {
delete_transient( 'imagify_custom-folders_optimize_running' );
delete_transient( 'imagify_wp_optimize_running' );
delete_transient( 'imagify_bulk_optimization_complete' );
delete_transient( 'imagify_missing_webp_total' );
}
/**
* Checks bulk optimization status after each optimization task
*
* @param ProcessInterface $process The optimization process.
* @param array $item The item being processed.
*
* @return void
*/
public function check_optimization_status( $process, $item ) {
$custom_folders = get_transient( 'imagify_custom-folders_optimize_running' );
$library_wp = get_transient( 'imagify_wp_optimize_running' );
if (
! $custom_folders
&&
! $library_wp
) {
return;
}
$data = $process->get_data();
$progress = get_transient( 'imagify_bulk_optimization_result' );
if ( $data->is_optimized() ) {
$size_data = $data->get_size_data();
if ( false === $progress ) {
$progress = [
'total' => 0,
'original_size' => 0,
'optimized_size' => 0,
];
}
$progress['total']++;
$progress['original_size'] += $size_data['original_size'];
$progress['optimized_size'] += $size_data['optimized_size'];
set_transient( 'imagify_bulk_optimization_result', $progress, DAY_IN_SECONDS );
}
$remaining = 0;
if ( false !== $custom_folders ) {
if ( false !== strpos( $item['process_class'], 'CustomFolders' ) ) {
$custom_folders['remaining']--;
set_transient( 'imagify_custom-folders_optimize_running', $custom_folders, DAY_IN_SECONDS );
$remaining += $custom_folders['remaining'];
}
}
if ( false !== $library_wp ) {
if ( false !== strpos( $item['process_class'], 'WP' ) ) {
$library_wp['remaining']--;
set_transient( 'imagify_wp_optimize_running', $library_wp, DAY_IN_SECONDS );
$remaining += $library_wp['remaining'];
}
}
if ( 0 >= $remaining ) {
delete_transient( 'imagify_custom-folders_optimize_running' );
delete_transient( 'imagify_wp_optimize_running' );
set_transient( 'imagify_bulk_optimization_complete', 1, DAY_IN_SECONDS );
}
}
/**
* Decrease optimization running counter for the given context
*
* @param string $context Context to update.
*
* @return void
*/
private function decrease_counter( string $context ) {
$counter = get_transient( "imagify_{$context}_optimize_running" );
if ( false === $counter ) {
return;
}
$counter['total'] = $counter['total'] - 1;
$counter['remaining'] = $counter['remaining'] - 1;
if (
0 === $counter['total']
&&
0 >= $counter['remaining']
) {
delete_transient( "imagify_{$context}_optimize_running" );
}
set_transient( "imagify_{$context}_optimize_running", $counter, DAY_IN_SECONDS );
}
/**
* Process a media with the requested imagify bulk action.
*
* @since 2.1
*
* @param int $media_id Media ID.
* @param string $context Current context.
* @param int $optimization_level Optimization level.
*/
public function optimize_media( int $media_id, string $context, int $optimization_level ) {
if ( ! $media_id || ! $context || ! is_numeric( $optimization_level ) ) {
$this->decrease_counter( $context );
return;
}
$this->force_optimize( $media_id, $context, $optimization_level );
}
/**
* Runs the bulk optimization
*
* @param string $context Current context (WP/Custom folders).
* @param int $optimization_level Optimization level.
*
* @return array
*/
public function run_optimize( string $context, int $optimization_level ) {
if ( ! $this->can_optimize() ) {
return [
'success' => false,
'message' => 'over-quota',
];
}
$media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level );
if ( empty( $media_ids ) ) {
return [
'success' => false,
'message' => 'no-images',
];
}
foreach ( $media_ids as $media_id ) {
try {
as_enqueue_async_action(
'imagify_optimize_media',
[
'id' => $media_id,
'context' => $context,
'level' => $optimization_level,
],
"imagify-{$context}-optimize-media"
);
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// nothing to do.
}
}
$data = [
'total' => count( $media_ids ),
'remaining' => count( $media_ids ),
];
set_transient( "imagify_{$context}_optimize_running", $data, DAY_IN_SECONDS );
return [
'success' => true,
'message' => 'success',
];
}
/**
* Runs the WebP generation
*
* @param array $contexts An array of contexts (WP/Custom folders).
*
* @return array
*/
public function run_generate_webp( array $contexts ) {
if ( ! $this->can_optimize() ) {
return [
'success' => false,
'message' => 'over-quota',
];
}
delete_transient( 'imagify_stat_without_webp' );
$medias = [];
foreach ( $contexts as $context ) {
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_webp();
if ( ! $media['ids'] && $media['errors']['no_backup'] ) {
// No backup, no WebP.
return [
'success' => false,
'message' => 'no-backup',
];
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
// Error.
return [
'success' => false,
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
];
}
$medias[ $context ] = $media['ids'];
}
if ( empty( $medias ) ) {
return [
'success' => false,
'message' => 'no-images',
];
}
$total = 0;
foreach ( $medias as $context => $media_ids ) {
$total += count( $media_ids );
foreach ( $media_ids as $media_id ) {
try {
as_enqueue_async_action(
'imagify_convert_webp',
[
'id' => $media_id,
'context' => $context,
],
"imagify-{$context}-convert-webp"
);
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
// nothing to do.
}
}
}
set_transient( 'imagify_missing_webp_total', $total, HOUR_IN_SECONDS );
return [
'success' => true,
'message' => $total,
];
}
/**
* Get the Bulk class name depending on a context.
*
* @since 2.1
*
* @param string $context The context name. Default values are 'wp' and 'custom-folders'.
* @return string The Bulk class name.
*/
private function get_bulk_class_name( string $context ): string {
switch ( $context ) {
case 'wp':
$class_name = WP::class;
break;
case 'custom-folders':
$class_name = CustomFolders::class;
break;
default:
$class_name = Noop::class;
}
/**
* Filter the name of the class to use for bulk process.
*
* @since 1.9
*
* @param int $class_name The class name.
* @param string $context The context name.
*/
$class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context );
return '\\' . ltrim( $class_name, '\\' );
}
/**
* Get the Bulk instance depending on a context.
*
* @since 2.1
*
* @param string $context The context name. Default values are 'wp' and 'custom-folders'.
*
* @return BulkInterface The optimization process instance.
*/
public function get_bulk_instance( string $context ): BulkInterface {
$class_name = $this->get_bulk_class_name( $context );
return new $class_name();
}
/**
* Optimize all files from a media, whatever this medias previous optimization status (will be restored if needed).
* This is used by the bulk optimization page.
*
* @since 1.9
*
* @param int $media_id The media ID.
* @param string $context The context.
* @param int $level The optimization level.
*
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/
private function force_optimize( int $media_id, string $context, int $level ) {
if ( ! $this->can_optimize() ) {
$this->decrease_counter( $context );
return false;
}
$process = imagify_get_optimization_process( $media_id, $context );
$data = $process->get_data();
// Restore before re-optimizing.
if ( $data->is_optimized() ) {
$result = $process->restore();
if ( is_wp_error( $result ) ) {
$this->decrease_counter( $context );
// Return an error message.
return $result;
}
}
return $process->optimize( $level );
}
/**
* Generate WebP images if they are missing.
*
* @since 2.1
*
* @param int $media_id Media ID.
* @param string $context Current context.
*
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
*/
public function generate_webp_versions( int $media_id, string $context ) {
if ( ! $this->can_optimize() ) {
return false;
}
return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions();
}
/**
* Check if the user has a valid account and has quota. Die on failure.
*
* @since 2.1
*/
public function can_optimize() {
if ( ! \Imagify_Requirements::is_api_key_valid() ) {
return false;
}
if ( \Imagify_Requirements::is_over_quota() ) {
return false;
}
return true;
}
/**
* Get the submitted context.
*
* @since 1.9
*
* @param string $method The method used: 'GET' (default), or 'POST'.
* @param string $parameter The name of the parameter to look for.
*
* @return string
*/
public function get_context( $method = 'GET', $parameter = 'context' ) {
$context = 'POST' === $method ? wp_unslash( $_POST[ $parameter ] ) : wp_unslash( $_GET[ $parameter ] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
$context = htmlspecialchars( $context );
return imagify_sanitize_context( $context );
}
/**
* Get the submitted optimization level.
*
* @since 1.7
* @since 1.9 Added $method and $parameter parameters.
* @author Grégory Viguier
*
* @param string $method The method used: 'GET' (default), or 'POST'.
* @param string $parameter The name of the parameter to look for.
* @return int
*/
public function get_optimization_level( $method = 'GET', $parameter = 'optimization_level' ) {
$method = 'POST' === $method ? INPUT_POST : INPUT_GET;
$level = filter_input( $method, $parameter );
if ( ! is_numeric( $level ) || $level < 0 || $level > 2 ) {
if ( get_imagify_option( 'lossless' ) ) {
return 0;
}
return get_imagify_option( 'optimization_level' );
}
return (int) $level;
}
/** ----------------------------------------------------------------------------------------- */
/** BULK OPTIMIZATION CALLBACKS ============================================================= */
/** ----------------------------------------------------------------------------------------- */
/**
* Launch the bulk optimization action
*
* @return void
*/
public function bulk_optimize_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$context = $this->get_context();
$level = $this->get_optimization_level();
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
imagify_die();
}
$data = $this->run_optimize( $context, $level );
if ( false === $data['success'] ) {
wp_send_json_error( [ 'message' => $data['message'] ] );
}
wp_send_json_success( [ 'total' => $data['message'] ] );
}
/**
* Launch the missing WebP versions generation
*
* @return void
*/
public function missing_webp_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$contexts = explode( '_', sanitize_key( wp_unslash( $_GET['context'] ) ) );
foreach ( $contexts as $context ) {
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
imagify_die();
}
}
$data = $this->run_generate_webp( $contexts );
if ( false === $data['success'] ) {
wp_send_json_error( [ 'message' => $data['message'] ] );
}
wp_send_json_success( [ 'total' => $data['message'] ] );
}
/**
* Get stats data for a specific folder type.
*
* @since 1.7
*/
public function get_folder_type_data_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$context = $this->get_context();
if ( ! $context ) {
imagify_die( __( 'Invalid request', 'imagify' ) );
}
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
imagify_die();
}
$bulk = $this->get_bulk_instance( $context );
wp_send_json_success( $bulk->get_context_data() );
}
/**
* Set the "bulk info" popup state as "seen".
*
* @since 1.7
*/
public function bulk_info_seen_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$context = $this->get_context();
if ( ! $context ) {
imagify_die( __( 'Invalid request', 'imagify' ) );
}
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
imagify_die();
}
set_transient( 'imagify_bulk_optimization_infos', 1, WEEK_IN_SECONDS );
wp_send_json_success();
}
/**
* Get generic stats to display in the bulk page.
*
* @since 1.7.1
*/
public function bulk_get_stats_callback() {
imagify_check_nonce( 'imagify-bulk-optimize' );
$folder_types = filter_input( INPUT_GET, 'types', FILTER_REQUIRE_ARRAY );
$folder_types = is_array( $folder_types ) ? array_filter( $folder_types, 'is_string' ) : [];
if ( ! $folder_types ) {
imagify_die( __( 'Invalid request', 'imagify' ) );
}
foreach ( $folder_types as $folder_type_data ) {
$context = ! empty( $folder_type_data['context'] ) ? $folder_type_data['context'] : 'noop';
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
imagify_die();
}
}
wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) );
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Imagify\Bulk;
/**
* Interface to use for bulk.
*
* @since 1.9
*/
interface BulkInterface {
/**
* Get all unoptimized media ids.
*
* @since 1.9
*
* @param int $optimization_level The optimization level.
* @return array A list of unoptimized media. Array keys are media IDs prefixed with an underscore character, array values are the main files URL.
*/
public function get_unoptimized_media_ids( $optimization_level );
/**
* Get ids of all optimized media without WebP versions.
*
* @since 1.9
* @since 1.9.5 The method doesn't return the IDs directly anymore.
*
* @return array {
* @type array $ids A list of media IDs.
* @type array $errors {
* @type array $no_file_path A list of media IDs.
* @type array $no_backup A list of media IDs.
* }
* }
*/
public function get_optimized_media_ids_without_webp();
/**
* Tell if there are optimized media without WebP versions.
*
* @since 1.9
*
* @return int The number of media.
*/
public function has_optimized_media_without_webp();
/**
* Get the context data.
*
* @since 1.9
*
* @return array {
* The formated data.
* The array keys corresponds to the table cell classes: "imagify-cell-{key}".
*
* @type string $count-optimized Number of media optimized.
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
* @type string $optimized-size Optimized filesize.
* @type string $original-size Original filesize.
* }
*/
public function get_context_data();
}

View File

@@ -0,0 +1,178 @@
<?php
namespace Imagify\Bulk;
use Imagify_Custom_Folders;
use Imagify_Files_Scan;
use Imagify_Files_DB;
use Imagify_Folders_DB;
use Imagify_DB;
use Imagify_Files_Stats;
/**
* Class to use for bulk for custom folders.
*
* @since 1.9
*/
class CustomFolders extends AbstractBulk {
/**
* Context "short name".
*
* @var string
* @since 1.9
*/
protected $context = 'custom-folders';
/**
* Get all unoptimized media ids.
*
* @since 1.9
*
* @param int $optimization_level The optimization level.
*
* @return array A list of unoptimized media IDs.
*/
public function get_unoptimized_media_ids( $optimization_level ) {
$this->set_no_time_limit();
/**
* Get the folders from DB.
*/
$folders = Imagify_Custom_Folders::get_folders( [
'active' => true,
] );
if ( ! $folders ) {
return [];
}
/**
* Fires before getting file IDs.
*
* @since 1.7
*
* @param array $folders An array of folders data.
* @param int $optimization_level The optimization level that will be used for the optimization.
*/
do_action( 'imagify_bulk_optimize_files_before_get_files', $folders, $optimization_level );
/**
* Get the files from DB, and from the folders.
*/
$files = Imagify_Custom_Folders::get_files_from_folders( $folders, [
'optimization_level' => $optimization_level,
] );
if ( ! $files ) {
return [];
}
foreach ( $files as $k => $file ) {
$files[ $k ] = $file['file_id'];
}
return $files;
}
/**
* Get ids of all optimized media without WebP versions.
*
* @since 1.9
* @since 1.9.5 The method doesn't return the IDs directly anymore.
*
* @return array {
* @type array $ids A list of media IDs.
* @type array $errors {
* @type array $no_file_path A list of media IDs.
* @type array $no_backup A list of media IDs.
* }
* }
*/
public function get_optimized_media_ids_without_webp() {
global $wpdb;
$this->set_no_time_limit();
$files_table = Imagify_Files_DB::get_instance()->get_table_name();
$folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
$mime_types = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::WEBP_SUFFIX' );
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
"
SELECT fi.file_id, fi.path
FROM $files_table as fi
INNER JOIN $folders_table AS fo
ON ( fi.folder_id = fo.folder_id )
WHERE
fi.mime_type IN ( $mime_types )
AND ( fi.status = 'success' OR fi.status = 'already_optimized' )
AND ( fi.data NOT LIKE %s OR fi.data IS NULL )
ORDER BY fi.file_id DESC",
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
) );
$wpdb->flush();
unset( $mime_types, $files_table, $folders_table, $webp_suffix );
$data = [
'ids' => [],
'errors' => [
'no_file_path' => [],
'no_backup' => [],
],
];
if ( ! $files ) {
return $data;
}
foreach ( $files as $file ) {
$file_id = absint( $file->file_id );
if ( empty( $file->path ) ) {
// Problem.
$data['errors']['no_file_path'][] = $file_id;
continue;
}
$file_path = Imagify_Files_Scan::remove_placeholder( $file->path );
$backup_path = Imagify_Custom_Folders::get_file_backup_path( $file_path );
if ( ! $this->filesystem->exists( $backup_path ) ) {
// No backup, no WebP.
$data['errors']['no_backup'][] = $file_id;
continue;
}
$data['ids'][] = $file_id;
} // End foreach().
return $data;
}
/**
* Get the context data.
*
* @since 1.9
*
* @return array {
* The formated data.
*
* @type string $count-optimized Number of media optimized.
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
* @type string $optimized-size Optimized filesize.
* @type string $original-size Original filesize.
* }
*/
public function get_context_data() {
$data = [
'count-optimized' => Imagify_Files_Stats::count_optimized_files(),
'count-errors' => Imagify_Files_Stats::count_error_files(),
'optimized-size' => Imagify_Files_Stats::get_optimized_size(),
'original-size' => Imagify_Files_Stats::get_original_size(),
'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ),
];
return $this->format_context_data( $data );
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Imagify\Bulk;
/**
* Falback class for bulk.
*
* @since 1.9
*/
class Noop extends AbstractBulk {
/**
* Get all unoptimized media ids.
*
* @since 1.9
*
* @param int $optimization_level The optimization level.
* @return array A list of unoptimized media. Array keys are media IDs prefixed with an underscore character, array values are the main files URL.
*/
public function get_unoptimized_media_ids( $optimization_level ) {
return [];
}
/**
* Get ids of all optimized media without WebP versions.
*
* @since 1.9
* @since 1.9.5 The method doesn't return the IDs directly anymore.
*
* @return array {
* @type array $ids A list of media IDs.
* @type array $errors {
* @type array $no_file_path A list of media IDs.
* @type array $no_backup A list of media IDs.
* }
* }
*/
public function get_optimized_media_ids_without_webp() {
return [
'ids' => [],
'errors' => [
'no_file_path' => [],
'no_backup' => [],
],
];
}
/**
* Get the context data.
*
* @since 1.9
*
* @return array {
* The formated data.
*
* @type string $count-optimized Number of media optimized.
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
* @type string $optimized-size Optimized filesize.
* @type string $original-size Original filesize.
* }
*/
public function get_context_data() {
$data = [
'count-optimized' => 0,
'count-errors' => 0,
'optimized-size' => 0,
'original-size' => 0,
'errors_url' => get_imagify_admin_url( 'folder-errors', 'noop' ),
];
return $this->format_context_data( $data );
}
}

View File

@@ -0,0 +1,303 @@
<?php
namespace Imagify\Bulk;
use Imagify_DB;
/**
* Class to use for bulk for WP attachments.
*
* @since 1.9
*/
class WP extends AbstractBulk {
/**
* Context "short name".
*
* @var string
* @since 1.9
*/
protected $context = 'wp';
/**
* Get all unoptimized media ids.
*
* @since 1.9
*
* @param int $optimization_level The optimization level.
*
* @return array A list of unoptimized media IDs.
*/
public function get_unoptimized_media_ids( $optimization_level ) {
global $wpdb;
$this->set_no_time_limit();
$mime_types = Imagify_DB::get_mime_types();
$statuses = Imagify_DB::get_post_statuses();
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
'prepared' => true,
] );
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
"
SELECT DISTINCT p.ID
FROM $wpdb->posts AS p
$nodata_join
LEFT JOIN $wpdb->postmeta AS mt1
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
LEFT JOIN $wpdb->postmeta AS mt2
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_optimization_level' )
WHERE
p.post_mime_type IN ( $mime_types )
AND (
mt1.meta_value = 'error'
OR
mt2.meta_value != %d
OR
mt2.post_id IS NULL
)
AND p.post_type = 'attachment'
AND p.post_status IN ( $statuses )
$nodata_where
ORDER BY
CASE mt1.meta_value
WHEN 'already_optimized' THEN 2
ELSE 1
END ASC,
p.ID DESC
LIMIT 0, %d",
$optimization_level,
imagify_get_unoptimized_attachment_limit()
) );
$wpdb->flush();
unset( $mime_types );
$ids = array_filter( array_map( 'absint', $ids ) );
if ( ! $ids ) {
return [];
}
$metas = Imagify_DB::get_metas( [
// Get attachments filename.
'filenames' => '_wp_attached_file',
// Get attachments data.
'data' => '_imagify_data',
// Get attachments optimization level.
'optimization_levels' => '_imagify_optimization_level',
// Get attachments status.
'statuses' => '_imagify_status',
], $ids );
// First run.
foreach ( $ids as $i => $id ) {
$attachment_status = isset( $metas['statuses'][ $id ] ) ? $metas['statuses'][ $id ] : false;
$attachment_optimization_level = isset( $metas['optimization_levels'][ $id ] ) ? $metas['optimization_levels'][ $id ] : false;
$attachment_error = '';
if ( isset( $metas['data'][ $id ]['sizes']['full']['error'] ) ) {
$attachment_error = $metas['data'][ $id ]['sizes']['full']['error'];
}
// Don't try to re-optimize if the optimization level is still the same.
if ( $optimization_level === $attachment_optimization_level && is_string( $attachment_error ) ) {
unset( $ids[ $i ] );
continue;
}
// Don't try to re-optimize images already compressed.
if ( 'already_optimized' === $attachment_status && $attachment_optimization_level >= $optimization_level ) {
unset( $ids[ $i ] );
continue;
}
$attachment_error = trim( $attachment_error );
// Don't try to re-optimize images with an empty error message.
if ( 'error' === $attachment_status && empty( $attachment_error ) ) {
unset( $ids[ $i ] );
}
}
if ( ! $ids ) {
return [];
}
$ids = array_values( $ids );
/**
* Fires before testing for file existence.
*
* @since 1.6.7
*
* @param array $ids An array of attachment IDs.
* @param array $metas An array of the data fetched from the database.
* @param int $optimization_level The optimization level that will be used for the optimization.
*/
do_action( 'imagify_bulk_optimize_before_file_existence_tests', $ids, $metas, $optimization_level );
$data = [];
foreach ( $ids as $i => $id ) {
if ( empty( $metas['filenames'][ $id ] ) ) {
// Problem.
continue;
}
$file_path = get_imagify_attached_file( $metas['filenames'][ $id ] );
if ( ! $file_path || ! $this->filesystem->exists( $file_path ) ) {
continue;
}
$attachment_backup_path = get_imagify_attachment_backup_path( $file_path );
$attachment_status = isset( $metas['statuses'][ $id ] ) ? $metas['statuses'][ $id ] : false;
$attachment_optimization_level = isset( $metas['optimization_levels'][ $id ] ) ? $metas['optimization_levels'][ $id ] : false;
// Don't try to re-optimize if there is no backup file.
if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $this->filesystem->exists( $attachment_backup_path ) ) {
continue;
}
$data[] = $id;
} // End foreach().
return $data;
}
/**
* Get ids of all optimized media without WebP versions.
*
* @since 1.9
* @since 1.9.5 The method doesn't return the IDs directly anymore.
*
* @return array {
* @type array $ids A list of media IDs.
* @type array $errors {
* @type array $no_file_path A list of media IDs.
* @type array $no_backup A list of media IDs.
* }
* }
*/
public function get_optimized_media_ids_without_webp() {
global $wpdb;
$this->set_no_time_limit();
$mime_types = Imagify_DB::get_mime_types( 'image' );
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
$statuses = Imagify_DB::get_post_statuses();
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
'prepared' => true,
] );
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::WEBP_SUFFIX' );
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
"
SELECT p.ID
FROM $wpdb->posts AS p
$nodata_join
LEFT JOIN $wpdb->postmeta AS mt1
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
LEFT JOIN $wpdb->postmeta AS mt2
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_data' )
WHERE
p.post_mime_type IN ( $mime_types )
AND ( mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' )
AND mt2.meta_value NOT LIKE %s
AND p.post_type = 'attachment'
AND p.post_status IN ( $statuses )
$nodata_where
ORDER BY p.ID DESC
LIMIT 0, %d",
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
imagify_get_unoptimized_attachment_limit()
) );
$wpdb->flush();
unset( $mime_types, $statuses, $webp_suffix );
$ids = array_filter( array_map( 'absint', $ids ) );
$data = [
'ids' => [],
'errors' => [
'no_file_path' => [],
'no_backup' => [],
],
];
if ( ! $ids ) {
return $data;
}
$metas = Imagify_DB::get_metas( [
// Get attachments filename.
'filenames' => '_wp_attached_file',
], $ids );
/**
* Fires before testing for file existence.
*
* @since 1.9
*
* @param array $ids An array of attachment IDs.
* @param array $metas An array of the data fetched from the database.
* @param string $context The context.
*/
do_action( 'imagify_bulk_generate_webp_before_file_existence_tests', $ids, $metas, 'wp' );
foreach ( $ids as $i => $id ) {
if ( empty( $metas['filenames'][ $id ] ) ) {
// Problem. Should not happen, thanks to the wpdb query.
$data['errors']['no_file_path'][] = $id;
continue;
}
$file_path = get_imagify_attached_file( $metas['filenames'][ $id ] );
if ( ! $file_path ) {
// Main file not found.
$data['errors']['no_file_path'][] = $id;
continue;
}
$backup_path = get_imagify_attachment_backup_path( $file_path );
if ( ! $this->filesystem->exists( $backup_path ) ) {
// No backup, no WebP.
$data['errors']['no_backup'][] = $id;
continue;
}
$data['ids'][] = $id;
} // End foreach().
return $data;
}
/**
* Get the context data.
*
* @since 1.9
*
* @return array {
* The formated data.
*
* @type string $count-optimized Number of media optimized.
* @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
* @type string $optimized-size Optimized filesize.
* @type string $original-size Original filesize.
* }
*/
public function get_context_data() {
$total_saving_data = imagify_count_saving_data();
$data = [
'count-optimized' => imagify_count_optimized_attachments(),
'count-errors' => imagify_count_error_attachments(),
'optimized-size' => $total_saving_data['optimized_size'],
'original-size' => $total_saving_data['original_size'],
'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ),
];
return $this->format_context_data( $data );
}
}