PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 1.2.5
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v1.2.5
2.1.3 2.1.2 2.1.1 2.1.0 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 trunk 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 All 57 releases
darkify / admin / appsero / Insights.php

Insights.php in Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) 1.2.5, at admin/appsero/Insights.php

1,126 lines 36.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace DarkifyAppSero;
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, 'DarkifyAppSero\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 }
103 }
104
105 /**
106 * Initialize plugin hooks
107 *
108 * @return void
109 */
110 public function init_plugin() {
111 // plugin deactivate popup
112 if ( ! $this->is_local_server() ) {
113 add_filter( 'plugin_action_links_' . $this->client->basename, array( $this, 'plugin_action_links' ) );
114 add_action( 'admin_footer', array( $this, 'deactivate_scripts' ) );
115 }
116
117 $this->init_common();
118
119 register_activation_hook( $this->client->file, array( $this, 'activate_plugin' ) );
120 register_deactivation_hook( $this->client->file, array( $this, 'deactivation_cleanup' ) );
121 }
122
123 /**
124 * Initialize common hooks
125 *
126 * @return void
127 */
128 protected function init_common() {
129
130 if ( $this->show_notice ) {
131 // tracking notice
132 add_action( 'admin_notices', array( $this, 'admin_notice' ) );
133 }
134
135 add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
136
137 // uninstall reason
138 add_action( 'wp_ajax_' . wp_kses_post($this->client->slug) . '_submit-uninstall-reason', array( $this, 'uninstall_reason_submission' ) );
139
140 // cron events
141 add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) );
142 add_action( $this->client->slug . '_tracker_send_event', array( $this, 'send_tracking_data' ) );
143 // add_action( 'admin_init', array( $this, 'send_tracking_data' ) ); // test
144 }
145
146 /**
147 * Send tracking data to AppSero server
148 *
149 * @param boolean $override
150 *
151 * @return void
152 */
153 public function send_tracking_data( $override = false ) {
154 // skip on AJAX Requests
155 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
156 return;
157 }
158
159 if ( ! $this->tracking_allowed() && ! $override ) {
160 return;
161 }
162
163 // Send a maximum of once per week
164 $last_send = $this->get_last_send();
165
166 if ( $last_send && $last_send > strtotime( '-1 week' ) ) {
167 return;
168 }
169
170 $tracking_data = $this->get_tracking_data();
171
172 $response = $this->client->send_request( $tracking_data, 'track' );
173
174 update_option( $this->client->slug . '_tracking_last_send', time() );
175 }
176
177 /**
178 * Get the tracking data points
179 *
180 * @return array
181 */
182 protected function get_tracking_data() {
183 $all_plugins = $this->get_all_plugins();
184
185 $users = get_users( array(
186 'role' => 'administrator',
187 'orderby' => 'ID',
188 'order' => 'ASC',
189 'number' => 1,
190 'paged' => 1,
191 ) );
192
193 $admin_user = ( is_array( $users ) && ! empty( $users ) ) ? $users[0] : false;
194 $first_name = $last_name = '';
195
196 if ( $admin_user ) {
197 $first_name = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name;
198 $last_name = $admin_user->last_name;
199 }
200
201 $data = array(
202 'url' => esc_url( home_url() ),
203 'site' => $this->get_site_name(),
204 'admin_email' => get_option( 'admin_email' ),
205 'first_name' => $first_name,
206 'last_name' => $last_name,
207 'hash' => $this->client->hash,
208 'server' => $this->get_server_info(),
209 'wp' => $this->get_wp_info(),
210 'users' => $this->get_user_counts(),
211 'active_plugins' => count( $all_plugins['active_plugins'] ),
212 'inactive_plugins' => count( $all_plugins['inactive_plugins'] ),
213 'ip_address' => $this->get_user_ip_address(),
214 'project_version' => $this->client->project_version,
215 'tracking_skipped' => false,
216 );
217
218 // Add metadata
219 if ( $extra = $this->get_extra_data() ) {
220 $data['extra'] = $extra;
221 }
222
223 // Check this has previously skipped tracking
224 $skipped = get_option( $this->client->slug . '_tracking_skipped' );
225
226 if ( $skipped === 'yes' ) {
227 delete_option( $this->client->slug . '_tracking_skipped' );
228
229 $data['tracking_skipped'] = true;
230 }
231
232 return apply_filters( $this->client->slug . '_tracker_data', $data );
233 }
234
235 /**
236 * If a child class wants to send extra data
237 *
238 * @return mixed
239 */
240 protected function get_extra_data() {
241 if ( is_callable( $this->extra_data ) ) {
242 return call_user_func( $this->extra_data );
243 }
244
245 if ( is_array( $this->extra_data ) ) {
246 return $this->extra_data;
247 }
248
249 return array();
250 }
251
252 /**
253 * Explain the user which data we collect
254 *
255 * @return array
256 */
257 protected function data_we_collect() {
258 $data = array(
259 'Server environment details (php, mysql, server, WordPress versions)',
260 'Number of users in your site',
261 'Site language',
262 'Number of active and inactive plugins',
263 'Site name and url',
264 'Your name and email address',
265 );
266
267 return $data;
268 }
269
270 /**
271 * Check if the user has opted into tracking
272 *
273 * @return bool
274 */
275 public function tracking_allowed() {
276 $allow_tracking = get_option( $this->client->slug . '_allow_tracking', 'no' );
277
278 return $allow_tracking == 'yes';
279 }
280
281 /**
282 * Get the last time a tracking was sent
283 *
284 * @return false|string
285 */
286 private function get_last_send() {
287 return get_option( $this->client->slug . '_tracking_last_send', false );
288 }
289
290 /**
291 * Check if the notice has been dismissed or enabled
292 *
293 * @return boolean
294 */
295 public function notice_dismissed() {
296 $hide_notice = get_option( $this->client->slug . '_tracking_notice', null );
297
298 if ( 'hide' == $hide_notice ) {
299 return true;
300 }
301
302 return false;
303 }
304
305 /**
306 * Check if the current server is localhost
307 *
308 * @return boolean
309 */
310 private function is_local_server() {
311 return false;
312
313 $is_local = in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
314
315 return apply_filters( 'appsero_is_local', $is_local );
316 }
317
318 /**
319 * Schedule the event weekly
320 *
321 * @return void
322 */
323 private function schedule_event() {
324 $hook_name = $this->client->slug . '_tracker_send_event';
325
326 if ( ! wp_next_scheduled( $hook_name ) ) {
327 wp_schedule_event( time(), 'weekly', $hook_name );
328 }
329 }
330
331 /**
332 * Clear any scheduled hook
333 *
334 * @return void
335 */
336 private function clear_schedule_event() {
337 wp_clear_scheduled_hook( $this->client->slug . '_tracker_send_event' );
338 }
339
340 /**
341 * Display the admin notice to users that have not opted-in or out
342 *
343 * @return void
344 */
345 public function admin_notice() {
346
347 if ( $this->notice_dismissed() ) {
348 return;
349 }
350
351 if ( $this->tracking_allowed() ) {
352 return;
353 }
354
355 if ( ! current_user_can( 'manage_options' ) ) {
356 return;
357 }
358
359 // don't show tracking if a local server
360 if ( $this->is_local_server() ) {
361 return;
362 }
363
364 $optin_url = add_query_arg( $this->client->slug . '_tracker_optin', 'true' );
365 $optout_url = add_query_arg( $this->client->slug . '_tracker_optout', 'true' );
366
367 if ( empty( $this->notice ) ) {
368 $notice = sprintf( $this->client->darkify_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 );
369 } else {
370 $notice = $this->notice;
371 }
372
373 $policy_url = 'https://' . 'appsero.com/privacy-policy/';
374
375 $notice .= ' (<a class="' . wp_kses_post($this->client->slug) . '-insights-data-we-collect" href="#">' . $this->client->darkify_trans( 'what we collect' ) . '</a>)';
376 $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked. ';
377 $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>';
378
379 echo '<div class="updated"><p>';
380 echo wp_kses_post($notice);
381 echo '</p><p class="submit">';
382 echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . wp_kses_post($this->client->darkify_trans( 'Allow' )) . '</a>';
383 echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . wp_kses_post($this->client->darkify_trans( 'No thanks' )) . '</a>';
384 echo '</p></div>';
385
386 echo "<script type='text/javascript'>jQuery('." . wp_kses_post($this->client->slug) . "-insights-data-we-collect').on('click', function(e) {
387 e.preventDefault();
388 jQuery(this).parents('.updated').find('p.description').slideToggle('fast');
389 });
390 </script>
391 ";
392 }
393
394 /**
395 * handle the optin/optout
396 *
397 * @return void
398 */
399 public function handle_optin_optout() {
400
401 if ( isset( $_GET[ $this->client->slug . '_tracker_optin' ] ) && $_GET[ $this->client->slug . '_tracker_optin' ] == 'true' ) {
402 $this->optin();
403
404 wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optin' ) );
405 exit;
406 }
407
408 if ( isset( $_GET[ $this->client->slug . '_tracker_optout' ] ) && $_GET[ $this->client->slug . '_tracker_optout' ] == 'true' ) {
409 $this->optout();
410
411 wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optout' ) );
412 exit;
413 }
414 }
415
416 /**
417 * Tracking optin
418 *
419 * @return void
420 */
421 public function optin() {
422 update_option( $this->client->slug . '_allow_tracking', 'yes' );
423 update_option( $this->client->slug . '_tracking_notice', 'hide' );
424
425 $this->clear_schedule_event();
426 $this->schedule_event();
427 $this->send_tracking_data();
428 }
429
430 /**
431 * Optout from tracking
432 *
433 * @return void
434 */
435 public function optout() {
436 update_option( $this->client->slug . '_allow_tracking', 'no' );
437 update_option( $this->client->slug . '_tracking_notice', 'hide' );
438
439 $this->send_tracking_skipped_request();
440
441 $this->clear_schedule_event();
442 }
443
444 /**
445 * Get the number of post counts
446 *
447 * @param string $post_type
448 *
449 * @return integer
450 */
451 public function get_post_count( $post_type ) {
452 global $wpdb;
453
454 return (int) $wpdb->get_var( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = '$post_type' and post_status = 'publish'");
455 }
456
457 /**
458 * Get server related info.
459 *
460 * @return array
461 */
462 private static function get_server_info() {
463 global $wpdb;
464
465 $server_data = array();
466
467 if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
468 $server_data['software'] = $_SERVER['SERVER_SOFTWARE'];
469 }
470
471 if ( function_exists( 'phpversion' ) ) {
472 $server_data['php_version'] = phpversion();
473 }
474
475 $server_data['mysql_version'] = $wpdb->db_version();
476
477 $server_data['php_max_upload_size'] = size_format( wp_max_upload_size() );
478 $server_data['php_default_timezone'] = date_default_timezone_get();
479 $server_data['php_soap'] = class_exists( 'SoapClient' ) ? 'Yes' : 'No';
480 $server_data['php_fsockopen'] = function_exists( 'fsockopen' ) ? 'Yes' : 'No';
481 $server_data['php_curl'] = function_exists( 'curl_init' ) ? 'Yes' : 'No';
482
483 return $server_data;
484 }
485
486 /**
487 * Get WordPress related data.
488 *
489 * @return array
490 */
491 private function get_wp_info() {
492 $wp_data = array();
493
494 $wp_data['memory_limit'] = WP_MEMORY_LIMIT;
495 $wp_data['debug_mode'] = ( defined('WP_DEBUG') && WP_DEBUG ) ? 'Yes' : 'No';
496 $wp_data['locale'] = get_locale();
497 $wp_data['version'] = get_bloginfo( 'version' );
498 $wp_data['multisite'] = is_multisite() ? 'Yes' : 'No';
499 $wp_data['theme_slug'] = get_stylesheet();
500
501 $theme = wp_get_theme( $wp_data['theme_slug'] );
502
503 $wp_data['theme_name'] = $theme->get( 'Name' );
504 $wp_data['theme_version'] = $theme->get( 'Version' );
505 $wp_data['theme_uri'] = $theme->get( 'ThemeURI' );
506 $wp_data['theme_author'] = $theme->get( 'Author' );
507
508 return $wp_data;
509 }
510
511 /**
512 * Get the list of active and inactive plugins
513 *
514 * @return array
515 */
516 private function get_all_plugins() {
517 // Ensure get_plugins function is loaded
518 if ( ! function_exists( 'get_plugins' ) ) {
519 include ABSPATH . '/wp-admin/includes/plugin.php';
520 }
521
522 $plugins = get_plugins();
523 $active_plugins_keys = get_option( 'active_plugins', array() );
524 $active_plugins = array();
525
526 foreach ( $plugins as $k => $v ) {
527 // Take care of formatting the data how we want it.
528 $formatted = array();
529 $formatted['name'] = strip_tags( $v['Name'] );
530
531 if ( isset( $v['Version'] ) ) {
532 $formatted['version'] = strip_tags( $v['Version'] );
533 }
534
535 if ( isset( $v['Author'] ) ) {
536 $formatted['author'] = strip_tags( $v['Author'] );
537 }
538
539 if ( isset( $v['Network'] ) ) {
540 $formatted['network'] = strip_tags( $v['Network'] );
541 }
542
543 if ( isset( $v['PluginURI'] ) ) {
544 $formatted['plugin_uri'] = strip_tags( $v['PluginURI'] );
545 }
546
547 if ( in_array( $k, $active_plugins_keys ) ) {
548 // Remove active plugins from list so we can show active and inactive separately
549 unset( $plugins[$k] );
550 $active_plugins[$k] = $formatted;
551 } else {
552 $plugins[$k] = $formatted;
553 }
554 }
555
556 return array( 'active_plugins' => $active_plugins, 'inactive_plugins' => $plugins );
557 }
558
559 /**
560 * Get user totals based on user role.
561 *
562 * @return array
563 */
564 public function get_user_counts() {
565 $user_count = array();
566 $user_count_data = count_users();
567 $user_count['total'] = $user_count_data['total_users'];
568
569 // Get user count based on user role
570 foreach ( $user_count_data['avail_roles'] as $role => $count ) {
571 if ( ! $count ) {
572 continue;
573 }
574
575 $user_count[ $role ] = $count;
576 }
577
578 return $user_count;
579 }
580
581 /**
582 * Add weekly cron schedule
583 *
584 * @param array $schedules
585 *
586 * @return array
587 */
588 public function add_weekly_schedule( $schedules ) {
589
590 $schedules['weekly'] = array(
591 'interval' => DAY_IN_SECONDS * 7,
592 'display' => 'Once Weekly',
593 );
594
595 return $schedules;
596 }
597
598 /**
599 * Plugin activation hook
600 *
601 * @return void
602 */
603 public function activate_plugin() {
604 $allowed = get_option( $this->client->slug . '_allow_tracking', 'no' );
605
606 // if it wasn't allowed before, do nothing
607 if ( 'yes' !== $allowed ) {
608 return;
609 }
610
611 // re-schedule and delete the last sent time so we could force send again
612 $hook_name = $this->client->slug . '_tracker_send_event';
613 if ( ! wp_next_scheduled( $hook_name ) ) {
614 wp_schedule_event( time(), 'weekly', $hook_name );
615 }
616
617 delete_option( $this->client->slug . '_tracking_last_send' );
618
619 $this->send_tracking_data( true );
620 }
621
622 /**
623 * Clear our options upon deactivation
624 *
625 * @return void
626 */
627 public function deactivation_cleanup() {
628 $this->clear_schedule_event();
629 delete_option( $this->client->slug . '_tracking_notice' );
630 }
631
632 /**
633 * Hook into action links and modify the deactivate link
634 *
635 * @param array $links
636 *
637 * @return array
638 */
639 public function plugin_action_links( $links ) {
640
641 if ( array_key_exists( 'deactivate', $links ) ) {
642 $links['deactivate'] = str_replace( '<a', '<a class="' . wp_kses_post($this->client->slug) . '-deactivate-link"', $links['deactivate'] );
643 }
644
645 return $links;
646 }
647
648 /**
649 * Plugin uninstall reasons
650 *
651 * @return array
652 */
653 private function get_uninstall_reasons() {
654 $reasons = array(
655 array(
656 'id' => 'could-not-understand',
657 'text' => $this->client->darkify_trans( "Couldn't understand" ),
658 'placeholder' => $this->client->darkify_trans( 'Would you like us to assist you?' ),
659 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#171717"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 10.6 23 9.6 22.9 8.8 22.7L8.8 22.6C9.3 22.5 9.7 22.3 10 21.9 10.3 21.6 10.4 21.3 10.4 20.9 10.8 21 11.1 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2 6.3 2 2 6.3 2 11.5 2 13 2.3 14.3 2.9 15.6 2.7 16 2.4 16.3 2.2 16.8L2.1 17.1 2.1 17.3C2 17.5 2 17.7 2 18 0.7 16.1 0 13.9 0 11.5 0 5.1 5.1 0 11.5 0ZM6 13.6C6 13.7 6.1 13.8 6.1 13.9 6.3 14.5 6.2 15.7 6.1 16.4 6.1 16.6 6 16.9 6 17.1 6 17.1 6.1 17.1 6.1 17.1 7.1 16.9 8.2 16 9.3 15.5 9.8 15.2 10.4 15 10.9 15 11.2 15 11.4 15 11.6 15.2 11.9 15.4 12.1 16 11.6 16.4 11.5 16.5 11.3 16.6 11.1 16.7 10.5 17 9.9 17.4 9.3 17.7 9 17.9 9 18.1 9.1 18.5 9.2 18.9 9.3 19.4 9.3 19.8 9.4 20.3 9.3 20.8 9 21.2 8.8 21.5 8.5 21.6 8.1 21.7 7.9 21.8 7.6 21.9 7.3 21.9L6.5 22C6.3 22 6 21.9 5.8 21.9 5 21.8 4.4 21.5 3.9 20.9 3.3 20.4 3.1 19.6 3 18.8L3 18.5C3 18.2 3 17.9 3.1 17.7L3.1 17.6C3.2 17.1 3.5 16.7 3.7 16.3 4 15.9 4.2 15.4 4.3 15 4.4 14.6 4.4 14.5 4.6 14.2 4.6 13.9 4.7 13.7 4.9 13.6 5.2 13.2 5.7 13.2 6 13.6ZM11.7 11.2C13.1 11.2 14.3 11.7 15.2 12.9 15.3 13 15.4 13.1 15.4 13.2 15.4 13.4 15.3 13.8 15.2 13.8 15 13.9 14.9 13.8 14.8 13.7 14.6 13.5 14.4 13.2 14.1 13.1 13.5 12.6 12.8 12.3 12 12.2 10.7 12.1 9.5 12.3 8.4 12.8 8.3 12.8 8.2 12.8 8.1 12.8 7.9 12.8 7.8 12.4 7.8 12.2 7.7 12.1 7.8 11.9 8 11.8 8.4 11.7 8.8 11.5 9.2 11.4 10 11.2 10.9 11.1 11.7 11.2ZM16.3 5.9C17.3 5.9 18 6.6 18 7.6 18 8.5 17.3 9.3 16.3 9.3 15.4 9.3 14.7 8.5 14.7 7.6 14.7 6.6 15.4 5.9 16.3 5.9ZM8.3 5C9.2 5 9.9 5.8 9.9 6.7 9.9 7.7 9.2 8.4 8.2 8.4 7.3 8.4 6.6 7.7 6.6 6.7 6.6 5.8 7.3 5 8.3 5Z"/></g></g></svg>'
660 ),
661 array(
662 'id' => 'found-better-plugin',
663 'text' => $this->client->darkify_trans( 'Found a better plugin' ),
664 'placeholder' => $this->client->darkify_trans( 'Which plugin?' ),
665 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#171717"><path d="M17.1 14L22.4 19.3C23.2 20.2 23.2 21.5 22.4 22.4 21.5 23.2 20.2 23.2 19.3 22.4L19.3 22.4 14 17.1C15.3 16.3 16.3 15.3 17.1 14L17.1 14ZM8.6 0C13.4 0 17.3 3.9 17.3 8.6 17.3 13.4 13.4 17.2 8.6 17.2 3.9 17.2 0 13.4 0 8.6 0 3.9 3.9 0 8.6 0ZM8.6 2.2C5.1 2.2 2.2 5.1 2.2 8.6 2.2 12.2 5.1 15.1 8.6 15.1 12.2 15.1 15.1 12.2 15.1 8.6 15.1 5.1 12.2 2.2 8.6 2.2ZM8.6 3.6L8.6 5C6.6 5 5 6.6 5 8.6L5 8.6 3.6 8.6C3.6 5.9 5.9 3.6 8.6 3.6L8.6 3.6Z"/></g></g></svg>',
666 ),
667 array(
668 'id' => 'not-have-that-feature',
669 'text' => $this->client->darkify_trans( "Missing a specific feature" ),
670 'placeholder' => $this->client->darkify_trans( 'Could you tell us more about that feature?' ),
671 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#171717"><path d="M19.4 0C19.7 0.6 19.8 1.3 19.8 2 19.8 3.2 19.4 4.4 18.5 5.3 17.6 6.2 16.5 6.7 15.2 6.7 15.2 6.7 15.2 6.7 15.2 6.7 14 6.7 12.9 6.2 12 5.3 11.2 4.4 10.7 3.3 10.7 2 10.7 1.3 10.8 0.6 11.1 0L7.6 0 7 0 6.5 0 6.5 5.7C6.3 5.6 5.9 5.3 5.6 5.1 5 4.6 4.3 4.3 3.5 4.3 3.5 4.3 3.5 4.3 3.4 4.3 1.6 4.4 0 5.9 0 7.9 0 8.6 0.2 9.2 0.5 9.7 1.1 10.8 2.2 11.5 3.5 11.5 4.3 11.5 5 11.2 5.6 10.8 6 10.5 6.3 10.3 6.5 10.2L6.5 10.2 6.5 17 6.5 17 7 17 7.6 17 22.5 17C23.3 17 24 16.3 24 15.5L24 0 19.4 0Z"/></g></g></svg>',
672 ),
673 array(
674 'id' => 'is-not-working',
675 'text' => $this->client->darkify_trans( 'Not working' ),
676 'placeholder' => $this->client->darkify_trans( 'Could you tell us a bit more whats not working?' ),
677 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#171717"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.8 14.4C11.2 14.4 10.7 14.8 10.7 15.4 10.7 16 11.2 16.4 11.8 16.4 12.4 16.4 12.8 16 12.8 15.4 12.8 14.8 12.4 14.4 11.8 14.4ZM12 7C10.1 7 9.1 8.1 9 9.6L10.5 9.6C10.5 8.8 11.1 8.3 11.9 8.3 12.7 8.3 13.2 8.8 13.2 9.5 13.2 10.1 13 10.4 12.2 10.9 11.3 11.4 10.9 12 11 12.9L11 13.4 12.5 13.4 12.5 13C12.5 12.4 12.7 12.1 13.5 11.6 14.4 11.1 14.9 10.4 14.9 9.4 14.9 8 13.7 7 12 7Z"/></g></g></svg>',
678 ),
679 array(
680 'id' => 'looking-for-other',
681 'text' => $this->client->darkify_trans( "Not what I was looking" ),
682 'placeholder' => $this->client->darkify_trans( 'Could you tell us a bit more?' ),
683 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#171717"><path d="M23.5 9C23.5 9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.4 8.6 23.2 8.3 23 8 22.2 6.5 20.6 3.7 19.8 2.6 18.8 1.3 17.7 0 16.1 0 15.7 0 15.3 0.1 14.9 0.2 13.8 0.6 12.6 1.2 12.3 2.7L11.7 2.7C11.4 1.2 10.2 0.6 9.1 0.2 8.7 0.1 8.3 0 7.9 0 6.3 0 5.2 1.3 4.2 2.6 3.4 3.7 1.8 6.5 1 8 0.8 8.3 0.6 8.6 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 9 0.5 9 0.2 9.7 0 10.5 0 11.3 0 14.4 2.5 17 5.5 17 7.3 17 8.8 16.1 9.8 14.8L14.2 14.8C15.2 16.1 16.7 17 18.5 17 21.5 17 24 14.4 24 11.3 24 10.5 23.8 9.7 23.5 9ZM5.5 15C3.6 15 2 13.2 2 11 2 8.8 3.6 7 5.5 7 7.4 7 9 8.8 9 11 9 13.2 7.4 15 5.5 15ZM18.5 15C16.6 15 15 13.2 15 11 15 8.8 16.6 7 18.5 7 20.4 7 22 8.8 22 11 22 13.2 20.4 15 18.5 15Z"/></g></g></svg>',
684 ),
685 array(
686 'id' => 'did-not-work-as-expected',
687 'text' => $this->client->darkify_trans( "Didn't work as expected" ),
688 'placeholder' => $this->client->darkify_trans( 'What did you expect?' ),
689 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#171717"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.5 2C6.3 2 2 6.3 2 11.5 2 16.7 6.3 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2ZM12.5 12.9L12.7 5 10.2 5 10.5 12.9 12.5 12.9ZM11.5 17.4C12.4 17.4 13 16.8 13 15.9 13 15 12.4 14.4 11.5 14.4 10.6 14.4 10 15 10 15.9 10 16.8 10.6 17.4 11.5 17.4Z"/></g></g></svg>',
690 ),
691 array(
692 'id' => 'other',
693 'text' => $this->client->darkify_trans( 'Others' ),
694 'placeholder' => $this->client->darkify_trans( 'Could you tell us a bit more?' ),
695 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="23" viewBox="0 0 24 6"><g fill="none"><g fill="#171717"><path d="M3 0C4.7 0 6 1.3 6 3 6 4.7 4.7 6 3 6 1.3 6 0 4.7 0 3 0 1.3 1.3 0 3 0ZM12 0C13.7 0 15 1.3 15 3 15 4.7 13.7 6 12 6 10.3 6 9 4.7 9 3 9 1.3 10.3 0 12 0ZM21 0C22.7 0 24 1.3 24 3 24 4.7 22.7 6 21 6 19.3 6 18 4.7 18 3 18 1.3 19.3 0 21 0Z"/></g></g></svg>',
696 ),
697 );
698
699 return $reasons;
700 }
701
702 /**
703 * Plugin deactivation uninstall reason submission
704 *
705 * @return void
706 */
707 public function uninstall_reason_submission() {
708
709 if ( ! isset( $_POST['reason_id'] ) ) {
710 wp_send_json_error();
711 }
712
713 $data = $this->get_tracking_data();
714 $data['reason_id'] = sanitize_text_field( $_POST['reason_id'] );
715 $data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '';
716
717 $this->client->send_request( $data, 'deactivate' );
718
719 wp_send_json_success();
720 }
721
722 /**
723 * Handle the plugin deactivation feedback
724 *
725 * @return void
726 */
727 public function deactivate_scripts() {
728 global $pagenow;
729
730 if ( 'plugins.php' != $pagenow ) {
731 return;
732 }
733
734 $this->deactivation_modal_styles();
735 $reasons = $this->get_uninstall_reasons();
736 $custom_reasons = apply_filters( 'appsero_custom_deactivation_reasons', [] );
737 ?>
738
739 <div class="wd-dr-modal" id="<?php echo esc_attr($this->client->slug); ?>-wd-dr-modal">
740 <div class="wd-dr-modal-wrap">
741 <div class="wd-dr-modal-header">
742 <h3><?php $this->client->_etrans( 'Goodbyes are always hard. If you have a moment, please let us know how we can improve.' ); ?>
743 </h3>
744 </div>
745
746 <div class="wd-dr-modal-body">
747 <ul class="wd-de-reasons">
748 <?php foreach ( $reasons as $reason ) { ?>
749 <li data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>">
750 <label>
751 <input type="radio" name="selected-reason" value="<?php echo esc_attr($reason['id']); ?>">
752 <div class="wd-de-reason-icon"><?php echo $reason['icon']; ?></div>
753 <div class="wd-de-reason-text"><?php echo wp_kses_post($reason['text']); ?></div>
754 </label>
755 </li>
756 <?php } ?>
757 </ul>
758 <?php if ( $custom_reasons && is_array( $custom_reasons ) ) : ?>
759 <ul class="wd-de-reasons wd-de-others-reasons">
760 <?php foreach ( $custom_reasons as $reason ) { ?>
761 <li data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>" data-customreason="true">
762 <label>
763 <input type="radio" name="selected-reason" value="<?php echo esc_attr($reason['id']); ?>">
764 <div class="wd-de-reason-icon"><?php echo wp_kses_post($reason['icon']); ?></div>
765 <div class="wd-de-reason-text"><?php echo wp_kses_post($reason['text']); ?></div>
766 </label>
767 </li>
768 <?php } ?>
769 </ul>
770 <?php endif; ?>
771 <div class="wd-dr-modal-reason-input"><textarea></textarea></div>
772 <p class="wd-dr-modal-reasons-bottom">
773 <?php
774 echo sprintf(
775 wp_kses_post($this->client->darkify_trans( 'We share your data with <a href="%1$s" target="_blank">Appsero</a> to troubleshoot problems &amp; make product improvements. <a href="%2$s" target="_blank">Learn more</a> about how Appsero handles your data.')),
776 esc_url( 'https://appsero.com/' ),
777 esc_url( 'https://appsero.com/privacy-policy' )
778 );
779 ?>
780 </p>
781 </div>
782
783 <div class="wd-dr-modal-footer">
784 <a href="#"
785 class="dont-bother-me wd-dr-button-secondary"><?php $this->client->_etrans( "Skip & Deactivate" ); ?></a>
786 <button
787 class="wd-dr-button-secondary wd-dr-cancel-modal"><?php $this->client->_etrans( 'Cancel' ); ?></button>
788 <button class="wd-dr-submit-modal"><?php $this->client->_etrans( 'Submit & Deactivate' ); ?></button>
789 </div>
790 </div>
791 </div>
792
793 <script type="text/javascript">
794 (function($) {
795 $(function() {
796 var modal = $('#<?php echo esc_attr($this->client->slug); ?>-wd-dr-modal');
797 var deactivateLink = '';
798
799 // Open modal
800 $('#the-list').on('click', 'a.<?php echo esc_attr($this->client->slug); ?>-deactivate-link', function(e) {
801 e.preventDefault();
802
803 modal.addClass('modal-active');
804 deactivateLink = $(this).attr('href');
805 modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
806 });
807
808 // Close modal; Cancel
809 modal.on('click', 'button.wd-dr-cancel-modal', function(e) {
810 e.preventDefault();
811 modal.removeClass('modal-active');
812 });
813
814 // Reason change
815 modal.on('click', 'input[type="radio"]', function() {
816 var parent = $(this).parents('li');
817 var isCustomReason = parent.data('customreason');
818 var inputValue = $(this).val();
819
820 if (isCustomReason) {
821 $('ul.wd-de-reasons.wd-de-others-reasons li').removeClass('wd-de-reason-selected');
822 } else {
823 $('ul.wd-de-reasons li').removeClass('wd-de-reason-selected');
824
825 if ("other" != inputValue) {
826 $('ul.wd-de-reasons.wd-de-others-reasons').css('display', 'none');
827 }
828 }
829
830 // Show if has custom reasons
831 if ("other" == inputValue) {
832 $('ul.wd-de-reasons.wd-de-others-reasons').css('display', 'flex');
833 }
834
835 parent.addClass('wd-de-reason-selected');
836 $('.wd-dr-modal-reason-input').show();
837
838 $('.wd-dr-modal-reason-input textarea').attr('placeholder', parent.data('placeholder'))
839 .focus();
840 });
841
842 // Submit response
843 modal.on('click', 'button.wd-dr-submit-modal', function(e) {
844 e.preventDefault();
845
846 var button = $(this);
847
848 if (button.hasClass('disabled')) {
849 return;
850 }
851
852 var $radio = $('input[type="radio"]:checked', modal);
853 var $input = $('.wd-dr-modal-reason-input textarea');
854
855 $.ajax({
856 url: ajaxurl,
857 type: 'POST',
858 data: {
859 action: '<?php echo esc_attr($this->client->slug); ?>_submit-uninstall-reason',
860 reason_id: (0 === $radio.length) ? 'none' : $radio.val(),
861 reason_info: (0 !== $input.length) ? $input.val().trim() : ''
862 },
863 beforeSend: function() {
864 button.addClass('disabled');
865 button.text('Processing...');
866 },
867 complete: function() {
868 window.location.href = deactivateLink;
869 }
870 });
871 });
872 });
873 }(jQuery));
874 </script>
875
876 <?php
877 }
878
879 /**
880 * Get user IP Address
881 */
882 private function get_user_ip_address() {
883 $response = wp_remote_get( 'https://icanhazip.com/' );
884
885 if ( is_wp_error( $response ) ) {
886 return '';
887 }
888
889 $ip = trim( wp_remote_retrieve_body( $response ) );
890
891 if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
892 return '';
893 }
894
895 return $ip;
896 }
897
898 /**
899 * Get site name
900 */
901 private function get_site_name() {
902 $site_name = get_bloginfo( 'name' );
903
904 if ( empty( $site_name ) ) {
905 $site_name = get_bloginfo( 'description' );
906 $site_name = wp_trim_words( $site_name, 3, '' );
907 }
908
909 if ( empty( $site_name ) ) {
910 $site_name = esc_url( home_url() );
911 }
912
913 return $site_name;
914 }
915
916 /**
917 * Send request to appsero if user skip to send tracking data
918 */
919 private function send_tracking_skipped_request() {
920 $skipped = get_option( $this->client->slug . '_tracking_skipped' );
921
922 $data = [
923 'hash' => $this->client->hash,
924 'previously_skipped' => false,
925 ];
926
927 if ( $skipped === 'yes' ) {
928 $data['previously_skipped'] = true;
929 } else {
930 update_option( $this->client->slug . '_tracking_skipped', 'yes' );
931 }
932
933 $this->client->send_request( $data, 'tracking-skipped' );
934 }
935
936 /**
937 * Deactivation modal styles
938 */
939 private function deactivation_modal_styles() {
940 ?>
941 <style type="text/css">
942 .wd-dr-modal {
943 position: fixed;
944 z-index: 99999;
945 top: 0;
946 right: 0;
947 bottom: 0;
948 left: 0;
949 background: rgba(0, 0, 0, 0.5);
950 display: none;
951 box-sizing: border-box;
952 overflow: scroll;
953 }
954
955 .wd-dr-modal * {
956 box-sizing: border-box;
957 }
958
959 .wd-dr-modal.modal-active {
960 display: block;
961 }
962
963 .wd-dr-modal-wrap {
964 max-width: 870px;
965 width: 100%;
966 position: relative;
967 margin: 10% auto;
968 background: #fff;
969 }
970
971 .wd-dr-modal-header {
972 border-bottom: 1px solid #E8E8E8;
973 padding: 20px 20px 18px 20px;
974 }
975
976 .wd-dr-modal-header h3 {
977 line-height: 1.8;
978 margin: 0;
979 color: #4A5568;
980 }
981
982 .wd-dr-modal-body {
983 padding: 5px 20px 20px 20px;
984 }
985
986 .wd-dr-modal-body .reason-input {
987 margin-top: 5px;
988 margin-left: 20px;
989 }
990
991 .wd-dr-modal-footer {
992 border-top: 1px solid #E8E8E8;
993 padding: 20px;
994 text-align: right;
995 }
996
997 .wd-dr-modal-reasons-bottom {
998 margin: 0;
999 }
1000
1001 ul.wd-de-reasons {
1002 display: flex;
1003 margin: 0 -5px 0 -5px;
1004 padding: 15px 0 20px 0;
1005 }
1006
1007 ul.wd-de-reasons.wd-de-others-reasons {
1008 padding-top: 0;
1009 display: none;
1010 }
1011
1012 ul.wd-de-reasons li {
1013 padding: 0 5px;
1014 margin: 0;
1015 width: 14.26%;
1016 }
1017
1018 ul.wd-de-reasons label {
1019 position: relative;
1020 border: 1px solid #E8E8E8;
1021 border-radius: 4px;
1022 display: block;
1023 text-align: center;
1024 height: 100%;
1025 padding: 15px 3px 8px 3px;
1026 }
1027
1028 ul.wd-de-reasons label:after {
1029 width: 0;
1030 height: 0;
1031 border-left: 8px solid transparent;
1032 border-right: 8px solid transparent;
1033 border-top: 10px solid #171717;
1034 position: absolute;
1035 left: 50%;
1036 top: 100%;
1037 margin-left: -8px;
1038 }
1039
1040 ul.wd-de-reasons label input[type="radio"] {
1041 position: absolute;
1042 left: 0;
1043 right: 0;
1044 visibility: hidden;
1045 }
1046
1047 .wd-de-reason-text {
1048 color: #4A5568;
1049 font-size: 13px;
1050 }
1051
1052 .wd-de-reason-icon {
1053 margin-bottom: 7px;
1054 }
1055
1056 ul.wd-de-reasons li.wd-de-reason-selected label {
1057 background-color: #171717;
1058 border-color: #171717;
1059 }
1060
1061 li.wd-de-reason-selected .wd-de-reason-icon svg,
1062 li.wd-de-reason-selected .wd-de-reason-icon svg g {
1063 fill: #fff;
1064 }
1065
1066 li.wd-de-reason-selected .wd-de-reason-text {
1067 color: #fff;
1068 }
1069
1070 ul.wd-de-reasons li.wd-de-reason-selected label:after {
1071 content: "";
1072 }
1073
1074 .wd-dr-modal-reason-input {
1075 margin-bottom: 15px;
1076 display: none;
1077 }
1078
1079 .wd-dr-modal-reason-input textarea {
1080 background: #FAFAFA;
1081 border: 1px solid #287EB8;
1082 border-radius: 4px;
1083 width: 100%;
1084 height: 100px;
1085 color: #524242;
1086 font-size: 13px;
1087 line-height: 1.4;
1088 padding: 11px 15px;
1089 resize: none;
1090 }
1091
1092 .wd-dr-modal-reason-input textarea:focus {
1093 outline: 0 none;
1094 box-shadow: 0 0 0;
1095 }
1096
1097 .wd-dr-button-secondary,
1098 .wd-dr-button-secondary:hover {
1099 border: 1px solid #EBEBEB;
1100 border-radius: 3px;
1101 font-size: 13px;
1102 line-height: 1.5;
1103 color: #718096;
1104 padding: 5px 12px;
1105 cursor: pointer;
1106 background-color: transparent;
1107 text-decoration: none;
1108 }
1109
1110 .wd-dr-submit-modal,
1111 .wd-dr-submit-modal:hover {
1112 border: 1px solid #171717;
1113 background-color: #171717;
1114 border-radius: 3px;
1115 font-size: 13px;
1116 line-height: 1.5;
1117 color: #fff;
1118 padding: 5px 12px;
1119 cursor: pointer;
1120 margin-left: 4px;
1121 }
1122 </style>
1123 <?php
1124 }
1125
1126 }