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

673 lines 19.4 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 'button' => [
239 'text' => esc_html__( 'Sure! I\'d love to help', 'elementor' ),
240 'url' => $optin_url,
241 'type' => 'cta',
242 ],
243 'button_secondary' => [
244 'text' => esc_html__( 'No thanks', 'elementor' ),
245 'url' => $optout_url,
246 'variant' => 'outline',
247 'type' => 'cta',
248 ],
249 ];
250
251 $this->print_admin_notice( $options );
252
253 return true;
254 }
255
256 private function notice_rate_us_feedback() {
257 $notice_id = 'rate_us_feedback';
258
259 if ( ! current_user_can( 'manage_options' ) ) {
260 return false;
261 }
262
263 if ( 'dashboard' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
264 return false;
265 }
266
267 if ( 10 >= $this->get_elementor_pages_count() ) {
268 return false;
269 }
270
271 $dismiss_url = add_query_arg( [
272 'action' => 'elementor_set_admin_notice_viewed',
273 'notice_id' => esc_attr( $notice_id ),
274 ], admin_url( 'admin-post.php' ) );
275
276 $options = [
277 'title' => esc_html__( 'Congrats!', 'elementor' ),
278 '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' ),
279 'id' => $notice_id,
280 'button' => [
281 'text' => esc_html__( 'Happy To Help', 'elementor' ),
282 'url' => 'https://go.elementor.com/admin-review/',
283 'new_tab' => true,
284 'type' => 'cta',
285 ],
286 'button_secondary' => [
287 'text' => esc_html__( 'Hide Notification', 'elementor' ),
288 'classes' => [ 'e-notice-dismiss' ],
289 'url' => esc_url_raw( $dismiss_url ),
290 'new_tab' => true,
291 'type' => 'cta',
292 ],
293 ];
294
295 $this->print_admin_notice( $options );
296
297 return true;
298 }
299
300 private function notice_role_manager_promote() {
301 $notice_id = 'role_manager_promote';
302
303 if ( Utils::has_pro() ) {
304 return false;
305 }
306
307 if ( ! current_user_can( 'manage_options' ) ) {
308 return false;
309 }
310
311 if ( 'elementor_page_elementor-role-manager' !== $this->current_screen_id || User::is_user_notice_viewed( $notice_id ) ) {
312 return false;
313 }
314
315 $users = new \WP_User_Query( [
316 'fields' => 'ID',
317 'number' => 10,
318 ] );
319
320 if ( 5 > $users->get_total() ) {
321 return false;
322 }
323
324 $options = [
325 'title' => esc_html__( 'Managing a multi-user site?', 'elementor' ),
326 'description' => esc_html__( 'With Elementor Pro, you can control user access and make sure no one messes up your design.', 'elementor' ),
327 'id' => $notice_id,
328
329 'button' => [
330 'text' => esc_html__( 'Learn More', 'elementor' ),
331 'url' => 'https://go.elementor.com/plugin-promotion-role-manager/',
332 'new_tab' => true,
333 'type' => 'cta',
334 ],
335 ];
336
337 $options = Filtered_Promotions_Manager::get_filtered_promotion_data( $options, 'core/admin/notice_role_manager_promote', 'button', 'url' );
338
339 $this->print_admin_notice( $options );
340
341 return true;
342 }
343
344 private function notice_experiment_promotion() {
345 $notice_id = 'experiment_promotion';
346
347 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
348 return false;
349 }
350
351 $experiments = Plugin::$instance->experiments;
352 $is_all_performance_features_active = (
353 $experiments->is_feature_active( 'e_element_cache' ) &&
354 $experiments->is_feature_active( 'e_font_icon_svg' )
355 );
356
357 if ( $is_all_performance_features_active ) {
358 return false;
359 }
360
361 $options = [
362 'title' => esc_html__( 'Improve your site’s performance score.', 'elementor' ),
363 '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' ),
364 'id' => $notice_id,
365 'button' => [
366 'text' => esc_html__( 'Try it out', 'elementor' ),
367 'url' => Settings::get_settings_tab_url( 'experiments' ),
368 'type' => 'cta',
369 ],
370 'button_secondary' => [
371 'text' => esc_html__( 'Learn more', 'elementor' ),
372 'url' => 'https://go.elementor.com/wp-dash-experiment-promotion/',
373 'new_tab' => true,
374 'type' => 'cta',
375 ],
376 ];
377
378 $this->print_admin_notice( $options );
379
380 return true;
381 }
382
383 private function site_has_forms_plugins() {
384 return defined( 'WPFORMS_VERSION' ) || defined( 'WPCF7_VERSION' ) || defined( 'FLUENTFORM_VERSION' ) || class_exists( '\GFCommon' ) || class_exists( '\Ninja_Forms' ) || function_exists( 'load_formidable_forms' );
385 }
386
387 private function site_has_woocommerce() {
388 return class_exists( 'WooCommerce' );
389 }
390
391 private function notice_site_mailer_promotion() {
392 $notice_id = 'site_mailer_promotion';
393 $has_forms = $this->site_has_forms_plugins();
394 $has_woocommerce = $this->site_has_woocommerce();
395
396 if ( ! $has_forms && ! $has_woocommerce ) {
397 return false;
398 }
399
400 if ( ! $this->is_elementor_page() && ! in_array( $this->current_screen_id, [ 'toplevel_page_elementor', 'edit-elementor_library', 'dashboard' ], true ) ) {
401 return false;
402 }
403
404 if ( ( Utils::has_pro() && ! $has_woocommerce ) || ! current_user_can( 'install_plugins' ) || User::is_user_notice_viewed( $notice_id ) ) {
405 return false;
406 }
407
408 $plugin_file_path = 'site-mailer/site-mailer.php';
409 $plugin_slug = 'site-mailer';
410
411 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
412 if ( empty( $cta_data ) ) {
413 return false;
414 }
415
416 $options = [
417 'title' => esc_html__( 'Ensure your form emails avoid the spam folder!', 'elementor' ),
418 'description' => esc_html__( 'Use Site Mailer for improved email deliverability, detailed email logs, and an easy setup.', 'elementor' ),
419 'id' => $notice_id,
420 'type' => 'cta',
421 'button' => [
422 'text' => $cta_data['text'],
423 'url' => $cta_data['url'],
424 'type' => 'cta',
425 ],
426 'button_secondary' => [
427 'text' => esc_html__( 'Learn more', 'elementor' ),
428 'url' => 'https://go.elementor.com/sm-core-form/',
429 'new_tab' => true,
430 'type' => 'cta',
431 ],
432 ];
433
434 if ( $this->should_render_woocommerce_hint( $has_forms, $has_woocommerce ) ) {
435 // We include WP's default notice class so it will be properly handled by WP's js handler
436 // And add a new one to distinguish between the two types of notices
437 $options['classes'] = [ 'notice', 'e-notice', 'sm-notice-wc' ];
438 $options['title'] = esc_html__( 'Improve Transactional Email Deliverability', 'elementor' );
439 $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' );
440 }
441
442 $this->print_admin_notice( $options );
443
444 return true;
445 }
446
447 private function should_render_woocommerce_hint( $has_forms, $has_woocommerce ): bool {
448 if ( ! $has_forms && ! $has_woocommerce ) {
449 return false;
450 }
451
452 if ( ! $has_forms && $has_woocommerce ) {
453 return true;
454 }
455
456 if ( $has_forms && $has_woocommerce && Utils::has_pro() ) {
457 return true;
458 }
459
460 return (bool) mt_rand( 0, 1 );
461 }
462
463 private function is_elementor_page(): bool {
464 return 0 === strpos( $this->current_screen_id, 'elementor_page' );
465 }
466
467 private function get_plugin_cta_data( $plugin_slug, $plugin_file_path ) {
468 if ( is_plugin_active( $plugin_file_path ) ) {
469 return false;
470 }
471
472 if ( $this->is_plugin_installed( $plugin_file_path ) ) {
473 $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 );
474 $cta_text = esc_html__( 'Activate Plugin', 'elementor' );
475 } else {
476 $url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $plugin_slug ), 'install-plugin_' . $plugin_slug );
477 $cta_text = esc_html__( 'Install Plugin', 'elementor' );
478 }
479
480 return [
481 'url' => $url,
482 'text' => $cta_text,
483 ];
484 }
485
486 // For testing purposes
487 public function get_elementor_version() {
488 return ELEMENTOR_VERSION;
489 }
490
491 private function notice_plugin_image_optimization() {
492 $notice_id = 'plugin_image_optimization';
493
494 if ( 'upload' !== $this->current_screen_id ) {
495 return false;
496 }
497
498 if ( ! current_user_can( 'manage_options' ) || User::is_user_notice_viewed( $notice_id ) ) {
499 return false;
500 }
501
502 $attachments = new \WP_Query( [
503 'post_type' => 'attachment',
504 'post_status' => 'any',
505 'fields' => 'ids',
506 ] );
507
508 if ( 1 > $attachments->found_posts ) {
509 return false;
510 }
511
512 $plugin_file_path = 'image-optimization/image-optimization.php';
513 $plugin_slug = 'image-optimization';
514
515 $cta_data = $this->get_plugin_cta_data( $plugin_slug, $plugin_file_path );
516
517 if ( empty( $cta_data ) ) {
518 return false;
519 }
520
521 $options = [
522 'title' => esc_html__( 'Speed up your website with Image Optimizer by Elementor', 'elementor' ),
523 'description' => esc_html__( 'Automatically compress and optimize images, resize larger files, or convert to WebP. Optimize images individually, in bulk, or on upload.', 'elementor' ),
524 'id' => $notice_id,
525 'type' => 'cta',
526 'button_secondary' => [
527 'text' => $cta_data['text'],
528 'url' => $cta_data['url'],
529 'type' => 'cta',
530 ],
531 ];
532
533 $this->print_admin_notice( $options );
534
535 return true;
536 }
537
538 private function is_plugin_installed( $file_path ): bool {
539 $installed_plugins = get_plugins();
540
541 return isset( $installed_plugins[ $file_path ] );
542 }
543
544 public function print_admin_notice( array $options, $exclude_pages = self::DEFAULT_EXCLUDED_PAGES ) {
545 global $pagenow;
546
547 if ( in_array( $pagenow, $exclude_pages, true ) ) {
548 return;
549 }
550
551 $default_options = [
552 'id' => null,
553 'title' => '',
554 'description' => '',
555 'classes' => [ 'notice', 'e-notice' ], // We include WP's default notice class so it will be properly handled by WP's js handler
556 'type' => '',
557 'dismissible' => true,
558 'icon' => 'eicon-elementor',
559 'button' => [],
560 'button_secondary' => [],
561 ];
562
563 $options = array_replace_recursive( $default_options, $options );
564
565 $notice_classes = $options['classes'];
566 $dismiss_button = '';
567 $icon = '';
568
569 if ( $options['type'] ) {
570 $notice_classes[] = 'e-notice--' . $options['type'];
571 }
572
573 if ( $options['dismissible'] ) {
574 $label = esc_html__( 'Dismiss this notice.', 'elementor' );
575 $notice_classes[] = 'e-notice--dismissible';
576 $dismiss_button = '<i class="e-notice__dismiss" role="button" aria-label="' . $label . '" tabindex="0"></i>';
577 }
578
579 if ( $options['icon'] ) {
580 $notice_classes[] = 'e-notice--extended';
581 $icon = '<div class="e-notice__icon-wrapper"><i class="' . esc_attr( $options['icon'] ) . '" aria-hidden="true"></i></div>';
582 }
583
584 $wrapper_attributes = [
585 'class' => $notice_classes,
586 ];
587
588 if ( $options['id'] ) {
589 $wrapper_attributes['data-notice_id'] = $options['id'];
590 }
591 ?>
592 <div <?php Utils::print_html_attributes( $wrapper_attributes ); ?>>
593 <?php echo $dismiss_button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
594 <div class="e-notice__aside">
595 <?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
596 </div>
597 <div class="e-notice__content">
598 <?php if ( $options['title'] ) { ?>
599 <h3><?php echo wp_kses_post( $options['title'] ); ?></h3>
600 <?php } ?>
601
602 <?php if ( $options['description'] ) { ?>
603 <p><?php echo wp_kses_post( $options['description'] ); ?></p>
604 <?php } ?>
605
606 <?php if ( ! empty( $options['button']['text'] ) || ! empty( $options['button_secondary']['text'] ) ) { ?>
607 <div class="e-notice__actions">
608 <?php
609 foreach ( [ $options['button'], $options['button_secondary'] ] as $index => $button_settings ) {
610 if ( empty( $button_settings['variant'] ) && $index ) {
611 $button_settings['variant'] = 'outline';
612 }
613
614 if ( empty( $button_settings['text'] ) ) {
615 continue;
616 }
617
618 $button = new Button( $button_settings );
619 $button->print_button();
620 } ?>
621 </div>
622 <?php } ?>
623 </div>
624 </div>
625 <?php }
626
627 public function admin_notices() {
628 $this->install_time = Plugin::$instance->get_install_time();
629 $this->current_screen_id = get_current_screen()->id;
630
631 foreach ( $this->plain_notices as $notice ) {
632 $method_callback = "notice_{$notice}";
633 if ( $this->$method_callback() ) {
634 return;
635 }
636 }
637
638 /** @var Base_Notice $notice_instance */
639 foreach ( $this->get_notices() as $notice_instance ) {
640 if ( ! $notice_instance->should_print() ) {
641 continue;
642 }
643
644 $this->print_admin_notice( $notice_instance->get_config() );
645
646 // It exits the method to make sure it prints only one notice.
647 return;
648 }
649 }
650
651 /**
652 * @since 2.9.0
653 * @access public
654 */
655 public function __construct() {
656 add_action( 'admin_notices', [ $this, 'admin_notices' ], 20 );
657 }
658
659 /**
660 * Get module name.
661 *
662 * Retrieve the module name.
663 *
664 * @since 2.9.0
665 * @access public
666 *
667 * @return string Module name.
668 */
669 public function get_name() {
670 return 'admin-notices';
671 }
672 }
673