PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.7.4
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.7.4
1.7.7 1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
image-optimization / modules / core / module.php

module.php in Image Optimizer – Compress Images and Convert to WebP or AVIF 1.7.4, at modules/core/module.php

477 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ImageOptimization\Modules\Core;
3
4 use ImageOptimization\Modules\Optimization\{
5 Classes\Validate_Image,
6 Rest\Cancel_Bulk_Optimization,
7 Rest\Optimize_Bulk,
8 };
9 use ImageOptimization\Modules\Settings\Classes\Settings;
10 use ImageOptimization\Modules\Backups\Rest\{
11 Restore_All,
12 Remove_Backups,
13 };
14 use ImageOptimization\Classes\{
15 Async_Operation\Async_Operation,
16 Async_Operation\Async_Operation_Queue,
17 Async_Operation\Queries\Operation_Query,
18 Image\Image_Meta,
19 Image\Image_Optimization_Error_Type,
20 Image\Image_Status,
21 Migration\Migration_Manager,
22 Module_Base,
23 Utils,
24 };
25
26 use ImageOptimization\Modules\Connect\Module as Connect_Module;
27 use ImageOptimization\Modules\Connect\Classes\Config;
28
29 use ImageOptimization\Plugin;
30
31 if ( ! defined( 'ABSPATH' ) ) {
32 exit; // Exit if accessed directly.
33 }
34
35 class Module extends Module_Base {
36
37 const ONE_MISMATCH_URL = '/wp-admin/admin.php?page=elementor-home#/home/url-mismatch';
38
39 public function get_name(): string {
40 return 'core';
41 }
42
43 public static function component_list(): array {
44 return [
45 'Pointers',
46 'Migrations',
47 'Conflicts',
48 'User_Feedback',
49 'Not_Connected',
50 'Not_Connected_Modal',
51 'Renewal_Notice',
52 ];
53 }
54
55 private function render_top_bar() {
56 ?>
57 <div id="image-optimization-top-bar"></div>
58 <?php
59 }
60
61 private function render_app() {
62 ?>
63 <div class="clear"></div>
64 <div id="image-optimization-app"></div>
65 <?php
66 }
67
68 public function maybe_add_quota_reached_notice() {
69
70 // @var ImageOptimizer/Modules/ConnectManager/Module
71 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
72 $upgrade_link = Utils::get_upgrade_link( 'https://go.elementor.com/io-quota-upgrade/' );
73
74 if ( ! $module->connect_instance->get_connect_status() || $module->connect_instance->images_left() > 0 ) {
75 return;
76 }
77
78 ?>
79 <div class="notice notice-warning notice image-optimizer__notice image-optimizer__notice--warning">
80 <p>
81 <b>
82 <?php esc_html_e(
83 'You’ve reached your plan quota.',
84 'image-optimization'
85 ); ?>
86 </b>
87
88 <span>
89 <?php esc_html_e(
90 'You have no images left to optimize in your current plan.',
91 'image-optimization'
92 ); ?>
93
94 <a href="<?php echo esc_url( $upgrade_link ); ?>">
95 <?php esc_html_e(
96 'Upgrade plan now',
97 'image-optimization'
98 ); ?>
99 </a>
100 </span>
101 </p>
102 </div>
103 <?php
104 }
105
106 public function maybe_add_80_quota_reached_notice() {
107 // @var ImageOptimizer/Modules/ConnectManager/Module
108 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
109
110 $connect_status = $module->connect_instance->get_connect_status();
111
112 if ( ! isset( $connect_status->quota ) || ! isset( $connect_status->used_quota ) ) {
113 return;
114 }
115
116 $usage = $connect_status->used_quota / $connect_status->quota * 100;
117
118 if ( $usage < 80 || 100 === $usage ) {
119 return;
120 }
121
122 $upgrade_link = Utils::get_upgrade_link( 'https://go.elementor.com/io-quota-upgrade/' );
123
124 ?>
125 <div class="notice notice-warning notice image-optimizer__notice image-optimizer__notice--warning">
126 <p>
127 <b>
128 <?php esc_html_e(
129 'You’ve used 80% of your plan quota.',
130 'image-optimization'
131 ); ?>
132 </b>
133
134 <span>
135 <?php esc_html_e(
136 'Upgrade now to avoid interruptions.',
137 'image-optimization'
138 ); ?>
139
140 <a href="<?php echo esc_url( $upgrade_link ); ?>">
141 <?php esc_html_e(
142 'Upgrade plan now',
143 'image-optimization'
144 ); ?>
145 </a>
146 </span>
147 </p>
148 </div>
149 <?php
150 }
151
152 public function maybe_add_url_mismatch_notice() {
153 // @var ImageOptimizer/Modules/ConnectManager/Module
154 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
155
156 if ( $module->connect_instance->is_valid_home_url() ) {
157 return;
158 }
159
160 $onclick = self::is_elementor_one() ? 'window.location.href = "' . self::ONE_MISMATCH_URL . '";' : 'document.dispatchEvent( new Event( "image-optimizer/auth/url-mismatch-modal/open" ) );';
161
162 ?>
163 <div class="notice notice-error notice image-optimizer__notice image-optimizer__notice--error">
164 <p>
165 <b>
166 <?php esc_html_e(
167 'Your license key does not match your current domain, causing a mismatch.',
168 'image-optimization'
169 ); ?>
170 </b>
171
172 <span>
173 <?php esc_html_e(
174 'This is most likely due to a change in the domain URL of your site (including HTTP/SSL migration).',
175 'image-optimization'
176 ); ?>
177
178 <button type="button" onclick="<?php echo esc_js( $onclick ); ?>">
179 <?php esc_html_e(
180 'Fix mismatched URL',
181 'image-optimization'
182 ); ?>
183 </button>
184 </span>
185 </p>
186 </div>
187 <?php
188 }
189
190 public function add_plugin_links( $links, $plugin_file_name ): array {
191 if ( ! str_ends_with( $plugin_file_name, '/image-optimization.php' ) ) {
192 return (array) $links;
193 }
194
195 $custom_links = [
196 'settings' => sprintf(
197 '<a href="%s">%s</a>',
198 admin_url( 'admin.php?page=' . \ImageOptimization\Modules\Settings\Module::SETTING_BASE_SLUG ),
199 esc_html__( 'Settings', 'image-optimization' )
200 ),
201 ];
202 // @var ImageOptimizer/Modules/ConnectManager/Module
203 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
204
205 if ( $module->connect_instance->is_connected() && ! self::is_elementor_one() ) {
206 $custom_links['upgrade'] = sprintf(
207 '<a href="%s" style="color: #524cff; font-weight: 700;" target="_blank" rel="noopener noreferrer">%s</a>',
208 Utils::get_upgrade_link( 'https://go.elementor.com/io-plugins-upgrade/' ),
209 esc_html__( 'Upgrade', 'image-optimization' )
210 );
211 }
212 if ( ! $module->connect_instance->is_connected() ) {
213 $custom_links['connect'] = sprintf(
214 '<a href="%s" style="color: #524cff; font-weight: 700;">%s</a>',
215 admin_url( 'admin.php?page=' . \ImageOptimization\Modules\Settings\Module::SETTING_BASE_SLUG ),
216 esc_html__( 'Connect', 'image-optimization' )
217 );
218 }
219
220 return array_merge( $custom_links, $links );
221 }
222
223 public function enqueue_global_assets() {
224 wp_enqueue_style(
225 'image-optimization-admin-fonts',
226 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
227 [],
228 IMAGE_OPTIMIZATION_VERSION
229 );
230
231 wp_enqueue_style(
232 'image-optimization-core-style-admin',
233 $this->get_css_assets_url( 'style-admin', 'assets/build/' ),
234 [],
235 IMAGE_OPTIMIZATION_VERSION,
236 );
237 }
238
239 /**
240 * Enqueue styles and scripts
241 */
242 private function enqueue_scripts() {
243 $asset_file = require IMAGE_OPTIMIZATION_ASSETS_PATH . 'build/admin.asset.php';
244
245 foreach ( $asset_file['dependencies'] as $style ) {
246 wp_enqueue_style( $style );
247 }
248
249 wp_enqueue_script(
250 'image-optimization-admin',
251 $this->get_js_assets_url( 'admin' ),
252 array_merge( $asset_file['dependencies'], [ 'wp-util' ] ),
253 $asset_file['version'],
254 true
255 );
256
257 wp_localize_script(
258 'image-optimization-admin',
259 'imageOptimizerAppSettings',
260 [
261 'siteUrl' => wp_parse_url( get_site_url(), PHP_URL_HOST ),
262 'thumbnailSizes' => wp_get_registered_image_subsizes(),
263 'isDevelopment' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
264 'isRTL' => is_rtl(),
265 'version' => IMAGE_OPTIMIZATION_VERSION,
266 ]
267 );
268
269 /**
270 * @var ImageOptimizer\Modules\ConnectManager\Module $module
271 */
272 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
273 $connect_email = $module->connect_instance->get_connect_data()['user']['email'] ?? null;
274 $show_reset = ! $module->connect_instance->is_connected()
275 && ( $module->connect_instance->get_client_id() || $module->connect_instance->get_client_secret() );
276
277 wp_localize_script(
278 'image-optimization-admin',
279 'imageOptimizerUserData',
280 [
281 'isConnectOnFly' => self::is_connect_on_fly(),
282 'isConnected' => $module->connect_instance->is_connected(),
283 'isElementorOne' => self::is_elementor_one(),
284 'currentPage' => get_current_screen()->base,
285 'isActivated' => $module->connect_instance->is_activated(),
286 'isUrlMismatch' => ! $module->connect_instance->is_valid_home_url(),
287 'planData' => $module->connect_instance->is_activated() ? $module->connect_instance->get_connect_status() : null,
288 'licenseKey' => $module->connect_instance->is_activated() ? $module->connect_instance->get_activation_state() : null,
289 'imagesLeft' => $module->connect_instance->is_activated() ? $module->connect_instance->images_left() : null,
290 'isOwner' => $module->connect_instance->is_connected() ? $module->connect_instance->user_is_subscription_owner() : null,
291 'subscriptionEmail' => $connect_email ? $connect_email : null,
292 'showResetButton' => $show_reset,
293 'maxFileSize' => Validate_Image::get_max_file_size(),
294 'helpVideos' => Settings::get( Settings::HELP_VIDEOS ),
295
296 'wpRestNonce' => wp_create_nonce( 'wp_rest' ),
297 'disconnect' => wp_create_nonce( 'wp_rest' ),
298 'authInitNonce' => wp_create_nonce( $module->connect_instance->connect_init_nonce() ),
299 'authDisconnectNonce' => wp_create_nonce( $module->connect_instance->disconnect_nonce() ),
300 'authDeactivateNonce' => wp_create_nonce( $module->connect_instance->deactivate_nonce() ),
301 'authGetSubscriptionsNonce' => wp_create_nonce( $module->connect_instance->get_subscriptions_nonce() ),
302 'authActivateNonce' => wp_create_nonce( $module->connect_instance->activate_nonce() ),
303 'versionNonce' => wp_create_nonce( $module->connect_instance->version_nonce() ),
304 'removeBackupsNonce' => wp_create_nonce( Remove_Backups::NONCE_NAME ),
305 'restoreAllImagesNonce' => wp_create_nonce( Restore_All::NONCE_NAME ),
306 'optimizeBulkNonce' => wp_create_nonce( Optimize_Bulk::NONCE_NAME ),
307 'cancelBulkOptimizationNonce' => wp_create_nonce( Cancel_Bulk_Optimization::NONCE_NAME ),
308 ]
309 );
310
311 wp_set_script_translations( 'image-optimization-admin', 'image-optimization' );
312 }
313
314 private function should_render(): bool {
315 return ( Utils::is_media_page() || Utils::is_plugin_page() ) && Utils::user_is_admin();
316 }
317
318 private static function is_connect_on_fly(): bool {
319 /**
320 * @var ImageOptimizer\Modules\ConnectManager\Module $module
321 */
322 $module = Plugin::instance()->modules_manager->get_modules( 'connect-manager' );
323 return $module->connect_instance->get_is_connect_on_fly();
324 }
325
326 public static function is_elementor_one(): bool {
327
328 if ( ! self::is_connect_on_fly() ) {
329 return false;
330 }
331
332 $connect = Connect_Module::get_connect();
333
334 if ( ! $connect ) {
335 return false;
336 }
337
338 return Connect_Module::get_connect()->get_config( 'app_type' ) !== Config::APP_TYPE;
339 }
340
341 public static function on_deactivation(): void {
342 $optimization_query = ( new Operation_Query() )
343 ->set_queue( Async_Operation_Queue::OPTIMIZE )
344 ->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
345 ->set_limit( -1 );
346
347 $restoring_query = ( new Operation_Query() )
348 ->set_queue( Async_Operation_Queue::RESTORE )
349 ->set_status( [ Async_Operation::OPERATION_STATUS_PENDING, Async_Operation::OPERATION_STATUS_RUNNING ] )
350 ->set_limit( -1 );
351
352 $optimization_operations = Async_Operation::get( $optimization_query );
353 $restoring_operations = Async_Operation::get( $restoring_query );
354
355 foreach ( $optimization_operations as $operation ) {
356 $image_id = $operation->get_args()['attachment_id'];
357
358 if ( ! $image_id ) {
359 continue;
360 }
361
362 Async_Operation::remove( [ $operation->get_id() ] );
363
364 $image_meta = new Image_Meta( $image_id );
365
366 if ( empty( $image_meta->get_optimized_sizes() ) ) {
367 $image_meta->delete();
368 } else {
369 $image_meta
370 ->set_status( Image_Status::OPTIMIZATION_FAILED )
371 ->set_error_type( Image_Optimization_Error_Type::PLUGIN_DEACTIVATION )
372 ->save();
373 }
374 }
375
376 foreach ( $restoring_operations as $operation ) {
377 $image_id = $operation->get_args()['attachment_id'];
378
379 if ( ! $image_id ) {
380 continue;
381 }
382
383 Async_Operation::remove( [ $operation->get_id() ] );
384
385 $image_meta = new Image_Meta( $image_id );
386
387 $image_meta
388 ->set_status( Image_Status::RESTORING_FAILED )
389 ->set_error_type( Image_Optimization_Error_Type::PLUGIN_DEACTIVATION )
390 ->save();
391 }
392 }
393
394 /**
395 * Renders the Bulk Optimization link on the media pages.
396 *
397 * @return void
398 */
399 public function add_bulk_optimization_links(): void {
400 $page_url = add_query_arg(
401 [
402 'page' => 'image-optimization-settings',
403 'tab' => 'image-optimization-bulk-optimization',
404 ],
405 admin_url( 'admin.php' )
406 );
407
408 ?>
409 <script>
410 document.addEventListener( 'DOMContentLoaded', function () {
411 // Grid media is rendered by JS, so the timeout is required
412 setTimeout( () => {
413 const targetButton = document.querySelector( '.filter-items .actions input[type=submit]' ) ||
414 document.querySelector( '.media-toolbar-secondary .select-mode-toggle-button' );
415
416 if ( targetButton ) {
417 const link = document.createElement('a');
418
419 link.href = '<?php echo esc_js( $page_url ); ?>';
420 link.innerText = '<?php echo esc_js( __( 'Bulk Optimization', 'image-optimization' ) ); ?>';
421 link.className = 'button is-primary image-optimizer__button image-optimizer__button--pink';
422
423 targetButton.insertAdjacentElement( 'afterend', link );
424 }
425 }, 100 )
426 } );
427 </script>
428 <?php
429 }
430
431 /**
432 * Module constructor.
433 */
434 public function __construct() {
435 $this->register_components();
436
437 add_action( 'action_scheduler_init', [ Migration_Manager::class, 'init' ] );
438
439 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_global_assets' ] );
440 add_filter( 'plugin_action_links', [ $this, 'add_plugin_links' ], 10, 2 );
441
442 add_action('current_screen', function () {
443 if ( ! $this->should_render() ) {
444 return;
445 }
446
447 if ( ! self::is_elementor_one() ) {
448 add_action( 'admin_notices', [ $this, 'maybe_add_quota_reached_notice' ] );
449 add_action( 'admin_notices', [ $this, 'maybe_add_80_quota_reached_notice' ] );
450 }
451
452 add_action( 'admin_notices', [ $this, 'maybe_add_url_mismatch_notice' ] );
453
454 if ( Utils::is_media_page() ) {
455
456 add_action('all_admin_notices', function () {
457 $this->render_app();
458 });
459 }
460
461 if ( Utils::is_plugin_page() ) {
462 add_action('in_admin_header', function () {
463 $this->render_top_bar();
464 });
465 }
466
467 add_action('admin_enqueue_scripts', function () {
468 $this->enqueue_scripts();
469 });
470
471 if ( Utils::is_media_page() ) {
472 add_action( 'admin_enqueue_scripts', [ $this, 'add_bulk_optimization_links' ] );
473 }
474 });
475 }
476 }
477