Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions classes/Bulk/Bulk.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,61 @@ public function run_optimize( string $context, int $optimization_level ) {
];
}

/**
* Runs the bulk restore for a given context.
*
* Restores all optimized media to their original state synchronously.
* Does not use the Action Scheduler since restore is a local file
* operation that does not consume API quota.
*
* @since 2.3
*
* @param string $context Current context (WP/Custom folders).
*
* @return array {
* @type bool $success Whether the operation was successful.
* @type string $message Status message.
* @type int $restored Number of media successfully restored.
* @type int $errors Number of media that failed to restore.
* @type int $total Total number of media processed.
* }
*/
public function run_restore( string $context ) {
$media_ids = $this->get_bulk_instance( $context )->get_optimized_media_ids();

if ( empty( $media_ids ) ) {
return [
'success' => false,
'message' => 'no-images',
'restored' => 0,
'errors' => 0,
'total' => 0,
];
}

$restored = 0;
$errors = 0;

foreach ( $media_ids as $media_id ) {
$process = imagify_get_optimization_process( $media_id, $context );
$result = $process->restore();

if ( is_wp_error( $result ) ) {
++$errors;
} else {
++$restored;
}
}

return [
'success' => true,
'message' => 'success',
'restored' => $restored,
'errors' => $errors,
'total' => count( $media_ids ),
];
}

/**
* Runs the next-gen generation
*
Expand Down
9 changes: 9 additions & 0 deletions classes/Bulk/BulkInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ interface BulkInterface {
*/
public function get_unoptimized_media_ids( $optimization_level );

/**
* Get all optimized media ids that can be restored.
*
* @since 2.3
*
* @return array A list of optimized media IDs with backup files available.
*/
public function get_optimized_media_ids();

/**
* Get ids of all optimized media without Next gen versions.
*
Expand Down
54 changes: 54 additions & 0 deletions classes/Bulk/CustomFolders.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,60 @@ public function get_unoptimized_media_ids( $optimization_level ) {
return $files;
}

/**
* Get all optimized media ids that can be restored.
*
* @since 2.3
*
* @return array A list of optimized media IDs with backup files available.
*/
public function get_optimized_media_ids() {
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();

$files = $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"
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.status = 'success'
ORDER BY fi.file_id DESC"
);

$wpdb->flush();

if ( ! $files ) {
return [];
}

$data = [];

foreach ( $files as $file ) {
$file_id = absint( $file->file_id );

if ( empty( $file->path ) ) {
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 ) ) {
continue;
}

$data[] = $file_id;
}

return $data;
}

/**
* Get ids of all optimized media without Next gen versions.
*
Expand Down
11 changes: 11 additions & 0 deletions classes/Bulk/Noop.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ public function get_unoptimized_media_ids( $optimization_level ) {
return [];
}

/**
* Get all optimized media ids that can be restored.
*
* @since 2.3
*
* @return array A list of optimized media IDs with backup files available.
*/
public function get_optimized_media_ids() {
return [];
}

/**
* * Get ids of all optimized media without Next gen versions.
*
Expand Down
77 changes: 77 additions & 0 deletions classes/Bulk/WP.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,83 @@ public function get_unoptimized_media_ids( $optimization_level ) {
return $data;
}

/**
* Get all optimized media ids that can be restored.
*
* @since 2.3
*
* @return array A list of optimized media IDs with backup files available.
*/
public function get_optimized_media_ids() {
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( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"
SELECT DISTINCT p.ID
FROM $wpdb->posts AS p
$nodata_join
INNER JOIN $wpdb->postmeta AS mt1
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
WHERE
p.post_mime_type IN ( $mime_types )
AND mt1.meta_value = 'success'
AND p.post_type = 'attachment'
AND p.post_status IN ( $statuses )
$nodata_where
ORDER BY p.ID DESC"
);

$wpdb->flush();
$ids = array_filter( array_map( 'absint', $ids ) );

if ( ! $ids ) {
return [];
}

$metas = Imagify_DB::get_metas(
[
// Get attachments filename.
'filenames' => '_wp_attached_file',
],
$ids
);

$data = [];

foreach ( $ids as $id ) {
if ( empty( $metas['filenames'][ $id ] ) ) {
continue;
}

$file_path = get_imagify_attached_file( $metas['filenames'][ $id ] );

if ( ! $file_path ) {
continue;
}

$backup_path = get_imagify_attachment_backup_path( $file_path );

if ( ! $this->filesystem->exists( $backup_path ) ) {
continue;
}

$data[] = $id;
}

return $data;
}

/**
* Get ids of all optimized media without Next gen versions.
*
Expand Down
151 changes: 151 additions & 0 deletions classes/CLI/RestoreCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php
declare(strict_types=1);

namespace Imagify\CLI;

use Imagify\Bulk\Bulk;

/**
* Command class for the bulk restore.
*
* Restores all optimized media back to their original state.
*
* ## EXAMPLES
*
* # Restore all optimized images (library + custom folders).
* $ wp imagify restore
*
* # Restore only WordPress media library images.
* $ wp imagify restore library
*
* # Restore only custom folders images.
* $ wp imagify restore custom-folders
*
* # Restore both contexts explicitly.
* $ wp imagify restore library custom-folders
*
* @since 2.3
*/
class RestoreCommand extends AbstractCommand {

/**
* Map of user-friendly context names to internal context identifiers.
*
* @var array<string, string>
*/
private const CONTEXT_MAP = [
'library' => 'wp',
'custom-folders' => 'custom-folders',
];

/**
* Executes the command.
*
* @param array $arguments Positional argument.
* @param array $options Optional arguments.
*/
public function __invoke( $arguments, $options ) {
// Default to all contexts if none specified.
if ( empty( $arguments ) ) {
$arguments = array_keys( self::CONTEXT_MAP );
}

// Validate and map contexts.
$contexts = [];

foreach ( $arguments as $arg ) {
if ( ! isset( self::CONTEXT_MAP[ $arg ] ) ) {
\WP_CLI::error(
sprintf(
'Invalid context: "%s". Valid values are: %s',
$arg,
implode( ', ', array_keys( self::CONTEXT_MAP ) )
)
);
}

$contexts[] = self::CONTEXT_MAP[ $arg ];
}

$total_restored = 0;
$total_errors = 0;

foreach ( $contexts as $index => $context ) {
$label = $arguments[ $index ];

\WP_CLI::log( sprintf( 'Restoring optimized media for: %s', $label ) );

$result = Bulk::get_instance()->run_restore( $context );

if ( ! $result['success'] ) {
\WP_CLI::warning( sprintf( 'No optimized media to restore for: %s', $label ) );
continue;
}

$total_restored += $result['restored'];
$total_errors += $result['errors'];

\WP_CLI::log(
sprintf(
'%s: %d restored, %d errors out of %d total.',
ucfirst( $label ),
$result['restored'],
$result['errors'],
$result['total']
)
);
}

if ( 0 === $total_restored && 0 === $total_errors ) {
\WP_CLI::warning( 'No optimized media found to restore.' );
return;
}

if ( $total_errors > 0 ) {
\WP_CLI::warning(
sprintf(
'Restore completed with errors: %d restored, %d failed.',
$total_restored,
$total_errors
)
);
return;
}

\WP_CLI::success(
sprintf(
'Restore completed: %d media restored successfully.',
$total_restored
)
);
}

/**
* {@inheritdoc}
*/
protected function get_command_name(): string {
return 'restore';
}

/**
* {@inheritdoc}
*/
public function get_description(): string {
return 'Restore all optimized media to their original state';
}

/**
* {@inheritdoc}
*/
public function get_synopsis(): array {
return [
[
'type' => 'positional',
'name' => 'contexts',
'description' => 'The context(s) to restore. Possible values are library and custom-folders. Defaults to all if omitted.',
'optional' => true,
'repeating' => true,
],
];
}
}
Loading