Insights.php (26194B)
1 <?php 2 namespace ReduxAppsero; 3 4 /** 5 * Appsero Insights 6 * 7 * This is a tracker class to track plugin usage based on if the customer has opted in. 8 * No personal information is being tracked by this class, only general settings, active plugins, environment details 9 * and admin email. 10 */ 11 class Insights { 12 13 /** 14 * The notice text 15 * 16 * @var string 17 */ 18 public $notice; 19 20 /** 21 * Wheather to the notice or not 22 * 23 * @var boolean 24 */ 25 protected $show_notice = true; 26 27 /** 28 * If extra data needs to be sent 29 * 30 * @var array 31 */ 32 protected $extra_data = array(); 33 34 /** 35 * AppSero\Client 36 * 37 * @var object 38 */ 39 protected $client; 40 41 /** 42 * Initialize the class 43 * 44 * @param AppSero\Client 45 */ 46 public function __construct( $client, $name = null, $file = null ) { 47 48 if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) { 49 $client = new Client( $client, $name, $file ); 50 } 51 52 if ( is_object( $client ) && is_a( $client, 'ReduxAppsero\Client' ) ) { 53 $this->client = $client; 54 } 55 } 56 57 /** 58 * Don't show the notice 59 * 60 * @return \self 61 */ 62 public function hide_notice() { 63 $this->show_notice = false; 64 65 return $this; 66 } 67 68 /** 69 * Add extra data if needed 70 * 71 * @param array $data 72 * 73 * @return \self 74 */ 75 public function add_extra( $data = array() ) { 76 $this->extra_data = $data; 77 78 return $this; 79 } 80 81 /** 82 * Set custom notice text 83 * 84 * @param string $text 85 * 86 * @return \self 87 */ 88 public function notice( $text ) { 89 $this->notice = $text; 90 91 return $this; 92 } 93 94 /** 95 * Initialize insights 96 * 97 * @return void 98 */ 99 public function init() { 100 if ( $this->client->type == 'plugin' ) { 101 $this->init_plugin(); 102 } elseif ( $this->client->type == 'theme' ) { 103 $this->init_theme(); 104 } 105 } 106 107 /** 108 * Initialize theme hooks 109 * 110 * @return void 111 */ 112 public function init_theme() { 113 $this->init_common(); 114 115 add_action( 'switch_theme', array( $this, 'deactivation_cleanup' ) ); 116 add_action( 'switch_theme', array( $this, 'theme_deactivated' ), 12, 3 ); 117 } 118 119 /** 120 * Initialize plugin hooks 121 * 122 * @return void 123 */ 124 public function init_plugin() { 125 // plugin deactivate popup 126 if ( ! $this->is_local_server() ) { 127 add_filter( 'plugin_action_links_' . $this->client->basename, array( $this, 'plugin_action_links' ) ); 128 add_action( 'admin_footer', array( $this, 'deactivate_scripts' ) ); 129 } 130 131 $this->init_common(); 132 133 register_activation_hook( $this->client->file, array( $this, 'activate_plugin' ) ); 134 register_deactivation_hook( $this->client->file, array( $this, 'deactivation_cleanup' ) ); 135 } 136 137 /** 138 * Initialize common hooks 139 * 140 * @return void 141 */ 142 protected function init_common() { 143 144 if ( $this->show_notice ) { 145 // tracking notice 146 add_action( 'admin_notices', array( $this, 'admin_notice' ) ); 147 } 148 149 add_action( 'admin_init', array( $this, 'handle_optin_optout' ) ); 150 151 // uninstall reason 152 add_action( 'wp_ajax_' . $this->client->slug . '_submit-uninstall-reason', array( $this, 'uninstall_reason_submission' ) ); 153 154 // cron events 155 add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) ); 156 add_action( $this->client->slug . '_tracker_send_event', array( $this, 'send_tracking_data' ) ); 157 // add_action( 'admin_init', array( $this, 'send_tracking_data' ) ); // test 158 } 159 160 /** 161 * Send tracking data to AppSero server 162 * 163 * @param boolean $override 164 * 165 * @return void 166 */ 167 public function send_tracking_data( $override = false ) { 168 // skip on AJAX Requests 169 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 170 return; 171 } 172 173 if ( ! $this->tracking_allowed() && ! $override ) { 174 return; 175 } 176 177 // Send a maximum of once per week 178 $last_send = $this->get_last_send(); 179 180 if ( $last_send && $last_send > strtotime( '-1 week' ) ) { 181 return; 182 } 183 184 $tracking_data = $this->get_tracking_data(); 185 186 $response = $this->client->send_request( $tracking_data, 'track' ); 187 188 update_option( $this->client->slug . '_tracking_last_send', time() ); 189 } 190 191 /** 192 * Get the tracking data points 193 * 194 * @return array 195 */ 196 protected function get_tracking_data() { 197 $all_plugins = $this->get_all_plugins(); 198 199 $users = get_users( 200 array( 201 'role' => 'administrator', 202 'orderby' => 'ID', 203 'order' => 'ASC', 204 'number' => 1, 205 'paged' => 1, 206 ) 207 ); 208 209 $admin_user = ( is_array( $users ) && ! empty( $users ) ) ? $users[0] : false; 210 $first_name = $last_name = ''; 211 212 if ( $admin_user ) { 213 $first_name = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name; 214 $last_name = $admin_user->last_name; 215 } 216 217 $data = array( 218 'version' => $this->client->project_version, 219 'url' => esc_url( home_url() ), 220 'site' => $this->get_site_name(), 221 'admin_email' => get_option( 'admin_email' ), 222 'first_name' => $first_name, 223 'last_name' => $last_name, 224 'hash' => $this->client->hash, 225 'server' => $this->get_server_info(), 226 'wp' => $this->get_wp_info(), 227 'users' => $this->get_user_counts(), 228 'active_plugins' => count( $all_plugins['active_plugins'] ), 229 'inactive_plugins' => count( $all_plugins['inactive_plugins'] ), 230 'ip_address' => $this->get_user_ip_address(), 231 'theme' => get_stylesheet(), 232 'project_version' => $this->client->project_version, 233 'tracking_skipped' => false, 234 ); 235 236 // Add metadata 237 if ( $extra = $this->get_extra_data() ) { 238 $data['extra'] = $extra; 239 } 240 241 // Check this has previously skipped tracking 242 $skipped = get_option( $this->client->slug . '_tracking_skipped' ); 243 244 if ( $skipped === 'yes' ) { 245 delete_option( $this->client->slug . '_tracking_skipped' ); 246 247 $data['tracking_skipped'] = true; 248 } 249 250 return apply_filters( $this->client->slug . '_tracker_data', $data ); 251 } 252 253 /** 254 * If a child class wants to send extra data 255 * 256 * @return mixed 257 */ 258 protected function get_extra_data() { 259 if ( is_callable( $this->extra_data ) ) { 260 return call_user_func( $this->extra_data ); 261 } 262 263 if ( is_array( $this->extra_data ) ) { 264 return $this->extra_data; 265 } 266 267 return array(); 268 } 269 270 /** 271 * Explain the user which data we collect 272 * 273 * @return string 274 */ 275 protected function data_we_collect() { 276 $data = array( 277 'Server environment details (php, mysql, server, WordPress versions)', 278 'Number of users in your site', 279 'Site language', 280 'Number of active and inactive plugins', 281 'Site name and url', 282 'Your name and email address', 283 ); 284 285 return $data; 286 } 287 288 /** 289 * Check if the user has opted into tracking 290 * 291 * @return bool 292 */ 293 public function tracking_allowed() { 294 $allow_tracking = get_option( $this->client->slug . '_allow_tracking', 'no' ); 295 296 return $allow_tracking == 'yes'; 297 } 298 299 /** 300 * Get the last time a tracking was sent 301 * 302 * @return false|string 303 */ 304 private function get_last_send() { 305 return get_option( $this->client->slug . '_tracking_last_send', false ); 306 } 307 308 /** 309 * Check if the notice has been dismissed or enabled 310 * 311 * @return boolean 312 */ 313 public function notice_dismissed() { 314 $hide_notice = get_option( $this->client->slug . '_tracking_notice', null ); 315 316 if ( 'hide' == $hide_notice ) { 317 return true; 318 } 319 320 return false; 321 } 322 323 /** 324 * Check if the current server is localhost 325 * 326 * @return boolean 327 */ 328 private function is_local_server() { 329 330 $is_local = false; 331 332 $domains_to_check = array_unique( 333 array( 334 'siteurl' => wp_parse_url( get_site_url(), PHP_URL_HOST ), 335 'homeurl' => wp_parse_url( get_home_url(), PHP_URL_HOST ), 336 ) 337 ); 338 339 $forbidden_domains = array( 340 'wordpress.com', 341 'localhost', 342 'localhost.localdomain', 343 '127.0.0.1', 344 '::1', 345 'local.wordpress.test', // VVV pattern. 346 'local.wordpress-trunk.test', // VVV pattern. 347 'src.wordpress-develop.test', // VVV pattern. 348 'build.wordpress-develop.test', // VVV pattern. 349 ); 350 351 foreach ( $domains_to_check as $domain ) { 352 // If it's empty, just fail out. 353 if ( ! $domain ) { 354 $is_local = true; 355 break; 356 } 357 358 // None of the explicit localhosts. 359 if ( in_array( $domain, $forbidden_domains, true ) ) { 360 $is_local = true; 361 break; 362 } 363 364 // No .test or .local domains. 365 if ( preg_match( '#\.(test|local)$#i', $domain ) ) { 366 $is_local = true; 367 break; 368 } 369 } 370 371 return apply_filters( 'appsero_is_local', $is_local ); 372 } 373 374 /** 375 * Schedule the event weekly 376 * 377 * @return void 378 */ 379 private function schedule_event() { 380 $hook_name = $this->client->slug . '_tracker_send_event'; 381 382 if ( ! wp_next_scheduled( $hook_name ) ) { 383 wp_schedule_event( time(), 'weekly', $hook_name ); 384 } 385 } 386 387 /** 388 * Clear any scheduled hook 389 * 390 * @return void 391 */ 392 private function clear_schedule_event() { 393 wp_clear_scheduled_hook( $this->client->slug . '_tracker_send_event' ); 394 } 395 396 /** 397 * Display the admin notice to users that have not opted-in or out 398 * 399 * @return void 400 */ 401 public function admin_notice() { 402 403 if ( $this->notice_dismissed() ) { 404 return; 405 } 406 407 if ( $this->tracking_allowed() ) { 408 return; 409 } 410 411 if ( ! current_user_can( 'manage_options' ) ) { 412 return; 413 } 414 415 // don't show tracking if a local server 416 if ( $this->is_local_server() ) { 417 return; 418 } 419 420 $optin_url = add_query_arg( $this->client->slug . '_tracker_optin', 'true' ); 421 $optout_url = add_query_arg( $this->client->slug . '_tracker_optout', 'true' ); 422 423 if ( empty( $this->notice ) ) { 424 $notice = sprintf( $this->client->__trans( 'Want to help make <strong>%1$s</strong> even more awesome? Allow %1$s to collect non-sensitive diagnostic data and usage information.' ), $this->client->name ); 425 } else { 426 $notice = $this->notice; 427 } 428 429 $policy_url = 'https://' . 'appsero.com/privacy-policy/'; 430 431 $notice .= ' (<a class="' . $this->client->slug . '-insights-data-we-collect" href="#">' . $this->client->__trans( 'what we collect' ) . '</a>)'; 432 $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked. '; 433 $notice .= 'We are using Appsero to collect your data. <a href="' . $policy_url . '">Learn more</a> about how Appsero collects and handle your data.</p>'; 434 435 echo '<div class="updated"><p>'; 436 echo $notice; 437 echo '</p><p class="submit">'; 438 echo ' <a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . $this->client->__trans( 'Allow' ) . '</a>'; 439 echo ' <a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . $this->client->__trans( 'No thanks' ) . '</a>'; 440 echo '</p></div>'; 441 442 echo "<script type='text/javascript'>jQuery('." . $this->client->slug . "-insights-data-we-collect').on('click', function(e) { 443 e.preventDefault(); 444 jQuery(this).parents('.updated').find('p.description').slideToggle('fast'); 445 }); 446 </script> 447 "; 448 449 } 450 451 /** 452 * handle the optin/optout 453 * 454 * @return void 455 */ 456 public function handle_optin_optout() { 457 458 if ( isset( $_GET[ $this->client->slug . '_tracker_optin' ] ) && $_GET[ $this->client->slug . '_tracker_optin' ] == 'true' ) { 459 $this->optin(); 460 461 wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optin' ) ); 462 exit; 463 } 464 465 if ( isset( $_GET[ $this->client->slug . '_tracker_optout' ] ) && $_GET[ $this->client->slug . '_tracker_optout' ] == 'true' ) { 466 $this->optout(); 467 468 wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optout' ) ); 469 exit; 470 } 471 } 472 473 /** 474 * Tracking optin 475 * 476 * @return void 477 */ 478 public function optin() { 479 update_option( $this->client->slug . '_allow_tracking', 'yes' ); 480 update_option( $this->client->slug . '_tracking_notice', 'hide' ); 481 482 $this->clear_schedule_event(); 483 $this->schedule_event(); 484 $this->send_tracking_data(); 485 } 486 487 /** 488 * Optout from tracking 489 * 490 * @return void 491 */ 492 public function optout() { 493 update_option( $this->client->slug . '_allow_tracking', 'no' ); 494 update_option( $this->client->slug . '_tracking_notice', 'hide' ); 495 496 $this->send_tracking_skipped_request(); 497 498 $this->clear_schedule_event(); 499 } 500 501 /** 502 * Get the number of post counts 503 * 504 * @param string $post_type 505 * 506 * @return integer 507 */ 508 public function get_post_count( $post_type ) { 509 global $wpdb; 510 511 return (int) $wpdb->get_var( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = '$post_type' and post_status = 'publish'" ); 512 } 513 514 /** 515 * Get server related info. 516 * 517 * @return array 518 */ 519 private static function get_server_info() { 520 global $wpdb; 521 522 $server_data = array(); 523 524 if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) { 525 $server_data['software'] = $_SERVER['SERVER_SOFTWARE']; 526 } 527 528 if ( function_exists( 'phpversion' ) ) { 529 $server_data['php_version'] = phpversion(); 530 } 531 532 $server_data['mysql_version'] = $wpdb->db_version(); 533 534 $server_data['php_max_upload_size'] = size_format( wp_max_upload_size() ); 535 $server_data['php_default_timezone'] = date_default_timezone_get(); 536 $server_data['php_soap'] = class_exists( 'SoapClient' ) ? 'Yes' : 'No'; 537 $server_data['php_fsockopen'] = function_exists( 'fsockopen' ) ? 'Yes' : 'No'; 538 $server_data['php_curl'] = function_exists( 'curl_init' ) ? 'Yes' : 'No'; 539 540 return $server_data; 541 } 542 543 /** 544 * Get WordPress related data. 545 * 546 * @return array 547 */ 548 private function get_wp_info() { 549 $wp_data = array(); 550 551 $wp_data['memory_limit'] = WP_MEMORY_LIMIT; 552 $wp_data['debug_mode'] = ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? 'Yes' : 'No'; 553 $wp_data['locale'] = get_locale(); 554 $wp_data['version'] = get_bloginfo( 'version' ); 555 $wp_data['multisite'] = is_multisite() ? 'Yes' : 'No'; 556 $wp_data['theme_slug'] = get_stylesheet(); 557 558 $theme = wp_get_theme( $wp_data['theme_slug'] ); 559 560 $wp_data['theme_name'] = $theme->get( 'Name' ); 561 $wp_data['theme_version'] = $theme->get( 'Version' ); 562 $wp_data['theme_uri'] = $theme->get( 'ThemeURI' ); 563 $wp_data['theme_author'] = $theme->get( 'Author' ); 564 565 return $wp_data; 566 } 567 568 /** 569 * Get the list of active and inactive plugins 570 * 571 * @return array 572 */ 573 private function get_all_plugins() { 574 // Ensure get_plugins function is loaded 575 if ( ! function_exists( 'get_plugins' ) ) { 576 require_once ABSPATH . '/wp-admin/includes/plugin.php'; 577 } 578 579 $plugins = get_plugins(); 580 $active_plugins_keys = get_option( 'active_plugins', array() ); 581 $active_plugins = array(); 582 583 foreach ( $plugins as $k => $v ) { 584 // Take care of formatting the data how we want it. 585 $formatted = array(); 586 $formatted['name'] = strip_tags( $v['Name'] ); 587 588 if ( isset( $v['Version'] ) ) { 589 $formatted['version'] = strip_tags( $v['Version'] ); 590 } 591 592 if ( isset( $v['Author'] ) ) { 593 $formatted['author'] = strip_tags( $v['Author'] ); 594 } 595 596 if ( isset( $v['Network'] ) ) { 597 $formatted['network'] = strip_tags( $v['Network'] ); 598 } 599 600 if ( isset( $v['PluginURI'] ) ) { 601 $formatted['plugin_uri'] = strip_tags( $v['PluginURI'] ); 602 } 603 604 if ( in_array( $k, $active_plugins_keys ) ) { 605 // Remove active plugins from list so we can show active and inactive separately 606 unset( $plugins[ $k ] ); 607 $active_plugins[ $k ] = $formatted; 608 } else { 609 $plugins[ $k ] = $formatted; 610 } 611 } 612 613 return array( 614 'active_plugins' => $active_plugins, 615 'inactive_plugins' => $plugins, 616 ); 617 } 618 619 /** 620 * Get user totals based on user role. 621 * 622 * @return array 623 */ 624 public function get_user_counts() { 625 $user_count = array(); 626 $user_count_data = count_users(); 627 $user_count['total'] = $user_count_data['total_users']; 628 629 // Get user count based on user role 630 foreach ( $user_count_data['avail_roles'] as $role => $count ) { 631 if ( ! $count ) { 632 continue; 633 } 634 $user_count[ $role ] = $count; 635 } 636 637 return $user_count; 638 } 639 640 /** 641 * Add weekly cron schedule 642 * 643 * @param array $schedules 644 * 645 * @return array 646 */ 647 public function add_weekly_schedule( $schedules ) { 648 649 $schedules['weekly'] = array( 650 'interval' => DAY_IN_SECONDS * 7, 651 'display' => 'Once Weekly', 652 ); 653 654 return $schedules; 655 } 656 657 /** 658 * Plugin activation hook 659 * 660 * @return void 661 */ 662 public function activate_plugin() { 663 $allowed = get_option( $this->client->slug . '_allow_tracking', 'no' ); 664 665 // if it wasn't allowed before, do nothing 666 if ( 'yes' !== $allowed ) { 667 return; 668 } 669 670 // re-schedule and delete the last sent time so we could force send again 671 $hook_name = $this->client->slug . '_tracker_send_event'; 672 if ( ! wp_next_scheduled( $hook_name ) ) { 673 wp_schedule_event( time(), 'weekly', $hook_name ); 674 } 675 676 delete_option( $this->client->slug . '_tracking_last_send' ); 677 678 $this->send_tracking_data( true ); 679 } 680 681 /** 682 * Clear our options upon deactivation 683 * 684 * @return void 685 */ 686 public function deactivation_cleanup() { 687 $this->clear_schedule_event(); 688 689 if ( 'theme' == $this->client->type ) { 690 delete_option( $this->client->slug . '_tracking_last_send' ); 691 delete_option( $this->client->slug . '_allow_tracking' ); 692 } 693 694 delete_option( $this->client->slug . '_tracking_notice' ); 695 } 696 697 /** 698 * Hook into action links and modify the deactivate link 699 * 700 * @param array $links 701 * 702 * @return array 703 */ 704 public function plugin_action_links( $links ) { 705 706 if ( array_key_exists( 'deactivate', $links ) ) { 707 $links['deactivate'] = str_replace( '<a', '<a class="' . $this->client->slug . '-deactivate-link"', $links['deactivate'] ); 708 } 709 710 return $links; 711 } 712 713 /** 714 * Plugin uninstall reasons 715 * 716 * @return array 717 */ 718 private function get_uninstall_reasons() { 719 $reasons = array( 720 array( 721 'id' => 'could-not-understand', 722 'text' => $this->client->__trans( 'I couldn\'t understand how to make it work' ), 723 'type' => 'textarea', 724 'placeholder' => $this->client->__trans( 'Would you like us to assist you?' ) 725 ), 726 array( 727 'id' => 'found-better-plugin', 728 'text' => $this->client->__trans( 'I found a better plugin' ), 729 'type' => 'text', 730 'placeholder' => $this->client->__trans( 'Which plugin?' ) 731 ), 732 array( 733 'id' => 'not-have-that-feature', 734 'text' => $this->client->__trans( 'The plugin is great, but I need specific feature that you don\'t support' ), 735 'type' => 'textarea', 736 'placeholder' => $this->client->__trans( 'Could you tell us more about that feature?' ) 737 ), 738 array( 739 'id' => 'is-not-working', 740 'text' => $this->client->__trans( 'The plugin is not working' ), 741 'type' => 'textarea', 742 'placeholder' => $this->client->__trans( 'Could you tell us a bit more whats not working?' ) 743 ), 744 array( 745 'id' => 'looking-for-other', 746 'text' => $this->client->__trans( 'It\'s not what I was looking for' ), 747 'type' => '', 748 'placeholder' => '' 749 ), 750 array( 751 'id' => 'did-not-work-as-expected', 752 'text' => $this->client->__trans( 'The plugin didn\'t work as expected' ), 753 'type' => 'textarea', 754 'placeholder' => $this->client->__trans( 'What did you expect?' ) 755 ), 756 array( 757 'id' => 'other', 758 'text' => $this->client->__trans( 'Other' ), 759 'type' => 'textarea', 760 'placeholder' => $this->client->__trans( 'Could you tell us a bit more?' ) 761 ), 762 ); 763 764 return $reasons; 765 } 766 767 /** 768 * Plugin deactivation uninstall reason submission 769 * 770 * @return void 771 */ 772 public function uninstall_reason_submission() { 773 774 if ( ! isset( $_POST['reason_id'] ) ) { 775 wp_send_json_error(); 776 } 777 778 $data = $this->get_tracking_data(); 779 $data['reason_id'] = sanitize_text_field( $_POST['reason_id'] ); 780 $data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : ''; 781 782 $this->client->send_request( $data, 'deactivate' ); 783 784 wp_send_json_success(); 785 } 786 787 /** 788 * Handle the plugin deactivation feedback 789 * 790 * @return void 791 */ 792 public function deactivate_scripts() { 793 global $pagenow; 794 795 if ( 'plugins.php' != $pagenow ) { 796 return; 797 } 798 799 $reasons = $this->get_uninstall_reasons(); 800 ?> 801 802 <div class="wd-dr-modal" id="<?php echo $this->client->slug; ?>-wd-dr-modal"> 803 <div class="wd-dr-modal-wrap"> 804 <div class="wd-dr-modal-header"> 805 <h3><?php $this->client->_etrans( 'If you have a moment, please let us know why you are deactivating:' ); ?></h3> 806 </div> 807 808 <div class="wd-dr-modal-body"> 809 <ul class="reasons"> 810 <?php foreach ($reasons as $reason) { ?> 811 <li data-type="<?php echo esc_attr( $reason['type'] ); ?>" data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>"> 812 <label><input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>"> <?php echo $reason['text']; ?></label> 813 </li> 814 <?php } ?> 815 </ul> 816 <p class="wd-dr-modal-reasons-bottom"> 817 <?php 818 echo sprintf( 819 $this->client->__trans( 'We use this information to troubleshoot problems and make product improvements. <a href="%1$s" target="_blank">Learn more</a> about how we handle you data.' ), 820 esc_url( 'https://redux.io/privacy?utm_source=plugin&utm_medium=appsero&utm_campaign=deactivate' ) 821 ); 822 ?> 823 </p> 824 </div> 825 826 <div class="wd-dr-modal-footer"> 827 <a href="#" class="dont-bother-me"><?php $this->client->_etrans( "I rather wouldn't say" ); ?></a> 828 <button class="button-secondary"><?php $this->client->_etrans( 'Submit & Deactivate' ); ?></button> 829 <button class="button-primary"><?php $this->client->_etrans( 'Cancel' ); ?></button> 830 </div> 831 </div> 832 </div> 833 834 <style type="text/css"> 835 .wd-dr-modal { 836 position: fixed; 837 z-index: 99999; 838 top: 0; 839 right: 0; 840 bottom: 0; 841 left: 0; 842 background: rgba(0,0,0,0.5); 843 display: none; 844 } 845 846 .wd-dr-modal.modal-active { 847 display: block; 848 } 849 850 .wd-dr-modal-wrap { 851 width: 475px; 852 position: relative; 853 margin: 10% auto; 854 background: #fff; 855 } 856 857 .wd-dr-modal-header { 858 border-bottom: 1px solid #eee; 859 padding: 8px 20px; 860 } 861 862 .wd-dr-modal-header h3 { 863 line-height: 150%; 864 margin: 0; 865 } 866 867 .wd-dr-modal-body { 868 padding: 5px 20px 20px 20px; 869 } 870 871 .wd-dr-modal-body .reason-input { 872 margin-top: 5px; 873 margin-left: 20px; 874 } 875 .wd-dr-modal-footer { 876 border-top: 1px solid #eee; 877 padding: 12px 20px; 878 text-align: right; 879 } 880 .wd-dr-modal-reasons-bottom { 881 margin: 15px 0 0 0; 882 } 883 </style> 884 885 <script type="text/javascript"> 886 (function($) { 887 $(function() { 888 var modal = $( '#<?php echo $this->client->slug; ?>-wd-dr-modal' ); 889 var deactivateLink = ''; 890 891 $( '#the-list' ).on('click', 'a.<?php echo $this->client->slug; ?>-deactivate-link', function(e) { 892 e.preventDefault(); 893 894 modal.addClass('modal-active'); 895 deactivateLink = $(this).attr('href'); 896 modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left'); 897 }); 898 899 modal.on('click', 'button.button-primary', function(e) { 900 e.preventDefault(); 901 902 modal.removeClass('modal-active'); 903 }); 904 905 modal.on('click', 'input[type="radio"]', function () { 906 var parent = $(this).parents('li:first'); 907 908 modal.find('.reason-input').remove(); 909 910 var inputType = parent.data('type'), 911 inputPlaceholder = parent.data('placeholder'), 912 reasonInputHtml = '<div class="reason-input">' + ( ( 'text' === inputType ) ? '<input type="text" size="40" />' : '<textarea rows="5" cols="45"></textarea>' ) + '</div>'; 913 914 if ( inputType !== '' ) { 915 parent.append( $(reasonInputHtml) ); 916 parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus(); 917 } 918 }); 919 920 modal.on('click', 'button.button-secondary', function(e) { 921 e.preventDefault(); 922 923 var button = $(this); 924 925 if ( button.hasClass('disabled') ) { 926 return; 927 } 928 929 var $radio = $( 'input[type="radio"]:checked', modal ); 930 931 var $selected_reason = $radio.parents('li:first'), 932 $input = $selected_reason.find('textarea, input[type="text"]'); 933 934 $.ajax({ 935 url: ajaxurl, 936 type: 'POST', 937 data: { 938 action: '<?php echo $this->client->slug; ?>_submit-uninstall-reason', 939 reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(), 940 reason_info: ( 0 !== $input.length ) ? $input.val().trim() : '' 941 }, 942 beforeSend: function() { 943 button.addClass('disabled'); 944 button.text('Processing...'); 945 }, 946 complete: function() { 947 window.location.href = deactivateLink; 948 } 949 }); 950 }); 951 }); 952 }(jQuery)); 953 </script> 954 955 <?php 956 } 957 958 /** 959 * Run after theme deactivated 960 * 961 * @param string $new_name 962 * @param object $new_theme 963 * @param object $old_theme 964 * @return void 965 */ 966 public function theme_deactivated( $new_name, $new_theme, $old_theme ) { 967 // Make sure this is appsero theme 968 if ( $old_theme->get_template() == $this->client->slug ) { 969 $this->client->send_request( $this->get_tracking_data(), 'deactivate' ); 970 } 971 } 972 973 /** 974 * Get user IP Address 975 */ 976 private function get_user_ip_address() { 977 $response = wp_remote_get( 'https://icanhazip.com/' ); 978 979 if ( is_wp_error( $response ) ) { 980 return ''; 981 } 982 983 $ip = trim( wp_remote_retrieve_body( $response ) ); 984 985 if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { 986 return ''; 987 } 988 989 return $ip; 990 } 991 992 /** 993 * Get site name 994 */ 995 private function get_site_name() { 996 $site_name = get_bloginfo( 'name' ); 997 998 if ( empty( $site_name ) ) { 999 $site_name = get_bloginfo( 'description' ); 1000 $site_name = wp_trim_words( $site_name, 3, '' ); 1001 } 1002 1003 if ( empty( $site_name ) ) { 1004 $site_name = esc_url( home_url() ); 1005 } 1006 1007 return $site_name; 1008 } 1009 1010 /** 1011 * Send request to appsero if user skip to send tracking data 1012 */ 1013 private function send_tracking_skipped_request() { 1014 $skipped = get_option( $this->client->slug . '_tracking_skipped' ); 1015 1016 $data = [ 1017 'hash' => $this->client->hash, 1018 'previously_skipped' => false, 1019 ]; 1020 1021 if ( $skipped === 'yes' ) { 1022 $data['previously_skipped'] = true; 1023 } else { 1024 update_option( $this->client->slug . '_tracking_skipped', 'yes' ); 1025 } 1026 1027 $this->client->send_request( $data, 'tracking-skipped' ); 1028 } 1029 }