PluginProbe
Elementor Website Builder – more than just a page builder / 3.34.0-beta1
Elementor Website Builder – more than just a page builder v3.34.0-beta1
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.34.0-beta1, at core/admin/admin-notices.php

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