namespace ImageOptimization\Modules\Core;
use ImageOptimization\Modules\Optimization\{
Rest\Cancel_Bulk_Optimization,
use ImageOptimization\Modules\Settings\Classes\Settings;
use ImageOptimization\Modules\Backups\Rest\{
use ImageOptimization\Classes\{
Async_Operation\Async_Operation,
Async_Operation\Async_Operation_Queue,
Async_Operation\Queries\Operation_Query,
Image\Image_Optimization_Error_Type,
Migration\Migration_Manager,
use ImageOptimization\Plugin;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
class Module extends Module_Base {
public function get_name(): string {
public static function component_list() : array {
private function render_top_bar() {
<div id="image-optimization-top-bar"></div>
private function render_app() {
<div class="clear"></div>
<div id="image-optimization-app"></div>
public function maybe_add_quota_reached_notice() {
// @var ImageOptimizer/Modules/ConnectManager/Module
$module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
if ( ! $module->connect_instance->get_connect_status() || $module->connect_instance->images_left() > 0 ) {
<div class="notice notice-warning notice image-optimizer__notice image-optimizer__notice--warning">
'You’ve reached your plan quota.',
'You have no images left to optimize in your current plan.',
<a href="https://go.elementor.com/io-quota-upgrade/">
public function maybe_add_80_quota_reached_notice() {
// @var ImageOptimizer/Modules/ConnectManager/Module
$module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
$connect_status = $module->connect_instance->get_connect_status();
if ( ! isset( $connect_status->quota ) && ! isset( $connect_status->used_quota ) ) {
$usage = $connect_status->used_quota / $connect_status->quota * 100;
if ( ! $module->connect_instance->get_connect_status() || ( $usage < 80 || $usage === 100 ) ) {
<div class="notice notice-warning notice image-optimizer__notice image-optimizer__notice--warning">
'You’ve used 80% of your plan quota.',
'Upgrade now to avoid interruptions.',
<a href="https://go.elementor.com/io-quota-upgrade/">
public function maybe_add_url_mismatch_notice() {
// @var ImageOptimizer/Modules/ConnectManager/Module
$module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
if ( $module->connect_instance->is_valid_home_url() ) {
<div class="notice notice-error notice image-optimizer__notice image-optimizer__notice--error">
'Your license key does not match your current domain, causing a mismatch.',
'This is most likely due to a change in the domain URL of your site (including HTTP/SSL migration).',
<button type="button" onclick="document.dispatchEvent( new Event( 'image-optimizer/auth/url-mismatch-modal/open' ) );">
public function add_plugin_links( $links, $plugin_file_name ): array {
if ( ! str_ends_with( $plugin_file_name, '/image-optimization.php' ) ) {
admin_url( 'admin.php?page=' . \ImageOptimization\Modules\Settings\Module::SETTING_BASE_SLUG ),
esc_html__( 'Settings', 'image-optimization' )
// @var ImageOptimizer/Modules/ConnectManager/Module
$module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
if ( $module->connect_instance->is_connected() ) {
$custom_links['upgrade'] = sprintf(
'<a href="%s" style="color: #524CFF; font-weight: 700;" target="_blank" rel="noopener noreferrer">%s</a>',
'https://go.elementor.com/io-plugins-upgrade/',
esc_html__( 'Upgrade', 'image-optimization' )
$custom_links['connect'] = sprintf(
'<a href="%s" style="color: #524CFF; font-weight: 700;">%s</a>',
admin_url( 'admin.php?page=' . \ImageOptimization\Modules\Settings\Module::SETTING_BASE_SLUG ),
esc_html__( 'Connect', 'image-optimization' )
return array_merge( $custom_links, $links );
public function enqueue_global_assets() {
'image-optimization-admin-fonts',
'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
IMAGE_OPTIMIZATION_VERSION
'image-optimization-core-style-admin',
$this->get_css_assets_url( 'style-admin', 'assets/build/' ),
IMAGE_OPTIMIZATION_VERSION,
* Enqueue styles and scripts
private function enqueue_scripts() {
$asset_file = require IMAGE_OPTIMIZATION_ASSETS_PATH . 'build/admin.asset.php';
foreach ( $asset_file['dependencies'] as $style ) {
wp_enqueue_style( $style );
'image-optimization-admin',
$this->get_js_assets_url( 'admin' ),
array_merge( $asset_file['dependencies'], [ 'wp-util' ] ),
'image-optimization-admin',
'imageOptimizerAppSettings',
'siteUrl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
'thumbnailSizes' => wp_get_registered_image_subsizes(),
'isDevelopment' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
* @var ImageOptimizer\Modules\ConnectManager\Module $module
$module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
$is_connect_on_fly = $module->connect_instance->get_is_connect_on_fly();
$connect_email = $module->connect_instance->get_connect_data()['user']['email'] ?? null;
$show_reset = ! $module->connect_instance->is_connected()
&& ( $module->connect_instance->get_client_id() || $module->connect_instance->get_client_secret() );
'image-optimization-admin',
'imageOptimizerUserData',
'isConnectOnFly' => $is_connect_on_fly,
'isConnected' => $module->connect_instance->is_connected(),
'isActivated' => $module->connect_instance->is_activated(),
'isUrlMismatch' => ! $module->connect_instance->is_valid_home_url(),
'planData' => $module->connect_instance->is_activated() ? $module->connect_instance->get_connect_status() : null,
'licenseKey' => $module->connect_instance->is_activated() ? $module->connect_instance->get_activation_state() : null,
'imagesLeft' => $module->connect_instance->is_activated() ? $module->connect_instance->images_left() : null,
'isOwner' => $module->connect_instance->is_connected() ? $module->connect_instance->user_is_subscription_owner() : null,
'subscriptionEmail' => $connect_email ? $connect_email : null,
'showResetButton' => $show_reset,
'maxFileSize' => Validate_Image::get_max_file_size(),
'helpVideos' => Settings::get( Settings::HELP_VIDEOS ),
'wpRestNonce' => wp_create_nonce( 'wp_rest' ),
'disconnect' => wp_create_nonce( 'wp_rest' ),
'authInitNonce' => wp_create_nonce( $module->connect_instance->connect_init_nonce() ),
'authDisconnectNonce' => wp_create_nonce( $module->connect_instance->disconnect_nonce() ),
'authDeactivateNonce' => wp_create_nonce( $module->connect_instance->deactivate_nonce() ),
'authGetSubscriptionsNonce' => wp_create_nonce( $module->connect_instance->get_subscriptions_nonce() ),
'authActivateNonce' => wp_create_nonce( $module->connect_instance->activate_nonce() ),
'versionNonce' => wp_create_nonce( $module->connect_instance->version_nonce() ),
'removeBackupsNonce' => wp_create_nonce( Remove_Backups::NONCE_NAME ),
'restoreAllImagesNonce' => wp_create_nonce( Restore_All::NONCE_NAME ),
'optimizeBulkNonce' => wp_create_nonce( Optimize_Bulk::NONCE_NAME ),
'cancelBulkOptimizationNonce' => wp_create_nonce( Cancel_Bulk_Optimization::NONCE_NAME ),
wp_set_script_translations( 'image-optimization-admin', 'image-optimization' );
private function should_render(): bool {
return ( Utils::is_media_page() || Utils::is_plugin_page() ) && Utils::user_is_admin();
public static function on_deactivation(): void {
$optimization_query = ( new Operation_Query() )
->set_queue( Async_Operation_Queue::OPTIMIZE )
->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
$restoring_query = ( new Operation_Query() )
->set_queue( Async_Operation_Queue::RESTORE )
->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
$optimization_operations = Async_Operation::get( $optimization_query );
$restoring_operations = Async_Operation::get( $restoring_query );
foreach ( $optimization_operations as $operation ) {
$image_id = $operation->get_args()['attachment_id'];
Async_Operation::remove( [ $operation->get_id() ] );
$image_meta = new Image_Meta( $image_id );
if ( empty( $image_meta->get_optimized_sizes() ) ) {
->set_status( Image_Status::OPTIMIZATION_FAILED )
->set_error_type( Image_Optimization_Error_Type::PLUGIN_DEACTIVATION )
foreach ( $restoring_operations as $operation ) {
$image_id = $operation->get_args()['attachment_id'];
Async_Operation::remove( [ $operation->get_id() ] );
$image_meta = new Image_Meta( $image_id );
->set_status( Image_Status::RESTORING_FAILED )
->set_error_type( Image_Optimization_Error_Type::PLUGIN_DEACTIVATION )
* Renders the Bulk Optimization link on the media pages.
public function add_bulk_optimization_links(): void {
$page_url = add_query_arg(
[ 'page' => 'image-optimization-bulk-optimization' ],
admin_url( 'upload.php' )
document.addEventListener( 'DOMContentLoaded', function () {
// Grid media is rendered by JS, so the timeout is required
const targetButton = document.querySelector( '.filter-items .actions input[type=submit]' ) ||
document.querySelector( '.media-toolbar-secondary .select-mode-toggle-button' );
const link = document.createElement('a');
link.href = '<?php echo esc_js( $page_url ); ?>';
link.innerText = '<?php echo esc_js( __( 'Bulk Optimization', 'image-optimization' ) ); ?>';
link.className = 'button is-primary image-optimizer__button image-optimizer__button--pink';
targetButton.insertAdjacentElement( 'afterend', link );
public function __construct() {
$this->register_components();
add_action( 'action_scheduler_init', [ Migration_Manager::class, 'init' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_global_assets' ] );
add_filter( 'plugin_action_links', [ $this, 'add_plugin_links' ], 10, 2 );
add_action('current_screen', function () {
if ( ! $this->should_render() ) {
add_action( 'admin_notices', [ $this, 'maybe_add_quota_reached_notice' ] );
add_action( 'admin_notices', [ $this, 'maybe_add_80_quota_reached_notice' ] );
add_action( 'admin_notices', [ $this, 'maybe_add_url_mismatch_notice' ] );
if ( Utils::is_media_page() ) {
add_action('in_admin_header', function () {
add_action('all_admin_notices', function () {
if ( Utils::is_plugin_page() ) {
add_action('in_admin_header', function () {
add_action('admin_enqueue_scripts', function () {
$this->enqueue_scripts();
if ( Utils::is_media_page() ) {
add_action( 'admin_enqueue_scripts', [ $this, 'add_bulk_optimization_links' ] );