PluginProbe
Elementor Website Builder – more than just a page builder / 3.25.8
Elementor Website Builder – more than just a page builder v3.25.8
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 / modules / floating-buttons / module.php

module.php in Elementor Website Builder – more than just a page builder 3.25.8, at modules/floating-buttons/module.php

608 lines 18.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\FloatingButtons;
4
5 use Elementor\Controls_Manager;
6 use Elementor\Core\Admin\Menu\Admin_Menu_Manager;
7 use Elementor\Core\Base\Document;
8 use Elementor\Core\Base\Module as BaseModule;
9 use Elementor\Core\Documents_Manager;
10 use Elementor\Core\Experiments\Manager;
11 use Elementor\Modules\FloatingButtons\Base\Widget_Floating_Bars_Base;
12 use Elementor\Modules\FloatingButtons\AdminMenuItems\Floating_Buttons_Empty_View_Menu_Item;
13 use Elementor\Modules\FloatingButtons\AdminMenuItems\Floating_Buttons_Menu_Item;
14 use Elementor\Modules\FloatingButtons\Base\Widget_Contact_Button_Base;
15 use Elementor\Modules\FloatingButtons\Control\Hover_Animation_Floating_Buttons;
16 use Elementor\Modules\FloatingButtons\Documents\Floating_Buttons;
17 use Elementor\Plugin;
18 use Elementor\TemplateLibrary\Source_Local;
19 use Elementor\Utils as ElementorUtils;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit; // Exit if accessed directly
23 }
24
25 class Module extends BaseModule {
26
27 const EXPERIMENT_NAME = 'floating-buttons';
28 const FLOATING_ELEMENTS_TYPE_META_KEY = '_elementor_floating_elements_type';
29 const ROUTER_OPTION_KEY = 'elementor_floating_buttons_router_version';
30 const META_CLICK_TRACKING = '_elementor_click_tracking';
31 const CLICK_TRACKING_NONCE = 'elementor-conversion-center-click';
32 const FLOATING_BUTTONS_DOCUMENT_TYPE = 'floating-buttons';
33 const CPT_FLOATING_BUTTONS = 'e-floating-buttons';
34 const ADMIN_PAGE_SLUG_CONTACT = 'edit.php?post_type=e-floating-buttons';
35 const WIDGET_HAS_CUSTOM_BREAKPOINTS = true;
36
37 private $has_contact_pages = null;
38 private $trashed_contact_pages;
39
40 public static function is_active(): bool {
41 return Plugin::$instance->experiments->is_feature_active( 'container' );
42 }
43
44 public static function get_floating_elements_types() {
45 return [
46 'floating-buttons' => esc_html__( 'Floating Buttons', 'elementor' ),
47 'floating-bars' => esc_html__( 'Floating Bars', 'elementor' ),
48 ];
49 }
50
51 // TODO: This is a hidden experiment which needs to remain enabled like this until 3.26 for pro compatibility.
52 public static function get_experimental_data() {
53 return [
54 'name' => self::EXPERIMENT_NAME,
55 'title' => esc_html__( 'Floating Buttons', 'elementor' ),
56 'hidden' => true,
57 'default' => Manager::STATE_ACTIVE,
58 'release_status' => Manager::RELEASE_STATUS_STABLE,
59 'mutable' => false,
60 ];
61 }
62
63 public function get_name(): string {
64 return static::EXPERIMENT_NAME;
65 }
66
67 public function get_widgets(): array {
68
69 return [
70 'Contact_Buttons',
71 'Floating_Bars_Var_1',
72 ];
73 }
74
75 private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) {
76 $menu_args = $this->get_contact_menu_args();
77 $function = $menu_args['function'];
78 if ( is_callable( $function ) ) {
79 $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Empty_View_Menu_Item( $function ) );
80 } else {
81 $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Menu_Item() );
82 };
83
84 }
85
86 public function __construct() {
87 parent::__construct();
88
89 if ( Floating_Buttons::is_creating_floating_buttons_page() || Floating_Buttons::is_editing_existing_floating_buttons_page() ) {
90 Controls_Manager::add_tab(
91 Widget_Contact_Button_Base::TAB_ADVANCED,
92 esc_html__( 'Advanced', 'elementor' )
93 );
94
95 Controls_Manager::add_tab(
96 Widget_Floating_Bars_Base::TAB_ADVANCED,
97 esc_html__( 'Advanced', 'elementor' )
98 );
99 }
100
101 $this->register_contact_pages_cpt();
102
103 if ( ! ElementorUtils::has_pro() ) {
104 add_action( 'elementor/documents/register', function ( Documents_Manager $documents_manager ) {
105 $documents_manager->register_document_type(
106 static::FLOATING_BUTTONS_DOCUMENT_TYPE,
107 Floating_Buttons::get_class_full_name()
108 );
109 } );
110 }
111
112 add_action( 'current_screen', function() {
113 $screen = get_current_screen();
114 if ( $screen && 'edit-e-floating-buttons' === $screen->id ) {
115 $this->flush_permalinks_on_elementor_version_change();
116 }
117 });
118
119 add_action( 'wp_ajax_elementor_send_clicks', [ $this, 'handle_click_tracking' ] );
120 add_action( 'wp_ajax_nopriv_elementor_send_clicks', [ $this, 'handle_click_tracking' ] );
121
122 add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] );
123
124 add_action( 'elementor/controls/register', function ( Controls_Manager $controls_manager ) {
125 $controls_manager->register( new Hover_Animation_Floating_Buttons() );
126 });
127
128 add_filter( 'elementor/widget/common/register_css_attributes_control', function ( $common_controls ) {
129 if ( Floating_Buttons::is_creating_floating_buttons_page() || Floating_Buttons::is_editing_existing_floating_buttons_page() ) {
130 return false;
131 }
132
133 return $common_controls;
134 } );
135
136 add_filter( 'elementor/settings/controls/checkbox_list_cpt/post_type_objects', function ( $post_types ) {
137 unset( $post_types[ static::CPT_FLOATING_BUTTONS ] );
138
139 return $post_types;
140 } );
141
142 add_filter(
143 'elementor/template_library/sources/local/is_valid_template_type',
144 function ( $is_valid_template_type, $cpt ) {
145
146 if ( in_array( static::CPT_FLOATING_BUTTONS, $cpt, true ) ) {
147 return true;
148 }
149
150 return $is_valid_template_type;
151 },
152 10,
153 2
154 );
155
156 if ( ! ElementorUtils::has_pro() ) {
157 add_action( 'wp_footer', function () {
158 $this->render_floating_buttons();
159 } );
160 }
161
162 add_action( 'elementor/admin-top-bar/is-active', function ( $is_top_bar_active, $current_screen ) {
163
164 if ( strpos( $current_screen->id ?? '', static::CPT_FLOATING_BUTTONS ) !== false ) {
165 return true;
166 }
167
168 return $is_top_bar_active;
169 }, 10, 2 );
170
171 add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) {
172 $this->register_admin_menu_legacy( $admin_menu );
173 }, Source_Local::ADMIN_MENU_PRIORITY + 20 );
174
175 add_action( 'elementor/admin/localize_settings', function ( array $settings ) {
176 return $this->admin_localize_settings( $settings );
177 } );
178
179 add_action( 'elementor/editor/localize_settings', function ( $data ) {
180 return $this->editor_localize_settings( $data );
181 } );
182
183 add_filter( 'elementor/template_library/sources/local/register_taxonomy_cpts', function ( array $cpts ) {
184 $cpts[] = static::CPT_FLOATING_BUTTONS;
185
186 return $cpts;
187 } );
188
189 add_action( 'admin_init', function () {
190 $action = filter_input( INPUT_GET, 'action' );
191 $menu_args = $this->get_contact_menu_args();
192
193 switch ( $action ) {
194 case 'remove_from_entire_site':
195 $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
196 check_admin_referer( 'remove_from_entire_site_' . $post );
197 delete_post_meta( $post, '_elementor_conditions' );
198
199 wp_redirect( $menu_args['menu_slug'] );
200 exit;
201 case 'set_as_entire_site':
202 $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
203 check_admin_referer( 'set_as_entire_site_' . $post );
204
205 $posts = get_posts( [
206 'post_type' => static::CPT_FLOATING_BUTTONS,
207 'posts_per_page' => -1,
208 'post_status' => 'publish',
209 'fields' => 'ids',
210 'no_found_rows' => true,
211 'update_post_term_cache' => false,
212 'update_post_meta_cache' => false,
213 'meta_query' => Floating_Buttons::get_meta_query_for_floating_buttons(
214 Floating_Buttons::get_floating_element_type( $post )
215 ),
216 ] );
217
218 foreach ( $posts as $post_id ) {
219 delete_post_meta( $post_id, '_elementor_conditions' );
220 }
221
222 update_post_meta( $post, '_elementor_conditions', [ 'include/general' ] );
223
224 wp_redirect( $menu_args['menu_slug'] );
225 exit;
226 default:
227 break;
228 }
229 } );
230
231 add_action( 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_columns', function( $posts_columns ) {
232 $source_local = Plugin::$instance->templates_manager->get_source( 'local' );
233 unset( $posts_columns['date'] );
234 unset( $posts_columns['comments'] );
235 $posts_columns['click_tracking'] = esc_html__( 'Click Tracking', 'elementor' );
236
237 if ( ! ElementorUtils::has_pro() ) {
238 $posts_columns['instances'] = esc_html__( 'Instances', 'elementor' );
239 }
240
241 return $source_local->admin_columns_headers( $posts_columns );
242 } );
243
244 add_action(
245 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_custom_column',
246 [ $this, 'set_admin_columns_content' ],
247 10,
248 2
249 );
250
251 add_action( 'admin_bar_menu', function ( $admin_bar ) {
252
253 $this->override_admin_bar_add_contact( $admin_bar );
254 }, 100 );
255 }
256
257 public function is_preview_for_document( $post_id ) {
258 $preview_id = ElementorUtils::get_super_global_value( $_GET, 'preview_id' );
259 $preview = ElementorUtils::get_super_global_value( $_GET, 'preview' );
260
261 return 'true' === $preview && (int) $post_id === (int) $preview_id;
262 }
263
264 public function handle_click_tracking() {
265 $data = filter_input_array( INPUT_POST, [
266 'clicks' => [
267 'filter' => FILTER_VALIDATE_INT,
268 'flags' => FILTER_REQUIRE_ARRAY,
269 ],
270 '_nonce' => FILTER_UNSAFE_RAW,
271 ] );
272
273 if ( ! wp_verify_nonce( $data['_nonce'], static::CLICK_TRACKING_NONCE ) ) {
274 wp_send_json_error( [ 'message' => 'Invalid nonce' ] );
275 }
276
277 if ( ! check_ajax_referer( static::CLICK_TRACKING_NONCE, '_nonce', false ) ) {
278 wp_send_json_error( [ 'message' => 'Invalid referrer' ] );
279 }
280
281 $posts_to_update = [];
282
283 foreach ( $data['clicks'] as $post_id ) {
284 if ( ! isset( $posts_to_update[ $post_id ] ) ) {
285 $starting_clicks = (int) get_post_meta( $post_id, static::META_CLICK_TRACKING, true );
286 $posts_to_update[ $post_id ] = $starting_clicks ? $starting_clicks : 0;
287 }
288 $posts_to_update[ $post_id ] ++;
289 }
290
291 foreach ( $posts_to_update as $post_id => $clicks ) {
292 if ( self::CPT_FLOATING_BUTTONS !== get_post_type( $post_id ) ) {
293 continue;
294 }
295
296 if ( 'publish' !== get_post_status( $post_id ) ) {
297 continue;
298 }
299
300 update_post_meta( $post_id, static::META_CLICK_TRACKING, $clicks );
301 }
302
303 wp_send_json_success();
304 }
305
306 public function set_admin_columns_content( $column_name, $post_id ) {
307 $document = Plugin::$instance->documents->get( $post_id );
308
309 if ( method_exists( $document, 'admin_columns_content' ) ) {
310 $document->admin_columns_content( $column_name );
311 }
312
313 switch ( $column_name ) {
314 case 'click_tracking':
315 $click_tracking = get_post_meta( $post_id, static::META_CLICK_TRACKING, true );
316 echo esc_html( $click_tracking );
317 break;
318 case 'instances':
319 if ( ElementorUtils::has_pro() ) {
320 break;
321 }
322 $instances = get_post_meta( $post_id, '_elementor_conditions', true );
323 if ( $instances ) {
324 echo esc_html__( 'Entire Site', 'elementor' );
325 }
326 break;
327 default:
328 break;
329 }
330 }
331
332 public function flush_permalinks_on_elementor_version_change() {
333 if ( get_option( static::ROUTER_OPTION_KEY ) !== ELEMENTOR_VERSION ) {
334 flush_rewrite_rules();
335 update_option( static::ROUTER_OPTION_KEY, ELEMENTOR_VERSION );
336 }
337 }
338
339 private function get_trashed_contact_posts(): array {
340 if ( $this->trashed_contact_pages ) {
341 return $this->trashed_contact_pages;
342 }
343
344 $this->trashed_contact_pages = $this->get_trashed_posts(
345 static::CPT_FLOATING_BUTTONS,
346 static::FLOATING_BUTTONS_DOCUMENT_TYPE
347 );
348
349 return $this->trashed_contact_pages;
350 }
351
352 private function get_trashed_posts( string $cpt, string $document_type ) {
353 $query = new \WP_Query( [
354 'no_found_rows' => true,
355 'post_type' => $cpt,
356 'post_status' => 'trash',
357 'posts_per_page' => 1,
358 'meta_key' => '_elementor_template_type',
359 'meta_value' => $document_type,
360 ] );
361
362 return $query->posts;
363 }
364
365 private function get_add_new_contact_page_url() {
366 if ( ElementorUtils::has_pro() ) {
367 return Plugin::$instance->documents->get_create_new_post_url(
368 static::CPT_FLOATING_BUTTONS,
369 static::FLOATING_BUTTONS_DOCUMENT_TYPE
370 );
371 }
372
373 return Plugin::$instance->documents->get_create_new_post_url(
374 static::CPT_FLOATING_BUTTONS,
375 static::FLOATING_BUTTONS_DOCUMENT_TYPE
376 ) . '#library';
377 }
378
379 public function print_empty_contact_pages_page() {
380 $template_sources = Plugin::$instance->templates_manager->get_registered_sources();
381 $source_local = $template_sources['local'];
382 $trashed_posts = $this->get_trashed_contact_posts();
383
384 ?>
385 <div class="e-landing-pages-empty">
386 <?php
387 /** @var Source_Local $source_local */
388 $source_local->print_blank_state_template(
389 esc_html__( 'Floating Element', 'elementor' ),
390 $this->get_add_new_contact_page_url(),
391 nl2br( esc_html__( 'Add a Floating element so your users can easily get in touch!', 'elementor' ) )
392 );
393
394 if ( ! empty( $trashed_posts ) ) : ?>
395 <div class="e-trashed-items">
396 <?php
397 printf(
398 /* translators: %1$s Link open tag, %2$s: Link close tag. */
399 esc_html__( 'Or view %1$sTrashed Items%1$s', 'elementor' ),
400 '<a href="' . esc_url( admin_url( 'edit.php?post_status=trash&post_type=' . self::CPT_FLOATING_BUTTONS ) ) . '">',
401 '</a>'
402 );
403 ?>
404 </div>
405 <?php endif; ?>
406 </div>
407 <?php
408 }
409
410 private function admin_localize_settings( $settings ) {
411 $contact_menu_slug = $this->get_contact_menu_args()['menu_slug'];
412
413 if ( static::CPT_FLOATING_BUTTONS === $contact_menu_slug ) {
414 $contact_menu_slug = 'admin.php?page=' . $contact_menu_slug;
415 }
416
417 $additional_settings = [
418 'urls' => [
419 'addNewLinkUrlContact' => $this->get_add_new_contact_page_url(),
420 'viewContactPageUrl' => $contact_menu_slug,
421 ],
422 'contactPages' => [
423 'hasPages' => $this->has_contact_pages(),
424 ],
425 ];
426
427 return array_replace_recursive( $settings, $additional_settings );
428 }
429
430 private function register_contact_pages_cpt() {
431 $this->register_post_type(
432 Floating_Buttons::get_labels(),
433 static::CPT_FLOATING_BUTTONS
434 );
435 }
436
437 private function register_post_type( array $labels, string $cpt ) {
438 $args = [
439 'labels' => $labels,
440 'public' => true,
441 'show_in_menu' => 'edit.php?post_type=elementor_library&tabs_group=library',
442 'show_in_nav_menus' => false,
443 'capability_type' => 'page',
444 'taxonomies' => [ Source_Local::TAXONOMY_TYPE_SLUG ],
445 'supports' => [
446 'title',
447 'editor',
448 'comments',
449 'revisions',
450 'trackbacks',
451 'author',
452 'excerpt',
453 'page-attributes',
454 'thumbnail',
455 'custom-fields',
456 'post-formats',
457 'elementor',
458 ],
459 ];
460
461 register_post_type( $cpt, $args );
462 }
463
464 private function has_contact_pages(): bool {
465 if ( null !== $this->has_contact_pages ) {
466 return $this->has_contact_pages;
467 }
468
469 $this->has_contact_pages = $this->has_pages(
470 static::CPT_FLOATING_BUTTONS,
471 static::FLOATING_BUTTONS_DOCUMENT_TYPE
472 );
473
474 return $this->has_contact_pages;
475 }
476
477 private function has_pages( string $cpt, string $document_type ): bool {
478 $posts_query = new \WP_Query( [
479 'no_found_rows' => true,
480 'post_type' => $cpt,
481 'post_status' => 'any',
482 'posts_per_page' => 1,
483 'meta_key' => '_elementor_template_type',
484 'meta_value' => $document_type,
485 ] );
486
487 return $posts_query->post_count > 0;
488
489 }
490
491 private function get_contact_menu_args(): array {
492 if ( $this->has_contact_pages() ) {
493 $menu_slug = static::ADMIN_PAGE_SLUG_CONTACT;
494 $function = null;
495 } else {
496 $menu_slug = static::CPT_FLOATING_BUTTONS;
497 $function = [ $this, 'print_empty_contact_pages_page' ];
498 }
499
500 return [
501 'menu_slug' => $menu_slug,
502 'function' => $function,
503 ];
504 }
505
506 public function override_admin_bar_add_contact( $admin_bar ): void {
507 $new_contact_page_node = $admin_bar->get_node( 'new-e-floating-buttons' );
508
509 if ( $new_contact_page_node ) {
510 $new_contact_page_node->href = $this->get_add_new_contact_page_url();
511
512 $admin_bar->add_node( $new_contact_page_node );
513 }
514 }
515
516 private function editor_localize_settings( $data ) {
517 $data['admin_floating_button_admin_url'] = admin_url( $this->get_contact_menu_args()['menu_slug'] );
518 return $data;
519 }
520
521 private function render_floating_buttons(): void {
522 if ( Plugin::$instance->preview->is_preview_mode() ) {
523 $post_id = ElementorUtils::get_super_global_value( $_GET, 'elementor-preview' );
524 $document = Plugin::$instance->documents->get( $post_id );
525
526 if (
527 $document instanceof Document &&
528 $document->get_name() === static::FLOATING_BUTTONS_DOCUMENT_TYPE
529 ) {
530 return;
531 }
532 }
533
534 $query = new \WP_Query( [
535 'post_type' => static::CPT_FLOATING_BUTTONS,
536 'posts_per_page' => - 1,
537 'post_status' => 'publish',
538 'fields' => 'ids',
539 'meta_key' => '_elementor_conditions',
540 'meta_compare' => 'EXISTS',
541 ] );
542
543 if ( ! $query->have_posts() ) {
544 return;
545 }
546
547 foreach ( $query->posts as $post_id ) {
548 $conditions = get_post_meta( $post_id, '_elementor_conditions', true );
549
550 if ( ! $conditions ) {
551 continue;
552 }
553
554 if (
555 in_array( 'include/general', $conditions ) &&
556 ! $this->is_preview_for_document( $post_id ) &&
557 get_the_ID() !== $post_id
558 ) {
559 $document = Plugin::$instance->documents->get( $post_id );
560 $document->print_content();
561 }
562 }
563 }
564
565 /**
566 * Register styles.
567 *
568 * At build time, Elementor compiles `/modules/floating-buttons/assets/scss/widgets/*.scss`
569 * to `/assets/css/widget-*.min.css`.
570 *
571 * @return void
572 */
573 public function register_styles() {
574 $direction_suffix = is_rtl() ? '-rtl' : '';
575 $widget_styles = $this->get_widgets_style_list();
576 $has_custom_breakpoints = Plugin::$instance->breakpoints->has_custom_breakpoints();
577
578 foreach ( $widget_styles as $widget_style_name => $widget_has_responsive_style ) {
579 $should_load_responsive_css = $widget_has_responsive_style ? $has_custom_breakpoints : false;
580
581 wp_register_style(
582 $widget_style_name,
583 $this->get_frontend_file_url( "{$widget_style_name}{$direction_suffix}.min.css", $should_load_responsive_css ),
584 [ 'elementor-frontend', 'elementor-icons' ],
585 $should_load_responsive_css ? null : ELEMENTOR_VERSION
586 );
587 }
588 }
589
590 private function get_widgets_style_list():array {
591 return [
592 'widget-floating-buttons' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS, // TODO: Remove in v3.27.0 [ED-15717]
593 'widget-floating-bars-base' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
594 'widget-floating-bars-var-2' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
595 'widget-floating-bars-var-3' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
596 'widget-contact-buttons-base' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
597 'widget-contact-buttons-var-1' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
598 'widget-contact-buttons-var-3' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
599 'widget-contact-buttons-var-4' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
600 'widget-contact-buttons-var-6' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
601 'widget-contact-buttons-var-7' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
602 'widget-contact-buttons-var-8' => ! self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
603 'widget-contact-buttons-var-9' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
604 'widget-contact-buttons-var-10' => self::WIDGET_HAS_CUSTOM_BREAKPOINTS,
605 ];
606 }
607 }
608