PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.2.12
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.2.12
3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 All 156 releases
notificationx / includes / Admin / PluginInsights.php

PluginInsights.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.2.12, at includes/Admin/PluginInsights.php

1,067 lines 42.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace NotificationX\Admin;
4 #[\AllowDynamicProperties]
5 class PluginInsights {
6 /**
7 * WP Insights Version
8 */
9 const WPINS_VERSION = '3.0.1';
10 /**
11 * API URL
12 */
13 const API_URL = 'https://send.wpinsight.com/process-plugin-data';
14 /**
15 * Installed Plugin File
16 *
17 * @var string
18 */
19 private $plugin_file = null;
20 /**
21 * Installed Plugin Name
22 *
23 * @var string
24 */
25 private $plugin_name = null;
26 /**
27 * How often the event should subsequently
28 *
29 * @var string
30 */
31 public $recurrence = 'daily';
32 private $event_hook = null;
33 private $has_notice = false;
34 /**
35 * Instace of WPInsights_NotificationX
36 *
37 * @var WPInsights_NotificationX
38 */
39 private static $_instance = null;
40 /**
41 * Get Instance of WPInsights_NotificationX
42 *
43 * @return WPInsights_NotificationX
44 */
45 public static function get_instance( $plugin_file, $args = [] ) {
46 if ( is_null( static::$_instance ) ) {
47 static::$_instance = new static( $plugin_file, $args );
48 }
49 return static::$_instance;
50 }
51 /**
52 * Automatically Invoked when initialized.
53 *
54 * @param array $args
55 */
56 public function __construct( $plugin_file, $args = [] ) {
57 $this->plugin_file = $plugin_file;
58 $this->plugin_name = basename( $this->plugin_file, '.php' );
59 $this->disabled_wp_cron = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON == true;
60 $this->enable_self_cron = $this->disabled_wp_cron == true ? true : false;
61
62 $this->event_hook = 'put_do_weekly_action';
63
64 $this->require_optin = isset( $args['opt_in'] ) ? $args['opt_in'] : true;
65 $this->include_goodbye_form = isset( $args['goodbye_form'] ) ? $args['goodbye_form'] : true;
66 $this->marketing = isset( $args['email_marketing'] ) ? $args['email_marketing'] : true;
67 $this->options = isset( $args['options'] ) ? $args['options'] : [];
68 $this->item_id = isset( $args['item_id'] ) ? $args['item_id'] : false;
69 /**
70 * Activation Hook
71 */
72 register_activation_hook( $this->plugin_file, array( $this, 'activate_this_plugin' ) );
73 /**
74 * Deactivation Hook
75 */
76 register_deactivation_hook( $this->plugin_file, array( $this, 'deactivate_this_plugin' ) );
77 }
78 /**
79 * When user agreed to opt-in tracking schedule is enabled.
80 *
81 * @since 3.0.0
82 */
83 public function schedule_tracking() {
84 if ( $this->disabled_wp_cron ) {
85 return;
86 }
87 if ( ! wp_next_scheduled( $this->event_hook ) ) {
88 wp_schedule_event( time(), $this->recurrence, $this->event_hook );
89 }
90 }
91 /**
92 * Add the schedule event if the plugin is tracked.
93 *
94 * @return void
95 */
96 public function activate_this_plugin() {
97 $allow_tracking = $this->is_tracking_allowed();
98 if ( ! $allow_tracking ) {
99 return;
100 }
101 $this->schedule_tracking();
102 }
103 /**
104 * Remove the schedule event when plugin is deactivated and send the deactivated reason to inishghts if user submitted.
105 *
106 * @since 3.0.0
107 */
108 public function deactivate_this_plugin() {
109 /**
110 * Check tracking is allowed or not.
111 */
112 $allow_tracking = $this->is_tracking_allowed();
113 if ( ! $allow_tracking ) {
114 return;
115 }
116 $body = $this->get_data();
117 $body['status'] = 'Deactivated';
118 $body['deactivated_date'] = time();
119
120 // Check deactivation reason and add for insights data.
121 if ( false !== get_option( 'wpins_deactivation_reason_' . $this->plugin_name ) ) {
122 $body['deactivation_reason'] = get_option( 'wpins_deactivation_reason_' . $this->plugin_name );
123 }
124 if ( false !== get_option( 'wpins_deactivation_details_' . $this->plugin_name ) ) {
125 $body['deactivation_details'] = get_option( 'wpins_deactivation_details_' . $this->plugin_name );
126 }
127
128 $this->send_data( $body );
129 delete_option( 'wpins_deactivation_reason_' . $this->plugin_name );
130 delete_option( 'wpins_deactivation_details_' . $this->plugin_name );
131 /**
132 * Clear the event schedule.
133 */
134 if ( ! $this->disabled_wp_cron ) {
135 wp_clear_scheduled_hook( $this->event_hook );
136 }
137 }
138 /**
139 * Initial Method to Hook Everything.
140 *
141 * @return void
142 */
143 public function init() {
144 // $this->clicked();
145 add_action( 'wpdeveloper_notice_clicked_for_' . $this->plugin_name, array( $this, 'clicked' ) );
146 add_action( $this->event_hook, array( $this, 'do_tracking' ) );
147 // For Test
148 // add_action( 'admin_init', array( $this, 'force_tracking' ) );
149 // add_action( 'admin_notices', array( $this, 'notice' ) );
150 add_action( 'wpdeveloper_optin_notice_for_' . $this->plugin_name, array( $this, 'notice' ) );
151 /**
152 * Deactivation Reason Form and Submit Data to Insights.
153 */
154 add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'deactivate_action_links' ) );
155 add_action( 'admin_print_footer_scripts', array( $this, 'notice_script' ) );
156 add_action( 'admin_print_footer_scripts-plugins.php', array( $this, 'deactivate_reasons_form_script' ) );
157 add_action( 'admin_print_styles-plugins.php', array( $this, 'deactivate_reasons_form_style' ) );
158 add_action( 'wp_ajax_deactivation_form_' . esc_attr( $this->plugin_name ), array( $this, 'deactivate_reasons_form_submit' ) );
159 }
160 /**
161 * For Redirecting Current Page without Arguments!
162 *
163 * @return void
164 */
165 private function redirect_to() {
166 $current_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
167 $request_uri = wp_parse_url( $current_uri, PHP_URL_PATH );
168 $query_string = wp_parse_url( $current_uri, PHP_URL_QUERY );
169 parse_str( $query_string, $current_url );
170
171 $unset_array = array( 'dismiss', 'plugin', '_wpnonce', 'later', 'plugin_action', 'marketing_optin' );
172
173 foreach ( $unset_array as $value ) {
174 if ( isset( $current_url[ $value ] ) ) {
175 unset( $current_url[ $value ] );
176 }
177 }
178
179 $current_url = http_build_query( $current_url );
180 $redirect_url = $request_uri . '?' . $current_url;
181 return $redirect_url;
182 }
183 /**
184 * This method forcing the do_tracking method to execute instant.
185 *
186 * @return void
187 */
188 public function force_tracking() {
189 $this->do_tracking( true );
190 }
191 /**
192 * Programmatically opt the site into tracking and (optionally) send the
193 * data to the insights API immediately. Used by the onboarding Setup Wizard
194 * when the user proceeds past the Welcome step (which is the consent point).
195 *
196 * @param bool $send Send the collected data right away.
197 * @return bool|\WP_Error
198 */
199 public function optin( $send = true ) {
200 $this->schedule_tracking();
201 $this->set_is_tracking_allowed( true, $this->plugin_name );
202 $this->update_block_notice( $this->plugin_name );
203 if ( $send ) {
204 return $this->do_tracking( true );
205 }
206 return true;
207 }
208 /**
209 * This method is responsible for all the magic from the front of the plugin.
210 *
211 * @since 3.0.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 ( ( defined('NX_DEBUG') && NX_DEBUG ) || 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 3.0.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 3.0.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 3.0.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 3.0.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 3.0.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'] ) ? sanitize_text_field( wp_unslash( $_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 3.0.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.', 'notificationx' );
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 3.0.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
422 /**
423 * Allow plugin-specific usage data to be merged into the tracking
424 * payload (e.g. NotificationX notification type & source breakdown).
425 *
426 * @see \NotificationX\Core\UsageTracker
427 * @since 3.3.0
428 * @param array $body Collected tracking data.
429 * @param PluginInsights $this Current insights instance.
430 */
431 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
432 $body = apply_filters( 'nx_plugin_usage_tracker_data', $body, $this );
433
434 return $body;
435 }
436
437 /**
438 * Collect plugin data,
439 * Retrieve current plugin information
440 *
441 * @since 3.0.0
442 */
443 public function plugin_data() {
444 if ( ! function_exists( 'get_plugin_data' ) ) {
445 include ABSPATH . '/wp-admin/includes/plugin.php';
446 }
447 $plugin = get_plugin_data( $this->plugin_file );
448 return $plugin;
449 }
450 /**
451 * Send the data to insights.
452 *
453 * @since 3.0.0
454 */
455 public function send_data( $body ) {
456 /**
457 * Get SITE ID
458 */
459 $site_id_key = "wpins_{$this->plugin_name}_site_id";
460 $site_id = get_option( $site_id_key, false );
461 $failed_data = [];
462 $site_url = get_bloginfo( 'url' );
463 $original_site_url = get_option( "wpins_{$this->plugin_name}_original_url", false );
464 if ( $original_site_url === false && version_compare( $body['wpins_version'], '3.0.1', '==' ) ) {
465 $site_id = false;
466 }
467 /**
468 * Send Initial Data to API
469 */
470 if ( $site_id == false && $this->item_id !== false && $original_site_url === false ) {
471 if ( isset( $_SERVER['REMOTE_ADDR'] ) && ! empty( $_SERVER['REMOTE_ADDR'] && $_SERVER['REMOTE_ADDR'] != '127.0.0.1' ) ) {
472 $country_request = wp_remote_get( 'http://ip-api.com/json/' . sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) . '?fields=country' );
473 if ( ! is_wp_error( $country_request ) && $country_request['response']['code'] == 200 ) {
474 $ip_data = json_decode( $country_request['body'] );
475 $body['country'] = isset( $ip_data->country ) ? $ip_data->country : 'NOT SET';
476 }
477 }
478
479 $body['plugin_slug'] = $this->plugin_name;
480 $body['url'] = $site_url;
481 $body['item_id'] = $this->item_id;
482
483 $request = $this->remote_post( $body );
484 if ( ! is_wp_error( $request ) && $request['response']['code'] == 200 ) {
485 $retrieved_body = json_decode( wp_remote_retrieve_body( $request ), true );
486 if ( is_array( $retrieved_body ) && isset( $retrieved_body['siteId'] ) ) {
487 update_option( $site_id_key, $retrieved_body['siteId'], 'no' );
488 update_option( "wpins_{$this->plugin_name}_original_url", $site_url, 'no' );
489 update_option( "wpins_{$this->plugin_name}_{$retrieved_body['siteId']}", $body, 'no' );
490 }
491 } else {
492 $failed_data = $body;
493 }
494 }
495
496 $site_id_data_key = "wpins_{$this->plugin_name}_{$site_id}";
497 $site_id_data_failed_key = "wpins_{$this->plugin_name}_{$site_id}_send_failed";
498
499 if ( $site_id != false ) {
500 $old_sent_data = get_option( $site_id_data_key, [] );
501 $diff_data = $this->diff( $body, $old_sent_data );
502 $failed_data = get_option( $site_id_data_failed_key, [] );
503 if ( ! empty( $failed_data ) && $diff_data != $failed_data ) {
504 $failed_data = array_merge( $failed_data, $diff_data );
505 }
506 }
507
508 if ( ! empty( $failed_data ) && $site_id != false ) {
509 $failed_data['plugin_slug'] = $this->plugin_name;
510 $failed_data['url'] = $site_url;
511 $failed_data['site_id'] = $site_id;
512 if ( $original_site_url != false ) {
513 $failed_data['original_url'] = $original_site_url;
514 }
515
516 $request = $this->remote_post( $failed_data );
517 if ( ! is_wp_error( $request ) ) {
518 delete_option( $site_id_data_failed_key );
519 $replaced_data = array_merge( $old_sent_data, $failed_data );
520 update_option( $site_id_data_key, $replaced_data, 'no' );
521 }
522 }
523
524 if ( ! empty( $diff_data ) && $site_id != false && empty( $failed_data ) ) {
525 $diff_data['plugin_slug'] = $this->plugin_name;
526 $diff_data['url'] = $site_url;
527 $diff_data['site_id'] = $site_id;
528 if ( $original_site_url != false ) {
529 $diff_data['original_url'] = $original_site_url;
530 }
531
532 $request = $this->remote_post( $diff_data );
533 if ( is_wp_error( $request ) ) {
534 update_option( $site_id_data_failed_key, $diff_data, 'no' );
535 } else {
536 $replaced_data = array_merge( $old_sent_data, $diff_data );
537 update_option( $site_id_data_key, $replaced_data, 'no' );
538 }
539 }
540
541 $this->set_track_time();
542
543 if ( isset( $request ) && is_wp_error( $request ) ) {
544 return $request;
545 }
546
547 if ( isset( $request ) ) {
548 return true;
549 }
550 return false;
551 }
552 /**
553 * WP_REMOTE_POST method responsible for send data to the API_URL
554 *
555 * @param array $data
556 * @param array $args
557 * @return void
558 */
559 protected function remote_post( $data = array(), $args = array() ) {
560 if ( empty( $data ) ) {
561 return;
562 }
563
564 $args = wp_parse_args( $args, array(
565 'method' => 'POST',
566 'timeout' => 30,
567 'redirection' => 5,
568 'httpversion' => '1.1',
569 'blocking' => true,
570 'body' => $data,
571 'user-agent' => 'PUT/1.0.0; ' . get_bloginfo( 'url' ),
572 )
573 );
574
575 $request = wp_remote_post( esc_url( self::API_URL ), $args );
576 if ( is_wp_error( $request ) || ( isset( $request['response'], $request['response']['code'] ) && $request['response']['code'] != 200 ) ) {
577 return new \WP_Error( 500, 'Something went wrong.' );
578 }
579 return $request;
580 }
581 /**
582 * Difference between old and new data
583 *
584 * @param array $new_data
585 * @param array $old_data
586 * @return void
587 */
588 protected function diff( $new_data, $old_data ) {
589 $data = [];
590 if ( ! empty( $new_data ) ) {
591 foreach ( $new_data as $key => $value ) {
592 if ( isset( $old_data[ $key ] ) ) {
593 if ( $old_data[ $key ] == $value ) {
594 continue;
595 }
596 }
597 $data[ $key ] = $value;
598 }
599 }
600 return $data;
601 }
602 /**
603 * Display the admin notice to users to allow them to opt in
604 *
605 * @since 3.0.0
606 */
607 public function notice() {
608 /**
609 * Return if notice is not set.
610 */
611 if ( ! isset( $this->notice_options['notice'] ) ) {
612 return;
613 }
614 /**
615 * Check is allowed or blocked for notice.
616 */
617 $block_notice = get_option( 'wpins_block_notice' );
618 if ( isset( $block_notice[ $this->plugin_name ] ) ) {
619 return;
620 }
621 if ( ! current_user_can( 'manage_options' ) ) {
622 return;
623 }
624
625 $this->has_notice = true;
626
627 $url_yes = add_query_arg( [
628 'plugin' => $this->plugin_name,
629 'plugin_action' => 'yes',
630 ]
631 );
632 $url_no = add_query_arg( array(
633 'plugin' => $this->plugin_name,
634 'plugin_action' => 'no',
635 )
636 );
637
638 $url_yes = wp_nonce_url( $url_yes, '_wpnonce_optin_' . $this->plugin_name );
639 $url_no = wp_nonce_url( $url_no, '_wpnonce_optin_' . $this->plugin_name );
640
641 // Decide on notice text
642 $notice_text = $this->notice_options['notice'] . ' <a href="#" class="wpinsights-' . esc_attr( $this->plugin_name ) . '-collect" >' . $this->notice_options['consent_button_text'] . '</a>';
643 $extra_notice_text = $this->notice_options['extra_notice'];
644
645 ?>
646
647 <div class="nx-optin">
648 <p><?php echo wp_kses_post( $notice_text ); ?></p>
649 <div class="wpinsights-data" style="display: none;">
650 <p><?php echo wp_kses_post( $extra_notice_text ); ?></p>
651 </div>
652 <p>
653 <a href="<?php echo esc_url( $url_yes ); ?>" class="button-primary">
654 <?php echo esc_html( $this->notice_options['yes'] ); ?>
655 </a>&nbsp;
656 <a href="<?php echo esc_url( $url_no ); ?>" class="button-secondary">
657 <?php echo esc_html( $this->notice_options['no'] ); ?>
658 </a>
659 </p>
660 </div>
661
662 <?php
663 }
664
665 public function notice_script() {
666 if ( $this->has_notice ) {
667 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>";
668 }
669 }
670 /**
671 * Set all notice options to customized notice.
672 *
673 * @since 3.0.0
674 * @param array $options
675 * @return void
676 */
677 public function set_notice_options( $options = [] ) {
678 $default_options = [
679 'consent_button_text' => __( 'What we collect.', 'notificationx' ),
680 'yes' => __( 'Sure, I\'d like to help', 'notificationx' ),
681 'no' => __( 'No Thanks.', 'notificationx' ),
682 ];
683 $options = wp_parse_args( $options, $default_options );
684 $this->notice_options = $options;
685 }
686 /**
687 * Responsible for track the click from Notice.
688 *
689 * @return void
690 */
691 public function clicked( $notice = null ) {
692 if ( isset( $_GET[ '_wpnonce' ] ) && isset( $_GET['plugin'] ) && isset( $_GET['plugin_action'] ) ) {
693 if ( isset( $_GET['tab'] ) && $_GET['tab'] === 'plugin-information' ) {
694 return;
695 }
696
697 if( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ '_wpnonce' ] ) ), '_wpnonce_optin_' . $this->plugin_name ) ) {
698 return;
699 }
700
701 $plugin = sanitize_text_field( wp_unslash( $_GET['plugin'] ) );
702 $action = sanitize_text_field( wp_unslash( $_GET['plugin_action'] ) );
703 if ( $action == 'yes' ) {
704 $this->schedule_tracking();
705 $this->set_is_tracking_allowed( true, $plugin );
706 if ( $this->do_tracking( true ) ) {
707 $this->update_block_notice( $plugin );
708 }
709 } else {
710 $this->set_is_tracking_allowed( false, $plugin );
711 $this->update_block_notice( $plugin );
712 }
713
714 if( ! is_null ( $notice ) ) {
715 $notice->dismiss->dismiss_notice();
716 }
717
718 /**
719 * Redirect User To the Current URL, but without set query arguments.
720 */
721 wp_safe_redirect( $this->redirect_to() );
722 }
723 }
724 /**
725 * Set if we should block the opt-in notice for this plugin
726 *
727 * @since 3.0.0
728 */
729 public function update_block_notice( $plugin = null ) {
730 if ( empty( $plugin ) ) {
731 $plugin = $this->plugin_name;
732 }
733 $block_notice = get_option( 'wpins_block_notice' );
734 if ( empty( $block_notice ) || ! is_array( $block_notice ) ) {
735 $block_notice = array( $plugin => $plugin );
736 } else {
737 $block_notice[ $plugin ] = $plugin;
738 }
739 update_option( 'wpins_block_notice', $block_notice, 'no' );
740 }
741 /**
742 * AJAX callback when the deactivated form is submitted.
743 *
744 * @since 3.0.0
745 */
746 public function deactivate_reasons_form_submit() {
747 check_ajax_referer( 'wpins_deactivation_nonce', 'security' );
748 if ( isset( $_POST['values'] ) ) {
749 $values = sanitize_text_field( wp_unslash( $_POST['values'] ) );
750 update_option( 'wpins_deactivation_reason_' . $this->plugin_name, $values, 'no' );
751 }
752 if ( isset( $_POST['details'] ) ) {
753 $details = sanitize_text_field( wp_unslash( $_POST['details'] ) );
754 update_option( 'wpins_deactivation_details_' . $this->plugin_name, $details, 'no' );
755 }
756 echo 'success';
757 wp_die();
758 }
759 /**
760 * Filter the deactivation link to allow us to present a form when the user deactivates the plugin
761 *
762 * @since 3.0.0
763 */
764 public function deactivate_action_links( $links ) {
765 /**
766 * Check is tracking allowed or not.
767 */
768 if ( ! $this->is_tracking_allowed() ) {
769 return $links;
770 }
771 if ( isset( $links['deactivate'] ) && $this->include_goodbye_form ) {
772 $deactivation_link = $links['deactivate'];
773 /**
774 * Change the default deactivate button link.
775 */
776 $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 );
777 $links['deactivate'] = $deactivation_link;
778 }
779 return $links;
780 }
781 /**
782 * ALL Deactivate Reasons.
783 *
784 * @since 3.0.0
785 */
786 public function deactivation_reasons() {
787 $form = array();
788 $form['heading'] = __( 'Sorry to see you go', 'notificationx' );
789 $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', 'notificationx' );
790
791 $form['options'] = array(
792 __( 'I no longer need the plugin', 'notificationx' ),
793 [
794 'label' => __( 'I found a better plugin', 'notificationx' ),
795 'extra_field' => __( 'Please share which plugin', 'notificationx' ),
796 ],
797 __( "I couldn't get the plugin to work", 'notificationx' ),
798 __( 'It\'s a temporary deactivation', 'notificationx' ),
799 [
800 'label' => __( 'Other', 'notificationx' ),
801 'extra_field' => __( 'Please share the reason', 'notificationx' ),
802 'type' => 'textarea',
803 ],
804 );
805 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
806 return apply_filters( 'wpins_form_text_' . $this->plugin_name, $form );
807 }
808 /**
809 * Deactivate Reasons Form.
810 * This form will appears when user wants to deactivate the plugin to send you deactivated reasons.
811 *
812 * @since 3.0.0
813 */
814 public function deactivate_reasons_form_style() {
815 ?>
816 <style type="text/css">
817 .wpinsights-form-active-notificationx .wpinsights-goodbye-form-bg {
818 background: rgba(0, 0, 0, .8);
819 position: fixed;
820 top: 0;
821 left: 0;
822 width: 100%;
823 height: 100%;
824 z-index: 9;
825 }
826
827 .wpinsights-goodbye-form-wrapper-notificationx {
828 position: relative;
829 display: none;
830 }
831
832 .wpinsights-form-active-notificationx .wpinsights-goodbye-form-wrapper-notificationx {
833 display: flex !important;
834 position: fixed;
835 top: 0;
836 left: 0;
837 width: 100%;
838 height: 100%;
839 justify-content: center;
840 align-items: center;
841 }
842
843 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form {
844 display: none;
845 }
846
847 .wpinsights-form-active-notificationx .wpinsights-goodbye-form {
848 position: relative !important;
849 width: 550px;
850 max-width: 80%;
851 background: #fff;
852 box-shadow: 2px 8px 23px 3px rgba(0, 0, 0, .2);
853 border-radius: 3px;
854 white-space: normal;
855 overflow: hidden;
856 display: block;
857 z-index: 999999;
858 }
859
860 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-head {
861 background: #fff;
862 color: #495157;
863 padding: 18px;
864 box-shadow: 0 0 8px rgba(0, 0, 0, .1);
865 font-size: 15px;
866 }
867
868 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form .wpinsights-goodbye-form-head strong {
869 font-size: 15px;
870 }
871
872 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body {
873 padding: 8px 18px;
874 color: #333;
875 }
876
877 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body label {
878 padding-left: 5px;
879 color: #6d7882;
880 }
881
882 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body .wpinsights-goodbye-form-caption {
883 font-weight: 500;
884 font-size: 15px;
885 color: #495157;
886 line-height: 1.4;
887 }
888
889 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options {
890 padding-top: 5px;
891 }
892
893 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li {
894 margin-bottom: 15px;
895 }
896
897 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div {
898 display: inline;
899 padding-left: 3px;
900 }
901
902 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div>input,
903 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-body #wpinsights-goodbye-options ul>li>div>textarea {
904 margin: 10px 18px;
905 padding: 8px;
906 width: 80%;
907 }
908
909 .wpinsights-goodbye-form-wrapper-notificationx .deactivating-spinner {
910 display: none;
911 padding-bottom: 20px !important;
912 }
913
914 .wpinsights-goodbye-form-wrapper-notificationx .deactivating-spinner .spinner {
915 float: none;
916 margin: 4px 4px 0 18px;
917 vertical-align: bottom;
918 visibility: visible;
919 }
920
921 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer {
922 padding: 8px 18px;
923 margin-bottom: 15px;
924 }
925
926 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer>.wpinsights-goodbye-form-buttons {
927 display: flex;
928 align-items: center;
929 justify-content: space-between;
930 }
931
932 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer .wpinsights-submit-btn {
933 background-color: #d30c5c;
934 -webkit-border-radius: 3px;
935 border-radius: 3px;
936 color: #fff;
937 line-height: 1;
938 padding: 15px 20px;
939 font-size: 13px;
940 }
941
942 .wpinsights-goodbye-form-wrapper-notificationx .wpinsights-goodbye-form-footer .wpinsights-deactivate-btn {
943 font-size: 13px;
944 color: #a4afb7;
945 background: none;
946 float: right;
947 padding-right: 10px;
948 width: auto;
949 text-decoration: underline;
950 }
951
952 .wpinsights-goodbye-form-wrapper-notificationx .test {}
953 </style>
954
955 <?php
956 }
957
958 /**
959 * Deactivate Reasons Form.
960 * This form will appears when user wants to deactivate the plugin to send you deactivated reasons.
961 *
962 * @since 3.0.0
963 */
964 public function deactivate_reasons_form_script() {
965 $form = $this->deactivation_reasons();
966
967 $html = '<div class="wpinsights-goodbye-form-head"><strong>' . esc_html( $form['heading'] ) . '</strong></div>';
968 $html .= '<div class="wpinsights-goodbye-form-body"><p class="wpinsights-goodbye-form-caption">' . esc_html( $form['body'] ) . '</p>';
969 if ( is_array( $form['options'] ) ) {
970 $html .= '<div id="wpinsights-goodbye-options" class="wpinsights-goodbye-options"><ul>';
971 foreach ( $form['options'] as $option ) {
972 if ( is_array( $option ) ) {
973 $id = strtolower( str_replace( ' ', '_', esc_attr( $option['label'] ) ) );
974 $id = $id . '_' . $this->plugin_name;
975 $html .= '<li class="has-goodbye-extra">';
976 $html .= '<input type="radio" name="wpinsights-' . esc_attr( $this->plugin_name ) . '-goodbye-options" id="' . esc_attr( $id ) . '" value="' . esc_attr( $option['label'] ) . '">';
977 $html .= '<div><label for="' . esc_attr( $id ) . '">' . esc_attr( $option['label'] ) . '</label>';
978 if ( isset( $option['extra_field'] ) && ! isset( $option['type'] ) ) {
979 $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'] ) . '">';
980 }
981 if ( isset( $option['extra_field'] ) && isset( $option['type'] ) ) {
982 $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'] . '>';
983 }
984 $html .= '</div></li>';
985 } else {
986 $id = strtolower( str_replace( ' ', '_', esc_attr( $option ) ) );
987 $id = $id . '_' . $this->plugin_name;
988 $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>';
989 }
990 }
991 $html .= '</ul></div><!-- .wpinsights-' . esc_attr( $this->plugin_name ) . '-goodbye-options -->';
992 }
993 $html .= '</div><!-- .wpinsights-goodbye-form-body -->';
994 $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', 'notificationx' ) . '</p>';
995
996 ?>
997 <script type="text/javascript">
998 jQuery(document).ready(function($){
999 $("#wpinsights-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>").on("click",function(){
1000 // We'll send the user to this deactivation link when they've completed or dismissed the form
1001 var url = document.getElementById("wpinsights-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>");
1002 $('body').toggleClass('wpinsights-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
1003 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> #wpinsights-goodbye-form").fadeIn();
1004 <?php
1005 /*
1006 * $html is assembled above with esc_html()/esc_attr() applied to every
1007 * interpolated value. It holds the radio/textarea controls of the
1008 * deactivation form, which wp_kses_post() and nx_allowed_html() would
1009 * both strip, breaking the form.
1010 */
1011 ?>
1012 $(".wpinsights-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> #wpinsights-goodbye-form").html( '<?php echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>' + '<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', 'notificationx' ); ?></a>&nbsp;<a class="wpsp-put-deactivate-btn" href="'+url+'"><?php esc_html_e( 'Just Deactivate', 'notificationx' ); ?></a></div></div>');
1013 $('#wpinsights-submit-form-<?php echo esc_attr( $this->plugin_name ); ?>').on('click', function(e){
1014 // As soon as we click, the body of the form should disappear
1015 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpinsights-goodbye-form-body").fadeOut();
1016 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpinsights-goodbye-form-footer").fadeOut();
1017 // Fade in spinner
1018 $("#wpinsights-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .deactivating-spinner").fadeIn();
1019 e.preventDefault();
1020 var checkedInput = $("input[name='wpinsights-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options']:checked"),
1021 checkedInputVal, details;
1022 if( checkedInput.length > 0 ) {
1023 checkedInputVal = checkedInput.val();
1024 details = $('input[name="'+ checkedInput[0].id +'"], textarea[name="'+ checkedInput[0].id +'"]').val();
1025 }
1026
1027 if( typeof details === 'undefined' ) {
1028 details = '';
1029 }
1030 if( typeof checkedInputVal === 'undefined' ) {
1031 checkedInputVal = 'No Reason';
1032 }
1033
1034 var data = {
1035 'action': 'deactivation_form_<?php echo esc_attr( $this->plugin_name ); ?>',
1036 'values': checkedInputVal,
1037 'details': details,
1038 'security': "<?php echo esc_js( wp_create_nonce( 'wpins_deactivation_nonce' ) ); ?>",
1039 'dataType': "json"
1040 }
1041
1042 $.post(
1043 ajaxurl,
1044 data,
1045 function(response){
1046 // Redirect to original deactivation URL
1047 window.location.href = url;
1048 }
1049 );
1050 });
1051 $('#wpinsights-goodbye-options > ul ').on('click', 'li label, li > input', function( e ){
1052 var parent = $(this).parents('li');
1053 parent.siblings().find('label').next('input, textarea').css('display', 'none');
1054 parent.find('label').next('input, textarea').css('display', 'block');
1055 });
1056 // If we click outside the form, the form will close
1057 $('.wpinsights-goodbye-form-bg').on('click',function(){
1058 $("#wpinsights-goodbye-form").fadeOut();
1059 $('body').removeClass('wpinsights-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
1060 });
1061 });
1062 });
1063 </script>
1064 <?php
1065 }
1066 }
1067