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