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 / includes / tracker.php

tracker.php in Elementor Website Builder – more than just a page builder 3.28.0-dev3, at includes/tracker.php

650 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 use Elementor\Core\Common\Modules\EventTracker\DB as Events_DB_Manager;
5 use Elementor\Core\Experiments\Experiments_Reporter;
6 use Elementor\Modules\System_Info\Module as System_Info_Module;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 /**
13 * Elementor tracker.
14 *
15 * Elementor tracker handler class is responsible for sending non-sensitive plugin
16 * data to Elementor servers for users that actively allowed data tracking.
17 *
18 * @since 1.0.0
19 */
20 class Tracker {
21
22 /**
23 * API URL.
24 *
25 * Holds the URL of the Tracker API.
26 *
27 * @since 1.0.0
28 * @access private
29 *
30 * @var string API URL.
31 */
32 private static $api_url = 'https://my.elementor.com/api/v1/tracker/';
33
34 private static $notice_shown = false;
35
36 /**
37 * Init.
38 *
39 * Initialize Elementor tracker.
40 *
41 * @since 1.0.0
42 * @access public
43 * @static
44 */
45 public static function init() {
46 add_action( 'elementor/tracker/send_event', [ __CLASS__, 'send_tracking_data' ] );
47 add_action( 'admin_init', [ __CLASS__, 'handle_tracker_actions' ] );
48 }
49
50 /**
51 * Check for settings opt-in.
52 *
53 * Checks whether the site admin has opted-in for data tracking, or not.
54 *
55 * @since 1.0.0
56 * @access public
57 * @static
58 *
59 * @param string $new_value Allowed tracking value.
60 *
61 * @return string Return `yes` if tracking allowed, `no` otherwise.
62 */
63 public static function check_for_settings_optin( $new_value ) {
64 $old_value = get_option( 'elementor_allow_tracking', 'no' );
65 if ( $old_value !== $new_value && 'yes' === $new_value ) {
66 Plugin::$instance->custom_tasks->add_tasks_requested_to_run( [
67 'opt_in_recalculate_usage',
68 'opt_in_send_tracking_data',
69 ] );
70 }
71
72 if ( empty( $new_value ) ) {
73 $new_value = 'no';
74 }
75
76 return $new_value;
77 }
78
79 /**
80 * Send tracking data.
81 *
82 * Decide whether to send tracking data, or not.
83 *
84 * @since 1.0.0
85 * @access public
86 * @static
87 *
88 * @param bool $override
89 */
90 public static function send_tracking_data( $override = false ) {
91 // Don't trigger this on AJAX Requests.
92 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
93 return;
94 }
95
96 if ( ! self::is_allow_track() ) {
97 return;
98 }
99
100 $last_send = self::get_last_send_time();
101
102 /**
103 * Tracker override send.
104 *
105 * Filters whether to override sending tracking data or not.
106 *
107 * @since 1.0.0
108 *
109 * @param bool $override Whether to override default setting or not.
110 */
111 $override = apply_filters( 'elementor/tracker/send_override', $override );
112
113 if ( ! $override ) {
114 $last_send_interval = strtotime( '-1 week' );
115
116 /**
117 * Tracker last send interval.
118 *
119 * Filters the interval of between two tracking requests.
120 *
121 * @since 1.0.0
122 *
123 * @param int $last_send_interval A date/time string. Default is `strtotime( '-1 week' )`.
124 */
125 $last_send_interval = apply_filters( 'elementor/tracker/last_send_interval', $last_send_interval );
126
127 // Send a maximum of once per week by default.
128 if ( $last_send && $last_send > $last_send_interval ) {
129 return;
130 }
131 } elseif ( $last_send && $last_send > strtotime( '-1 hours' ) ) {
132 return;
133 }
134
135 // Update time first before sending to ensure it is set.
136 update_option( 'elementor_tracker_last_send', time() );
137
138 $params = self::get_tracking_data( empty( $last_send ) );
139
140 // Tracking data is used for System Info reports, and events should not be included in System Info reports,
141 // so it is added here.
142 $params['analytics_events'] = self::get_events();
143
144 add_filter( 'https_ssl_verify', '__return_false' );
145
146 wp_safe_remote_post(
147 self::$api_url,
148 [
149 'timeout' => 25,
150 'blocking' => false,
151 'body' => [
152 'data' => wp_json_encode( $params ),
153 ],
154 ]
155 );
156
157 // After sending the event tracking data, we reset the events table.
158 Events_DB_Manager::reset_table();
159 }
160
161 /**
162 * Is allow track.
163 *
164 * Checks whether the site admin has opted-in for data tracking, or not.
165 *
166 * @since 1.0.0
167 * @access public
168 * @static
169 */
170 public static function is_allow_track() {
171 return 'yes' === get_option( 'elementor_allow_tracking', 'no' );
172 }
173
174 /**
175 * Handle tracker actions.
176 *
177 * Check if the user opted-in or opted-out and update the database.
178 *
179 * Fired by `admin_init` action.
180 *
181 * @since 1.0.0
182 * @access public
183 * @static
184 */
185 public static function handle_tracker_actions() {
186 if ( ! isset( $_GET['elementor_tracker'] ) ) {
187 return;
188 }
189
190 if ( 'opt_into' === $_GET['elementor_tracker'] ) {
191 check_admin_referer( 'opt_into' );
192
193 self::set_opt_in( true );
194 }
195
196 if ( 'opt_out' === $_GET['elementor_tracker'] ) {
197 check_admin_referer( 'opt_out' );
198
199 self::set_opt_in( false );
200 }
201
202 wp_safe_redirect( remove_query_arg( 'elementor_tracker' ) );
203 exit;
204 }
205
206 /**
207 * @since 2.2.0
208 * @access public
209 * @static
210 */
211 public static function is_notice_shown() {
212 return self::$notice_shown;
213 }
214
215 public static function set_opt_in( $value ) {
216 if ( $value ) {
217 update_option( 'elementor_allow_tracking', 'yes' );
218 self::send_tracking_data( true );
219 } else {
220 update_option( 'elementor_allow_tracking', 'no' );
221 update_option( 'elementor_tracker_notice', '1' );
222 }
223 }
224
225 /**
226 * Get system reports data.
227 *
228 * Retrieve the data from system reports.
229 *
230 * @since 2.0.0
231 * @access private
232 * @static
233 *
234 * @return array The data from system reports.
235 */
236 private static function get_system_reports_data() {
237 $reports = Plugin::$instance->system_info->load_reports( System_Info_Module::get_allowed_reports() );
238
239 // The log report should not be sent with the usage data - it is not used and causes bloat.
240 if ( isset( $reports['log'] ) ) {
241 unset( $reports['log'] );
242 }
243
244 $system_reports = [];
245 foreach ( $reports as $report_key => $report_details ) {
246 $system_reports[ $report_key ] = [];
247 foreach ( $report_details['report']->get_report() as $sub_report_key => $sub_report_details ) {
248 $system_reports[ $report_key ][ $sub_report_key ] = $sub_report_details['value'];
249 }
250 }
251 return $system_reports;
252 }
253
254 /**
255 * Get last send time.
256 *
257 * Retrieve the last time tracking data was sent.
258 *
259 * @since 2.0.0
260 * @access private
261 * @static
262 *
263 * @return int|false The last time tracking data was sent, or false if
264 * tracking data never sent.
265 */
266 private static function get_last_send_time() {
267 $last_send_time = get_option( 'elementor_tracker_last_send', false );
268
269 /**
270 * Tracker last send time.
271 *
272 * Filters the last time tracking data was sent.
273 *
274 * @since 1.0.0
275 *
276 * @param int|false $last_send_time The last time tracking data was sent,
277 * or false if tracking data never sent.
278 */
279 $last_send_time = apply_filters( 'elementor/tracker/last_send_time', $last_send_time );
280
281 return $last_send_time;
282 }
283
284 /**
285 * Get non elementor post usages.
286 *
287 * Retrieve the number of posts that not using elementor.
288
289 * @return array The number of posts using not used by Elementor grouped by post types
290 * and post status.
291 */
292 public static function get_non_elementor_posts_usage() {
293 global $wpdb;
294
295 $usage = [];
296
297 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
298 $results = $wpdb->get_results(
299 "SELECT `post_type`, `post_status`, COUNT(`ID`) `hits`
300 FROM {$wpdb->posts} `p`
301 LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id` AND `meta_key` = '_elementor_edit_mode' )
302 WHERE `post_type` != 'elementor_library' AND `meta_value` IS NULL
303 GROUP BY `post_type`, `post_status`;"
304 );
305
306 if ( $results ) {
307 foreach ( $results as $result ) {
308 $usage[ $result->post_type ][ $result->post_status ] = $result->hits;
309 }
310 }
311
312 return $usage;
313 }
314
315 /**
316 * Get posts usage.
317 *
318 * Retrieve the number of posts using Elementor.
319 *
320 * @since 2.0.0
321 * @access public
322 * @static
323 *
324 * @return array The number of posts using Elementor grouped by post types
325 * and post status.
326 */
327 public static function get_posts_usage() {
328 global $wpdb;
329
330 $usage = [];
331
332 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
333 $results = $wpdb->get_results(
334 "SELECT `post_type`, `post_status`, COUNT(`ID`) `hits`
335 FROM {$wpdb->posts} `p`
336 LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id`)
337 WHERE `post_type` != 'elementor_library'
338 AND `meta_key` = '_elementor_edit_mode' AND `meta_value` = 'builder'
339 GROUP BY `post_type`, `post_status`;"
340 );
341
342 if ( $results ) {
343 foreach ( $results as $result ) {
344 $usage[ $result->post_type ][ $result->post_status ] = (int) $result->hits;
345 }
346 }
347
348 return $usage;
349 }
350
351 /**
352 * Get library usage.
353 *
354 * Retrieve the number of Elementor library items saved.
355 *
356 * @since 2.0.0
357 * @access public
358 * @static
359 *
360 * @return array The number of Elementor library items grouped by post types
361 * and meta value.
362 */
363 public static function get_library_usage() {
364 global $wpdb;
365
366 $usage = [];
367
368 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
369 $results = $wpdb->get_results(
370 "SELECT `meta_value`, COUNT(`ID`) `hits`
371 FROM {$wpdb->posts} `p`
372 LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id`)
373 WHERE `post_type` = 'elementor_library'
374 AND `meta_key` = '_elementor_template_type'
375 GROUP BY `post_type`, `meta_value`;"
376 );
377
378 if ( $results ) {
379 foreach ( $results as $result ) {
380 $usage[ $result->meta_value ] = $result->hits;
381 }
382 }
383
384 return $usage;
385 }
386
387 /**
388 * Get usage of general settings.
389 * 'Elementor->Settings->General'.
390 *
391 * @return array
392 */
393 public static function get_settings_general_usage() {
394 return self::get_tracking_data_from_settings( 'general' );
395 }
396
397 /**
398 * Get usage of advanced settings.
399 * 'Elementor->Settings->Advanced'.
400 *
401 * @return array
402 */
403 public static function get_settings_advanced_usage() {
404 return self::get_tracking_data_from_settings( 'advanced' );
405 }
406
407 /**
408 * Get usage of performance settings.
409 * 'Elementor->Settings->Performance'.
410 *
411 * @return array
412 */
413 public static function get_settings_performance_usage() {
414 return self::get_tracking_data_from_settings( 'performance' );
415 }
416
417 /**
418 * Get usage of experiments settings.
419 *
420 * 'Elementor->Settings->Experiments'.
421 *
422 * @return array
423 */
424 public static function get_settings_experiments_usage() {
425 $system_info = Plugin::$instance->system_info;
426
427 /**
428 * @var $experiments_report Experiments_Reporter
429 */
430 $experiments_report = $system_info->create_reporter( [
431 'class_name' => Experiments_Reporter::class,
432 ] );
433
434 return $experiments_report->get_experiments()['value'];
435 }
436
437 /**
438 * Get usage of general tools.
439 * 'Elementor->Tools->General'.
440 *
441 * @return array
442 */
443 public static function get_tools_general_usage() {
444 return self::get_tracking_data_from_tools( 'general' );
445 }
446
447 /**
448 * Get usage of 'version control' tools.
449 * 'Elementor->Tools->Version Control'.
450 *
451 * @return array
452 */
453 public static function get_tools_version_control_usage() {
454 return self::get_tracking_data_from_tools( 'versions' );
455 }
456
457 /**
458 * Get usage of 'maintenance' tools.
459 * 'Elementor->Tools->Maintenance'.
460 *
461 * @return array
462 */
463 public static function get_tools_maintenance_usage() {
464 return self::get_tracking_data_from_tools( 'maintenance_mode' );
465 }
466
467 /**
468 * Get library usage extend.
469 *
470 * Retrieve the number of Elementor library items saved.
471 *
472 * @return array The number of Elementor library items grouped by post types, post status
473 * and meta value.
474 */
475 public static function get_library_usage_extend() {
476 global $wpdb;
477
478 $usage = [];
479
480 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
481 $results = $wpdb->get_results(
482 "SELECT `meta_value`, COUNT(`ID`) `hits`, `post_status`
483 FROM {$wpdb->posts} `p`
484 LEFT JOIN {$wpdb->postmeta} `pm` ON(`p`.`ID` = `pm`.`post_id`)
485 WHERE `post_type` = 'elementor_library'
486 AND `meta_key` = '_elementor_template_type'
487 GROUP BY `post_type`, `meta_value`, `post_status`;"
488 );
489
490 if ( $results ) {
491 foreach ( $results as $result ) {
492 if ( empty( $usage[ $result->meta_value ] ) ) {
493 $usage[ $result->meta_value ] = [];
494 }
495
496 if ( empty( $usage[ $result->meta_value ][ $result->post_status ] ) ) {
497 $usage[ $result->meta_value ][ $result->post_status ] = 0;
498 }
499
500 $usage[ $result->meta_value ][ $result->post_status ] += $result->hits;
501 }
502 }
503
504 return $usage;
505 }
506
507 public static function get_events() {
508 global $wpdb;
509 $table_name = $wpdb->prefix . Events_DB_Manager::TABLE_NAME;
510
511 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
512 $results = $wpdb->get_results( "SELECT event_data FROM {$table_name}" );
513
514 $events_data = [];
515
516 foreach ( $results as $event ) {
517 // Results are stored in the database as a JSON string. Since all tracking data is encoded right before
518 // being sent, it is now decoded.
519 $events_data[] = json_decode( $event->event_data, true );
520 }
521
522 return $events_data;
523 }
524
525 /**
526 * Get the tracking data
527 *
528 * Retrieve tracking data and apply filter
529 *
530 * @access public
531 * @static
532 *
533 * @param bool $is_first_time
534 *
535 * @return array
536 */
537 public static function get_tracking_data( $is_first_time = false ) {
538 $params = [
539 'system' => self::get_system_reports_data(),
540 'site_lang' => get_bloginfo( 'language' ),
541 'email' => get_option( 'admin_email' ),
542 'usages' => [
543 'posts' => self::get_posts_usage(),
544 'non-elementor-posts' => self::get_non_elementor_posts_usage(),
545 'library' => self::get_library_usage(),
546 'settings' => [
547 'general' => self::get_settings_general_usage(),
548 'advanced' => self::get_settings_advanced_usage(),
549 'experiments' => self::get_settings_experiments_usage(),
550 ],
551 'tools' => [
552 'general' => self::get_tools_general_usage(),
553 'version' => self::get_tools_version_control_usage(),
554 'maintenance' => self::get_tools_maintenance_usage(),
555 ],
556 'library-details' => self::get_library_usage_extend(),
557 ],
558 'is_first_time' => $is_first_time,
559 'install_time' => Plugin::instance()->get_install_time(),
560 ];
561
562 $site_key = Api::get_site_key();
563 if ( ! empty( $site_key ) ) {
564 $params['site_key'] = $site_key;
565 }
566
567 /**
568 * Tracker send tracking data params.
569 *
570 * Filters the data parameters when sending tracking request.
571 *
572 * @param array $params Variable to encode as JSON.
573 *
574 * @since 1.0.0
575 */
576 $params = apply_filters( 'elementor/tracker/send_tracking_data_params', $params );
577
578 return $params;
579 }
580
581 /**
582 * @param string $tab_name
583 * @return array
584 */
585 private static function get_tracking_data_from_settings( $tab_name ) {
586 return self::get_tracking_data_from_settings_page(
587 Plugin::$instance->settings->get_tabs(),
588 $tab_name
589 );
590 }
591
592 /**
593 * @param string $tab_name
594 * @return array
595 */
596 private static function get_tracking_data_from_tools( $tab_name ) {
597 return self::get_tracking_data_from_settings_page(
598 Plugin::$instance->tools->get_tabs(),
599 $tab_name
600 );
601 }
602
603 private static function get_tracking_data_from_settings_page( $tabs, $tab_name ) {
604 $result = [];
605
606 if ( empty( $tabs[ $tab_name ] ) ) {
607 return $result;
608 }
609
610 $tab = $tabs[ $tab_name ];
611
612 foreach ( $tab['sections'] as $section_name => $section ) {
613 foreach ( $section['fields'] as $field_name => $field ) {
614 // Skips fields with '_' prefix.
615 if ( '_' === $field_name[0] ) {
616 continue;
617 }
618
619 $default_value = null;
620 $args = $field['field_args'];
621 switch ( $args['type'] ) {
622 case 'checkbox':
623 $default_value = $args['value'];
624 break;
625
626 case 'select':
627 case 'checkbox_list_cpt':
628 $default_value = $args['std'];
629 break;
630
631 case 'checkbox_list_roles':
632 $default_value = null;
633 break;
634
635 // 'raw_html' is used as action and not as data.
636 case 'raw_html':
637 continue 2; // Skip fields loop.
638
639 default:
640 trigger_error( 'Invalid type: \'' . $args['type'] . '\'' ); // phpcs:ignore
641 }
642
643 $result[ $field_name ] = get_option( 'elementor_' . $field_name, $default_value );
644 }
645 }
646
647 return $result;
648 }
649 }
650