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

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