PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 2.0.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v2.0.5
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.0.5, at includes/class-betterdocs-usage-tracker.php

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