PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 2.5.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v2.5.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 / Utils / Insights.php

Insights.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 2.5.5, at includes/Utils/Insights.php

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