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

679 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Core\Admin;
3
4 use Elementor\Api;
5 use Elementor\Core\Admin\UI\Components\Button;
6 use Elementor\Core\Base\Module;
7 use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
8 use Elementor\Plugin;
9 use Elementor\Settings;
10 use Elementor\Tracker;
11 use Elementor\User;
12 use Elementor\Utils;
13 use Elementor\Core\Admin\Notices\Base_Notice;
14 use Elementor\Core\Admin\Notices\Elementor_Dev_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
24 private $plain_notices = [
25 'api_notice',
26 'api_upgrade_plugin',
27 'tracker',
28 'rate_us_feedback',
29 'role_manager_promote',
30 'experiment_promotion',
31 'site_mailer_promotion',
32 'plugin_image_optimization',
33 ];
34
35 private $elementor_pages_count = null;
36
37 private $install_time = null;
38
39 private $current_screen_id = null;
40
41 private function get_notices() {
42 $notices = [
43 new Elementor_Dev_Notice(),
44 ];
45
46 /**
47 * Admin notices.
48 *
49 * Filters Elementor admin notices.
50 *
51 * This hook can be used by external developers to manage existing
52 * admin notice or to add new notices for Elementor add-ons.
53 *
54 * @param array $notices A list of notice classes.
55 */
56 $notices = apply_filters( 'elementor/core/admin/notices', $notices );
57
58 return $notices;
59 }
60
61 private function get_install_time() {
62 if ( null === $this->install_time ) {
63 $this->install_time = Plugin::$instance->get_install_time();
64 }
65
66 return $this->install_time;
67 }
68
69 private function get_elementor_pages_count() {
70 if ( null === $this->elementor_pages_count ) {
71 $elementor_pages = new \WP_Query( [
72 'no_found_rows' => true,
73 'post_type' => 'any',
74 'post_status' => 'publish',
75 'fields' => 'ids',
76 'update_post_meta_cache' => false,
77 'update_post_term_cache' => false,
78 'meta_key' => '_elementor_edit_mode',
79 'meta_value' => 'builder',
80 ] );
81
82 $this->elementor_pages_count = $elementor_pages->post_count;
83 }
84
85 return $this->elementor_pages_count;
86 }
87
88 private function notice_api_upgrade_plugin() {
89 $upgrade_notice = Api::get_upgrade_notice();
90 if ( empty( $upgrade_notice ) ) {
91 return false;
92 }
93
94 if ( ! current_user_can( 'update_plugins' ) ) {
95 return false;
96 }
97
98 if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
99 return false;
100 }
101
102 // Check for upgrades.
103 $update_plugins = get_site_transient( 'update_plugins' );
104
105 $has_remote_update_package = ! ( empty( $update_plugins ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ] ) || empty( $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ]->package ) );
106
107 if ( ! $has_remote_update_package && empty( $upgrade_notice['update_link'] ) ) {
108 return false;
109 }
110
111 if ( $has_remote_update_package ) {
112 $product = $update_plugins->response[ ELEMENTOR_PLUGIN_BASE ];
113
114 $details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $product->slug . '&section=changelog&TB_iframe=true&width=600&height=800' );
115 $upgrade_url = wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' . ELEMENTOR_PLUGIN_BASE ), 'upgrade-plugin_' . ELEMENTOR_PLUGIN_BASE );
116 $new_version = $product->new_version;
117 } else {
118 $upgrade_url = $upgrade_notice['update_link'];
119 $details_url = $upgrade_url;
120
121 $new_version = $upgrade_notice['version'];
122 }
123
124 // Check if upgrade messages should be shown.
125 if ( version_compare( ELEMENTOR_VERSION, $upgrade_notice['version'], '>=' ) ) {
126 return false;
127 }
128
129 $notice_id = 'upgrade_notice_' . $upgrade_notice['version'];
130 if ( User::is_user_notice_viewed( $notice_id ) ) {
131 return false;
132 }
133
134 $message = sprintf(
135 /* translators: 1: Details URL, 2: Accessibility text, 3: Version number, 4: Update URL, 5: Accessibility text. */
136 __( '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' ),
137 esc_url( $details_url ),
138 esc_attr( sprintf(
139 /* translators: %s: Elementor version. */
140 __( 'View Elementor version %s details', 'elementor' ),
141 $new_version
142 ) ),
143 $new_version,
144 esc_url( $upgrade_url ),
145 esc_attr( esc_html__( 'Update Now', 'elementor' ) )
146 );
147
148 $options = [
149 'title' => esc_html__( 'Update Notification', 'elementor' ),
150 'description' => $message,
151 'button' => [
152 'icon_classes' => 'dashicons dashicons-update',
153 'text' => esc_html__( 'Update Now', 'elementor' ),
154 'url' => $upgrade_url,
155 ],
156 'id' => $notice_id,
157 ];
158
159 $this->print_admin_notice( $options );
160
161 return true;
162 }
163
164 private function notice_api_notice() {
165 $admin_notice = Api::get_admin_notice();
166 if ( empty( $admin_notice ) ) {
167 return false;
168 }
169
170 if ( ! current_user_can( 'manage_options' ) ) {
171 return false;
172 }
173
174 if ( ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'elementor_page_elementor-system-info', 'dashboard' ], true ) ) {
175 return false;
176 }
177
178 $notice_id = 'admin_notice_api_' . $admin_notice['notice_id'];
179 if ( User::is_user_notice_viewed( $notice_id ) ) {
180 return false;
181 }
182
183 $options = [
184 'title' => esc_html__( 'Update Notification', 'elementor' ),
185 'description' => $admin_notice['notice_text'],
186 'id' => $notice_id,
187 ];
188
189 $this->print_admin_notice( $options );
190
191 return true;
192 }
193
194 private function notice_tracker() {
195 if ( ! current_user_can( 'manage_options' ) ) {
196 return false;
197 }
198
199 // Show tracker notice after 24 hours from installed time.
200 if ( strtotime( '+24 hours', $this->get_install_time() ) > time() ) {
201 return false;
202 }
203
204 if ( '1' === get_option( 'elementor_tracker_notice' ) ) {
205 return false;
206 }
207
208 if ( Tracker::is_allow_track() ) {
209 return false;
210 }
211
212 if ( 2 > $this->get_elementor_pages_count() ) {
213 return false;
214 }
215
216 // TODO: Skip for development env.
217 $optin_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_into' ), 'opt_into' );
218 $optout_url = wp_nonce_url( add_query_arg( 'elementor_tracker', 'opt_out' ), 'opt_out' );
219
220 $tracker_description_text = esc_html__( 'Become a super contributor by opting in to share non-sensitive plugin data and to receive periodic email updates from us.', 'elementor' );
221
222 /**
223 * Tracker admin description text.
224 *
225 * Filters the admin notice text for non-sensitive data collection.
226 *
227 * @since 1.0.0
228 *
229 * @param string $tracker_description_text Description text displayed in admin notice.
230 */
231 $tracker_description_text = apply_filters( 'elementor/tracker/admin_description_text', $tracker_description_text );
232
233 $message = esc_html( $tracker_description_text ) . ' <a href="https://go.elementor.com/usage-data-tracking/" target="_blank">' . esc_html__( 'Learn more.', 'elementor' ) . '</a>';
234
235 $options = [
236 'title' => esc_html__( 'Love using Elementor?', 'elementor' ),
237 'description' => $message,
238 'dismissible' => false,
239 'button' => [
240 'text' => esc_html__( 'Sure! I\'d love to help', 'elementor' ),
241 'url' => $optin_url,
242 'type' => 'cta',
243 ],
244 'button_secondary' => [
245 'text' => esc_html__( 'No thanks', 'elementor' ),
246 'url' => $optout_url,
247 'variant' => 'outline',
248 'type' => 'cta',
249 ],
250 ];
251
252 $this->print_admin_notice( $options );
253
254 return true;
255 }
256
257 private function notice_rate_us_feedback() {
258 $notice_id = 'rate_us_feedback';
259
260 if ( ! current_user_can( 'manage_options' ) ) {
261 return false;
262 }
263
264 if ( 'dashboard' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
265 return false;
266 }
267
268 if ( 10 >= $this->get_elementor_pages_count() ) {
269 return false;
270 }
271
272 $dismiss_url = add_query_arg( [
273 'action' => 'elementor_set_admin_notice_viewed',
274 'notice_id' => esc_attr( $notice_id ),
275 '_wpnonce' => wp_create_nonce( 'elementor_set_admin_notice_viewed' ),
276 ], admin_url( 'admin-post.php' ) );
277
278 $options = [
279 'title' => esc_html__( 'Congrats!', 'elementor' ),
280 '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' ),
281 'id' => $notice_id,
282 'button' => [
283 'text' => esc_html__( 'Happy To Help', 'elementor' ),
284 'url' => 'https://go.elementor.com/admin-review/',
285 'new_tab' => true,
286 'type' => 'cta',
287 ],
288 'button_secondary' => [
289 'text' => esc_html__( 'Hide Notification', 'elementor' ),
290 'classes' => [ 'e-notice-dismiss' ],
291 'url' => esc_url_raw( $dismiss_url ),
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_role_manager_promote() {
303 $notice_id = 'role_manager_promote';
304
305 if ( Utils::has_pro() ) {
306 return false;
307 }
308
309 if ( ! current_user_can( 'manage_options' ) ) {
310 return false;
311 }
312
313 if ( 'elementor_page_elementor-role-manager' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
314 return false;
315 }
316
317 $users = new \WP_User_Query( [
318 'fields' => 'ID',
319 'number' => 10,
320 ] );
321
322 if ( 5 > $users->get_total() ) {
323 return false;
324 }
325
326 $options = [
327 'title' => esc_html__( 'Managing a multi-user site?', 'elementor' ),
328 'description' => esc_html__( 'With Elementor Pro, you can control user access and make sure no one messes up your design.', 'elementor' ),
329 'id' => $notice_id,
330
331 'button' => [
332 'text' => esc_html__( 'Learn More', 'elementor' ),
333 'url' => 'https://go.elementor.com/plugin-promotion-role-manager/',
334 'new_tab' => true,
335 'type' => 'cta',
336 ],
337 ];
338
339 $options = Filtered_Promotions_Manager::get_filtered_promotion_data( $options, 'core/admin/notice_role_manager_promote', 'button', 'url' );
340
341 $this->print_admin_notice( $options );
342
343 return true;
344 }
345
346 private function notice_experiment_promotion() {
347 $notice_id = 'experiment_promotion';
348
349 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
350 return false;
351 }
352
353 $experiments = Plugin::$instance->experiments;
354 $is_all_performance_features_active = (
355 $experiments->is_feature_active( 'e_element_cache' ) &&
356 $experiments->is_feature_active( 'e_font_icon_svg' )
357 );
358
359 if ( $is_all_performance_features_active ) {
360 return false;
361 }
362
363 $options = [
364 'title' => esc_html__( 'Improve your site’s performance score.', 'elementor' ),
365 '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' ),
366 'id' => $notice_id,
367 'button' => [
368 'text' => esc_html__( 'Try it out', 'elementor' ),
369 'url' => Settings::get_settings_tab_url( 'experiments' ),
370 'type' => 'cta',
371 ],
372 'button_secondary' => [
373 'text' => esc_html__( 'Learn more', 'elementor' ),
374 'url' => 'https://go.elementor.com/wp-dash-experiment-promotion/',
375 'new_tab' => true,
376 'type' => 'cta',
377 ],
378 ];
379
380 $this->print_admin_notice( $options );
381
382 return true;
383 }
384
385 private function site_has_forms_plugins() {
386 return defined( 'WPFORMS_VERSION' ) || defined( 'WPCF7_VERSION' ) || defined( 'FLUENTFORM_VERSION' ) || class_exists( '\GFCommon' ) || class_exists( '\Ninja_Forms' ) || function_exists( 'load_formidable_forms' );
387 }
388
389 private function site_has_woocommerce() {
390 return class_exists( 'WooCommerce' );
391 }
392
393 private function notice_site_mailer_promotion() {
394 $notice_id = 'site_mailer_promotion';
395 $has_forms = $this->site_has_forms_plugins();
396 $has_woocommerce = $this->site_has_woocommerce();
397
398 if ( ! $has_forms && ! $has_woocommerce ) {
399 return false;
400 }
401
402 if ( ! $this->is_elementor_page() && ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'dashboard' ], true ) ) {
403 return false;
404 }
405
406 if ( ( Utils::has_pro() && ! $has_woocommerce ) || ! current_user_can( 'install_plugins' ) || User::is_user_notice_viewed( $notice_id ) ) {
407 return false;
408 }
409
410 $plugin_file_path = 'site-mailer/site-mailer.php';
411 $plugin_slug = 'site-mailer';
412
413 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
414 if ( empty( $cta_data ) ) {
415 return false;
416 }
417
418 $options = [
419 'title' => esc_html__( 'Ensure your form emails avoid the spam folder!', 'elementor' ),
420 'description' => esc_html__( 'Use Site Mailer for improved email deliverability, detailed email logs, and an easy setup.', 'elementor' ),
421 'id' => $notice_id,
422 'type' => 'cta',
423 'button' => [
424 'text' => $cta_data['text'],
425 'url' => $cta_data['url'],
426 'type' => 'cta',
427 ],
428 'button_secondary' => [
429 'text' => esc_html__( 'Learn more', 'elementor' ),
430 'url' => 'https://go.elementor.com/sm-core-form/',
431 'new_tab' => true,
432 'type' => 'cta',
433 ],
434 ];
435
436 if ( $this->should_render_woocommerce_hint( $has_forms, $has_woocommerce ) ) {
437 // We include WP's default notice class so it will be properly handled by WP's js handler
438 // And add a new one to distinguish between the two types of notices
439 $options['classes'] = [ 'notice', 'e-notice', 'sm-notice-wc' ];
440 $options['title'] = esc_html__( 'Improve Transactional Email Deliverability', 'elementor' );
441 $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' );
442 }
443
444 $this->print_admin_notice( $options );
445
446 return true;
447 }
448
449 private function should_render_woocommerce_hint( $has_forms, $has_woocommerce ): bool {
450 if ( ! $has_forms && ! $has_woocommerce ) {
451 return false;
452 }
453
454 if ( ! $has_forms && $has_woocommerce ) {
455 return true;
456 }
457
458 if ( $has_forms && $has_woocommerce && Utils::has_pro() ) {
459 return true;
460 }
461
462 return (bool) wp_rand( 0, 1 );
463 }
464
465 private function is_elementor_page(): bool {
466 return 0 === strpos( $this->current_screen_id, 'elementor_page' );
467 }
468
469 private function get_plugin_cta_data( $plugin_slug, $plugin_file_path ) {
470 if ( is_plugin_active( $plugin_file_path ) ) {
471 return false;
472 }
473
474 if ( $this->is_plugin_installed( $plugin_file_path ) ) {
475 $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 );
476 $cta_text = esc_html__( 'Activate Plugin', 'elementor' );
477 } else {
478 $url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
479 $cta_text = esc_html__( 'Install Plugin', 'elementor' );
480 }
481
482 return [
483 'url' => $url,
484 'text' => $cta_text,
485 ];
486 }
487
488 /**
489 * For testing purposes
490 */
491 public function get_elementor_version() {
492 return ELEMENTOR_VERSION;
493 }
494
495 private function notice_plugin_image_optimization() {
496 $notice_id = 'plugin_image_optimization';
497
498 if ( 'upload' !== $this->current_screen_id ) {
499 return false;
500 }
501
502 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
503 return false;
504 }
505
506 $attachments = new \WP_Query( [
507 'post_type' => 'attachment',
508 'post_status' => 'any',
509 'fields' => 'ids',
510 ] );
511
512 if ( 1 > $attachments->found_posts ) {
513 return false;
514 }
515
516 $plugin_file_path = 'image-optimization/image-optimization.php';
517 $plugin_slug = 'image-optimization';
518
519 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
520
521 if ( empty( $cta_data ) ) {
522 return false;
523 }
524
525 $options = [
526 'title' => esc_html__( 'Speed up your website with Image Optimizer by Elementor', 'elementor' ),
527 'description' => esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' ),
528 'id' => $notice_id,
529 'type' => 'cta',
530 'button_secondary' => [
531 'text' => $cta_data['text'],
532 'url' => $cta_data['url'],
533 'type' => 'cta',
534 ],
535 ];
536
537 $this->print_admin_notice( $options );
538
539 return true;
540 }
541
542 private function is_plugin_installed( $file_path ): bool {
543 $installed_plugins = get_plugins();
544
545 return isset( $installed_plugins[ $file_path ] );
546 }
547
548 public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
549 global $pagenow;
550
551 if ( in_array( $pagenow, $exclude_pages, true ) ) {
552 return;
553 }
554
555 $default_options = [
556 'id' => null,
557 'title' => '',
558 'description' => '',
559 'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
560 'type' => '',
561 'dismissible' => true,
562 'icon' => 'eicon-elementor',
563 'button' => [],
564 'button_secondary' => [],
565 ];
566
567 $options = array_replace_recursive( $default_options, $options );
568
569 $notice_classes = $options['classes'];
570 $dismiss_button = '';
571 $icon = '';
572
573 if ( $options['type'] ) {
574 $notice_classes[] = 'e-notice--' . $options['type'];
575 }
576
577 $wrapper_attributes = [];
578
579 if ( $options['dismissible'] ) {
580 $label = esc_html__( 'Dismiss this notice.', 'elementor' );
581 $notice_classes[] = 'e-notice--dismissible';
582 $dismiss_button = '<i class="e-notice__dismiss" role="button" aria-label="' . $label . '" tabindex="0"></i>';
583
584 $wrapper_attributes['data-nonce'] = wp_create_nonce( 'elementor_set_admin_notice_viewed' );
585 }
586
587 if ( $options['icon'] ) {
588 $notice_classes[] = 'e-notice--extended';
589 $icon = '<div class="e-notice__icon-wrapper"><i class="' . esc_attr( $options['icon'] ) . '" aria-hidden="true"></i></div>';
590 }
591
592 $wrapper_attributes['class'] = $notice_classes;
593
594 if ( $options['id'] ) {
595 $wrapper_attributes['data-notice_id'] = $options['id'];
596 }
597 ?>
598 <div <?php Utils::print_html_attributes( $wrapper_attributes ); ?>>
599 <?php echo $dismiss_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
600 <div class="e-notice__aside">
601 <?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
602 </div>
603 <div class="e-notice__content">
604 <?php if ( $options['title'] ) { ?>
605 <h3><?php echo wp_kses_post( $options['title'] ); ?></h3>
606 <?php } ?>
607
608 <?php if ( $options['description'] ) { ?>
609 <p><?php echo wp_kses_post( $options['description'] ); ?></p>
610 <?php } ?>
611
612 <?php if ( ! empty( $options['button']['text'] ) || ! empty( $options['button_secondary']['text'] ) ) { ?>
613 <div class="e-notice__actions">
614 <?php
615 foreach ( [ $options['button'], $options['button_secondary'] ] as $index => $button_settings ) {
616 if ( empty( $button_settings['variant'] ) && $index ) {
617 $button_settings['variant'] = 'outline';
618 }
619
620 if ( empty( $button_settings['text'] ) ) {
621 continue;
622 }
623
624 $button = new Button( $button_settings );
625 $button->print_button();
626 } ?>
627 </div>
628 <?php } ?>
629 </div>
630 </div>
631 <?php }
632
633 public function admin_notices() {
634 $this->install_time = Plugin::$instance->get_install_time();
635 $this->current_screen_id = get_current_screen()->id;
636
637 foreach ( $this->plain_notices as $notice ) {
638 $method_callback = "notice_{$notice}";
639 if ( $this->$method_callback() ) {
640 return;
641 }
642 }
643
644 /** @var Base_Notice $notice_instance */
645 foreach ( $this->get_notices() as $notice_instance ) {
646 if ( ! $notice_instance->should_print() ) {
647 continue;
648 }
649
650 $this->print_admin_notice( $notice_instance->get_config() );
651
652 // It exits the method to make sure it prints only one notice.
653 return;
654 }
655 }
656
657 /**
658 * @since 2.9.0
659 * @access public
660 */
661 public function __construct() {
662 add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
663 }
664
665 /**
666 * Get module name.
667 *
668 * Retrieve the module name.
669 *
670 * @since 2.9.0
671 * @access public
672 *
673 * @return string Module name.
674 */
675 public function get_name() {
676 return 'admin-notices';
677 }
678 }
679