PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.1
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.1
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 / Insights / Insights.php

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

1,201 lines 53.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPDeveloper\BetterDocs\Insights;
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.3';
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 = array();
50 private $notice_options = array();
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 = array() ) {
64 if ( is_null( static::$_instance ) ) {
65 static::$_instance = new static( $plugin_file, $args );
66 }
67 return static::$_instance;
68 }
69
70 /**
71 * Automatically Invoked when initialized.
72 *
73 * @param array $args
74 */
75 public function __construct( $plugin_file, $args = array() ) {
76 $this->plugin_file = $plugin_file;
77 $this->plugin_name = basename( $this->plugin_file, '.php' );
78 $this->disabled_wp_cron = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON == true;
79 $this->enable_self_cron = true == $this->disabled_wp_cron ? true : false;
80
81 $this->event_hook = 'put_do_weekly_action';
82
83 $this->require_optin = isset( $args[ 'opt_in' ] ) ? $args[ 'opt_in' ] : true;
84 $this->include_goodbye_form = isset( $args[ 'goodbye_form' ] ) ? $args[ 'goodbye_form' ] : true;
85 $this->marketing = isset( $args[ 'email_marketing' ] ) ? $args[ 'email_marketing' ] : true;
86 $this->options = isset( $args[ 'options' ] ) ? $args[ 'options' ] : array();
87 $this->item_id = isset( $args[ 'item_id' ] ) ? $args[ 'item_id' ] : false;
88 /**
89 * Activation Hook
90 */
91 register_activation_hook( $this->plugin_file, array( $this, 'activate_this_plugin' ) );
92 /**
93 * Deactivation Hook
94 */
95 register_deactivation_hook( $this->plugin_file, array( $this, 'deactivate_this_plugin' ) );
96 }
97
98 /**
99 * When user agreed to opt-in tracking schedule is enabled.
100 *
101 * @since 2.5.0
102 */
103 public function schedule_tracking() {
104 if ( $this->disabled_wp_cron ) {
105 return;
106 }
107 if ( ! wp_next_scheduled( $this->event_hook ) ) {
108 wp_schedule_event( time(), $this->recurrence, $this->event_hook );
109 }
110 }
111
112 /**
113 * Add the schedule event if the plugin is tracked.
114 *
115 * @return void
116 */
117 public function activate_this_plugin() {
118 $allow_tracking = $this->is_tracking_allowed();
119 if ( ! $allow_tracking ) {
120 return;
121 }
122 $this->schedule_tracking();
123 }
124
125 /**
126 * Remove the schedule event when plugin is deactivated and send the deactivated reason to inishghts if user submitted.
127 *
128 * @since 2.5.0
129 */
130 public function deactivate_this_plugin() {
131 /**
132 * Check tracking is allowed or not.
133 */
134 $allow_tracking = $this->is_tracking_allowed();
135 if ( ! $allow_tracking ) {
136 return;
137 }
138 $body = $this->get_data();
139 $body[ 'status' ] = 'Deactivated';
140 $body[ 'deactivated_date' ] = time();
141
142 // Check deactivation reason and add for insights data.
143 if ( false !== get_option( 'wpins_deactivation_reason_' . $this->plugin_name ) ) {
144 $body[ 'deactivation_reason' ] = get_option( 'wpins_deactivation_reason_' . $this->plugin_name );
145 }
146 if ( false !== get_option( 'wpins_deactivation_details_' . $this->plugin_name ) ) {
147 $body[ 'deactivation_details' ] = get_option( 'wpins_deactivation_details_' . $this->plugin_name );
148 }
149
150 $this->send_data( $body );
151 delete_option( 'wpins_deactivation_reason_' . $this->plugin_name );
152 delete_option( 'wpins_deactivation_details_' . $this->plugin_name );
153 /**
154 * Clear the event schedule.
155 */
156 if ( ! $this->disabled_wp_cron ) {
157 wp_clear_scheduled_hook( $this->event_hook );
158 }
159 }
160
161 /**
162 * Initial Method to Hook Everything.
163 *
164 * @return void
165 */
166 public function init() {
167 add_action( 'wpdeveloper_notice_clicked_for_' . $this->plugin_name, array( $this, 'clicked' ) );
168 add_action( $this->event_hook, array( $this, 'do_tracking' ) );
169 /**
170 * Self-heal the schedule. A file-only plugin update does NOT fire
171 * register_activation_hook(), so already-opted-in sites whose cron was
172 * never scheduled (or was lost) would otherwise never report again. Since
173 * init() runs on every request, re-ensure the event whenever tracking is
174 * allowed. schedule_tracking() guards with wp_next_scheduled(), so this is
175 * idempotent and respects DISABLE_WP_CRON.
176 */
177 if ( $this->is_tracking_allowed() ) {
178 $this->schedule_tracking();
179 }
180 // For Test
181 add_action( 'wpdeveloper_optin_notice_for_' . $this->plugin_name, array( $this, 'notice' ) );
182 /**
183 * Deactivation Reason Form and Submit Data to Insights.
184 */
185 if ( ! is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ) ) {
186 add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'deactivate_action_links' ) );
187 add_action( 'admin_print_footer_scripts', array( $this, 'notice_script' ) );
188 add_action( 'admin_footer-plugins.php', array( $this, 'deactivate_reasons_form' ) );
189 add_action( 'wp_ajax_deactivation_form_' . esc_attr( $this->plugin_name ), array( $this, 'deactivate_reasons_form_submit' ) );
190 }
191 }
192
193 /**
194 * For Redirecting Current Page without Arguments!
195 *
196 * @return string
197 */
198 private function redirect_to() {
199 $request_payload = isset( $_SERVER[ 'REQUEST_URI' ] ) ? esc_url_raw( wp_unslash( $_SERVER[ 'REQUEST_URI' ] ) ) : '';
200 $request_uri = wp_parse_url( $request_payload, PHP_URL_PATH );
201 $query_string = wp_parse_url( $request_payload, PHP_URL_QUERY );
202 parse_str( $query_string, $current_url );
203
204 $unset_array = array( 'dismiss', 'plugin', '_wpnonce', 'later', 'plugin_action', 'marketing_optin' );
205
206 foreach ( $unset_array as $value ) {
207 if ( isset( $current_url[ $value ] ) ) {
208 unset( $current_url[ $value ] );
209 }
210 }
211
212 $current_url = http_build_query( $current_url );
213 $redirect_url = $request_uri . '?' . $current_url;
214 return $redirect_url;
215 }
216
217 /**
218 * This method forcing the do_tracking method to execute instant.
219 *
220 * @return void
221 */
222 public function force_tracking() {
223 $this->do_tracking( true );
224 }
225
226 /**
227 * This method is responsible for all the magic from the front of the plugin.
228 *
229 * @since 2.5.0
230 * @param $force Force tracking if it's not the correct time to track/
231 */
232 public function do_tracking( $force = false ) {
233 /**
234 * Check URL is set or not.
235 */
236 if ( empty( self::API_URL ) ) {
237 return;
238 }
239 /**
240 * Check is tracking allowed or not.
241 */
242 if ( ! $this->is_tracking_allowed() ) {
243 return;
244 }
245 /**
246 * Check is this the correct time to track or not.
247 * or Force to track.
248 */
249 if ( ! $this->is_time_to_track() && ! $force ) {
250 return;
251 }
252 /**
253 * Get All Data.
254 */
255 $body = $this->get_data();
256 /**
257 * Send all data.
258 */
259 return $this->send_data( $body );
260 }
261
262 /**
263 * Is tracking allowed?
264 *
265 * @since 1.0.0
266 */
267 private function is_tracking_allowed() {
268 // First, check if the user has changed their mind and opted out of tracking
269 if ( $this->has_user_opted_out() ) {
270 $this->set_is_tracking_allowed( false, $this->plugin_name );
271 return false;
272 }
273 // The wpins_allow_tracking option is an array of plugins that are being tracked
274 $allow_tracking = get_option( 'wpins_allow_tracking' );
275 // If this plugin is in the array, then tracking is allowed
276 if ( isset( $allow_tracking[ $this->plugin_name ] ) ) {
277 return true;
278 }
279 return false;
280 }
281
282 /**
283 * Set a flag in DB If tracking is allowed.
284 *
285 * @since 2.5.0
286 * @param $is_allowed Boolean true if is allowed.
287 */
288 public function set_is_tracking_allowed( $is_allowed, $plugin = null ) {
289 if ( empty( $plugin ) ) {
290 $plugin = $this->plugin_name;
291 }
292 /**
293 * Get All Tracked Plugin List using this Tracker.
294 */
295 $allow_tracking = get_option( 'wpins_allow_tracking' );
296 /**
297 * Check user is opted out for tracking or not.
298 */
299 if ( $this->has_user_opted_out() ) {
300 if ( isset( $allow_tracking[ $plugin ] ) ) {
301 unset( $allow_tracking[ $plugin ] );
302 }
303 } elseif ( $is_allowed || ! $this->require_optin ) {
304 /**
305 * If user has agreed to allow tracking
306 */
307 if ( empty( $allow_tracking ) || ! is_array( $allow_tracking ) ) {
308 $allow_tracking = array( $plugin => $plugin );
309 } else {
310 $allow_tracking[ $plugin ] = $plugin;
311 }
312 } elseif ( isset( $allow_tracking[ $plugin ] ) ) {
313 unset( $allow_tracking[ $plugin ] );
314 }
315 update_option( 'wpins_allow_tracking', $allow_tracking, 'no' );
316 }
317
318 /**
319 * Check the user has opted out or not.
320 *
321 * @since 2.5.0
322 * @return boolean
323 */
324 protected function has_user_opted_out() {
325 if ( ! empty( $this->options ) ) {
326 foreach ( $this->options as $option_name ) {
327 $options = get_option( $option_name );
328 if ( ! empty( $options[ 'wpins_opt_out' ] ) ) {
329 return true;
330 }
331 }
332 }
333 return false;
334 }
335
336 /**
337 * Check if it's time to track
338 *
339 * @since 2.5.0
340 */
341 public function is_time_to_track() {
342 $track_times = get_option( 'wpins_last_track_time', array() );
343 return ! isset( $track_times[ $this->plugin_name ] ) ? true :
344 ( ( isset( $track_times[ $this->plugin_name ] ) && $track_times[ $this->plugin_name ] ) < strtotime( '-1 day' ) ? true : false );
345 }
346
347 /**
348 * Set tracking time.
349 *
350 * @since 2.5.0
351 */
352 public function set_track_time() {
353 $track_times = get_option( 'wpins_last_track_time', array() );
354 $track_times[ $this->plugin_name ] = time();
355 update_option( 'wpins_last_track_time', $track_times, 'no' );
356 }
357
358 /**
359 * This method is responsible for collecting all data.
360 *
361 * @since 2.5.0
362 */
363 public function get_data() {
364 $body = array(
365 'plugin_slug' => sanitize_text_field( $this->plugin_name ),
366 'url' => get_bloginfo( 'url' ),
367 'site_name' => get_bloginfo( 'name' ),
368 'site_version' => get_bloginfo( 'version' ),
369 'site_language' => get_bloginfo( 'language' ),
370 'charset' => get_bloginfo( 'charset' ),
371 'wpins_version' => self::WPINS_VERSION,
372 'php_version' => phpversion(),
373 'multisite' => is_multisite(),
374 'file_location' => __FILE__
375 );
376
377 // Collect the email if the correct option has been set
378 if ( $this->marketing ) {
379 if ( ! function_exists( 'wp_get_current_user' ) ) {
380 include ABSPATH . 'wp-includes/pluggable.php';
381 }
382 $current_user = wp_get_current_user();
383 $email = $current_user->user_email;
384 if ( is_email( $email ) ) {
385 $body[ 'email' ] = $email;
386 }
387 }
388 $body[ 'marketing_method' ] = $this->marketing;
389 $body[ 'server' ] = isset( $_SERVER[ 'SERVER_SOFTWARE' ] ) ? $_SERVER[ 'SERVER_SOFTWARE' ] : ''; //phpcs:ignore
390
391 /**
392 * Collect all active and inactive plugins
393 */
394 if ( ! function_exists( 'get_plugins' ) ) {
395 include ABSPATH . '/wp-admin/includes/plugin.php';
396 }
397 $plugins = array_keys( get_plugins() );
398 $active_plugins = is_network_admin() ? array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) : get_option( 'active_plugins', array() );
399 foreach ( $plugins as $key => $plugin ) {
400 if ( in_array( $plugin, $active_plugins ) ) {
401 unset( $plugins[ $key ] );
402 }
403 }
404 $body[ 'active_plugins' ] = $active_plugins;
405 $body[ 'inactive_plugins' ] = $plugins;
406
407 /**
408 * Text Direction.
409 */
410 $body[ 'text_direction' ] = ( function_exists( 'is_rtl' ) ? ( is_rtl() ? 'RTL' : 'LTR' ) : 'NOT SET' );
411 /**
412 * Get Our Plugin Data.
413 *
414 * @since 2.5.0
415 */
416 $plugin = $this->plugin_data();
417 if ( empty( $plugin ) ) {
418 $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' );
419 $body[ 'status' ] = 'NOT FOUND';
420 } else {
421 if ( isset( $plugin[ 'Name' ] ) ) {
422 $body[ 'plugin' ] = sanitize_text_field( $plugin[ 'Name' ] );
423 }
424 if ( isset( $plugin[ 'Version' ] ) ) {
425 $body[ 'version' ] = sanitize_text_field( $plugin[ 'Version' ] );
426 }
427 $body[ 'status' ] = 'Active';
428 }
429
430 /**
431 * Get active theme name and version
432 *
433 * @since 2.5.0
434 */
435 $theme = wp_get_theme();
436 if ( $theme->Name ) {
437 $body[ 'theme' ] = sanitize_text_field( $theme->Name );
438 }
439 if ( $theme->Version ) {
440 $body[ 'theme_version' ] = sanitize_text_field( $theme->Version );
441 }
442
443 /**
444 * Extension point for product-usage analytics.
445 *
446 * Collectors (Free / Pro / Chatbot) hook this to inject their `bd_*`
447 * metric groups into `$body['optional_data']` (a nested array). See
448 * `WPDeveloper\BetterDocs\Insights\Collector` and `docs/insights-tracking.md`.
449 *
450 * @param array $body The tracking payload.
451 * @param Insights $this The Insights instance.
452 */
453 $body = apply_filters( 'betterdocs_insights_data', $body, $this );
454
455 /**
456 * wpinsight persists only the fixed base fields plus a single
457 * `optional_data` field, which it expects as a JSON-encoded string.
458 * Collectors assemble it as a nested array; here we JSON-encode each
459 * first-layer group (bd_features, bd_counts, …) into its own string
460 * value, then JSON-encode the whole `optional_data` object.
461 */
462 if ( ! empty( $body[ 'optional_data' ] ) && is_array( $body[ 'optional_data' ] ) ) {
463 foreach ( $body[ 'optional_data' ] as $group => $value ) {
464 if ( is_array( $value ) ) {
465 $body[ 'optional_data' ][ $group ] = wp_json_encode( $value );
466 }
467 }
468 $body[ 'optional_data' ] = wp_json_encode( $body[ 'optional_data' ] );
469 }
470
471 return $body;
472 }
473
474 /**
475 * Collect plugin data,
476 * Retrieve current plugin information
477 *
478 * @since 2.5.0
479 */
480 public function plugin_data() {
481 if ( ! function_exists( 'get_plugin_data' ) ) {
482 include ABSPATH . '/wp-admin/includes/plugin.php';
483 }
484 $plugin = get_plugin_data( $this->plugin_file );
485 return $plugin;
486 }
487
488 /**
489 * Send the data to insights.
490 *
491 * @since 2.5.0
492 */
493 public function send_data( $body ) {
494 /**
495 * Get SITE ID
496 */
497 $site_id_key = "wpins_{$this->plugin_name}_site_id";
498 $site_id = get_option( $site_id_key, false );
499 $failed_data = array();
500 $site_url = get_bloginfo( 'url' );
501 $original_site_url = get_option( "wpins_{$this->plugin_name}_original_url", false );
502 // Re-register when the site has no recorded origin URL yet, or when the
503 // current URL no longer matches it (domain migration). Without the URL-change
504 // arm (added in tracker 3.0.3), a moved site keeps reporting under its old
505 // site ID with stale identity data. Mirrors EA Lite's 3.0.3 send_data().
506 if ( ( false === $original_site_url || $original_site_url != $site_url ) && version_compare( $body[ 'wpins_version' ], '3.0.1', '>=' ) ) {
507 $site_id = false;
508 }
509 /**
510 * Send Initial Data to API
511 */
512 if ( false == $site_id && false !== $this->item_id && ( false === $original_site_url || $original_site_url != $site_url ) ) {
513 if ( isset( $_SERVER[ 'REMOTE_ADDR' ] ) && ! empty( $_SERVER[ 'REMOTE_ADDR' ] && '127.0.0.1' != $_SERVER[ 'REMOTE_ADDR' ] ) ) { //phpcs:ignore
514 $country_request = wp_remote_get( 'http://ip-api.com/json/' . $_SERVER[ 'REMOTE_ADDR' ] . '?fields=country' ); //phpcs:ignore
515 if ( ! is_wp_error( $country_request ) && 200 == $country_request[ 'response' ][ 'code' ] ) {
516 $ip_data = json_decode( $country_request[ 'body' ] );
517 $body[ 'country' ] = isset( $ip_data->country ) ? $ip_data->country : 'NOT SET';
518 }
519 }
520
521 $body[ 'plugin_slug' ] = $this->plugin_name;
522 $body[ 'url' ] = $site_url;
523 $body[ 'item_id' ] = $this->item_id;
524
525 $request = $this->remote_post( $body );
526 if ( ! is_wp_error( $request ) && 200 == $request[ 'response' ][ 'code' ] ) {
527 $retrieved_body = json_decode( wp_remote_retrieve_body( $request ), true );
528 if ( is_array( $retrieved_body ) && isset( $retrieved_body[ 'siteId' ] ) ) {
529 update_option( $site_id_key, $retrieved_body[ 'siteId' ], 'no' );
530 update_option( "wpins_{$this->plugin_name}_original_url", $site_url, 'no' );
531 update_option( "wpins_{$this->plugin_name}_{$retrieved_body[ 'siteId' ]}", $body, 'no' );
532 }
533 } else {
534 $failed_data = $body;
535 }
536 }
537
538 $site_id_data_key = "wpins_{$this->plugin_name}_{$site_id}";
539 $site_id_data_failed_key = "wpins_{$this->plugin_name}_{$site_id}_send_failed";
540
541 if ( false != $site_id ) {
542 $old_sent_data = get_option( $site_id_data_key, array() );
543 $diff_data = $this->diff( $body, $old_sent_data );
544 $failed_data = get_option( $site_id_data_failed_key, array() );
545 if ( ! empty( $failed_data ) && $diff_data != $failed_data ) {
546 $failed_data = array_merge( $failed_data, $diff_data );
547 }
548 }
549
550 if ( ! empty( $failed_data ) && false != $site_id ) {
551 $failed_data[ 'plugin_slug' ] = $this->plugin_name;
552 $failed_data[ 'url' ] = $site_url;
553 $failed_data[ 'site_id' ] = $site_id;
554 if ( false != $original_site_url ) {
555 $failed_data[ 'original_url' ] = $original_site_url;
556 }
557
558 $request = $this->remote_post( $failed_data );
559 if ( ! is_wp_error( $request ) ) {
560 delete_option( $site_id_data_failed_key );
561 $replaced_data = array_merge( $old_sent_data, $failed_data );
562 update_option( $site_id_data_key, $replaced_data, 'no' );
563 }
564 }
565
566 if ( ! empty( $diff_data ) && false != $site_id && empty( $failed_data ) ) {
567 $diff_data[ 'plugin_slug' ] = $this->plugin_name;
568 $diff_data[ 'url' ] = $site_url;
569 $diff_data[ 'site_id' ] = $site_id;
570 if ( false != $original_site_url ) {
571 $diff_data[ 'original_url' ] = $original_site_url;
572 }
573
574 $request = $this->remote_post( $diff_data );
575 if ( is_wp_error( $request ) ) {
576 update_option( $site_id_data_failed_key, $diff_data, 'no' );
577 } else {
578 $replaced_data = array_merge( $old_sent_data, $diff_data );
579 update_option( $site_id_data_key, $replaced_data, 'no' );
580 }
581 }
582
583 $this->set_track_time();
584
585 if ( isset( $request ) && is_wp_error( $request ) ) {
586 return $request;
587 }
588
589 if ( isset( $request ) ) {
590 return true;
591 }
592 return false;
593 }
594
595 /**
596 * WP_REMOTE_POST method responsible for send data to the API_URL
597 *
598 * @param array $data
599 * @param array $args
600 * @return \WP_Error|array|null
601 */
602 protected function remote_post( $data = array(), $args = array() ) {
603 if ( empty( $data ) ) {
604 return;
605 }
606
607 $args = wp_parse_args(
608 $args,
609 array(
610 'method' => 'POST',
611 'timeout' => 30,
612 'redirection' => 5,
613 'httpversion' => '1.1',
614 'blocking' => true,
615 'body' => $data,
616 'user-agent' => 'PUT/1.0.0; ' . get_bloginfo( 'url' )
617 )
618 );
619
620 $request = wp_remote_post( esc_url( self::API_URL ), $args );
621
622 if ( is_wp_error( $request ) || ( isset( $request[ 'response' ], $request[ 'response' ][ 'code' ] ) && 200 != $request[ 'response' ][ 'code' ] ) ) {
623 return new \WP_Error( 500, 'Something went wrong.' );
624 }
625
626 return $request;
627 }
628
629 /**
630 * Difference between old and new data
631 *
632 * @param array $new_data
633 * @param array $old_data
634 * @return array
635 */
636 protected function diff( $new_data, $old_data ) {
637 $data = array();
638 if ( ! empty( $new_data ) ) {
639 foreach ( $new_data as $key => $value ) {
640 if ( isset( $old_data[ $key ] ) ) {
641 if ( $old_data[ $key ] == $value ) {
642 continue;
643 }
644 }
645 $data[ $key ] = $value;
646 }
647 }
648 return $data;
649 }
650
651 /**
652 * Display the admin notice to users to allow them to opt in
653 *
654 * @since 2.5.0
655 */
656 public function notice() {
657 /**
658 * Return if notice is not set.
659 */
660 if ( ! isset( $this->notice_options[ 'notice' ] ) ) {
661 return;
662 }
663 /**
664 * Check is allowed or blocked for notice.
665 */
666 $block_notice = get_option( 'wpins_block_notice' );
667 if ( isset( $block_notice[ $this->plugin_name ] ) ) {
668 return;
669 }
670 if ( ! current_user_can( 'manage_options' ) ) {
671 return;
672 }
673
674 $this->has_notice = true;
675
676 $url_yes = add_query_arg(
677 array(
678 'plugin' => $this->plugin_name,
679 'plugin_action' => 'yes'
680 )
681 );
682
683 $url_no = add_query_arg(
684 array(
685 'plugin' => $this->plugin_name,
686 'plugin_action' => 'no'
687 )
688 );
689
690 $url_yes = wp_nonce_url( $url_yes, '_wpnonce_optin_' . $this->plugin_name );
691 $url_no = wp_nonce_url( $url_no, '_wpnonce_optin_' . $this->plugin_name );
692
693 // Decide on notice text
694 $notice_text = $this->notice_options[ 'notice' ] . ' <a href="#" class="wpinsights-' . esc_attr( $this->plugin_name ) . '-collect" >' . $this->notice_options[ 'consent_button_text' ] . '</a>';
695 $extra_notice_text = $this->notice_options[ 'extra_notice' ];
696
697 ?>
698
699 <div class="nx-optin">
700 <p><?php echo wp_kses_post( $notice_text ); ?></p>
701 <div class="wpinsights-data" style="display: none;">
702 <p><?php echo wp_kses_post( $extra_notice_text ); ?></p>
703 </div>
704 <p>
705 <a href="<?php echo esc_url( $url_yes ); ?>" class="button-primary">
706 <?php echo esc_html( $this->notice_options[ 'yes' ] ); ?>
707 </a>&nbsp;
708 <a href="<?php echo esc_url( $url_no ); ?>" class="button-secondary">
709 <?php echo esc_html( $this->notice_options[ 'no' ] ); ?>
710 </a>
711 </p>
712 </div>
713
714 <?php
715 }
716
717 public function notice_script() {
718 if ( $this->has_notice ) {
719 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>";
720 }
721 }
722
723 /**
724 * Set all notice options to customized notice.
725 *
726 * @since 2.5.0
727 * @param array $options
728 * @return void
729 */
730 public function set_notice_options( $options = array() ) {
731 $default_options = array(
732 'consent_button_text' => __( 'What we collect.', 'betterdocs' ),
733 'yes' => __( 'Sure, I\'d like to help', 'betterdocs' ),
734 'no' => __( 'No Thanks.', 'betterdocs' )
735 );
736 $options = wp_parse_args( $options, $default_options );
737 $this->notice_options = $options;
738 }
739
740 /**
741 * Responsible for track the click from Notice.
742 *
743 * @return void
744 */
745 public function clicked( $notice = null ) {
746 if ( isset( $_GET[ '_wpnonce' ] ) && isset( $_GET[ 'plugin' ] ) && isset( $_GET[ 'plugin_action' ] ) ) {
747 $tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( wp_unslash( $_GET[ 'tab' ] ) ) : '';
748 if ( 'plugin-information' === $tab ) {
749 return;
750 }
751
752 $nonce = sanitize_text_field( wp_unslash( $_GET[ '_wpnonce' ] ) );
753 if ( ! wp_verify_nonce( $nonce, '_wpnonce_optin_' . $this->plugin_name ) ) {
754 return;
755 }
756
757 $plugin = sanitize_text_field( wp_unslash( $_GET[ 'plugin' ] ) );
758 $action = sanitize_text_field( wp_unslash( $_GET[ 'plugin_action' ] ) );
759 if ( 'yes' == $action ) {
760 $this->schedule_tracking();
761 $this->set_is_tracking_allowed( true, $plugin );
762 if ( $this->do_tracking( true ) ) {
763 $this->update_block_notice( $plugin );
764 }
765 } else {
766 $this->set_is_tracking_allowed( false, $plugin );
767 $this->update_block_notice( $plugin );
768 }
769
770 if ( ! is_null( $notice ) ) {
771 $notice->dismiss->dismiss_notice();
772 }
773
774 /**
775 * Redirect User To the Current URL, but without set query arguments.
776 */
777 wp_safe_redirect( $this->redirect_to() );
778 }
779 }
780
781 /**
782 * Set if we should block the opt-in notice for this plugin
783 *
784 * @since 2.5.0
785 */
786 public function update_block_notice( $plugin = null ) {
787 if ( empty( $plugin ) ) {
788 $plugin = $this->plugin_name;
789 }
790 $block_notice = get_option( 'wpins_block_notice' );
791 if ( empty( $block_notice ) || ! is_array( $block_notice ) ) {
792 $block_notice = array( $plugin => $plugin );
793 } else {
794 $block_notice[ $plugin ] = $plugin;
795 }
796 update_option( 'wpins_block_notice', $block_notice, 'no' );
797 }
798
799 /**
800 * AJAX callback when the deactivated form is submitted.
801 *
802 * @since 2.5.0
803 */
804 public function deactivate_reasons_form_submit() {
805 check_ajax_referer( 'wpins_deactivation_nonce', 'security' );
806 if ( isset( $_POST[ 'values' ] ) ) {
807 $values = sanitize_text_field( wp_unslash( $_POST[ 'values' ] ) );
808 update_option( 'wpins_deactivation_reason_' . $this->plugin_name, $values );
809 }
810 if ( isset( $_POST[ 'details' ] ) ) {
811 $details = sanitize_text_field( wp_unslash( $_POST[ 'details' ] ) );
812 update_option( 'wpins_deactivation_details_' . $this->plugin_name, $details );
813 }
814 wp_die();
815 }
816
817 /**
818 * Filter the deactivation link to allow us to present a form when the user deactivates the plugin
819 *
820 * @since 2.5.0
821 */
822 public function deactivate_action_links( $links ) {
823 /**
824 * Check is tracking allowed or not.
825 */
826 if ( ! $this->is_tracking_allowed() ) {
827 return $links;
828 }
829 if ( isset( $links[ 'deactivate' ] ) && $this->include_goodbye_form ) {
830 $deactivation_link = $links[ 'deactivate' ];
831 /**
832 * Change the default deactivate button link.
833 */
834 $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 );
835 $links[ 'deactivate' ] = $deactivation_link;
836 }
837 return $links;
838 }
839
840 /**
841 * ALL Deactivate Reasons.
842 *
843 * @since 2.5.0
844 */
845 public function deactivation_reasons() {
846 $form = array();
847 $form[ 'heading' ] = __( 'We Value Your Feedback', 'betterdocs' );
848 $form[ 'body' ] = __( "Could you please tell us why you're deactivating our plugin? Your insights will help us to improve.", 'betterdocs' );
849
850 $form[ 'options' ] = array(
851 __( 'I no longer need the plugin', 'betterdocs' ),
852 array(
853 'label' => __( 'I found a better plugin', 'betterdocs' ),
854 'extra_field' => __( 'Please share which plugin', 'betterdocs' )
855 ),
856 __( "I couldn't get the plugin to work", 'betterdocs' ),
857 __( 'It\'s a temporary deactivation', 'betterdocs' ),
858 array(
859 'label' => __( 'Other', 'betterdocs' ),
860 'extra_field' => __( 'Please share the reason', 'betterdocs' ),
861 'type' => 'textarea'
862 )
863 );
864 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WP Insights (appsero) library hook; name is defined by the upstream tracker library.
865 return apply_filters( 'wpins_form_text_' . $this->plugin_name, $form );
866 }
867
868 /**
869 * Deactivate Reasons Form.
870 * This form will appears when user wants to deactivate the plugin to send you deactivated reasons.
871 *
872 * @since 3.0.0
873 */
874 public function deactivate_reasons_form() {
875 $form = $this->deactivation_reasons();
876 $class_plugin_name = esc_attr( $this->plugin_name );
877 $html = '<div class="betterdocs__modal-close-btn"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z"/></svg></div><div class="wpinsights-goodbye-form-head"><span><svg width="65" height="64" viewBox="0 0 65 64" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="0.5" width="64" height="64" rx="16" fill="#e0fff6"/><path d="M41.8346 22.668L45.8346 18.668M19.168 45.3346L23.168 41.3346M26.5013 34.0013L29.8346 30.668M30.5013 38.0013L33.8346 34.668M24.9013 43.068C25.1986 43.3663 25.5518 43.603 25.9408 43.7645C26.3298 43.926 26.7468 44.0092 27.168 44.0092C27.5891 44.0092 28.0062 43.926 28.3951 43.7645C28.7841 43.603 29.1373 43.3663 29.4346 43.068L32.5013 40.0013L24.5013 32.0013L21.4346 35.068C21.1363 35.3653 20.8996 35.7185 20.7381 36.1075C20.5766 36.4964 20.4934 36.9135 20.4934 37.3346C20.4934 37.7558 20.5766 38.1728 20.7381 38.5618C20.8996 38.9508 21.1363 39.304 21.4346 39.6013L24.9013 43.068ZM32.5013 24.0013L40.5013 32.0013L43.568 28.9346C43.8663 28.6373 44.103 28.2841 44.2645 27.8951C44.426 27.5062 44.5092 27.0891 44.5092 26.668C44.5092 26.2468 44.426 25.8298 44.2645 25.4408C44.103 25.0518 43.8663 24.6986 43.568 24.4013L40.1013 20.9346C39.804 20.6363 39.4508 20.3996 39.0618 20.2381C38.6728 20.0766 38.2558 19.9934 37.8346 19.9934C37.4135 19.9934 36.9964 20.0766 36.6075 20.2381C36.2185 20.3996 35.8653 20.6363 35.568 20.9346L32.5013 24.0013Z" stroke="#00b884" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span><h1>' . $form[ 'heading' ] . '</h1></div>';
878 $html .= '<div class="wpinsights-goodbye-form-body"><p class="wpinsights-goodbye-form-caption">' . esc_html( $form[ 'body' ] ) . '</p>';
879 if ( is_array( $form[ 'options' ] ) ) {
880 $html .= '<div id="wpinsights-goodbye-options" class="wpinsights-goodbye-options"><ul>';
881 foreach ( $form[ 'options' ] as $option ) {
882 if ( is_array( $option ) ) {
883 $id = strtolower( str_replace( ' ', '_', esc_attr( $option[ 'label' ] ) ) );
884 $id = $id . '_' . $class_plugin_name;
885 $html .= '<li class="has-goodbye-extra">';
886 $html .= '<input type="radio" name="wpinsights-' . esc_attr( $class_plugin_name ) . '-goodbye-options" id="' . esc_attr( $id ) . '" value="' . esc_attr( $option[ 'label' ] ) . '">';
887 $html .= '<div><label for="' . $id . '">' . esc_attr( $option[ 'label' ] ) . '</label>';
888 if ( isset( $option[ 'extra_field' ] ) && ! isset( $option[ 'type' ] ) ) {
889 $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' ] ) . '">';
890 }
891 if ( isset( $option[ 'extra_field' ] ) && isset( $option[ 'type' ] ) ) {
892 $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' ] . '>';
893 }
894 $html .= '</div></li>';
895 } else {
896 $id = strtolower( str_replace( ' ', '_', esc_attr( $option ) ) );
897 $id = $id . '_' . $class_plugin_name;
898 $html .= '<li><input type="radio" name="wpinsights-' . $class_plugin_name . '-goodbye-options" id="' . esc_attr( $id ) . '" value="' . esc_attr( $option ) . '"> <label for="' . $id . '">' . esc_attr( $option ) . '</label></li>';
899 }
900 }
901 $html .= '</ul></div><!-- .wpinsights-' . $class_plugin_name . '-goodbye-options -->';
902 }
903 $html .= '</div><!-- .wpinsights-goodbye-form-body -->';
904 $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', 'betterdocs' ) . '</p>';
905
906 $wrapper_class = '.wpinsights-goodbye-form-wrapper-' . $class_plugin_name;
907
908 $styles = '';
909 $styles .= '<style type="text/css">';
910 $styles .= '.wpinsights-form-active-' . $class_plugin_name . ' .wpinsights-goodbye-form-bg {';
911 $styles .= 'background: rgba( 0, 0, 0, .8 );position: fixed;top: 0;left: 0;width: 100%;height: 100%;z-index: 9;';
912 $styles .= '}';
913 $styles .= $wrapper_class . '{';
914 $styles .= 'position: relative; display: none;';
915 $styles .= '}';
916 $styles .= '.wpinsights-form-active-' . $class_plugin_name . ' ' . $wrapper_class . '{';
917 $styles .= 'display: flex !important; position: fixed;top: 0;left: 0;width: 100%;height: 100%; justify-content: center; align-items: center;';
918 $styles .= '}';
919 $styles .= $wrapper_class . ' .wpinsights-goodbye-form { display: none; }';
920 $styles .= '.wpinsights-form-active-' . $class_plugin_name . ' .wpinsights-goodbye-form {';
921 $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;';
922 $styles .= '}';
923 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-head {';
924 $styles .= 'background: #fff; color: #495157; padding: 18px; box-shadow: 0 0 8px rgba(0,0,0,.1); font-size: 15px;';
925 $styles .= '}';
926 $styles .= $wrapper_class . ' .wpinsights-goodbye-form .wpinsights-goodbye-form-head strong { font-size: 15px; }';
927 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body { padding: 8px 18px; color: #333; }';
928 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body label { padding-left: 5px; color: #6d7882; }';
929 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body .wpinsights-goodbye-form-caption {';
930 $styles .= 'font-weight: 500; font-size: 15px; color: #495157; line-height: 1.4;';
931 $styles .= '}';
932 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body #wpinsights-goodbye-options { padding-top: 5px; }';
933 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li { margin-bottom: 15px; }';
934 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li > div { display: inline; padding-left: 3px; }';
935 $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 {';
936 $styles .= 'margin: 10px 18px; padding: 8px; width: 80%;';
937 $styles .= '}';
938 $styles .= $wrapper_class . ' .deactivating-spinner { display: none; padding-bottom: 20px !important; }';
939 $styles .= $wrapper_class . ' .deactivating-spinner .spinner { float: none; margin: 4px 4px 0 18px; vertical-align: bottom; visibility: visible; }';
940 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-footer { padding: 8px 18px; margin-bottom: 15px; }';
941 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-footer > .wpinsights-goodbye-form-buttons { display: flex; align-items: center; justify-content: space-between; }';
942 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-footer .wpinsights-submit-btn {';
943 $styles .= 'background-color: #f3bafd; -webkit-border-radius: 3px; border-radius: 3px; color: #0c0d0e; line-height: 1; padding: 10px 20px; font-size: 13px; font-weight: 500; text-transform: uppercase; transition: .3s;';
944 $styles .= '}';
945 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-footer .wpinsights-submit-btn:hover {';
946 $styles .= 'background-color: #f5d0fe;';
947 $styles .= '}';
948 $styles .= $wrapper_class . ' .wpinsights-goodbye-form-footer .wpinsights-deactivate-btn {';
949 $styles .= 'font-size: 13px; color: #a4afb7; background: none; float: right; padding-right: 10px; width: auto; text-decoration: underline;';
950 $styles .= '}';
951 $styles .= $wrapper_class . ' .test {';
952 $styles .= '}';
953 $styles .= '
954
955 .wpinsights-form-active-betterdocs .wpinsights-goodbye-form-wrapper-betterdocs {
956 display: flex !important;
957 position: fixed;
958 top: 0;
959 left: 0;
960 right: 0;
961 bottom: 0;
962 width: 100%;
963 z-index: 9999;
964 height: 100%;
965 justify-content: center;
966 align-items: center;
967 }
968 .wpinsights-form-active-betterdocs .wpinsights-goodbye-form {
969 border-radius: 8px;
970 width: 563px;
971 overflow: visible;
972 position: relative;
973 }
974 .wpinsights-goodbye-form .betterdocs__modal-close-btn {
975 position: absolute;
976 top: 0;
977 right: -55px;
978 width: 40px;
979 height: 40px;
980 background: #fff;
981 border-radius: 50%;
982 display: flex;
983 justify-content: center;
984 align-items: center;
985 font-size: 12px;
986 font-weight: 500;
987 color: #475467;
988 cursor: pointer;
989 }
990 .wpinsights-goodbye-form .betterdocs__modal-close-btn svg {
991 width: 20px;
992 }
993 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-head {
994 background: #fff;
995 color: #495157;
996 padding: 24px;
997 border-radius: 8px 8px 0 0;
998 box-shadow: none;
999 border-bottom: 1px solid #EAECF0;
1000 display: flex;
1001 flex-direction: column;
1002 gap: 16px;
1003 align-items: center;
1004 }
1005 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-head > span {
1006 display: inline-flex;
1007 }
1008 .wpinsights-goodbye-form-head h1 {
1009 font-size: 22px;
1010 font-weight: 450;
1011 color: #1D2939;
1012 padding: 0;
1013 }
1014 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body {
1015 padding: 32px 40px;
1016 max-height: calc(70vh - 230px);
1017 overflow: auto;
1018 }
1019 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body .wpinsights-goodbye-form-caption {
1020 font-size: 18px;
1021 color: #1D2939;
1022 margin: 0;
1023 padding-bottom: 16px;
1024 font-weight: 400;
1025 }
1026 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options {
1027 padding-top: 0px;
1028 }
1029 .wpinsights-goodbye-form-wrapper-betterdocs .widefat td ul {
1030 font-size: 14px;
1031 margin: 0;
1032 color: #475467;
1033 line-height: 1.4em;
1034 }
1035 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li:not(:last-child) {
1036 margin-bottom: 16px;
1037 }
1038 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li:last-child {
1039 margin-bottom: 0;
1040 }
1041 .wpinsights-goodbye-form-wrapper-betterdocs input[type=radio] {
1042 border-radius: 50%;
1043 margin-right: .25rem;
1044 line-height: .71428571;
1045 margin: 0;
1046 }
1047 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body label {
1048 padding-left: 4px;
1049 color: #475467;
1050 font-size: 14px;
1051 }
1052 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li > div > input, .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li > div > textarea {
1053 padding: 10px 16px;
1054 border-radius: 8px;
1055 width: 100%;
1056 border: 1px solid #D0D5DD;
1057 margin: 0;
1058 margin-top: 16px;
1059 color: #1D2939;
1060 font-size: 14px;
1061 line-height: 1.5em;
1062 resize: none;
1063 max-height: 44px;
1064 }
1065 .wpinsights-goodbye-form-wrapper-betterdocs input[type=radio] {
1066 border: 1px solid #D0D5DD;
1067 box-shadow: none;
1068 }
1069 .wpinsights-goodbye-form-wrapper-betterdocs input[type=radio]:checked::before {
1070 content: "";
1071 border-radius: 50%;
1072 width: .88rem;
1073 height: .88rem;
1074 margin: -.95px;
1075 background-color: #00b884;
1076 border: 1px solid #e0fff6;
1077 line-height: 1.14285714;
1078 outline: 0;
1079 }
1080 .wpinsights-goodbye-form-wrapper-betterdocs input[type=radio]:focus {
1081 border-color: #D0D5DD;
1082 box-shadow: none;
1083 outline: 0;
1084 }
1085 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li > div > input::placeholder,
1086 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul > li > div > textarea::placeholder{
1087 color: #C6CBD2 !important;
1088 }
1089 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-footer {
1090 padding: 16px 36px;
1091 margin-bottom: 0;
1092 border-top: 1px solid #EAECF0;
1093 }
1094 .wpinsights-goodbye-form-wrapper-betterdocs input:focus,
1095 .wpinsights-goodbye-form-wrapper-betterdocs textarea:focus {
1096 box-shadow: none !important;
1097 outline: 0 !important;
1098 }
1099 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-footer > .wpinsights-goodbye-form-buttons {
1100 display: flex;
1101 align-items: center;
1102 justify-content: flex-start;
1103 }
1104 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-footer .wpinsights-submit-btn {
1105 background-color: #00b884;
1106 -webkit-border-radius: 8px;
1107 border-radius: 8px;
1108 color: #fff;
1109 padding: 8px 16px;
1110 font-size: 14px;
1111 font-weight: 500;
1112 line-height: 1.5em;
1113 text-transform: capitalize;
1114 transition: .3s;
1115 }
1116 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-footer .wpinsights-submit-btn:hover {
1117 background: #00b884;
1118 }
1119
1120 .wpinsights-goodbye-form-wrapper-betterdocs .wpinsights-goodbye-form-footer .wpsp-put-deactivate-btn {
1121 font-size: 14px;
1122 font-weight: 500;
1123 color: #344054;
1124 margin-left: 10px;
1125 }
1126 .wp-person a:focus .gravatar, a:focus, a:focus .media-icon img, a:focus .plugin-icon {
1127 color: #043959;
1128 box-shadow: none;
1129 outline: 0;
1130 }
1131
1132 ';
1133 $styles .= '</style>';
1134 $styles .= '';
1135
1136 echo $styles; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is styles, which is why escaping is not needed
1137
1138 ?>
1139 <script type="text/javascript">
1140 jQuery(document).ready(function($){
1141 $("#wpinsights-goodbye-link-<?php echo esc_attr( $class_plugin_name ); ?>").on("click",function(){
1142 // We'll send the user to this deactivation link when they've completed or dismissed the form
1143 var url = document.getElementById("wpinsights-goodbye-link-<?php echo esc_attr( $class_plugin_name ); ?>");
1144 $('body').toggleClass('wpinsights-form-active-<?php echo esc_attr( $class_plugin_name ); ?>');
1145 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $class_plugin_name ); ?> #wpinsights-goodbye-form").fadeIn();
1146 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $class_plugin_name ); ?> #wpinsights-goodbye-form").html( '<?php echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is an html, not needed to escape ?>' + '<div class="wpinsights-goodbye-form-footer"><div class="wpinsights-goodbye-form-buttons"><a id="wpinsights-submit-form-<?php echo esc_attr( $class_plugin_name ); ?>" class="wpinsights-submit-btn" href="#"><?php esc_html_e( 'Submit and Deactivate', 'betterdocs' ); ?></a>&nbsp;<a class="wpsp-put-deactivate-btn" href="'+url+'"><?php esc_html_e( 'Just Deactivate', 'betterdocs' ); ?></a></div></div>');
1147 $('#wpinsights-submit-form-<?php echo esc_attr( $class_plugin_name ); ?>').on('click', function(e){
1148 // As soon as we click, the body of the form should disappear
1149 $("#wpinsights-goodbye-form-<?php echo esc_attr( $class_plugin_name ); ?> .wpinsights-goodbye-form-body").fadeOut();
1150 $("#wpinsights-goodbye-form-<?php echo esc_attr( $class_plugin_name ); ?> .wpinsights-goodbye-form-footer").fadeOut();
1151 // Fade in spinner
1152 $("#wpinsights-goodbye-form-<?php echo esc_attr( $class_plugin_name ); ?> .deactivating-spinner").fadeIn();
1153 e.preventDefault();
1154 var checkedInput = $("input[name='wpinsights-<?php echo esc_attr( $class_plugin_name ); ?>-goodbye-options']:checked"),
1155 checkedInputVal, details;
1156 if( checkedInput.length > 0 ) {
1157 checkedInputVal = checkedInput.val();
1158 details = $('input[name="'+ checkedInput[0].id +'"], textarea[name="'+ checkedInput[0].id +'"]').val();
1159 }
1160
1161 if( typeof details === 'undefined' ) {
1162 details = '';
1163 }
1164 if( typeof checkedInputVal === 'undefined' ) {
1165 checkedInputVal = 'No Reason';
1166 }
1167
1168 var data = {
1169 'action': 'deactivation_form_<?php echo esc_attr( $class_plugin_name ); ?>',
1170 'values': checkedInputVal,
1171 'details': details,
1172 'security': "<?php echo wp_create_nonce( 'wpins_deactivation_nonce' ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>",
1173 'dataType': "json"
1174 }
1175
1176 $.post(
1177 ajaxurl,
1178 data,
1179 function(response){
1180 // Redirect to original deactivation URL
1181 window.location.href = url;
1182 }
1183 );
1184 });
1185 $('#wpinsights-goodbye-options > ul ').on('click', 'li label, li > input', function( e ){
1186 var parent = $(this).parents('li');
1187 parent.siblings().find('label').next('input, textarea').css('display', 'none');
1188 parent.find('label').next('input, textarea').css('display', 'block');
1189 });
1190 // If we click outside the form, the form will close
1191 $('.wpinsights-goodbye-form-bg, #wpinsights-goodbye-form > .betterdocs__modal-close-btn').on('click',function(){
1192 $("#wpinsights-goodbye-form").fadeOut();
1193 $('body').removeClass('wpinsights-form-active-<?php echo esc_attr( $class_plugin_name ); ?>');
1194 });
1195 });
1196 });
1197 </script>
1198 <?php
1199 }
1200 }
1201