PluginProbe
Elementor Website Builder – more than just a page builder / 3.30.3
Elementor Website Builder – more than just a page builder v3.30.3
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / core / admin / admin-notices.php

admin-notices.php in Elementor Website Builder – more than just a page builder 3.30.3, at core/admin/admin-notices.php

925 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Core\Admin;
3
4 use Elementor\Api;
5 use Elementor\Core\Admin\UI\Components\Button;
6 use Elementor\Core\Base\Module;
7 use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
8 use Elementor\Plugin;
9 use Elementor\Settings;
10 use Elementor\Tracker;
11 use Elementor\User;
12 use Elementor\Utils;
13 use Elementor\Core\Admin\Notices\Base_Notice;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 class Admin_Notices extends Module {
20
21 const DEFAULT_EXCLUDED_PAGES = [ 'plugins.php', 'plugin-install.php', 'plugin-editor.php' ];
22
23 private $plain_notices = [
24 'api_notice',
25 'api_upgrade_plugin',
26 'tracker',
27 'tracker_last_update',
28 'rate_us_feedback',
29 'role_manager_promote',
30 'experiment_promotion',
31 'send_app_promotion',
32 'site_mailer_promotion',
33 'plugin_image_optimization',
34 'ally_pages_promotion',
35 ];
36
37 private $elementor_pages_count = null;
38
39 private $install_time = null;
40
41 private $current_screen_id = null;
42
43 private function get_notices() {
44 /**
45 * Admin notices.
46 *
47 * Filters Elementor admin notices.
48 *
49 * This hook can be used by external developers to manage existing
50 * admin notice or to add new notices for Elementor add-ons.
51 *
52 * @param array $notices A list of notice classes.
53 */
54 $notices = apply_filters( 'elementor/core/admin/notices', [] );
55
56 return $notices;
57 }
58
59 private function get_install_time() {
60 if ( null === $this->install_time ) {
61 $this->install_time = Plugin::$instance->get_install_time();
62 }
63
64 return $this->install_time;
65 }
66
67 private function get_elementor_pages_count() {
68 if ( null === $this->elementor_pages_count ) {
69 $elementor_pages = new \WP_Query( [
70 'no_found_rows' => true,
71 'post_type' => 'any',
72 'post_status' => 'publish',
73 'fields' => 'ids',
74 'update_post_meta_cache' => false,
75 'update_post_term_cache' => false,
76 'meta_key' => '_elementor_edit_mode',
77 'meta_value' => 'builder',
78 ] );
79
80 $this->elementor_pages_count = $elementor_pages->post_count;
81 }
82
83 return $this->elementor_pages_count;
84 }
85
86 private function notice_api_upgrade_plugin() {
87 $upgrade_notice = Api::get_upgrade_notice();
88 if ( empty( $upgrade_notice ) ) {
89 return false;
90 }
91
92 if ( ! current_user_can( 'update_plugins' ) ) {
93 return false;
94 }
95
96 if ( ! $this->is_elementor_admin_screen_with_system_info() ) {
97 return false;
98 }
99
100 // Check for upgrades.
101 $update_plugins = get_site_transient( 'update_plugins' );
102
103 $has_remote_update_package = ! ( empty( $update_plugins ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ] ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ]->package ) );
104
105 if ( ! $has_remote_update_package && empty( $upgrade_notice['update_link'] ) ) {
106 return false;
107 }
108
109 if ( $has_remote_update_package ) {
110 $product = $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ];
111
112 $details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $product->slug . '&section=changelog&TB_iframe=true&width=600&height=800' );
113 $upgrade_url = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . ELEMENTOR_PLUGIN_BASE ), 'upgrade-plugin_' . ELEMENTOR_PLUGIN_BASE );
114 $new_version = $product->new_version;
115 } else {
116 $upgrade_url = $upgrade_notice['update_link'];
117 $details_url = $upgrade_url;
118
119 $new_version = $upgrade_notice['version'];
120 }
121
122 // Check if upgrade messages should be shown.
123 if ( version_compare( ELEMENTOR_VERSION, $upgrade_notice['version'], '>=' ) ) {
124 return false;
125 }
126
127 $notice_id = 'upgrade_notice_' . $upgrade_notice['version'];
128 if ( User::is_user_notice_viewed( $notice_id ) ) {
129 return false;
130 }
131
132 $message = sprintf(
133 /* translators: 1: Details URL, 2: Accessibility text, 3: Version number, 4: Update URL, 5: Accessibility text. */
134 __( 'There is a new version of Elementor Page Builder available. <a href="%1$s" class="thickbox open-plugin-details-modal" aria-label="%2$s">View version %3$s details</a> or <a href="%4$s" class="update-link" aria-label="%5$s">update now</a>.', 'elementor' ),
135 esc_url( $details_url ),
136 esc_attr( sprintf(
137 /* translators: %s: Elementor version. */
138 __( 'View Elementor version %s details', 'elementor' ),
139 $new_version
140 ) ),
141 $new_version,
142 esc_url( $upgrade_url ),
143 esc_attr( esc_html__( 'Update Now', 'elementor' ) )
144 );
145
146 $options = [
147 'title' => esc_html__( 'Update Notification', 'elementor' ),
148 'description' => $message,
149 'button' => [
150 'icon_classes' => 'dashicons dashicons-update',
151 'text' => esc_html__( 'Update Now', 'elementor' ),
152 'url' => $upgrade_url,
153 ],
154 'id' => $notice_id,
155 ];
156
157 $this->print_admin_notice( $options );
158
159 return true;
160 }
161
162 private function notice_api_notice() {
163 $admin_notice = Api::get_admin_notice();
164 if ( empty( $admin_notice ) ) {
165 return false;
166 }
167
168 if ( ! current_user_can( 'manage_options' ) ) {
169 return false;
170 }
171
172 if ( ! $this->is_elementor_admin_screen_with_system_info() ) {
173 return false;
174 }
175
176 $notice_id = 'admin_notice_api_' . $admin_notice['notice_id'];
177 if ( User::is_user_notice_viewed( $notice_id ) ) {
178 return false;
179 }
180
181 $options = [
182 'title' => esc_html__( 'Update Notification', 'elementor' ),
183 'description' => $admin_notice['notice_text'],
184 'id' => $notice_id,
185 ];
186
187 $this->print_admin_notice( $options );
188
189 return true;
190 }
191
192 private function notice_tracker() {
193 if ( ! current_user_can( 'manage_options' ) ) {
194 return false;
195 }
196
197 // Show tracker notice after 24 hours from installed time.
198 if ( strtotime( '+24 hours', $this->get_install_time() ) > time() ) {
199 return false;
200 }
201
202 if ( '1' === get_option( 'elementor_tracker_notice' ) ) {
203 return false;
204 }
205
206 if ( Tracker::is_allow_track() ) {
207 return false;
208 }
209
210 if ( 2 > $this->get_elementor_pages_count() ) {
211 return false;
212 }
213
214 // TODO: Skip for development env.
215 $optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
216 $optout_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_out' ), 'opt_out' );
217
218 $tracker_description_text = esc_html__( 'Become a super contributor by helping us understand how you use our service to enhance your experience and improve our product.', 'elementor' );
219
220 /**
221 * Tracker admin description text.
222 *
223 * Filters the admin notice text for non-sensitive data collection.
224 *
225 * @since 1.0.0
226 *
227 * @param string $tracker_description_text Description text displayed in admin notice.
228 */
229 $tracker_description_text = apply_filters( 'elementor/tracker/admin_description_text', $tracker_description_text );
230
231 $message = esc_html( $tracker_description_text ) . ' <a href="https://go.elementor.com/usage-data-tracking/" target="_blank">' . esc_html__( 'Learn more.', 'elementor' ) . '</a>';
232
233 $options = [
234 'title' => esc_html__( 'Want to shape the future of web creation?', 'elementor' ),
235 'description' => $message,
236 'dismissible' => false,
237 'button' => [
238 'text' => esc_html__( 'Sure! I\'d love to help', 'elementor' ),
239 'url' => $optin_url,
240 'type' => 'cta',
241 ],
242 'button_secondary' => [
243 'text' => esc_html__( 'No thanks', 'elementor' ),
244 'url' => $optout_url,
245 'variant' => 'outline',
246 'type' => 'cta',
247 ],
248 ];
249
250 $this->print_admin_notice( $options );
251
252 return true;
253 }
254
255 private function notice_tracker_last_update() {
256 if ( ! current_user_can( 'manage_options' ) ) {
257 return false;
258 }
259
260 if ( ! Tracker::has_terms_changed() ) {
261 return false;
262 }
263
264 $notice_id = 'tracker_last_update_' . Tracker::LAST_TERMS_UPDATED;
265
266 if ( User::is_user_notice_viewed( $notice_id ) ) {
267 return false;
268 }
269
270 $optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
271
272 $message = esc_html__( 'We\'re updating our Terms and Conditions to include the collection of usage and behavioral data. This information helps us understand how you use Elementor so we can make informed improvements to the product.', 'elementor' );
273
274 $options = [
275 'id' => $notice_id,
276 'title' => esc_html__( 'Update regarding usage data collection', 'elementor' ),
277 'description' => $message,
278 'button' => [
279 'text' => esc_html__( 'Opt in', 'elementor' ),
280 'url' => $optin_url,
281 'type' => 'cta',
282 ],
283 'button_secondary' => [
284 'text' => esc_html__( 'Learn more', 'elementor' ),
285 'url' => 'https://go.elementor.com/wp-dash-update-usage-notice/',
286 'new_tab' => true,
287 'type' => 'cta',
288 ],
289 ];
290
291 $this->print_admin_notice( $options );
292
293 return true;
294 }
295
296 private function notice_rate_us_feedback() {
297 $notice_id = 'rate_us_feedback';
298
299 if ( ! current_user_can( 'manage_options' ) ) {
300 return false;
301 }
302
303 if ( 'dashboard' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
304 return false;
305 }
306
307 if ( 10 >= $this->get_elementor_pages_count() ) {
308 return false;
309 }
310
311 $dismiss_url = add_query_arg( [
312 'action' => 'elementor_set_admin_notice_viewed',
313 'notice_id' => esc_attr( $notice_id ),
314 '_wpnonce' => wp_create_nonce( 'elementor_set_admin_notice_viewed' ),
315 ], admin_url( 'admin-post.php' ) );
316
317 $options = [
318 'title' => esc_html__( 'Congrats!', 'elementor' ),
319 'description' => esc_html__( 'You created over 10 pages with Elementor. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'elementor' ),
320 'id' => $notice_id,
321 'button' => [
322 'text' => esc_html__( 'Happy To Help', 'elementor' ),
323 'url' => 'https://go.elementor.com/admin-review/',
324 'new_tab' => true,
325 'type' => 'cta',
326 ],
327 'button_secondary' => [
328 'text' => esc_html__( 'Hide Notification', 'elementor' ),
329 'classes' => [ 'e-notice-dismiss' ],
330 'url' => esc_url_raw( $dismiss_url ),
331 'new_tab' => true,
332 'type' => 'cta',
333 ],
334 ];
335
336 $this->print_admin_notice( $options );
337
338 return true;
339 }
340
341 private function notice_role_manager_promote() {
342 $notice_id = 'role_manager_promote';
343
344 if ( Utils::has_pro() ) {
345 return false;
346 }
347
348 if ( ! current_user_can( 'manage_options' ) ) {
349 return false;
350 }
351
352 if ( 'elementor_page_elementor-role-manager' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
353 return false;
354 }
355
356 $users = new \WP_User_Query( [
357 'fields' => 'ID',
358 'number' => 10,
359 ] );
360
361 if ( 5 > $users->get_total() ) {
362 return false;
363 }
364
365 $options = [
366 'title' => esc_html__( 'Managing a multi-user site?', 'elementor' ),
367 'description' => esc_html__( 'With Elementor Pro, you can control user access and make sure no one messes up your design.', 'elementor' ),
368 'id' => $notice_id,
369
370 'button' => [
371 'text' => esc_html__( 'Learn More', 'elementor' ),
372 'url' => 'https://go.elementor.com/plugin-promotion-role-manager/',
373 'new_tab' => true,
374 'type' => 'cta',
375 ],
376 ];
377
378 $options = Filtered_Promotions_Manager::get_filtered_promotion_data( $options, 'core/admin/notice_role_manager_promote', 'button', 'url' );
379
380 $this->print_admin_notice( $options );
381
382 return true;
383 }
384
385 private function notice_experiment_promotion() {
386 $notice_id = 'experiment_promotion';
387
388 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
389 return false;
390 }
391
392 $experiments = Plugin::$instance->experiments;
393 $is_all_performance_features_active = (
394 $experiments->is_feature_active( 'e_element_cache' ) &&
395 $experiments->is_feature_active( 'e_font_icon_svg' )
396 );
397
398 if ( $is_all_performance_features_active ) {
399 return false;
400 }
401
402 $options = [
403 'title' => esc_html__( 'Improve your site’s performance score.', 'elementor' ),
404 'description' => esc_html__( 'With our experimental speed boosting features you can go faster than ever before. Look for the Performance label on our Experiments page and activate those experiments to improve your site loading speed.', 'elementor' ),
405 'id' => $notice_id,
406 'button' => [
407 'text' => esc_html__( 'Try it out', 'elementor' ),
408 'url' => Settings::get_settings_tab_url( 'experiments' ),
409 'type' => 'cta',
410 ],
411 'button_secondary' => [
412 'text' => esc_html__( 'Learn more', 'elementor' ),
413 'url' => 'https://go.elementor.com/wp-dash-experiment-promotion/',
414 'new_tab' => true,
415 'type' => 'cta',
416 ],
417 ];
418
419 $this->print_admin_notice( $options );
420
421 return true;
422 }
423
424 private function site_has_forms_plugins() {
425 return defined( 'WPFORMS_VERSION' ) || defined( 'WPCF7_VERSION' ) || defined( 'FLUENTFORM_VERSION' ) || class_exists( '\GFCommon' ) || class_exists( '\Ninja_Forms' ) || function_exists( 'load_formidable_forms' ) || did_action( 'metform/after_load' ) || defined( 'FORMINATOR_PLUGIN_BASENAME' );
426 }
427
428 private function site_has_woocommerce() {
429 return class_exists( 'WooCommerce' );
430 }
431
432 private function get_installed_form_plugin_name() {
433 static $detected_form_plugin = null;
434
435 if ( null !== $detected_form_plugin ) {
436 return $detected_form_plugin;
437 }
438
439 $form_plugins_constants_to_name_mapper = [
440 'WPFORMS_VERSION' => 'WPForms',
441 'WPCF7_VERSION' => 'Contact Form 7',
442 ];
443
444 foreach ( $form_plugins_constants_to_name_mapper as $constant => $name ) {
445 if ( defined( $constant ) ) {
446 $detected_form_plugin = $name;
447 return $detected_form_plugin;
448 }
449 }
450
451 $form_plugins_classes_to_name_mapper = [
452 '\GFCommon' => 'Gravity Forms',
453 '\Ninja_Forms' => 'Ninja Forms',
454 ];
455
456 foreach ( $form_plugins_classes_to_name_mapper as $class => $name ) {
457 if ( class_exists( $class ) ) {
458 $detected_form_plugin = $name;
459 return $detected_form_plugin;
460 }
461 }
462
463 $detected_form_plugin = false;
464 return $detected_form_plugin;
465 }
466
467 private function notice_send_app_promotion() {
468 $notice_id = 'send_app_promotion';
469
470 if ( ! $this->is_elementor_page() && ! $this->is_elementor_admin_screen() ) {
471 return false;
472 }
473
474 if ( time() < $this->get_install_time() + ( 60 * DAY_IN_SECONDS ) ) {
475 return false;
476 }
477
478 if ( ! current_user_can( 'install_plugins' ) || User::is_user_notice_viewed( $notice_id ) ) {
479 return false;
480 }
481
482 $form_plugin_name = $this->get_installed_form_plugin_name();
483
484 if ( ! $form_plugin_name ) {
485 return false;
486 }
487
488 $plugin_file_path = 'send/send-app.php';
489 $plugin_slug = 'send-app';
490
491 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
492 if ( empty( $cta_data ) ) {
493 return false;
494 }
495
496 $title = sprintf(
497 /* translators: %s: Form plugin name */
498 esc_html__( 'Turn %s leads into loyal shoppers', 'elementor' ),
499 $form_plugin_name
500 );
501
502 $options = [
503 'title' => $title,
504 'description' => esc_html__( 'Collecting leads is just the beginning. With Send by Elementor, you can manage contacts, launch automations, and turn form submissions into sales.', 'elementor' ),
505 'id' => $notice_id,
506 'type' => 'cta',
507 'button' => [
508 'text' => $cta_data['text'],
509 'url' => $cta_data['url'],
510 'type' => 'cta',
511 ],
512 'button_secondary' => [
513 'text' => esc_html__( 'Learn more', 'elementor' ),
514 'url' => 'https://go.elementor.com/Formslearnmore',
515 'new_tab' => true,
516 'type' => 'cta',
517 ],
518 ];
519
520 $this->print_admin_notice( $options );
521
522 return true;
523 }
524
525 private function notice_ally_pages_promotion() {
526 global $pagenow;
527 $notice_id = 'ally_pages_promotion';
528
529 if ( 'edit.php' !== $pagenow || empty( $_GET['post_type'] ) || 'page' !== $_GET['post_type'] ) {
530 return false;
531 }
532
533 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
534 return false;
535 }
536
537 $plugin_file_path = 'pojo-accessibility/pojo-accessibility.php';
538 $plugin_slug = 'pojo-accessibility';
539
540 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
541 if ( empty( $cta_data ) ) {
542 return false;
543 }
544
545 $options = [
546 'title' => esc_html__( 'Make sure your site has an accessibility statement page', 'elementor' ),
547 'description' => esc_html__( 'Create a more inclusive site experience for all your visitors. With Ally, it\'s easy to add your statement page in just a few clicks.', 'elementor' ),
548 'id' => $notice_id,
549 'type' => 'cta',
550 'button' => [
551 'text' => $cta_data['text'],
552 'url' => self::add_plg_campaign_data( $cta_data['url'], [
553 'name' => 'elementor_ea11y_campaign',
554 'campaign' => 'acc-statement-plg-pages',
555 'source' => 'wp-pages',
556 'medium' => 'wp-dash',
557 ] ),
558 'type' => 'cta',
559 ],
560 'button_secondary' => [
561 'text' => esc_html__( 'Learn more', 'elementor' ),
562 'url' => 'https://go.elementor.com/acc-plg-learn-more',
563 'new_tab' => true,
564 'type' => 'cta',
565 ],
566 ];
567
568 $this->print_admin_notice( $options );
569
570 return true;
571 }
572
573 private function notice_site_mailer_promotion() {
574 $notice_id = 'site_mailer_promotion';
575 $has_forms = $this->site_has_forms_plugins();
576 $has_woocommerce = $this->site_has_woocommerce();
577
578 if ( ! $has_forms && ! $has_woocommerce ) {
579 return false;
580 }
581
582 if ( ! $this->is_elementor_page() && ! $this->is_elementor_admin_screen() ) {
583 return false;
584 }
585
586 if ( ( Utils::has_pro() && ! $has_woocommerce ) || ! current_user_can( 'install_plugins' ) || User::is_user_notice_viewed( $notice_id ) ) {
587 return false;
588 }
589
590 $plugin_file_path = 'site-mailer/site-mailer.php';
591 $plugin_slug = 'site-mailer';
592
593 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
594 if ( empty( $cta_data ) ) {
595 return false;
596 }
597
598 $options = [
599 'title' => esc_html__( 'Ensure your form emails avoid the spam folder!', 'elementor' ),
600 'description' => esc_html__( 'Use Site Mailer for improved email deliverability, detailed email logs, and an easy setup.', 'elementor' ),
601 'id' => $notice_id,
602 'type' => 'cta',
603 'button' => [
604 'text' => $cta_data['text'],
605 'url' => $cta_data['url'],
606 'type' => 'cta',
607 ],
608 'button_secondary' => [
609 'text' => esc_html__( 'Learn more', 'elementor' ),
610 'url' => 'https://go.elementor.com/sm-core-form/',
611 'new_tab' => true,
612 'type' => 'cta',
613 ],
614 ];
615
616 if ( $this->should_render_woocommerce_hint( $has_forms, $has_woocommerce ) ) {
617 // We include WP's default notice class so it will be properly handled by WP's js handler
618 // And add a new one to distinguish between the two types of notices
619 $options['classes'] = [ 'notice', 'e-notice', 'sm-notice-wc' ];
620 $options['title'] = esc_html__( 'Improve Transactional Email Deliverability', 'elementor' );
621 $options['description'] = esc_html__( 'Use Elementor\'s Site Mailer to ensure your store emails like purchase confirmations, shipping updates and more are reliably delivered.', 'elementor' );
622 }
623
624 $this->print_admin_notice( $options );
625
626 return true;
627 }
628
629 private function should_render_woocommerce_hint( $has_forms, $has_woocommerce ): bool {
630 if ( ! $has_forms && ! $has_woocommerce ) {
631 return false;
632 }
633
634 if ( ! $has_forms && $has_woocommerce ) {
635 return true;
636 }
637
638 if ( $has_forms && $has_woocommerce && Utils::has_pro() ) {
639 return true;
640 }
641
642 if ( ! $has_woocommerce ) {
643 return false;
644 }
645
646 return (bool) wp_rand( 0, 1 );
647 }
648
649 private function is_elementor_page(): bool {
650 return 0 === strpos( $this->current_screen_id, 'elementor_page' );
651 }
652
653 private function is_elementor_admin_screen(): bool {
654 return in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'dashboard' ], true );
655 }
656
657 private function is_elementor_admin_screen_with_system_info(): bool {
658 return in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true );
659 }
660
661 private function get_plugin_cta_data( $plugin_slug, $plugin_file_path ) {
662 if ( is_plugin_active( $plugin_file_path ) ) {
663 return false;
664 }
665
666 if ( $this->is_plugin_installed( $plugin_file_path ) ) {
667 $url = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $plugin_file_path . '&amp;plugin_status=all&amp;paged=1&amp;s', 'activate-plugin_' . $plugin_file_path );
668 $cta_text = esc_html__( 'Activate Plugin', 'elementor' );
669 } else {
670 $url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
671 $cta_text = esc_html__( 'Install Plugin', 'elementor' );
672 }
673
674 return [
675 'url' => $url,
676 'text' => $cta_text,
677 ];
678 }
679
680 /**
681 * For testing purposes
682 */
683 public function get_elementor_version() {
684 return ELEMENTOR_VERSION;
685 }
686
687 private function notice_plugin_image_optimization() {
688 $notice_id = 'plugin_image_optimization';
689
690 if ( 'upload' !== $this->current_screen_id ) {
691 return false;
692 }
693
694 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
695 return false;
696 }
697
698 $attachments = new \WP_Query( [
699 'post_type' => 'attachment',
700 'post_status' => 'any',
701 'fields' => 'ids',
702 ] );
703
704 if ( 1 > $attachments->found_posts ) {
705 return false;
706 }
707
708 $plugin_file_path = 'image-optimization/image-optimization.php';
709 $plugin_slug = 'image-optimization';
710
711 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
712
713 if ( empty( $cta_data ) ) {
714 return false;
715 }
716
717 $options = [
718 'title' => esc_html__( 'Speed up your website with Image Optimizer by Elementor', 'elementor' ),
719 'description' => esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' ),
720 'id' => $notice_id,
721 'type' => 'cta',
722 'button_secondary' => [
723 'text' => $cta_data['text'],
724 'url' => $cta_data['url'],
725 'type' => 'cta',
726 ],
727 ];
728
729 $this->print_admin_notice( $options );
730
731 return true;
732 }
733
734 private function is_plugin_installed( $file_path ): bool {
735 $installed_plugins = get_plugins();
736
737 return isset( $installed_plugins[ $file_path ] );
738 }
739
740 public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
741 global $pagenow;
742
743 if ( in_array( $pagenow, $exclude_pages, true ) ) {
744 return;
745 }
746
747 $default_options = [
748 'id' => null,
749 'title' => '',
750 'description' => '',
751 'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
752 'type' => '',
753 'dismissible' => true,
754 'icon' => 'eicon-elementor',
755 'button' => [],
756 'button_secondary' => [],
757 ];
758
759 $options = array_replace_recursive( $default_options, $options );
760
761 $notice_classes = $options['classes'];
762 $dismiss_button = '';
763 $icon = '';
764
765 if ( $options['type'] ) {
766 $notice_classes[] = 'e-notice--' . $options['type'];
767 }
768
769 $wrapper_attributes = [];
770
771 if ( $options['dismissible'] ) {
772 $label = esc_html__( 'Dismiss this notice.', 'elementor' );
773 $notice_classes[] = 'e-notice--dismissible';
774 $dismiss_button = '<i class="e-notice__dismiss" role="button" aria-label="' . $label . '" tabindex="0"></i>';
775
776 $wrapper_attributes['data-nonce'] = wp_create_nonce( 'elementor_set_admin_notice_viewed' );
777 }
778
779 if ( $options['icon'] ) {
780 $notice_classes[] = 'e-notice--extended';
781 $icon = '<div class="e-notice__icon-wrapper"><i class="' . esc_attr( $options['icon'] ) . '" aria-hidden="true"></i></div>';
782 }
783
784 $wrapper_attributes['class'] = $notice_classes;
785
786 if ( $options['id'] ) {
787 $wrapper_attributes['data-notice_id'] = $options['id'];
788 }
789 ?>
790 <div <?php Utils::print_html_attributes( $wrapper_attributes ); ?>>
791 <?php echo $dismiss_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
792 <div class="e-notice__aside">
793 <?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
794 </div>
795 <div class="e-notice__content">
796 <?php if ( $options['title'] ) { ?>
797 <h3><?php echo wp_kses_post( $options['title'] ); ?></h3>
798 <?php } ?>
799
800 <?php if ( $options['description'] ) { ?>
801 <p><?php echo wp_kses_post( $options['description'] ); ?></p>
802 <?php } ?>
803
804 <?php if ( ! empty( $options['button']['text'] ) || ! empty( $options['button_secondary']['text'] ) ) { ?>
805 <div class="e-notice__actions">
806 <?php
807 foreach ( [ $options['button'], $options['button_secondary'] ] as $index => $button_settings ) {
808 if ( empty( $button_settings['variant'] ) && $index ) {
809 $button_settings['variant'] = 'outline';
810 }
811
812 if ( empty( $button_settings['text'] ) ) {
813 continue;
814 }
815
816 $button = new Button( $button_settings );
817 $button->print_button();
818 } ?>
819 </div>
820 <?php } ?>
821 </div>
822 </div>
823 <?php }
824
825 public function admin_notices() {
826 $this->install_time = Plugin::$instance->get_install_time();
827 $this->current_screen_id = get_current_screen()->id;
828
829 foreach ( $this->plain_notices as $notice ) {
830 $method_callback = "notice_{$notice}";
831 if ( $this->$method_callback() ) {
832 return;
833 }
834 }
835
836 /** @var Base_Notice $notice_instance */
837 foreach ( $this->get_notices() as $notice_instance ) {
838 if ( ! $notice_instance->should_print() ) {
839 continue;
840 }
841
842 $this->print_admin_notice( $notice_instance->get_config() );
843
844 // It exits the method to make sure it prints only one notice.
845 return;
846 }
847 }
848
849 public function maybe_log_campaign() {
850 if ( empty( $_GET['plg_campaign'] ) || empty( $_GET['plg_campaign_name'] ) ) {
851 return;
852 }
853
854 $allowed_plgs = [
855 'elementor_image_optimization_campaign',
856 'elementor_ea11y_campaign',
857 'elementor_site_mailer_campaign',
858 ];
859
860 if ( ! in_array( $_GET['plg_campaign_name'], $allowed_plgs, true ) ) {
861 return;
862 }
863
864 if ( ! isset( $_GET['plg_campaign_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['plg_campaign_nonce'] ), sanitize_key( $_GET['plg_campaign_name'] ) ) ) {
865 return;
866 }
867
868 if ( ! current_user_can( 'manage_options' ) ) {
869 return;
870 }
871
872 if ( empty( $_GET['plg_source'] ) || empty( $_GET['plg_medium'] ) ) {
873 return;
874 }
875
876 $campaign_data = [
877 'source' => sanitize_key( $_GET['plg_source'] ),
878 'campaign' => sanitize_key( $_GET['plg_campaign'] ),
879 'medium' => sanitize_key( $_GET['plg_medium'] ),
880 ];
881
882 set_transient( sanitize_key( $_GET['plg_campaign_name'] ), $campaign_data, 30 * DAY_IN_SECONDS );
883 }
884
885 private static function add_plg_campaign_data( $url, $campaign_data ) {
886
887 foreach ( [ 'name', 'campaign' ] as $key ) {
888 if ( empty( $campaign_data[ $key ] ) ) {
889 return $url;
890 }
891 }
892
893 return add_query_arg( [
894 'plg_campaign_name' => $campaign_data['name'],
895 'plg_campaign' => $campaign_data['campaign'],
896 'plg_source' => empty( $campaign_data['source'] ) ? '' : $campaign_data['source'],
897 'plg_medium' => empty( $campaign_data['medium'] ) ? '' : $campaign_data['medium'],
898 'plg_campaign_nonce' => wp_create_nonce( $campaign_data['name'] ),
899 ], $url );
900 }
901
902 /**
903 * @since 2.9.0
904 * @access public
905 */
906 public function __construct() {
907 add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
908 add_action( 'admin_action_install-plugin', [ $this, 'maybe_log_campaign' ] );
909 }
910
911 /**
912 * Get module name.
913 *
914 * Retrieve the module name.
915 *
916 * @since 2.9.0
917 * @access public
918 *
919 * @return string Module name.
920 */
921 public function get_name() {
922 return 'admin-notices';
923 }
924 }
925