PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 2.3.6
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v2.3.6
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / class-betterdocs-usage-tracker.php

class-betterdocs-usage-tracker.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 2.3.6, at includes/class-betterdocs-usage-tracker.php

1,029 lines 40.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * BetterDocs_Plugin_Usage_Tracker
4 * This class is responsible for data sending to insights.
5 * @version 3.0.0
6 */
7
8 /**
9 * Exit if accessed directly
10 */
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 class BetterDocs_Plugin_Usage_Tracker {
16 /**
17 * WP Insights Version
18 */
19 const WPINS_VERSION = '3.0.1';
20 /**
21 * API URL
22 */
23 const API_URL = 'https://send.wpinsight.com/process-plugin-data';
24 /**
25 * Installed Plugin File
26 *
27 * @var string
28 */
29 private $plugin_file = null;
30 /**
31 * Installed Plugin Name
32 *
33 * @var string
34 */
35 private $plugin_name = null;
36 /**
37 * How often the event should subsequently
38 *
39 * @var string
40 */
41 public $recurrence = 'daily';
42 private $event_hook = null;
43 private $has_notice = false;
44 /**
45 * Instace of BetterDocs_Plugin_Usage_Tracker
46 *
47 * @var BetterDocs_Plugin_Usage_Tracker
48 */
49 private static $_instance = null;
50 /**
51 * Get Instance of BetterDocs_Plugin_Usage_Tracker
52 *
53 * @return BetterDocs_Plugin_Usage_Tracker
54 */
55 public static function get_instance( $plugin_file, $args = [] ) {
56 if ( is_null( static::$_instance ) ) {
57 static::$_instance = new static( $plugin_file, $args );
58 }
59 return static::$_instance;
60 }
61 /**
62 * Automatically Invoked when initialized.
63 *
64 * @param array $args
65 */
66 public function __construct( $plugin_file, $args = [] ) {
67 $this->plugin_file = $plugin_file;
68 $this->plugin_name = basename( $this->plugin_file, '.php' );
69 $this->disabled_wp_cron = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON == true;
70 $this->enable_self_cron = $this->disabled_wp_cron == true ? true : false;
71
72 $this->event_hook = 'put_do_weekly_action';
73
74 $this->require_optin = isset( $args['opt_in'] ) ? $args['opt_in'] : true;
75 $this->include_goodbye_form = isset( $args['goodbye_form'] ) ? $args['goodbye_form'] : true;
76 $this->marketing = isset( $args['email_marketing'] ) ? $args['email_marketing'] : true;
77 $this->options = isset( $args['options'] ) ? $args['options'] : [];
78 $this->item_id = isset( $args['item_id'] ) ? $args['item_id'] : false;
79 /**
80 * Activation Hook
81 */
82 register_activation_hook( $this->plugin_file, array( $this, 'activate_this_plugin' ) );
83 /**
84 * Deactivation Hook
85 */
86 register_deactivation_hook( $this->plugin_file, array( $this, 'deactivate_this_plugin' ) );
87 }
88 /**
89 * When user agreed to opt-in tracking schedule is enabled.
90 *
91 * @since 3.0.0
92 */
93 public function schedule_tracking() {
94 if ( $this->disabled_wp_cron ) {
95 return;
96 }
97 if ( ! wp_next_scheduled( $this->event_hook ) ) {
98 wp_schedule_event( time(), $this->recurrence, $this->event_hook );
99 }
100 }
101 /**
102 * Add the schedule event if the plugin is tracked.
103 *
104 * @return void
105 */
106 public function activate_this_plugin() {
107 $allow_tracking = $this->is_tracking_allowed();
108 if ( ! $allow_tracking ) {
109 return;
110 }
111 $this->schedule_tracking();
112 }
113 /**
114 * Remove the schedule event when plugin is deactivated and send the deactivated reason to inishghts if user submitted.
115 *
116 * @since 3.0.0
117 */
118 public function deactivate_this_plugin() {
119 /**
120 * Check tracking is allowed or not.
121 */
122 $allow_tracking = $this->is_tracking_allowed();
123 if ( ! $allow_tracking ) {
124 return;
125 }
126 $body = $this->get_data();
127 $body['status'] = 'Deactivated';
128 $body['deactivated_date'] = time();
129
130 // Check deactivation reason and add for insights data.
131 if ( false !== get_option( 'wpins_deactivation_reason_' . $this->plugin_name ) ) {
132 $body['deactivation_reason'] = get_option( 'wpins_deactivation_reason_' . $this->plugin_name );
133 }
134 if ( false !== get_option( 'wpins_deactivation_details_' . $this->plugin_name ) ) {
135 $body['deactivation_details'] = get_option( 'wpins_deactivation_details_' . $this->plugin_name );
136 }
137
138 $this->send_data( $body );
139 delete_option( 'wpins_deactivation_reason_' . $this->plugin_name );
140 delete_option( 'wpins_deactivation_details_' . $this->plugin_name );
141 /**
142 * Clear the event schedule.
143 */
144 if ( ! $this->disabled_wp_cron ) {
145 wp_clear_scheduled_hook( $this->event_hook );
146 }
147 }
148 /**
149 * Initial Method to Hook Everything.
150 *
151 * @return void
152 */
153 public function init() {
154 // $this->clicked();
155 add_action( 'wpdeveloper_notice_clicked_for_' . $this->plugin_name, array( $this, 'clicked' ) );
156 add_action( $this->event_hook, array( $this, 'do_tracking' ) );
157 // For Test
158 // add_action( 'admin_init', array( $this, 'force_tracking' ) );
159 // add_action( 'admin_notices', array( $this, 'notice' ) );
160 add_action( 'wpdeveloper_optin_notice_for_' . $this->plugin_name, array( $this, 'notice' ) );
161 /**
162 * Deactivation Reason Form and Submit Data to Insights.
163 */
164 add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'deactivate_action_links' ) );
165 add_action( 'admin_print_footer_scripts', array( $this, 'notice_script' ) );
166 add_action( 'admin_print_footer_scripts-plugins.php', array( $this, 'deactivate_reasons_form_script' ) );
167 add_action( 'admin_print_styles-plugins.php', array( $this, 'deactivate_reasons_form_style' ) );
168 add_action( 'wp_ajax_deactivation_form_' . esc_attr( $this->plugin_name ), array( $this, 'deactivate_reasons_form_submit' ) );
169 }
170 /**
171 * For Redirecting Current Page without Arguments!
172 *
173 * @return void
174 */
175 private function redirect_to() {
176 $request_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
177 $query_string = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
178 parse_str( $query_string, $current_url );
179
180 $unset_array = array( 'dismiss', 'plugin', '_wpnonce', 'later', 'plugin_action', 'marketing_optin' );
181
182 foreach ( $unset_array as $value ) {
183 if ( isset( $current_url[ $value ] ) ) {
184 unset( $current_url[ $value ] );
185 }
186 }
187
188 $current_url = http_build_query( $current_url );
189 $redirect_url = $request_uri . '?' . $current_url;
190 return $redirect_url;
191 }
192 /**
193 * This method forcing the do_tracking method to execute instant.
194 *
195 * @return void
196 */
197 public function force_tracking() {
198 $this->do_tracking( true );
199 }
200 /**
201 * This method is responsible for all the magic from the front of the plugin.
202 *
203 * @since 3.0.0
204 * @param $force Force tracking if it's not the correct time to track/
205 */
206 public function do_tracking( $force = false ) {
207 /**
208 * Check URL is set or not.
209 */
210 if ( empty( self::API_URL ) ) {
211 return;
212 }
213 /**
214 * Check is tracking allowed or not.
215 */
216 if ( ! $this->is_tracking_allowed() ) {
217 return;
218 }
219 /**
220 * Check is this the correct time to track or not.
221 * or Force to track.
222 */
223 if ( ! $this->is_time_to_track() && ! $force ) {
224 return;
225 }
226 /**
227 * Get All Data.
228 */
229 $body = $this->get_data();
230 /**
231 * Send all data.
232 */
233 return $this->send_data( $body );
234 }
235 /**
236 * Is tracking allowed?
237 *
238 * @since 1.0.0
239 */
240 private function is_tracking_allowed() {
241 // First, check if the user has changed their mind and opted out of tracking
242 if ( $this->has_user_opted_out() ) {
243 $this->set_is_tracking_allowed( false, $this->plugin_name );
244 return false;
245 }
246 // The wpins_allow_tracking option is an array of plugins that are being tracked
247 $allow_tracking = get_option( 'wpins_allow_tracking' );
248 // If this plugin is in the array, then tracking is allowed
249 if ( isset( $allow_tracking[ $this->plugin_name ] ) ) {
250 return true;
251 }
252 return false;
253 }
254 /**
255 * Set a flag in DB If tracking is allowed.
256 *
257 * @since 3.0.0
258 * @param $is_allowed Boolean true if is allowed.
259 */
260 protected function set_is_tracking_allowed( $is_allowed, $plugin = null ) {
261 if ( empty( $plugin ) ) {
262 $plugin = $this->plugin_name;
263 }
264 /**
265 * Get All Tracked Plugin List using this Tracker.
266 */
267 $allow_tracking = get_option( 'wpins_allow_tracking' );
268 /**
269 * Check user is opted out for tracking or not.
270 */
271 if ( $this->has_user_opted_out() ) {
272 if ( isset( $allow_tracking[ $plugin ] ) ) {
273 unset( $allow_tracking[ $plugin ] );
274 }
275 } elseif ( $is_allowed || ! $this->require_optin ) {
276 /**
277 * If user has agreed to allow tracking
278 */
279 if ( empty( $allow_tracking ) || ! is_array( $allow_tracking ) ) {
280 $allow_tracking = array( $plugin => $plugin );
281 } else {
282 $allow_tracking[ $plugin ] = $plugin;
283 }
284 } else {
285 if ( isset( $allow_tracking[ $plugin ] ) ) {
286 unset( $allow_tracking[ $plugin ] );
287 }
288 }
289 update_option( 'wpins_allow_tracking', $allow_tracking, 'no' );
290 }
291
292 /**
293 * Check the user has opted out or not.
294 *
295 * @since 3.0.0
296 * @return Boolean
297 */
298 protected function has_user_opted_out() {
299 if ( ! empty( $this->options ) ) {
300 foreach ( $this->options as $option_name ) {
301 $options = get_option( $option_name );
302 if ( ! empty( $options['wpins_opt_out'] ) ) {
303 return true;
304 }
305 }
306 }
307 return false;
308 }
309 /**
310 * Check if it's time to track
311 *
312 * @since 3.0.0
313 */
314 public function is_time_to_track() {
315 $track_times = get_option( 'wpins_last_track_time', array() );
316 return ! isset( $track_times[ $this->plugin_name ] ) ? true :
317 ( ( isset( $track_times[ $this->plugin_name ] ) && $track_times[ $this->plugin_name ] ) < strtotime( '-1 day' ) ? true : false );
318 }
319 /**
320 * Set tracking time.
321 *
322 * @since 3.0.0
323 */
324 public function set_track_time() {
325 $track_times = get_option( 'wpins_last_track_time', array() );
326 $track_times[ $this->plugin_name ] = time();
327 update_option( 'wpins_last_track_time', $track_times, 'no' );
328 }
329 /**
330 * This method is responsible for collecting all data.
331 *
332 * @since 3.0.0
333 */
334 public function get_data() {
335 $body = array(
336 'plugin_slug' => sanitize_text_field( $this->plugin_name ),
337 'url' => get_bloginfo( 'url' ),
338 'site_name' => get_bloginfo( 'name' ),
339 'site_version' => get_bloginfo( 'version' ),
340 'site_language' => get_bloginfo( 'language' ),
341 'charset' => get_bloginfo( 'charset' ),
342 'wpins_version' => self::WPINS_VERSION,
343 'php_version' => phpversion(),
344 'multisite' => is_multisite(),
345 'file_location' => __FILE__,
346 );
347
348 // Collect the email if the correct option has been set
349 if ( $this->marketing ) {
350 if ( ! function_exists( 'wp_get_current_user' ) ) {
351 include ABSPATH . 'wp-includes/pluggable.php';
352 }
353 $current_user = wp_get_current_user();
354 $email = $current_user->user_email;
355 if ( is_email( $email ) ) {
356 $body['email'] = $email;
357 }
358 }
359 $body['marketing_method'] = $this->marketing;
360 $body['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
361
362 /**
363 * Collect all active and inactive plugins
364 */
365 if ( ! function_exists( 'get_plugins' ) ) {
366 include ABSPATH . '/wp-admin/includes/plugin.php';
367 }
368 $plugins = array_keys( get_plugins() );
369 $active_plugins = is_network_admin() ? array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) : get_option( 'active_plugins', array() );
370 foreach ( $plugins as $key => $plugin ) {
371 if ( in_array( $plugin, $active_plugins ) ) {
372 unset( $plugins[ $key ] );
373 }
374 }
375 $body['active_plugins'] = $active_plugins;
376 $body['inactive_plugins'] = $plugins;
377
378 /**
379 * Text Direction.
380 */
381 $body['text_direction'] = ( function_exists( 'is_rtl' ) ? ( is_rtl() ? 'RTL' : 'LTR' ) : 'NOT SET' );
382 /**
383 * Get Our Plugin Data.
384 *
385 * @since 3.0.0
386 */
387 $plugin = $this->plugin_data();
388 if ( empty( $plugin ) ) {
389 $body['message'] .= __( 'We can\'t detect any plugin information. This is most probably because you have not included the code in the plugin main file.', 'plugin-usage-tracker' );
390 $body['status'] = 'NOT FOUND';
391 } else {
392 if ( isset( $plugin['Name'] ) ) {
393 $body['plugin'] = sanitize_text_field( $plugin['Name'] );
394 }
395 if ( isset( $plugin['Version'] ) ) {
396 $body['version'] = sanitize_text_field( $plugin['Version'] );
397 }
398 $body['status'] = 'Active';
399 }
400
401 /**
402 * Get active theme name and version
403 *
404 * @since 3.0.0
405 */
406 $theme = wp_get_theme();
407 if ( $theme->Name ) {
408 $body['theme'] = sanitize_text_field( $theme->Name );
409 }
410 if ( $theme->Version ) {
411 $body['theme_version'] = sanitize_text_field( $theme->Version );
412 }
413 return $body;
414 }
415
416 /**
417 * Collect plugin data,
418 * Retrieve current plugin information
419 *
420 * @since 3.0.0
421 */
422 public function plugin_data() {
423 if ( ! function_exists( 'get_plugin_data' ) ) {
424 include ABSPATH . '/wp-admin/includes/plugin.php';
425 }
426 $plugin = get_plugin_data( $this->plugin_file );
427 return $plugin;
428 }
429 /**
430 * Send the data to insights.
431 *
432 * @since 3.0.0
433 */
434 public function send_data( $body ) {
435 /**
436 * Get SITE ID
437 */
438 $site_id_key = "wpins_{$this->plugin_name}_site_id";
439 $site_id = get_option( $site_id_key, false );
440 $failed_data = [];
441 $site_url = get_bloginfo( 'url' );
442 $original_site_url = get_option( "wpins_{$this->plugin_name}_original_url", false );
443 if ( $original_site_url === false && version_compare( $body['wpins_version'], '3.0.1', '==' ) ) {
444 $site_id = false;
445 }
446 /**
447 * Send Initial Data to API
448 */
449 if ( $site_id == false && $this->item_id !== false && $original_site_url === false ) {
450 if ( isset( $_SERVER['REMOTE_ADDR'] ) && ! empty( $_SERVER['REMOTE_ADDR'] && $_SERVER['REMOTE_ADDR'] != '127.0.0.1' ) ) {
451 $country_request = wp_remote_get( 'http://ip-api.com/json/' . $_SERVER['REMOTE_ADDR'] . '?fields=country' );
452 if ( ! is_wp_error( $country_request ) && $country_request['response']['code'] == 200 ) {
453 $ip_data = json_decode( $country_request['body'] );
454 $body['country'] = isset( $ip_data->country ) ? $ip_data->country : 'NOT SET';
455 }
456 }
457
458 $body['plugin_slug'] = $this->plugin_name;
459 $body['url'] = $site_url;
460 $body['item_id'] = $this->item_id;
461
462 $request = $this->remote_post( $body );
463 if ( ! is_wp_error( $request ) && $request['response']['code'] == 200 ) {
464 $retrieved_body = json_decode( wp_remote_retrieve_body( $request ), true );
465 if ( is_array( $retrieved_body ) && isset( $retrieved_body['siteId'] ) ) {
466 update_option( $site_id_key, $retrieved_body['siteId'], 'no' );
467 update_option( "wpins_{$this->plugin_name}_original_url", $site_url, 'no' );
468 update_option( "wpins_{$this->plugin_name}_{$retrieved_body['siteId']}", $body, 'no' );
469 }
470 } else {
471 $failed_data = $body;
472 }
473 }
474
475 $site_id_data_key = "wpins_{$this->plugin_name}_{$site_id}";
476 $site_id_data_failed_key = "wpins_{$this->plugin_name}_{$site_id}_send_failed";
477
478 if ( $site_id != false ) {
479 $old_sent_data = get_option( $site_id_data_key, [] );
480 $diff_data = $this->diff( $body, $old_sent_data );
481 $failed_data = get_option( $site_id_data_failed_key, [] );
482 if ( ! empty( $failed_data ) && $diff_data != $failed_data ) {
483 $failed_data = array_merge( $failed_data, $diff_data );
484 }
485 }
486
487 if ( ! empty( $failed_data ) && $site_id != false ) {
488 $failed_data['plugin_slug'] = $this->plugin_name;
489 $failed_data['url'] = $site_url;
490 $failed_data['site_id'] = $site_id;
491 if ( $original_site_url != false ) {
492 $failed_data['original_url'] = $original_site_url;
493 }
494
495 $request = $this->remote_post( $failed_data );
496 if ( ! is_wp_error( $request ) ) {
497 delete_option( $site_id_data_failed_key );
498 $replaced_data = array_merge( $old_sent_data, $failed_data );
499 update_option( $site_id_data_key, $replaced_data, 'no' );
500 }
501 }
502
503 if ( ! empty( $diff_data ) && $site_id != false && empty( $failed_data ) ) {
504 $diff_data['plugin_slug'] = $this->plugin_name;
505 $diff_data['url'] = $site_url;
506 $diff_data['site_id'] = $site_id;
507 if ( $original_site_url != false ) {
508 $diff_data['original_url'] = $original_site_url;
509 }
510
511 $request = $this->remote_post( $diff_data );
512 if ( is_wp_error( $request ) ) {
513 update_option( $site_id_data_failed_key, $diff_data, 'no' );
514 } else {
515 $replaced_data = array_merge( $old_sent_data, $diff_data );
516 update_option( $site_id_data_key, $replaced_data, 'no' );
517 }
518 }
519
520 $this->set_track_time();
521
522 if ( isset( $request ) && is_wp_error( $request ) ) {
523 return $request;
524 }
525
526 if ( isset( $request ) ) {
527 return true;
528 }
529 return false;
530 }
531 /**
532 * WP_REMOTE_POST method responsible for send data to the API_URL
533 *
534 * @param array $data
535 * @param array $args
536 * @return void
537 */
538 protected function remote_post( $data = array(), $args = array() ) {
539 if ( empty( $data ) ) {
540 return;
541 }
542
543 $args = wp_parse_args( $args, array(
544 'method' => 'POST',
545 'timeout' => 30,
546 'redirection' => 5,
547 'httpversion' => '1.1',
548 'blocking' => true,
549 'body' => $data,
550 'user-agent' => 'PUT/1.0.0; ' . get_bloginfo( 'url' ),
551 )
552 );
553
554 $request = wp_remote_post( esc_url( self::API_URL ), $args );
555 if ( is_wp_error( $request ) || ( isset( $request['response'], $request['response']['code'] ) && $request['response']['code'] != 200 ) ) {
556 return new \WP_Error( 500, 'Something went wrong.' );
557 }
558
559 return $request;
560 }
561 /**
562 * Difference between old and new data
563 *
564 * @param array $new_data
565 * @param array $old_data
566 * @return void
567 */
568 protected function diff( $new_data, $old_data ) {
569 $data = [];
570 if ( ! empty( $new_data ) ) {
571 foreach ( $new_data as $key => $value ) {
572 if ( isset( $old_data[ $key ] ) ) {
573 if ( $old_data[ $key ] == $value ) {
574 continue;
575 }
576 }
577 $data[ $key ] = $value;
578 }
579 }
580 return $data;
581 }
582 /**
583 * Display the admin notice to users to allow them to opt in
584 *
585 * @since 3.0.0
586 */
587 public function notice() {
588 /**
589 * Return if notice is not set.
590 */
591 if ( ! isset( $this->notice_options['notice'] ) ) {
592 return;
593 }
594 /**
595 * Check is allowed or blocked for notice.
596 */
597 $block_notice = get_option( 'wpins_block_notice' );
598 if ( isset( $block_notice[ $this->plugin_name ] ) ) {
599 return;
600 }
601 if ( ! current_user_can( 'manage_options' ) ) {
602 return;
603 }
604
605 $this->has_notice = true;
606
607 $url_yes = add_query_arg( [
608 'plugin' => $this->plugin_name,
609 'plugin_action' => 'yes',
610 ]
611 );
612 $url_no = add_query_arg( array(
613 'plugin' => $this->plugin_name,
614 'plugin_action' => 'no',
615 )
616 );
617
618 // Decide on notice text
619 $notice_text = $this->notice_options['notice'] . ' <a href="#" class="wpinsights-' . esc_attr( $this->plugin_name ) . '-collect" >' . $this->notice_options['consent_button_text'] . '</a>';
620 $extra_notice_text = $this->notice_options['extra_notice'];
621
622 ?>
623
624 <div class="nx-optin">
625 <p><?php echo wp_kses_post( $notice_text ); ?></p>
626 <div class="wpinsights-data" style="display: none;">
627 <p><?php echo wp_kses_post( $extra_notice_text ); ?></p>
628 </div>
629 <p>
630 <a href="<?php echo esc_url( $url_yes ); ?>" class="button-primary">
631 <?php echo esc_html( $this->notice_options['yes'] ); ?>
632 </a>&nbsp;
633 <a href="<?php echo esc_url( $url_no ); ?>" class="button-secondary">
634 <?php echo esc_html( $this->notice_options['no'] ); ?>
635 </a>
636 </p>
637 </div>
638
639 <?php
640 }
641
642 public function notice_script() {
643 if ( $this->has_notice ) {
644 echo "<script type='text/javascript'>jQuery('.wpinsights-" . esc_attr( $this->plugin_name ) . "-collect').on('click', function(e) {e.preventDefault();jQuery('.wpinsights-data').slideToggle('fast');});</script>";
645 }
646 }
647 /**
648 * Set all notice options to customized notice.
649 *
650 * @since 3.0.0
651 * @param array $options
652 * @return void
653 */
654 public function set_notice_options( $options = [] ) {
655 $default_options = [
656 'consent_button_text' => __( 'What we collect.', 'wpinsight' ),
657 'yes' => __( 'Sure, I\'d like to help', 'wpinsight' ),
658 'no' => __( 'No Thanks.', 'wpinsight' ),
659 ];
660 $options = wp_parse_args( $options, $default_options );
661 $this->notice_options = $options;
662 }
663 /**
664 * Responsible for track the click from Notice.
665 *
666 * @return void
667 */
668 public function clicked( $notice = null ) {
669 if ( isset( $_GET['plugin'] ) && isset( $_GET['plugin_action'] ) ) {
670 if ( isset( $_GET['tab'] ) && $_GET['tab'] === 'plugin-information' ) {
671 return;
672 }
673 $plugin = sanitize_text_field( $_GET['plugin'] );
674 $action = sanitize_text_field( $_GET['plugin_action'] );
675 if ( $action == 'yes' ) {
676 $this->schedule_tracking();
677 $this->set_is_tracking_allowed( true, $plugin );
678 if ( $this->do_tracking( true ) ) {
679 $this->update_block_notice( $plugin );
680 }
681 } else {
682 $this->set_is_tracking_allowed( false, $plugin );
683 $this->update_block_notice( $plugin );
684 }
685
686 if( ! is_null ( $notice ) ) {
687 $notice->dismiss->dismiss_notice();
688 }
689
690 /**
691 * Redirect User To the Current URL, but without set query arguments.
692 */
693 wp_safe_redirect( $this->redirect_to() );
694 }
695 }
696 /**
697 * Set if we should block the opt-in notice for this plugin
698 *
699 * @since 3.0.0
700 */
701 public function update_block_notice( $plugin = null ) {
702 if ( empty( $plugin ) ) {
703 $plugin = $this->plugin_name;
704 }
705 $block_notice = get_option( 'wpins_block_notice' );
706 if ( empty( $block_notice ) || ! is_array( $block_notice ) ) {
707 $block_notice = array( $plugin => $plugin );
708 } else {
709 $block_notice[ $plugin ] = $plugin;
710 }
711 update_option( 'wpins_block_notice', $block_notice, 'no' );
712 }
713 /**
714 * AJAX callback when the deactivated form is submitted.
715 *
716 * @since 3.0.0
717 */
718 public function deactivate_reasons_form_submit() {
719 check_ajax_referer( 'wpins_deactivation_nonce', 'security' );
720 if ( isset( $_POST['values'] ) ) {
721 $values = sanitize_text_field( $_POST['values'] );
722 update_option( 'wpins_deactivation_reason_' . $this->plugin_name, $values, 'no' );
723 }
724 if ( isset( $_POST['details'] ) ) {
725 $details = sanitize_text_field( $_POST['details'] );
726 update_option( 'wpins_deactivation_details_' . $this->plugin_name, $details, 'no' );
727 }
728 echo 'success';
729 wp_die();
730 }
731 /**
732 * Filter the deactivation link to allow us to present a form when the user deactivates the plugin
733 *
734 * @since 3.0.0
735 */
736 public function deactivate_action_links( $links ) {
737 /**
738 * Check is tracking allowed or not.
739 */
740 if ( ! $this->is_tracking_allowed() ) {
741 return $links;
742 }
743 if ( isset( $links['deactivate'] ) && $this->include_goodbye_form ) {
744 $deactivation_link = $links['deactivate'];
745 /**
746 * Change the default deactivate button link.
747 */
748 $deactivation_link = str_replace( '<a ', '<div class="wpinsights-goodbye-form-wrapper-' . esc_attr( $this->plugin_name ) . '"><div class="wpinsights-goodbye-form-bg"></div><span class="wpinsights-goodbye-form" id="wpinsights-goodbye-form"></span></div><a onclick="javascript:event.preventDefault();" id="wpinsights-goodbye-link-' . esc_attr( $this->plugin_name ) . '" ', $deactivation_link );
749 $links['deactivate'] = $deactivation_link;
750 }
751 return $links;
752 }
753 /**
754 * ALL Deactivate Reasons.
755 *
756 * @since 3.0.0
757 */
758 public function deactivation_reasons() {
759 $form = array();
760 $form['heading'] = __( 'Sorry to see you go', 'wpinsight' );
761 $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', 'wpinsight' );
762
763 $form['options'] = array(
764 __( 'I no longer need the plugin', 'wpinsight' ),
765 [
766 'label' => __( 'I found a better plugin', 'wpinsight' ),
767 'extra_field' => __( 'Please share which plugin', 'wpinsight' ),
768 ],
769 __( "I couldn't get the plugin to work", 'wpinsight' ),
770 __( 'It\'s a temporary deactivation', 'wpinsight' ),
771 [
772 'label' => __( 'Other', 'wpinsight' ),
773 'extra_field' => __( 'Please share the reason', 'wpinsight' ),
774 'type' => 'textarea',
775 ],
776 );
777 return apply_filters( 'wpins_form_text_' . $this->plugin_name, $form );
778 }
779 /**
780 * Deactivate Reasons Form.
781 * This form will appears when user wants to deactivate the plugin to send you deactivated reasons.
782 *
783 * @since 3.0.0
784 */
785 public function deactivate_reasons_form_style() {
786 ?>
787 <style type="text/css">
788 .wpinsights-form-active-notificationx .wpinsights-goodbye-form-bg {
789 background: rgba(0, 0, 0, .8);
790 position: fixed;
791 top: 0;
792 left: 0;
793 width: 100%;
794 height: 100%;
795 z-index: 9;
796 }
797
798 .wpinsights-goodbye-form-wrapper-notificationx {
799 position: relative;
800 display: none;
801 }
802
803 .wpinsights-form-active-notificationx .wpinsights-goodbye-form-wrapper-notificationx {
804 display: flex !important;
805 position: fixed;
806 top: 0;
807 left: 0;
808 width: 100%;
809 height: 100%;
810 justify-content: center;
811 align-items: center;
812 }
813
814 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form {
815 display: none;
816 }
817
818 .wpinsights-form-active-notificationx .wpinsights-goodbye-form {
819 position: relative !important;
820 width: 550px;
821 max-width: 80%;
822 background: #fff;
823 box-shadow: 2px 8px 23px 3px rgba(0, 0, 0, .2);
824 border-radius: 3px;
825 white-space: normal;
826 overflow: hidden;
827 display: block;
828 z-index: 999999;
829 }
830
831 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-head {
832 background: #fff;
833 color: #495157;
834 padding: 18px;
835 box-shadow: 0 0 8px rgba(0, 0, 0, .1);
836 font-size: 15px;
837 }
838
839 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form .wpinsights-goodbye-form-head strong {
840 font-size: 15px;
841 }
842
843 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body {
844 padding: 8px 18px;
845 color: #333;
846 }
847
848 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body label {
849 padding-left: 5px;
850 color: #6d7882;
851 }
852
853 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body .wpinsights-goodbye-form-caption {
854 font-weight: 500;
855 font-size: 15px;
856 color: #495157;
857 line-height: 1.4;
858 }
859
860 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options {
861 padding-top: 5px;
862 }
863
864 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li {
865 margin-bottom: 15px;
866 }
867
868 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div {
869 display: inline;
870 padding-left: 3px;
871 }
872
873 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div>input,
874 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div>textarea {
875 margin: 10px 18px;
876 padding: 8px;
877 width: 80%;
878 }
879
880 .wpinsights-goodbye-form-wrapper-notificationx .deactivating-spinner {
881 display: none;
882 padding-bottom: 20px !important;
883 }
884
885 .wpinsights-goodbye-form-wrapper-notificationx .deactivating-spinner .spinner {
886 float: none;
887 margin: 4px 4px 0 18px;
888 vertical-align: bottom;
889 visibility: visible;
890 }
891
892 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer {
893 padding: 8px 18px;
894 margin-bottom: 15px;
895 }
896
897 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer>.wpinsights-goodbye-form-buttons {
898 display: flex;
899 align-items: center;
900 justify-content: space-between;
901 }
902
903 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer .wpinsights-submit-btn {
904 background-color: #d30c5c;
905 -webkit-border-radius: 3px;
906 border-radius: 3px;
907 color: #fff;
908 line-height: 1;
909 padding: 15px 20px;
910 font-size: 13px;
911 }
912
913 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer .wpinsights-deactivate-btn {
914 font-size: 13px;
915 color: #a4afb7;
916 background: none;
917 float: right;
918 padding-right: 10px;
919 width: auto;
920 text-decoration: underline;
921 }
922
923 .wpinsights-goodbye-form-wrapper-notificationx .test {}
924 </style>
925
926 <?php
927 }
928
929 /**
930 * Deactivate Reasons Form.
931 * This form will appears when user wants to deactivate the plugin to send you deactivated reasons.
932 *
933 * @since 3.0.0
934 */
935 public function deactivate_reasons_form_script() {
936 $form = $this->deactivation_reasons();
937
938 $html = '<div class="wpinsights-goodbye-form-head"><strong>' . esc_html( $form['heading'] ) . '</strong></div>';
939 $html .= '<div class="wpinsights-goodbye-form-body"><p class="wpinsights-goodbye-form-caption">' . esc_html( $form['body'] ) . '</p>';
940 if ( is_array( $form['options'] ) ) {
941 $html .= '<div id="wpinsights-goodbye-options" class="wpinsights-goodbye-options"><ul>';
942 foreach ( $form['options'] as $option ) {
943 if ( is_array( $option ) ) {
944 $id = strtolower( str_replace( ' ', '_', esc_attr( $option['label'] ) ) );
945 $id = $id . '_' . $this->plugin_name;
946 $html .= '<li class="has-goodbye-extra">';
947 $html .= '<input type="radio" name="wpinsights-' . esc_attr( $this->plugin_name ) . '-goodbye-options" id="' . esc_attr( $id ) . '" value="' . esc_attr( $option['label'] ) . '">';
948 $html .= '<div><label for="' . esc_attr( $id ) . '">' . esc_attr( $option['label'] ) . '</label>';
949 if ( isset( $option['extra_field'] ) && ! isset( $option['type'] ) ) {
950 $html .= '<input type="text" style="display: none" name="' . esc_attr( $id ) . '" id="' . str_replace( ' ', '', esc_attr( $option['extra_field'] ) ) . '" placeholder="' . esc_attr( $option['extra_field'] ) . '">';
951 }
952 if ( isset( $option['extra_field'] ) && isset( $option['type'] ) ) {
953 $html .= '<' . $option['type'] . ' style="display: none" type="text" name="' . esc_attr( $id ) . '" id="' . str_replace( ' ', '', esc_attr( $option['extra_field'] ) ) . '" placeholder="' . esc_attr( $option['extra_field'] ) . '"></' . $option['type'] . '>';
954 }
955 $html .= '</div></li>';
956 } else {
957 $id = strtolower( str_replace( ' ', '_', esc_attr( $option ) ) );
958 $id = $id . '_' . $this->plugin_name;
959 $html .= '<li><input type="radio" name="wpinsights-' . esc_attr( $this->plugin_name ) . '-goodbye-options" id="' . esc_attr( $id ) . '" value="' . esc_attr( $option ) . '"> <label for="' . esc_attr( $id ) . '">' . esc_attr( $option ) . '</label></li>';
960 }
961 }
962 $html .= '</ul></div><!-- .wpinsights-' . esc_attr( $this->plugin_name ) . '-goodbye-options -->';
963 }
964 $html .= '</div><!-- .wpinsights-goodbye-form-body -->';
965 $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', 'wpinsight' ) . '</p>';
966
967 ?>
968 <script type="text/javascript">
969 jQuery(document).ready(function($){
970 $("#wpinsights-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>").on("click",function(){
971 // We'll send the user to this deactivation link when they've completed or dismissed the form
972 var url = document.getElementById("wpinsights-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>");
973 $('body').toggleClass('wpinsights-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
974 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> #wpinsights-goodbye-form").fadeIn();
975 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> #wpinsights-goodbye-form").html( '<?php echo $html; ?>' + '<div class="wpinsights-goodbye-form-footer"><div class="wpinsights-goodbye-form-buttons"><a id="wpinsights-submit-form-<?php echo esc_attr( $this->plugin_name ); ?>" class="wpinsights-submit-btn" href="#"><?php esc_html_e( 'Submit and Deactivate', 'wpinsight' ); ?></a>&nbsp;<a class="wpsp-put-deactivate-btn" href="'+url+'"><?php esc_html_e( 'Just Deactivate', 'wpinsight' ); ?></a></div></div>');
976 $('#wpinsights-submit-form-<?php echo esc_attr( $this->plugin_name ); ?>').on('click', function(e){
977 // As soon as we click, the body of the form should disappear
978 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpinsights-goodbye-form-body").fadeOut();
979 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpinsights-goodbye-form-footer").fadeOut();
980 // Fade in spinner
981 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .deactivating-spinner").fadeIn();
982 e.preventDefault();
983 var checkedInput = $("input[name='wpinsights-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options']:checked"),
984 checkedInputVal, details;
985 if( checkedInput.length > 0 ) {
986 checkedInputVal = checkedInput.val();
987 details = $('input[name="'+ checkedInput[0].id +'"], textarea[name="'+ checkedInput[0].id +'"]').val();
988 }
989
990 if( typeof details === 'undefined' ) {
991 details = '';
992 }
993 if( typeof checkedInputVal === 'undefined' ) {
994 checkedInputVal = 'No Reason';
995 }
996
997 var data = {
998 'action': 'deactivation_form_<?php echo esc_attr( $this->plugin_name ); ?>',
999 'values': checkedInputVal,
1000 'details': details,
1001 'security': "<?php echo wp_create_nonce( 'wpins_deactivation_nonce' ); ?>",
1002 'dataType': "json"
1003 }
1004
1005 $.post(
1006 ajaxurl,
1007 data,
1008 function(response){
1009 // Redirect to original deactivation URL
1010 window.location.href = url;
1011 }
1012 );
1013 });
1014 $('#wpinsights-goodbye-options > ul ').on('click', 'li label, li > input', function( e ){
1015 var parent = $(this).parents('li');
1016 parent.siblings().find('label').next('input, textarea').css('display', 'none');
1017 parent.find('label').next('input, textarea').css('display', 'block');
1018 });
1019 // If we click outside the form, the form will close
1020 $('.wpinsights-goodbye-form-bg').on('click',function(){
1021 $("#wpinsights-goodbye-form").fadeOut();
1022 $('body').removeClass('wpinsights-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
1023 });
1024 });
1025 });
1026 </script>
1027 <?php
1028 }
1029 }