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

1,115 lines 34.8 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 '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_local_google_fonts_disabled() {
474
475 if ( ! $this->is_elementor_page() && ! $this->is_elementor_admin_screen() ) {
476 return false;
477 }
478
479 if ( ! Manager::had_install_prior_to( self::LOCAL_GOOGLE_FONTS_NOTICE_MIN_VERSION ) ) {
480 return false;
481 }
482
483 if ( User::is_user_notice_viewed( self::LOCAL_GOOGLE_FONTS_DISABLED_NOTICE_ID ) ) {
484 return false;
485 }
486
487 $is_local_gf_enabled = (bool) get_option( 'elementor_local_google_fonts', '0' );
488
489 if ( $is_local_gf_enabled ) {
490 return false;
491 }
492
493 $options = [
494 'title' => esc_html__( 'Important: Local Google Fonts Settings in Elementor', 'elementor' ),
495 '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' ),
496 'id' => self::LOCAL_GOOGLE_FONTS_DISABLED_NOTICE_ID,
497 'type' => '',
498 'button' => [
499 'text' => esc_html__( 'Take me there', 'elementor' ),
500 'url' => '../wp-admin/admin.php?page=elementor-settings#tab-performance',
501 'new_tab' => false,
502 'type' => 'cta',
503 ],
504 'button_secondary' => [
505 'text' => esc_html__( 'Learn more', 'elementor' ),
506 'url' => 'https://go.elementor.com/wp-dash-google-fonts-locally-notice/',
507 'new_tab' => true,
508 'type' => 'cta',
509 ],
510 ];
511
512 $this->print_admin_notice( $options );
513
514 return true;
515 }
516
517 private function notice_ally_pages_promotion() {
518 global $pagenow;
519 $notice_id = 'ally_pages_promotion';
520
521 if ( 'edit.php' !== $pagenow || empty( $_GET['post_type'] ) || 'page' !== $_GET['post_type'] ) {
522 return false;
523 }
524
525 if ( ! current_user_can( 'manage_options' ) ) {
526 return false;
527 }
528
529 if ( User::is_user_notice_viewed( $notice_id ) ) {
530 return false;
531 }
532
533 $plugin_slug = 'pojo-accessibility';
534 $plugin_file_path = 'pojo-accessibility/pojo-accessibility.php';
535
536 $one_subscription = Hints::is_plugin_connected_to_one_subscription();
537 $is_installed = Hints::is_plugin_installed( $plugin_slug );
538 $is_active = Hints::is_plugin_active( $plugin_slug );
539
540 if ( $is_active ) {
541 return false;
542 }
543
544 if ( $one_subscription ) {
545 $learn_more_url = 'https://go.elementor.com/acc-plg-learn-more-one';
546
547 if ( ! $is_installed ) {
548 $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' );
549 $button_text = esc_html__( 'Install now', 'elementor' );
550 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
551 $campaign_data = [
552 'name' => 'elementor_ea11y_campaign',
553 'campaign' => 'acc-statement-plg-pages-one-install',
554 'source' => 'wp-pages-one-install',
555 'medium' => 'wp-dash-one',
556 ];
557 } elseif ( ! $is_active ) {
558 $description = esc_html__( 'Your ONE subscription includes Ally. Activate it to create your accessibility statement page quickly.', 'elementor' );
559 $button_text = esc_html__( 'Activate now', 'elementor' );
560 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
561 $campaign_data = [
562 'name' => 'elementor_ea11y_campaign',
563 'campaign' => 'acc-statement-plg-pages-one-activate',
564 'source' => 'wp-pages-one-activate',
565 'medium' => 'wp-dash-one',
566 ];
567 }
568 } else {
569 $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' );
570 $learn_more_url = 'https://go.elementor.com/acc-plg-learn-more';
571
572 if ( ! $is_installed ) {
573 $button_text = esc_html__( 'Install now', 'elementor' );
574 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
575 $campaign_data = [
576 'name' => 'elementor_ea11y_campaign',
577 'campaign' => 'acc-statement-plg-pages-install',
578 'source' => 'wp-pages-install',
579 'medium' => 'wp-dash',
580 ];
581 } elseif ( ! $is_active ) {
582 $button_text = esc_html__( 'Activate now', 'elementor' );
583 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
584 $campaign_data = [
585 'name' => 'elementor_ea11y_campaign',
586 'campaign' => 'acc-statement-plg-pages-activate',
587 'source' => 'wp-pages-activate',
588 'medium' => 'wp-dash',
589 ];
590 }
591 }
592
593 $options = [
594 'title' => esc_html__( 'Make sure your site has an accessibility statement page', 'elementor' ),
595 'description' => $description,
596 'id' => $notice_id,
597 'type' => 'cta',
598 'button' => [
599 'text' => $button_text,
600 'url' => self::add_plg_campaign_data( $button_url, $campaign_data ),
601 'type' => 'cta',
602 ],
603 'button_secondary' => [
604 'text' => esc_html__( 'Learn more', 'elementor' ),
605 'url' => $learn_more_url,
606 'new_tab' => true,
607 'type' => 'cta',
608 ],
609 ];
610
611 $this->print_admin_notice( $options );
612
613 return true;
614 }
615
616 private function notice_site_mailer_promotion() {
617 $notice_id = 'site_mailer_promotion';
618 $has_forms = $this->site_has_forms_plugins();
619 $has_woocommerce = $this->site_has_woocommerce();
620
621 if ( ! $has_forms && ! $has_woocommerce ) {
622 return false;
623 }
624
625 if ( ! $this->is_elementor_page() && ! $this->is_elementor_admin_screen() ) {
626 return false;
627 }
628
629 if ( ( Utils::has_pro() && ! $has_woocommerce ) || ! current_user_can( 'install_plugins' ) || User::is_user_notice_viewed( $notice_id ) ) {
630 return false;
631 }
632
633 $plugin_slug = 'site-mailer';
634 $plugin_file_path = 'site-mailer/site-mailer.php';
635
636 $one_subscription = Hints::is_plugin_connected_to_one_subscription();
637 $is_woocommerce = $this->should_render_woocommerce_hint( $has_forms, $has_woocommerce );
638 $is_installed = Hints::is_plugin_installed( $plugin_slug );
639 $is_active = Hints::is_plugin_active( $plugin_slug );
640
641 if ( $is_active ) {
642 return false;
643 }
644
645 if ( $one_subscription ) {
646 if ( $is_woocommerce ) {
647 $title = esc_html__( 'Improve transactional email deliverability', 'elementor' );
648
649 if ( ! $is_installed ) {
650 $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' );
651 $button_text = esc_html__( 'Install now', 'elementor' );
652 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
653 $source = 'sm-core-woo-one-install';
654 } elseif ( ! $is_active ) {
655 $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' );
656 $button_text = esc_html__( 'Activate now', 'elementor' );
657 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
658 $source = 'sm-core-woo-one-activate';
659 }
660 } else {
661 $title = esc_html__( 'Keep your form emails out of the spam folder', 'elementor' );
662
663 if ( ! $is_installed ) {
664 $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' );
665 $button_text = esc_html__( 'Install now', 'elementor' );
666 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
667 $source = 'sm-core-form-one-install';
668 } elseif ( ! $is_active ) {
669 $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' );
670 $button_text = esc_html__( 'Activate now', 'elementor' );
671 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
672 $source = 'sm-core-form-one-activate';
673 }
674 }
675 // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
676 } else {
677 if ( $is_woocommerce ) {
678 $title = esc_html__( 'Improve Transactional Email Deliverability', 'elementor' );
679 $description = esc_html__( "Use Elementor's Site Mailer to ensure your store emails like purchase confirmations, shipping updates and more are reliably delivered.", 'elementor' );
680
681 if ( ! $is_installed ) {
682 $button_text = esc_html__( 'Install now', 'elementor' );
683 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
684 $source = 'sm-core-woo-install';
685 } elseif ( ! $is_active ) {
686 $button_text = esc_html__( 'Activate now', 'elementor' );
687 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
688 $source = 'sm-core-woo-activate';
689 }
690 } else {
691 $title = esc_html__( 'Ensure your form emails avoid the spam folder!', 'elementor' );
692 $description = esc_html__( 'Use Site Mailer for improved email deliverability, detailed email logs, and an easy setup.', 'elementor' );
693
694 if ( ! $is_installed ) {
695 $button_text = esc_html__( 'Install now', 'elementor' );
696 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
697 $source = 'sm-core-form-install';
698 } elseif ( ! $is_active ) {
699 $button_text = esc_html__( 'Activate now', 'elementor' );
700 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
701 $source = 'sm-core-form-activate';
702 }
703 }
704 }
705
706 $learn_more_url = $one_subscription && $is_woocommerce
707 ? 'https://go.elementor.com/sm-woo-learn-more-one'
708 : 'https://go.elementor.com/sm-core-form/';
709
710 $options = [
711 'title' => $title,
712 'description' => $description,
713 'id' => $notice_id,
714 'type' => 'cta',
715 'button' => [
716 'text' => $button_text,
717 'url' => $button_url,
718 'type' => 'cta',
719 'data' => [
720 'source' => $source,
721 ],
722 ],
723 'button_secondary' => [
724 'text' => esc_html__( 'Learn more', 'elementor' ),
725 'url' => $learn_more_url,
726 'new_tab' => true,
727 'type' => 'cta',
728 ],
729 ];
730
731 if ( $is_woocommerce ) {
732 // We include WP's default notice class so it will be properly handled by WP's js handler
733 // And add a new one to distinguish between the two types of notices
734 $options['classes'] = [ 'notice', 'e-notice', 'sm-notice-wc' ];
735 }
736
737 $this->print_admin_notice( $options );
738
739 return true;
740 }
741
742 private function should_render_woocommerce_hint( $has_forms, $has_woocommerce ): bool {
743 if ( ! $has_forms && ! $has_woocommerce ) {
744 return false;
745 }
746
747 if ( ! $has_forms && $has_woocommerce ) {
748 return true;
749 }
750
751 if ( $has_forms && $has_woocommerce && Utils::has_pro() ) {
752 return true;
753 }
754
755 if ( ! $has_woocommerce ) {
756 return false;
757 }
758
759 return (bool) wp_rand( 0, 1 );
760 }
761
762 private function is_elementor_page(): bool {
763 return 0 === strpos( $this->current_screen_id, 'elementor_page' );
764 }
765
766 private function is_elementor_admin_screen(): bool {
767 return in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library' ], true );
768 }
769
770 private function is_elementor_admin_screen_with_system_info(): bool {
771 return in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true );
772 }
773 private function get_plugin_button_install_url( $plugin_slug ) {
774 return wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
775 }
776
777 private function get_plugin_button_activate_url( $plugin_file_path ) {
778 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 );
779 }
780
781 private function get_plugin_button_connect_url( $plugin_settings_page ) {
782 return self_admin_url( $plugin_settings_page );
783 }
784
785 private function get_plugin_cta_data( $plugin_slug, $plugin_file_path ) {
786 if ( Hints::is_plugin_active( $plugin_slug ) ) {
787 return false;
788 }
789
790 if ( Hints::is_plugin_installed( $plugin_slug ) ) {
791 $url = $this->get_plugin_button_activate_url( $plugin_file_path );
792 $cta_text = esc_html__( 'Activate now', 'elementor' );
793 } else {
794 $url = $this->get_plugin_button_install_url( $plugin_slug );
795 $cta_text = esc_html__( 'Install now', 'elementor' );
796 }
797
798 return [
799 'url' => $url,
800 'text' => $cta_text,
801 ];
802 }
803
804 /**
805 * For testing purposes
806 */
807 public function get_elementor_version() {
808 return ELEMENTOR_VERSION;
809 }
810
811 private function notice_plugin_image_optimization() {
812 $notice_id = 'plugin_image_optimization';
813
814 if ( 'upload' !== $this->current_screen_id ) {
815 return false;
816 }
817
818 if ( ! current_user_can( 'manage_options' ) ) {
819 return false;
820 }
821
822 if ( User::is_user_notice_viewed( $notice_id ) ) {
823 return false;
824 }
825
826 $attachments = new \WP_Query( [
827 'post_type' => 'attachment',
828 'post_status' => 'any',
829 'fields' => 'ids',
830 ] );
831
832 if ( 1 > $attachments->found_posts ) {
833 return false;
834 }
835
836 $plugin_slug = 'image-optimization';
837 $plugin_file_path = 'image-optimization/image-optimization.php';
838
839 $one_subscription = Hints::is_plugin_connected_to_one_subscription();
840 $is_installed = Hints::is_plugin_installed( $plugin_slug );
841 $is_active = Hints::is_plugin_active( $plugin_slug );
842
843 if ( $is_active ) {
844 return false;
845 }
846
847 if ( $one_subscription ) {
848 if ( ! $is_installed ) {
849 $description = esc_html__( 'Automatically optimize images to improve site speed and performance. Included with your ONE subscription.', 'elementor' );
850 $button_text = esc_html__( 'Install now', 'elementor' );
851 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
852 $campaign_data = [
853 'name' => 'elementor_image_optimization_campaign',
854 'campaign' => 'image-optimization-plg-wp-media-library-one-install',
855 'source' => 'io-wp-media-library-one-install',
856 'medium' => 'wp-dash-one',
857 ];
858 } elseif ( ! $is_active ) {
859 $description = esc_html__( 'Your ONE subscription includes Image Optimizer. Activate it to optimize images and improve site performance.', 'elementor' );
860 $button_text = esc_html__( 'Activate now', 'elementor' );
861 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
862 $campaign_data = [
863 'name' => 'elementor_image_optimization_campaign',
864 'campaign' => 'image-optimization-plg-wp-media-library-one-activate',
865 'source' => 'io-wp-media-library-one-activate',
866 'medium' => 'wp-dash-one',
867 ];
868 }
869 } else {
870 $description = esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' );
871
872 if ( ! $is_installed ) {
873 $button_text = esc_html__( 'Install now', 'elementor' );
874 $button_url = $this->get_plugin_button_install_url( $plugin_slug );
875 $campaign_data = [
876 'name' => 'elementor_image_optimization_campaign',
877 'campaign' => 'image-optimization-plg-wp-media-library-install',
878 'source' => 'io-wp-media-library-install',
879 'medium' => 'wp-dash',
880 ];
881 } elseif ( ! $is_active ) {
882 $button_text = esc_html__( 'Activate now', 'elementor' );
883 $button_url = $this->get_plugin_button_activate_url( $plugin_file_path );
884 $campaign_data = [
885 'name' => 'elementor_image_optimization_campaign',
886 'campaign' => 'image-optimization-plg-wp-media-library-activate',
887 'source' => 'io-wp-media-library-activate',
888 'medium' => 'wp-dash',
889 ];
890 }
891 }
892
893 $options = [
894 'title' => esc_html__( 'Speed up your website with Image Optimizer', 'elementor' ),
895 'description' => $description,
896 'id' => $notice_id,
897 'type' => 'cta',
898 'button' => [
899 'text' => $button_text,
900 'url' => $button_url,
901 'type' => 'cta',
902 'data' => [
903 'source' => $campaign_data['source'],
904 ],
905 ],
906 ];
907
908 $this->print_admin_notice( $options );
909
910 return true;
911 }
912
913 public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
914 global $pagenow;
915
916 if ( in_array( $pagenow, $exclude_pages, true ) ) {
917 return;
918 }
919
920 $default_options = [
921 'id' => null,
922 'title' => '',
923 'description' => '',
924 'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
925 'type' => '',
926 'dismissible' => true,
927 'icon' => 'eicon-elementor-circle',
928 'button' => [],
929 'button_secondary' => [],
930 ];
931
932 $options = array_replace_recursive( $default_options, $options );
933
934 $notice_classes = $options['classes'];
935 $dismiss_button = '';
936 $icon = '';
937
938 if ( $options['type'] ) {
939 $notice_classes[] = 'e-notice--' . $options['type'];
940 }
941
942 $wrapper_attributes = [];
943
944 if ( $options['dismissible'] ) {
945 $label = esc_html__( 'Dismiss this notice.', 'elementor' );
946 $notice_classes[] = 'e-notice--dismissible';
947 $dismiss_button = '<i class="e-notice__dismiss" role="button" aria-label="' . $label . '" tabindex="0"></i>';
948
949 $wrapper_attributes['data-nonce'] = wp_create_nonce( 'elementor_set_admin_notice_viewed' );
950 }
951
952 if ( $options['icon'] ) {
953 $notice_classes[] = 'e-notice--extended';
954 $icon = '<div class="e-notice__icon-wrapper"><i class="' . esc_attr( $options['icon'] ) . '" aria-hidden="true"></i></div>';
955 }
956
957 $wrapper_attributes['class'] = $notice_classes;
958
959 if ( $options['id'] ) {
960 $wrapper_attributes['data-notice_id'] = $options['id'];
961 }
962
963 if ( ! empty( $options['source'] ) ) {
964 $wrapper_attributes['data-source'] = $options['source'];
965 }
966 ?>
967 <div <?php Utils::print_html_attributes( $wrapper_attributes ); ?>>
968 <?php echo $dismiss_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
969 <div class="e-notice__aside">
970 <?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
971 </div>
972 <div class="e-notice__content">
973 <?php if ( $options['title'] ) { ?>
974 <h3><?php echo wp_kses_post( $options['title'] ); ?></h3>
975 <?php } ?>
976
977 <?php if ( $options['description'] ) { ?>
978 <p><?php echo wp_kses_post( $options['description'] ); ?></p>
979 <?php } ?>
980
981 <?php if ( ! empty( $options['button']['text'] ) || ! empty( $options['button_secondary']['text'] ) ) { ?>
982 <div class="e-notice__actions">
983 <?php
984 $one_subscription = Hints::is_plugin_connected_to_one_subscription();
985 foreach ( [ $options['button'], $options['button_secondary'] ] as $index => $button_settings ) {
986 if ( empty( $button_settings['variant'] ) && $index ) {
987 $button_settings['variant'] = 'outline';
988 }
989
990 if ( empty( $button_settings['text'] ) ) {
991 continue;
992 }
993
994 if ( $one_subscription ) {
995 if ( ! isset( $button_settings['classes'] ) ) {
996 $button_settings['classes'] = [];
997 }
998 if ( ! is_array( $button_settings['classes'] ) ) {
999 $button_settings['classes'] = [ $button_settings['classes'] ];
1000 }
1001 // index 0 = button, index 1 = button_secondary
1002 $button_class = ( 0 === $index ) ? 'button-primary' : 'button-secondary';
1003 $button_settings['classes'][] = $button_class;
1004 }
1005
1006 $button = new Button( $button_settings );
1007 $button->print_button();
1008 } ?>
1009 </div>
1010 <?php } ?>
1011 </div>
1012 </div>
1013 <?php }
1014
1015 public function admin_notices() {
1016 $this->install_time = Plugin::$instance->get_install_time();
1017 $this->current_screen_id = get_current_screen()->id;
1018
1019 foreach ( $this->plain_notices as $notice ) {
1020 $method_callback = "notice_{$notice}";
1021 if ( $this->$method_callback() ) {
1022 return;
1023 }
1024 }
1025
1026 /** @var Base_Notice $notice_instance */
1027 foreach ( $this->get_notices() as $notice_instance ) {
1028 if ( ! $notice_instance->should_print() ) {
1029 continue;
1030 }
1031
1032 $this->print_admin_notice( $notice_instance->get_config() );
1033
1034 // It exits the method to make sure it prints only one notice.
1035 return;
1036 }
1037 }
1038
1039 public function maybe_log_campaign() {
1040 if ( empty( $_GET['plg_campaign'] ) || empty( $_GET['plg_campaign_name'] ) ) {
1041 return;
1042 }
1043
1044 $allowed_plgs = [
1045 'elementor_image_optimization_campaign',
1046 'elementor_ea11y_campaign',
1047 'elementor_site_mailer_campaign',
1048 ];
1049
1050 if ( ! in_array( $_GET['plg_campaign_name'], $allowed_plgs, true ) ) {
1051 return;
1052 }
1053
1054 if ( ! isset( $_GET['plg_campaign_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['plg_campaign_nonce'] ), sanitize_key( $_GET['plg_campaign_name'] ) ) ) {
1055 return;
1056 }
1057
1058 if ( ! current_user_can( 'manage_options' ) ) {
1059 return;
1060 }
1061
1062 if ( empty( $_GET['plg_source'] ) || empty( $_GET['plg_medium'] ) ) {
1063 return;
1064 }
1065
1066 $campaign_data = [
1067 'source' => sanitize_key( $_GET['plg_source'] ),
1068 'campaign' => sanitize_key( $_GET['plg_campaign'] ),
1069 'medium' => sanitize_key( $_GET['plg_medium'] ),
1070 ];
1071
1072 set_transient( sanitize_key( $_GET['plg_campaign_name'] ), $campaign_data, 30 * DAY_IN_SECONDS );
1073 }
1074
1075 public static function add_plg_campaign_data( $url, $campaign_data ) {
1076
1077 foreach ( [ 'name', 'campaign' ] as $key ) {
1078 if ( empty( $campaign_data[ $key ] ) ) {
1079 return $url;
1080 }
1081 }
1082
1083 return add_query_arg( [
1084 'plg_campaign_name' => $campaign_data['name'],
1085 'plg_campaign' => $campaign_data['campaign'],
1086 'plg_source' => empty( $campaign_data['source'] ) ? '' : $campaign_data['source'],
1087 'plg_medium' => empty( $campaign_data['medium'] ) ? '' : $campaign_data['medium'],
1088 'plg_campaign_nonce' => wp_create_nonce( $campaign_data['name'] ),
1089 ], $url );
1090 }
1091
1092 /**
1093 * @since 2.9.0
1094 * @access public
1095 */
1096 public function __construct() {
1097 add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
1098 add_action( 'admin_action_install-plugin', [ $this, 'maybe_log_campaign' ] );
1099 }
1100
1101 /**
1102 * Get module name.
1103 *
1104 * Retrieve the module name.
1105 *
1106 * @since 2.9.0
1107 * @access public
1108 *
1109 * @return string Module name.
1110 */
1111 public function get_name() {
1112 return 'admin-notices';
1113 }
1114 }
1115