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

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

530 lines 15.7 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\AdminMenuItems\Floating_Buttons_Empty_View_Menu_Item;
12 use Elementor\Modules\FloatingButtons\AdminMenuItems\Floating_Buttons_Menu_Item;
13 use Elementor\Modules\FloatingButtons\Base\Widget_Contact_Button_Base;
14 use Elementor\Modules\FloatingButtons\Control\Hover_Animation_Floating_Buttons;
15 use Elementor\Modules\FloatingButtons\Documents\Floating_Buttons;
16 use Elementor\Plugin;
17 use Elementor\TemplateLibrary\Source_Local;
18 use Elementor\Utils as ElementorUtils;
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit; // Exit if accessed directly
22 }
23
24 class Module extends BaseModule {
25
26 const EXPERIMENT_NAME = 'floating-buttons';
27
28 const ROUTER_VERSION = '1.0.0';
29 const ROUTER_OPTION_KEY = 'elementor_floating_buttons_router_version';
30
31 const META_CLICK_TRACKING = '_elementor_click_tracking';
32
33 const CLICK_TRACKING_NONCE = 'elementor-conversion-center-click';
34
35 const FLOATING_BUTTONS_DOCUMENT_TYPE = 'floating-buttons';
36 const CPT_FLOATING_BUTTONS = 'e-floating-buttons';
37 const ADMIN_PAGE_SLUG_CONTACT = 'edit.php?post_type=e-floating-buttons';
38
39 private $has_contact_pages = null;
40 private $trashed_contact_pages;
41 private $new_contact_pages_url;
42
43 public static function is_active(): bool {
44 return Plugin::$instance->experiments->is_feature_active( static::EXPERIMENT_NAME );
45 }
46
47 public function get_name(): string {
48 return static::EXPERIMENT_NAME;
49 }
50
51 public function get_widgets(): array {
52 return [
53 'Contact_Buttons',
54 ];
55 }
56
57 public static function get_experimental_data(): array {
58 return [
59 'name' => static::EXPERIMENT_NAME,
60 'title' => esc_html__( 'Floating Buttons', 'elementor' ),
61 'description' => esc_html__( 'Boost visitor engagement with Floating Buttons. The Floating Button template library offers a variety of interactive one-click contact options, highlighted links, and calls to action to increase your website conversions.', 'elementor' ),
62 'default' => Manager::STATE_INACTIVE,
63 'release_status' => Manager::RELEASE_STATUS_BETA,
64 'dependencies' => [
65 'container',
66 ],
67 'new_site' => [
68 'default_active' => true,
69 'minimum_installation_version' => '3.23.0',
70 ],
71 ];
72 }
73
74 private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) {
75 $menu_args = $this->get_contact_menu_args();
76 $function = $menu_args['function'];
77 if ( is_callable( $function ) ) {
78 $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Empty_View_Menu_Item( $function ) );
79 } else {
80 $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Menu_Item() );
81 };
82
83 }
84
85 public function __construct() {
86 parent::__construct();
87
88 if ( $this->is_editing_existing_floating_buttons_page() || $this->is_creating_floating_buttons_page() ) {
89 Controls_Manager::add_tab(
90 Widget_Contact_Button_Base::TAB_ADVANCED,
91 esc_html__( 'Advanced', 'elementor' )
92 );
93 }
94
95 $this->register_contact_pages_cpt();
96
97 if ( ! ElementorUtils::has_pro() ) {
98 add_action( 'elementor/documents/register', function ( Documents_Manager $documents_manager ) {
99 $documents_manager->register_document_type(
100 static::FLOATING_BUTTONS_DOCUMENT_TYPE,
101 Floating_Buttons::get_class_full_name()
102 );
103 } );
104 }
105
106 add_action( 'save_post_' . static::CPT_FLOATING_BUTTONS, [ $this, 'flush_permalinks_on_save' ] );
107
108 add_action( 'wp_ajax_elementor_send_clicks', [ $this, 'handle_click_tracking' ] );
109 add_action( 'wp_ajax_nopriv_elementor_send_clicks', [ $this, 'handle_click_tracking' ] );
110
111 add_action( 'elementor/controls/register', function ( Controls_Manager $controls_manager ) {
112 $controls_manager->register( new Hover_Animation_Floating_Buttons() );
113 });
114
115 if ( ! ElementorUtils::has_pro() ) {
116 add_action( 'wp_footer', function () {
117 $this->render_floating_buttons();
118 } );
119 }
120
121 add_action( 'elementor/common/localize_settings', function ( $settings ) {
122 $settings['floatingButtons'] = [
123 'nonce' => wp_create_nonce( static::CLICK_TRACKING_NONCE ),
124 'ajaxurl' => admin_url( 'admin-ajax.php' ),
125 ];
126
127 return $settings;
128 } );
129
130 add_action( 'elementor/admin-top-bar/is-active', function ( $is_top_bar_active, $current_screen ) {
131
132 if ( strpos( $current_screen->id ?? '', static::CPT_FLOATING_BUTTONS ) !== false ) {
133 return true;
134 }
135
136 return $is_top_bar_active;
137 }, 10, 2 );
138
139 add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) {
140 $this->register_admin_menu_legacy( $admin_menu );
141 }, Source_Local::ADMIN_MENU_PRIORITY + 20 );
142
143 add_action( 'elementor/admin/localize_settings', function ( array $settings ) {
144 return $this->admin_localize_settings( $settings );
145 } );
146
147 add_action( 'elementor/editor/localize_settings', function ( $data ) {
148 return $this->editor_localize_settings( $data );
149 } );
150
151 add_filter( 'elementor/template_library/sources/local/register_taxonomy_cpts', function ( array $cpts ) {
152 $cpts[] = static::CPT_FLOATING_BUTTONS;
153
154 return $cpts;
155 } );
156
157 add_action( 'admin_init', function () {
158 $action = filter_input( INPUT_GET, 'action' );
159 $menu_args = $this->get_contact_menu_args();
160
161 switch ( $action ) {
162 case 'remove_from_entire_site':
163 $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
164 check_admin_referer( 'remove_from_entire_site_' . $post );
165 delete_post_meta( $post, '_elementor_conditions' );
166 wp_redirect( $menu_args['menu_slug'] );
167 exit;
168 case 'set_as_entire_site':
169 $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
170 check_admin_referer( 'set_as_entire_site_' . $post );
171
172 $posts = get_posts( [
173 'post_type' => static::CPT_FLOATING_BUTTONS,
174 'posts_per_page' => -1,
175 'post_status' => 'publish',
176 'fields' => 'ids',
177 'meta_key' => '_elementor_conditions',
178 'meta_compare' => 'EXISTS',
179 ] );
180
181 foreach ( $posts as $post_id ) {
182 delete_post_meta( $post_id, '_elementor_conditions' );
183 }
184
185 update_post_meta( $post, '_elementor_conditions', [ 'include/general' ] );
186
187 wp_redirect( $menu_args['menu_slug'] );
188 exit;
189 default:
190 break;
191 }
192 } );
193
194 add_action( 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_columns', function( $posts_columns ) {
195 $source_local = Plugin::$instance->templates_manager->get_source( 'local' );
196 unset( $posts_columns['date'] );
197 unset( $posts_columns['comments'] );
198 $posts_columns['click_tracking'] = esc_html__( 'Click Tracking', 'elementor' );
199
200 if ( ! ElementorUtils::has_pro() ) {
201 $posts_columns['instances'] = esc_html__( 'Instances', 'elementor' );
202 }
203
204 return $source_local->admin_columns_headers( $posts_columns );
205 } );
206
207 add_action(
208 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_custom_column',
209 [ $this, 'set_admin_columns_content' ],
210 10,
211 2
212 );
213
214 add_action( 'admin_bar_menu', function ( $admin_bar ) {
215
216 $this->override_admin_bar_add_contact( $admin_bar );
217 }, 100 );
218 }
219
220 public function handle_click_tracking() {
221 $data = filter_input_array( INPUT_POST, [
222 'clicks' => [
223 'filter' => FILTER_VALIDATE_INT,
224 'flags' => FILTER_REQUIRE_ARRAY,
225 ],
226 '_nonce' => FILTER_UNSAFE_RAW,
227 ] );
228
229 if ( ! wp_verify_nonce( $data['_nonce'], static::CLICK_TRACKING_NONCE ) ) {
230 wp_send_json_error( [ 'message' => 'Invalid nonce' ] );
231 }
232
233 if ( ! check_ajax_referer( static::CLICK_TRACKING_NONCE, '_nonce', false ) ) {
234 wp_send_json_error( [ 'message' => 'Invalid referrer' ] );
235 }
236
237 $posts_to_update = [];
238
239 foreach ( $data['clicks'] as $post_id ) {
240 if ( ! isset( $posts_to_update[ $post_id ] ) ) {
241 $starting_clicks = (int) get_post_meta( $post_id, static::META_CLICK_TRACKING, true );
242 $posts_to_update[ $post_id ] = $starting_clicks ? $starting_clicks : 0;
243 }
244 $posts_to_update[ $post_id ] ++;
245 }
246
247 foreach ( $posts_to_update as $post_id => $clicks ) {
248 update_post_meta( $post_id, static::META_CLICK_TRACKING, $clicks );
249 }
250
251 wp_send_json_success();
252 }
253
254 public function set_admin_columns_content( $column_name, $post_id ) {
255 $document = Plugin::$instance->documents->get( $post_id );
256
257 if ( method_exists( $document, 'admin_columns_content' ) ) {
258 $document->admin_columns_content( $column_name );
259 }
260
261 switch ( $column_name ) {
262 case 'click_tracking':
263 $click_tracking = get_post_meta( $post_id, static::META_CLICK_TRACKING, true );
264 echo esc_html( $click_tracking );
265 break;
266 case 'instances':
267 if ( ElementorUtils::has_pro() ) {
268 break;
269 }
270 $instances = get_post_meta( $post_id, '_elementor_conditions', true );
271 if ( $instances ) {
272 echo esc_html__( 'Entire Site', 'elementor' );
273 }
274 break;
275 default:
276 break;
277 }
278 }
279
280 public function flush_permalinks_on_save() {
281 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
282 return;
283 }
284
285 if ( get_option( static::ROUTER_OPTION_KEY ) !== static::ROUTER_VERSION ) {
286 flush_rewrite_rules();
287 update_option( static::ROUTER_OPTION_KEY, static::ROUTER_VERSION );
288 }
289 }
290
291 private function get_trashed_contact_posts(): array {
292 if ( $this->trashed_contact_pages ) {
293 return $this->trashed_contact_pages;
294 }
295
296 $this->trashed_contact_pages = $this->get_trashed_posts(
297 static::CPT_FLOATING_BUTTONS,
298 static::FLOATING_BUTTONS_DOCUMENT_TYPE
299 );
300
301 return $this->trashed_contact_pages;
302 }
303
304 private function get_trashed_posts( string $cpt, string $document_type ) {
305 $query = new \WP_Query( [
306 'no_found_rows' => true,
307 'post_type' => $cpt,
308 'post_status' => 'trash',
309 'posts_per_page' => 1,
310 'meta_key' => '_elementor_template_type',
311 'meta_value' => $document_type,
312 ] );
313
314 return $query->posts;
315 }
316
317 private function get_add_new_contact_page_url() {
318 if ( ElementorUtils::has_pro() ) {
319 return Plugin::$instance->documents->get_create_new_post_url(
320 static::CPT_FLOATING_BUTTONS,
321 static::FLOATING_BUTTONS_DOCUMENT_TYPE
322 );
323 }
324
325 return Plugin::$instance->documents->get_create_new_post_url(
326 static::CPT_FLOATING_BUTTONS,
327 static::FLOATING_BUTTONS_DOCUMENT_TYPE
328 ) . '#library';
329 }
330
331 public function print_empty_contact_pages_page() {
332 $template_sources = Plugin::$instance->templates_manager->get_registered_sources();
333 $source_local = $template_sources['local'];
334 $trashed_posts = $this->get_trashed_contact_posts();
335
336 ?>
337 <div class="e-landing-pages-empty">
338 <?php
339 /** @var Source_Local $source_local */
340 $source_local->print_blank_state_template(
341 esc_html__( 'Floating Button', 'elementor' ),
342 $this->get_add_new_contact_page_url(),
343 nl2br( esc_html__( 'Add a Floating button so your users can easily get in touch!', 'elementor' ) )
344 );
345
346 if ( ! empty( $trashed_posts ) ) : ?>
347 <div class="e-trashed-items">
348 <?php
349 printf(
350 /* translators: %1$s Link open tag, %2$s: Link close tag. */
351 esc_html__( 'Or view %1$sTrashed Items%1$s', 'elementor' ),
352 '<a href="' . esc_url( admin_url( 'edit.php?post_status=trash&post_type=' . self::CPT_FLOATING_BUTTONS ) ) . '">',
353 '</a>'
354 );
355 ?>
356 </div>
357 <?php endif; ?>
358 </div>
359 <?php
360 }
361
362 private function admin_localize_settings( $settings ) {
363 $contact_menu_slug = $this->get_contact_menu_args()['menu_slug'];
364
365 if ( static::CPT_FLOATING_BUTTONS === $contact_menu_slug ) {
366 $contact_menu_slug = 'admin.php?page=' . $contact_menu_slug;
367 }
368
369 $additional_settings = [
370 'urls' => [
371 'addNewLinkUrlContact' => $this->get_add_new_contact_page_url(),
372 'viewContactPageUrl' => $contact_menu_slug,
373 ],
374 'contactPages' => [
375 'hasPages' => $this->has_contact_pages(),
376 ],
377 ];
378
379 return array_replace_recursive( $settings, $additional_settings );
380 }
381
382 private function register_contact_pages_cpt() {
383 $this->register_post_type(
384 Floating_Buttons::get_labels(),
385 static::CPT_FLOATING_BUTTONS
386 );
387 }
388
389 private function is_editing_existing_floating_buttons_page() {
390 $action = ElementorUtils::get_super_global_value( $_GET, 'action' );
391 $post_id = ElementorUtils::get_super_global_value( $_GET, 'post' );
392
393 return 'elementor' === $action && $this->is_floating_buttons_type_meta_key( $post_id );
394 }
395
396 private function is_creating_floating_buttons_page() {
397 $action = ElementorUtils::get_super_global_value( $_POST, 'action' ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
398 $post_id = ElementorUtils::get_super_global_value( $_POST, 'editor_post_id' ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
399
400 return 'elementor_ajax' === $action && $this->is_floating_buttons_type_meta_key( $post_id );
401 }
402
403 private function is_floating_buttons_type_meta_key( $post_id ) {
404 return static::FLOATING_BUTTONS_DOCUMENT_TYPE === get_post_meta( $post_id, Document::TYPE_META_KEY, true );
405 }
406
407 private function register_post_type( array $labels, string $cpt ) {
408 $args = [
409 'labels' => $labels,
410 'public' => true,
411 'show_in_menu' => 'edit.php?post_type=elementor_library&tabs_group=library',
412 'capability_type' => 'page',
413 'taxonomies' => [ Source_Local::TAXONOMY_TYPE_SLUG ],
414 'supports' => [
415 'title',
416 'editor',
417 'comments',
418 'revisions',
419 'trackbacks',
420 'author',
421 'excerpt',
422 'page-attributes',
423 'thumbnail',
424 'custom-fields',
425 'post-formats',
426 'elementor',
427 ],
428 ];
429
430 register_post_type( $cpt, $args );
431 }
432
433 private function has_contact_pages(): bool {
434 if ( null !== $this->has_contact_pages ) {
435 return $this->has_contact_pages;
436 }
437
438 $this->has_contact_pages = $this->has_pages(
439 static::CPT_FLOATING_BUTTONS,
440 static::FLOATING_BUTTONS_DOCUMENT_TYPE
441 );
442
443 return $this->has_contact_pages;
444 }
445
446 private function has_pages( string $cpt, string $document_type ): bool {
447 $posts_query = new \WP_Query( [
448 'no_found_rows' => true,
449 'post_type' => $cpt,
450 'post_status' => 'any',
451 'posts_per_page' => 1,
452 'meta_key' => '_elementor_template_type',
453 'meta_value' => $document_type,
454 ] );
455
456 return $posts_query->post_count > 0;
457
458 }
459
460 private function get_contact_menu_args(): array {
461 if ( $this->has_contact_pages() ) {
462 $menu_slug = static::ADMIN_PAGE_SLUG_CONTACT;
463 $function = null;
464 } else {
465 $menu_slug = static::CPT_FLOATING_BUTTONS;
466 $function = [ $this, 'print_empty_contact_pages_page' ];
467 }
468
469 return [
470 'menu_slug' => $menu_slug,
471 'function' => $function,
472 ];
473 }
474
475 public function override_admin_bar_add_contact( $admin_bar ): void {
476 $new_contact_page_node = $admin_bar->get_node( 'new-e-floating-buttons' );
477
478 if ( $new_contact_page_node ) {
479 $new_contact_page_node->href = $this->get_add_new_contact_page_url();
480
481 $admin_bar->add_node( $new_contact_page_node );
482 }
483 }
484
485 private function editor_localize_settings( $data ) {
486 $data['admin_floating_button_admin_url'] = admin_url( $this->get_contact_menu_args()['menu_slug'] );
487 return $data;
488 }
489
490 private function render_floating_buttons(): void {
491 if ( Plugin::$instance->preview->is_preview_mode() ) {
492 $post_id = ElementorUtils::get_super_global_value( $_GET, 'elementor-preview' );
493 $document = Plugin::$instance->documents->get( $post_id );
494
495 if (
496 $document instanceof Document &&
497 $document->get_name() === static::FLOATING_BUTTONS_DOCUMENT_TYPE
498 ) {
499 return;
500 }
501 }
502
503 $query = new \WP_Query( [
504 'post_type' => static::CPT_FLOATING_BUTTONS,
505 'posts_per_page' => - 1,
506 'post_status' => 'publish',
507 'fields' => 'ids',
508 'meta_key' => '_elementor_conditions',
509 'meta_compare' => 'EXISTS',
510 ] );
511
512 if ( ! $query->have_posts() ) {
513 return;
514 }
515
516 foreach ( $query->posts as $post_id ) {
517 $conditions = get_post_meta( $post_id, '_elementor_conditions', true );
518 if ( ! $conditions ) {
519 continue;
520 }
521 if ( in_array( 'include/general', $conditions ) ) {
522 $document = Plugin::$instance->documents->get( $post_id );
523 $document->print_content();
524 break;
525 }
526 }
527 }
528
529 }
530