PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / vendor / wp-media / plugin-family / src / Controller / PluginFamily.php

PluginFamily.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at vendor/wp-media/plugin-family/src/Controller/PluginFamily.php

606 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Imagify\Dependencies\WPMedia\PluginFamily\Controller;
4
5 /**
6 * Handles installation and Activation of plugin family members.
7 */
8 class PluginFamily implements PluginFamilyInterface {
9
10 /**
11 * Plugin family version.
12 *
13 * @var string
14 */
15 private $version = '1.0.9';
16
17 /**
18 * Error transient.
19 *
20 * @var string
21 */
22 protected $error_transient = 'plugin_family_error';
23
24 /**
25 * Admin screen IDs where assets should load.
26 *
27 * @var array
28 */
29 private $screen_ids;
30
31 /**
32 * Text used in the uploader notice.
33 *
34 * @var string
35 */
36 private $notice_text = '';
37
38 /**
39 * Returns an array of events this subscriber listens to
40 *
41 * @return array
42 */
43 public static function get_subscribed_events(): array {
44 $events = self::get_post_install_event();
45 $events['admin_notices'] = 'display_error_notice';
46 $events['enqueue_block_editor_assets'] = 'enqueue_assets';
47 $events['wp_ajax_install_imagify'] = 'install_imagify';
48 $events['wp_ajax_dismiss_promote_imagify'] = 'dismiss_promote_imagify';
49 $events['admin_enqueue_scripts'] = 'enqueue_admin_assets';
50 $events['admin_footer'] = 'insert_footer_templates';
51
52 return $events;
53 }
54
55 /**
56 * Constructor with explicit parameters.
57 *
58 * @param string[] $screen_ids Admin screen ids where assets should load. (Default: post.php, post-new.php, upload.php).
59 * @param string $notice_text Text used in the uploader notice. Empty string keeps default copy.
60 */
61 public function __construct(
62 array $screen_ids = [ 'post.php', 'post-new.php', 'upload.php' ],
63 string $notice_text = ''
64 ) {
65 // Normalize/validate inputs a bit.
66 $this->screen_ids = $screen_ids;
67 $this->notice_text = (string) $notice_text;
68 }
69
70 /**
71 * Set post install event.
72 *
73 * @return array
74 */
75 public static function get_post_install_event(): array {
76 $allowed_plugin = [
77 'uk-cookie-consent',
78 'backwpup',
79 'imagify',
80 'seo-by-rank-math',
81 'wp-rocket',
82 ];
83
84 if ( ! isset( $_GET['action'], $_GET['_wpnonce'], $_GET['plugin_to_install'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
85 return [];
86 }
87
88 $plugin = str_replace( 'plugin_family_install_', '', sanitize_text_field( wp_unslash( $_GET['action'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
89
90 if ( ! in_array( $plugin, $allowed_plugin, true ) ) {
91 return [];
92 }
93
94 return [
95 'admin_post_plugin_family_install_' . $plugin => 'install_activate',
96 ];
97 }
98
99 /**
100 * Process to install and activate plugin.
101 *
102 * @return void
103 */
104 public function install_activate() {
105 if ( ! $this->is_allowed() ) {
106 wp_die(
107 'Plugin Installation is not allowed.',
108 '',
109 [ 'back_link' => true ]
110 );
111 }
112
113 // Install plugin.
114 $this->install();
115
116 // Activate plugin.
117 $result = activate_plugin( $this->get_plugin(), '', is_multisite() );
118
119 if ( is_wp_error( $result ) ) {
120 $this->set_error( $result );
121 }
122
123 wp_safe_redirect( wp_get_referer() );
124 exit;
125 }
126
127 /**
128 * Install plugin.
129 *
130 * @param string $slug Plugin slug if found.
131 * @return void
132 */
133 private function install( $slug = '' ) {
134 if ( $this->is_installed( $slug ) ) {
135 return;
136 }
137
138 $upgrader_class = ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
139
140 if ( ! defined( 'ABSPATH' ) || ! file_exists( $upgrader_class ) ) {
141 wp_die(
142 'Plugin Installation failed. class-wp-upgrader.php not found',
143 '',
144 [ 'back_link' => true ]
145 );
146 }
147
148 require_once $upgrader_class; // @phpstan-ignore-line
149
150 $upgrader = new \Plugin_Upgrader( new \Automatic_Upgrader_Skin() );
151 $result = $upgrader->install( $this->get_download_url( $slug ) );
152
153 if ( is_wp_error( $result ) ) {
154 $this->set_error( $result );
155 }
156
157 clearstatcache();
158 }
159
160 /**
161 * Check if plugin is installed.
162 *
163 * @param string $slug Plugin slug if found.
164 * @return boolean
165 */
166 private function is_installed( $slug = '' ): bool {
167 return file_exists( WP_PLUGIN_DIR . '/' . $this->get_plugin( $slug ) );
168 }
169
170 /**
171 * Check if installation is allowed.
172 *
173 * @return boolean
174 */
175 private function is_allowed(): bool {
176 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'plugin_family_install_' . $this->get_slug() ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
177 return false;
178 }
179
180 if ( ! current_user_can( is_multisite() ? 'manage_network_plugins' : 'install_plugins' ) ) {
181 return false;
182 }
183
184 return true;
185 }
186
187 /**
188 * Get plugin slug.
189 *
190 * @return string
191 */
192 private function get_slug(): string {
193 return dirname( rawurldecode( sanitize_text_field( wp_unslash( $_GET['plugin_to_install'] ) ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
194 }
195
196 /**
197 * Get plugin identifier.
198 *
199 * @param string $slug Plugin slug if found.
200 * @return string
201 */
202 private function get_plugin( $slug = '' ): string {
203 if ( 'imagify' === $slug ) {
204 return 'imagify/imagify.php';
205 }
206 if ( empty( $_GET['plugin_to_install'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
207 return '';
208 }
209 return rawurldecode( sanitize_text_field( wp_unslash( $_GET['plugin_to_install'] ) ) ) . '.php'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
210 }
211
212 /**
213 * Get plugin download url.
214 *
215 * @param string $slug Plugin slug if found.
216 * @return string
217 */
218 private function get_download_url( $slug = '' ): string {
219
220 $custom_download_url = $this->maybe_get_custom_download_url( $slug );
221
222 if ( false !== $custom_download_url ) {
223 return $custom_download_url;
224 }
225
226 $plugin_install = ABSPATH . 'wp-admin/includes/plugin-install.php';
227
228 if ( ! defined( 'ABSPATH' ) || ! file_exists( $plugin_install ) ) {
229 wp_die(
230 'Plugin Installation failed. plugin-install.php not found',
231 '',
232 [ 'back_link' => true ]
233 );
234 }
235
236 require_once $plugin_install; // @phpstan-ignore-line
237
238 if ( empty( $slug ) ) {
239 $slug = $this->get_slug();
240 }
241
242 $data = [
243 'slug' => $slug,
244 'fields' => [
245 'download_link' => true,
246 'short_description' => false,
247 'sections' => false,
248 'rating' => false,
249 'ratings' => false,
250 'downloaded' => false,
251 'last_updated' => false,
252 'added' => false,
253 'tags' => false,
254 'homepage' => false,
255 'donate_link' => false,
256 ],
257 ];
258
259 // Get Plugin Infos.
260 $plugin_info = plugins_api( 'plugin_information', $data );
261
262 if ( is_wp_error( $plugin_info ) ) {
263 $this->set_error( $plugin_info );
264 }
265
266 // Ensure that $plugin_info is an object before accessing the property.
267 if ( ! is_object( $plugin_info ) || ! isset( $plugin_info->download_link ) ) {
268 return '';
269 }
270
271 return $plugin_info->download_link;
272 }
273
274 /**
275 * Maybe display error notice.
276 *
277 * @return void
278 */
279 public function display_error_notice() {
280 $errors = get_transient( $this->error_transient );
281
282 if ( ! $errors ) {
283 return;
284 }
285
286 if ( ! is_wp_error( $errors ) ) {
287 delete_transient( $this->error_transient );
288 return;
289 }
290
291 $errors = $errors->get_error_messages();
292
293 if ( ! $errors ) {
294 $errors[] = 'Installation process failed';
295 }
296
297 $notice = '<div class="error notice is-dismissible"><p>' . implode( '<br/>', $errors ) . '</p></div>';
298 echo wp_kses_post( $notice );
299
300 // Remove transient after displaying notice.
301 delete_transient( $this->error_transient );
302 }
303
304 /**
305 * Store an error message in a transient then redirect.
306 *
307 * @param object $error A WP_Error object.
308 * @return void
309 */
310 private function set_error( $error ) {
311 set_transient( $this->error_transient, $error, 30 );
312
313 wp_safe_redirect( wp_get_referer() );
314 exit;
315 }
316
317 /**
318 * Returns a custom download url for plugin if exists.
319 *
320 * @param string $plugin_slug plugin slug.
321 * @return string|bool
322 */
323 private function maybe_get_custom_download_url( string $plugin_slug ) {
324 $parent_plugin_slug = $this->get_parent_plugin_slug();
325
326 $urls = [
327 'seo-by-rank-math' => 'https://rankmath.com/downloads/plugin-family/' . $parent_plugin_slug,
328 ];
329
330 if ( ! isset( $urls[ $plugin_slug ] ) ) {
331 return false;
332 }
333
334 return $urls[ $plugin_slug ];
335 }
336
337 /**
338 * Get parent plugin slug.
339 *
340 * @return string
341 */
342 private function get_parent_plugin_slug(): string {
343 $plugin_path = plugin_basename( __FILE__ );
344 $chunks = explode( '/', $plugin_path );
345
346 return $chunks[0];
347 }
348
349 /**
350 * Check if imagify is installed or not.
351 *
352 * @return bool
353 */
354 private function is_imagify_installed(): bool {
355 return file_exists( WP_PLUGIN_DIR . '/imagify/imagify.php' );
356 }
357
358 /**
359 * Check if imagify is activated or not.
360 *
361 * @return bool
362 */
363 private function is_imagify_activated(): bool {
364 return defined( 'IMAGIFY_VERSION' );
365 }
366
367 /**
368 * Enqueue block editor assets
369 *
370 * @return void
371 */
372 public function enqueue_assets() {
373 if (
374 $this->is_promote_imagify_dismissed()
375 ||
376 $this->is_imagify_activated()
377 ||
378 wp_script_is( 'plugin-family-script' )
379 ) {
380 return;
381 }
382
383 if ( ! $this->should_show_imagify_banner() ) {
384 return;
385 }
386
387 $script_url = plugin_dir_url( __DIR__ ) . 'assets/js/index.js';
388
389 wp_enqueue_script(
390 'plugin-family-script',
391 $script_url,
392 [ 'react-jsx-runtime', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives' ],
393 $this->version,
394 [
395 'in_footer' => true,
396 ]
397 );
398
399 $this->add_install_imagify_localized_script( 'plugin-family-script' );
400 }
401
402 /**
403 * Install Imagify using the ajax request.
404 *
405 * @return void
406 */
407 public function install_imagify() {
408 check_ajax_referer( 'install-imagify-nonce' );
409
410 if ( ! current_user_can( is_multisite() ? 'manage_network_plugins' : 'install_plugins' ) ) {
411 wp_send_json_error( __( 'Not Allowed', 'imagify' ) );
412 }
413
414 if ( ! $this->is_imagify_installed() ) {
415 $this->install( 'imagify' );
416 }
417
418 $activated = activate_plugin( $this->get_plugin( 'imagify' ), '', is_multisite() );
419 if ( is_wp_error( $activated ) ) {
420 wp_send_json_error( $activated->get_error_message() );
421 }
422
423 $this->set_imagify_partner( '' );
424 /**
425 * Fires after Imagify is installed and activated via Plugin Family.
426 * Allows integrators to track installation/activation.
427 */
428 do_action( 'wpmedia_plugin_family_imagify_installed' );
429 wp_send_json_success( __( 'Imagify installed! Click here to start using it.', 'imagify' ) );
430 }
431
432 /**
433 * Set the imagify plugin partner.
434 *
435 * @param string $plugin Current plugin.
436 * @return void
437 */
438 private function set_imagify_partner( $plugin ) {
439 update_option( 'imagifyp_id', $plugin, false );
440 }
441
442 /**
443 * Check if we can enqueue admin assets or not.
444 *
445 * @param string $page Current page ID if found.
446 * @return bool
447 */
448 private function can_enqueue_admin_assets( $page = '' ): bool {
449 if ( $this->is_promote_imagify_dismissed() ) {
450 return false;
451 }
452
453 $allowed_pages = $this->screen_ids;
454 $can_enqueue = in_array( $page, $allowed_pages, true );
455
456 if ( empty( $page ) ) {
457 // Map configured admin pages to corresponding get_current_screen()->id values.
458 $allowed_screen_ids = array_unique(
459 array_map(
460 static function ( $p ) {
461 switch ( $p ) {
462 case 'post.php':
463 case 'post-new.php':
464 return 'post';
465 case 'upload.php':
466 return 'upload';
467 default:
468 // Allow passing raw screen ids directly (e.g., custom settings screens).
469 return $p;
470 }
471 },
472 $allowed_pages
473 )
474 );
475
476 $can_enqueue = in_array( get_current_screen()->id, $allowed_screen_ids, true );
477 }
478
479 return $this->should_show_imagify_banner( $can_enqueue );
480 }
481
482 /**
483 * Add localized script to be used by scripts.
484 *
485 * @param string $script_id Script ID.
486 * @return void
487 */
488 private function add_install_imagify_localized_script( $script_id ) {
489 $data = [
490 'ajax_url' => admin_url( 'admin-ajax.php' ),
491 'nonce' => wp_create_nonce( 'install-imagify-nonce' ),
492 'plugins_page_url' => admin_url( 'plugins.php' ),
493 'notice_text' => ! empty( $this->notice_text ) ? $this->notice_text : __( 'Boost your site\'s performance by compressing images with Imagify, developed by WP Rocket.', 'imagify' ),
494 ];
495
496 wp_add_inline_script(
497 $script_id,
498 'window.wpmedia_pluginfamily = ' . wp_json_encode( $data ) . ';',
499 'before'
500 );
501 }
502
503 /**
504 * Enqueue Admin assets.
505 *
506 * @param string $page Page ID.
507 * @return void
508 */
509 public function enqueue_admin_assets( $page ) {
510 if ( ! $this->can_enqueue_admin_assets( $page ) ) {
511 return;
512 }
513
514 if ( $this->is_imagify_activated() || wp_script_is( 'plugin-family-admin-script' ) ) {
515 return;
516 }
517
518 $script_url = plugin_dir_url( __DIR__ ) . 'assets/js/admin.js';
519 wp_enqueue_script(
520 'plugin-family-admin-script',
521 $script_url,
522 [ 'jquery' ], // jQuery as a dependency.
523 $this->version,
524 [
525 'in_footer' => true,
526 ]
527 );
528
529 $this->add_install_imagify_localized_script( 'plugin-family-admin-script' );
530
531 $style_url = plugin_dir_url( __DIR__ ) . 'assets/css/style.css';
532 wp_enqueue_style(
533 'plugin-family-admin-style',
534 $style_url,
535 [],
536 $this->version
537 );
538 }
539
540 /**
541 * Insert admin footer JS templates.
542 *
543 * @return void
544 */
545 public function insert_footer_templates() {
546 if ( ! $this->can_enqueue_admin_assets() ) {
547 return;
548 }
549 // Make notice text available to the included template while preserving default text if empty.
550 $notice = ! empty( $this->notice_text )
551 ? $this->notice_text
552 : sprintf(
553 // translators: %1$s = Plugin Name.
554 __( '%1$s recommends you to optimize your images for even better website performance.', 'imagify' ),
555 'WP Rocket'
556 );
557 include_once __DIR__ . '/../View/promote-imagify-uploader.php';
558 }
559
560 /**
561 * Dismiss promote Imagify using the ajax request.
562 *
563 * @return void
564 */
565 public function dismiss_promote_imagify() {
566 check_ajax_referer( 'install-imagify-nonce' );
567
568 if ( ! current_user_can( is_multisite() ? 'manage_network_plugins' : 'install_plugins' ) ) {
569 wp_send_json_error( __( 'Not Allowed', 'imagify' ) );
570 }
571
572 update_option( 'plugin_family_dismiss_promote_imagify', true );
573 wp_send_json_success( __( 'Dismissed.', 'imagify' ) );
574 }
575
576 /**
577 * Check if promote imagify message is dismissed.
578 *
579 * @return bool
580 */
581 private function is_promote_imagify_dismissed() {
582 return ! empty( get_option( 'plugin_family_dismiss_promote_imagify' ) );
583 }
584
585 /**
586 * Check if the Imagify banner should be shown.
587 *
588 * Applies the filter to allow developers to control banner visibility.
589 *
590 * @since 1.0.8
591 *
592 * @param bool $default_value The default value to filter.
593 * @return bool Whether to show the Imagify banner.
594 */
595 private function should_show_imagify_banner( bool $default_value = true ): bool {
596 /**
597 * Filters whether to show the Imagify banner on Media gallery components.
598 *
599 * @since 1.0.8
600 *
601 * @param bool $show_banner Whether to show the Imagify banner.
602 */
603 return wpm_apply_filters_typed( 'boolean', 'wpmedia_plugin_family_show_imagify_banner', $default_value );
604 }
605 }
606