PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 1.1.2
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v1.1.2
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 / class-plugin-usage-tracker.php

class-plugin-usage-tracker.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 1.1.2, at includes/class-plugin-usage-tracker.php

999 lines 36.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This is the class that sends all the data back to the home site
4 * It also handles opting in and deactivation
5 * @version 1.1.2
6 */
7
8 // Exit if accessed directly
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 if( ! class_exists( 'NotificationX_Plugin_Usage_Tracker') ) :
14
15 class NotificationX_Plugin_Usage_Tracker {
16 private $wpins_version = '1.1.3';
17 private $home_url = '';
18 private $plugin_file = '';
19 private $plugin_name = '';
20 private $options = array();
21 private $require_optin = true;
22 private $include_goodbye_form = true;
23 private $marketing = false;
24 private $collect_email = false;
25
26 /**
27 * Class constructor
28 *
29 * @param $_home_url The URL to the site we're sending data to
30 * @param $_plugin_file The file path for this plugin
31 * @param $_options Plugin options to track
32 * @param $_require_optin Whether user opt-in is required (always required on WordPress.org)
33 * @param $_include_goodbye_form Whether to include a form when the user deactivates
34 * @param $_marketing Marketing method:
35 * 0: Don't collect email addresses
36 * 1: Request permission same time as tracking opt-in
37 * 2: Request permission after opt-in
38 */
39 public function __construct(
40 $_plugin_file,
41 $_home_url,
42 $_options,
43 $_require_optin=true,
44 $_include_goodbye_form=true,
45 $_marketing=false ) {
46
47 $this->plugin_file = $_plugin_file;
48 $this->home_url = trailingslashit( $_home_url );
49 $this->plugin_name = basename( $this->plugin_file, '.php' );
50 $this->options = $_options;
51 $this->require_optin = $_require_optin;
52 $this->include_goodbye_form = $_include_goodbye_form;
53 $this->marketing = $_marketing;
54
55 // Schedule some tracking when activated
56 register_activation_hook( $this->plugin_file, array( $this, 'schedule_tracking' ) );
57 // Deactivation hook
58 register_deactivation_hook( $this->plugin_file, array( $this, 'deactivate_this_plugin' ) );
59
60 // Get it going
61 $this->init();
62
63 }
64
65 public function init() {
66 // Check marketing
67 if( $this->marketing == 3 ) {
68 $this->set_can_collect_email( true, $this->plugin_name );
69 }
70 // Check whether opt-in is required
71 // If not, then tracking is allowed
72 if( ! $this->require_optin ) {
73 $this->set_can_collect_email( true, $this->plugin_name );
74 $this->set_is_tracking_allowed( true );
75 $this->update_block_notice();
76 $this->do_tracking( true );
77 }
78 // Hook our do_tracking function to the daily action
79 add_action( 'wpdeveloper_notice_clicked_for_' . $this->plugin_name, array( $this, 'clicked' ) );
80
81 add_action( 'put_do_weekly_action', array( $this, 'do_tracking' ) );
82 // Use this action for local testing and for one time force tracking in a life time.
83 // add_action( 'admin_init', array( $this, 'force_tracking' ) );
84
85 // Display the admin notice on activation
86 add_action( 'wpdeveloper_optin_notice_for_' . $this->plugin_name, array( $this, 'optin_notice' ) );
87 add_action( 'admin_notices', array( $this, 'marketing_notice' ) );
88
89 // Deactivation
90 add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), array( $this, 'filter_action_links' ) );
91 add_action( 'admin_footer-plugins.php', array( $this, 'goodbye_ajax' ) );
92 add_action( 'wp_ajax_goodbye_form_' . esc_attr( $this->plugin_name ), array( $this, 'goodbye_form_callback' ) );
93
94 }
95
96 /**
97 * When the plugin is activated
98 * Create scheduled event
99 * And check if tracking is enabled - perhaps the plugin has been reactivated
100 *
101 * @since 1.0.0
102 */
103 public function schedule_tracking() {
104 // For historical reasons, this is called 'weekly' but is in fact daily
105 if ( ! wp_next_scheduled( 'put_do_weekly_action' ) ) {
106 wp_schedule_event( time(), 'daily', 'put_do_weekly_action' );
107 }
108 }
109 /**
110 * This function is responsible for force tracking the plugin,
111 * if users are allowed to do!
112 *
113 * @return void
114 */
115 public function force_tracking(){
116 $this->do_tracking( true );
117 }
118
119 /**
120 * This is our function to get everything going
121 * Check that user has opted in
122 * Collect data
123 * Then send it back
124 *
125 * @since 1.0.0
126 * @param $force Force tracking if it's not time
127 */
128 public function do_tracking( $force=false ) {
129 // If the home site hasn't been defined, we just drop out. Nothing much we can do.
130 if ( ! $this->home_url ) {
131 return;
132 }
133
134 // Check to see if the user has opted in to tracking
135 $allow_tracking = $this->get_is_tracking_allowed();
136 if( ! $allow_tracking ) {
137 return;
138 }
139
140 // Check to see if it's time to track
141 $track_time = $this->get_is_time_to_track();
142 if( ! $track_time && ! $force ) {
143 return;
144 }
145
146 $this->set_admin_email();
147
148 // Get our data
149 $body = $this->get_data();
150
151 // Send the data
152 $this->send_data( $body );
153 }
154
155 /**
156 * Send the data to the home site
157 *
158 * @since 1.0.0
159 */
160 public function send_data( $body ) {
161
162 $request = wp_remote_post(
163 esc_url( $this->home_url . '?usage_tracker=hello' ),
164 array(
165 'method' => 'POST',
166 'timeout' => 20,
167 'redirection' => 5,
168 'httpversion' => '1.1',
169 'blocking' => true,
170 'body' => $body,
171 'user-agent' => 'PUT/1.0.0; ' . get_bloginfo( 'url' )
172 )
173 );
174
175 $this->set_track_time();
176
177 if( is_wp_error( $request ) ) {
178 return $request;
179 }
180
181 }
182
183 /**
184 * Here we collect most of the data
185 *
186 * @since 1.0.0
187 */
188 public function get_data() {
189
190 // Use this to pass error messages back if necessary
191 $body['message'] = '';
192
193 // Use this array to send data back
194 $body = array(
195 'plugin_slug' => sanitize_text_field( $this->plugin_name ),
196 'url' => get_bloginfo( 'url' ),
197 'site_name' => get_bloginfo( 'name' ),
198 'site_version' => get_bloginfo( 'version' ),
199 'site_language' => get_bloginfo( 'language' ),
200 'charset' => get_bloginfo( 'charset' ),
201 'wpins_version' => $this->wpins_version,
202 'php_version' => phpversion(),
203 'multisite' => is_multisite(),
204 'file_location' => __FILE__
205 );
206
207 // Collect the email if the correct option has been set
208 if( $this->get_can_collect_email() ) {
209 $body['email'] = $this->get_admin_email();
210 }
211 $body['marketing_method'] = $this->marketing;
212
213 $body['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
214
215 // Retrieve current plugin information
216 if( ! function_exists( 'get_plugins' ) ) {
217 include ABSPATH . '/wp-admin/includes/plugin.php';
218 }
219
220 $plugins = array_keys( get_plugins() );
221 $active_plugins = get_option( 'active_plugins', array() );
222
223 foreach ( $plugins as $key => $plugin ) {
224 if ( in_array( $plugin, $active_plugins ) ) {
225 // Remove active plugins from list so we can show active and inactive separately
226 unset( $plugins[$key] );
227 }
228 }
229
230 $body['active_plugins'] = $active_plugins;
231 $body['inactive_plugins'] = $plugins;
232
233 // Check text direction
234 $body['text_direction'] = 'LTR';
235 if( function_exists( 'is_rtl' ) ) {
236 if( is_rtl() ) {
237 $body['text_direction'] = 'RTL';
238 }
239 } else {
240 $body['text_direction'] = 'not set';
241 }
242
243 /**
244 * Get our plugin data
245 * Currently we grab plugin name and version
246 * Or, return a message if the plugin data is not available
247 * @since 1.0.0
248 */
249 $plugin = $this->plugin_data();
250 if( empty( $plugin ) ) {
251 // We can't find the plugin data
252 // Send a message back to our home site
253 $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' );
254 $body['status'] = 'Data not found'; // Never translated
255 } else {
256 if( isset( $plugin['Name'] ) ) {
257 $body['plugin'] = sanitize_text_field( $plugin['Name'] );
258 }
259 if( isset( $plugin['Version'] ) ) {
260 $body['version'] = sanitize_text_field( $plugin['Version'] );
261 }
262 $body['status'] = 'Active'; // Never translated
263 }
264
265 /**
266 * Get our plugin options
267 * @since 1.0.0
268 */
269 $options = $this->options;
270 $plugin_options = array();
271 if( ! empty( $options ) && is_array( $options ) ) {
272 foreach( $options as $option ) {
273 $fields = get_option( $option );
274 // Check for permission to send this option
275 if( isset( $fields['wpins_registered_setting'] ) ) {
276 foreach( $fields as $key=>$value ) {
277 $plugin_options[$key] = $value;
278 }
279 }
280 }
281 }
282 $body['plugin_options'] = $this->options; // Returns array
283 $body['plugin_options_fields'] = $plugin_options; // Returns object
284
285 /**
286 * Get our theme data
287 * Currently we grab theme name and version
288 * @since 1.0.0
289 */
290 $theme = wp_get_theme();
291 if( $theme->Name ) {
292 $body['theme'] = sanitize_text_field( $theme->Name );
293 }
294 if( $theme->Version ) {
295 $body['theme_version'] = sanitize_text_field( $theme->Version );
296 }
297
298 // Return the data
299 return $body;
300
301 }
302
303 /**
304 * Return plugin data
305 * @since 1.0.0
306 */
307 public function plugin_data() {
308 // Being cautious here
309 if( ! function_exists( 'get_plugin_data' ) ) {
310 include ABSPATH . '/wp-admin/includes/plugin.php';
311 }
312 // Retrieve current plugin information
313 $plugin = get_plugin_data( $this->plugin_file );
314 return $plugin;
315 }
316
317 /**
318 * Deactivating plugin
319 * @since 1.0.0
320 */
321 public function deactivate_this_plugin() {
322 // Check to see if the user has opted in to tracking
323 $allow_tracking = $this->get_is_tracking_allowed();
324 if( ! $allow_tracking ) {
325 return;
326 }
327 $body = $this->get_data();
328 $body['status'] = 'Deactivated'; // Never translated
329 $body['deactivated_date'] = time();
330
331 // Add deactivation form data
332 if( false !== get_option( 'wpins_deactivation_reason_' . $this->plugin_name ) ) {
333 $body['deactivation_reason'] = get_option( 'wpins_deactivation_reason_' . $this->plugin_name );
334 }
335 if( false !== get_option( 'wpins_deactivation_details_' . $this->plugin_name ) ) {
336 $body['deactivation_details'] = get_option( 'wpins_deactivation_details_' . $this->plugin_name );
337 }
338
339 $this->send_data( $body );
340 // Clear scheduled update
341 wp_clear_scheduled_hook( 'put_do_weekly_action' );
342 }
343
344 /**
345 * Is tracking allowed?
346 * @since 1.0.0
347 */
348 public function get_is_tracking_allowed() {
349 // First, check if the user has changed their mind and opted out of tracking
350 if( $this->has_user_opted_out() ) {
351 $this->set_is_tracking_allowed( false, $this->plugin_name );
352 return false;
353 }
354 // The wpins_allow_tracking option is an array of plugins that are being tracked
355 $allow_tracking = get_option( 'wpins_allow_tracking' );
356 // If this plugin is in the array, then tracking is allowed
357 if( isset( $allow_tracking[$this->plugin_name] ) ) {
358 return true;
359 }
360 return false;
361 }
362
363 /**
364 * Set if tracking is allowed
365 * Option is an array of all plugins with tracking permitted
366 * More than one plugin may be using the tracker
367 * @since 1.0.0
368 * @param $is_allowed Boolean true if tracking is allowed, false if not
369 */
370 public function set_is_tracking_allowed( $is_allowed, $plugin=null ) {
371 if( empty( $plugin ) ) {
372 $plugin = $this->plugin_name;
373 }
374 // The wpins_allow_tracking option is an array of plugins that are being tracked
375 $allow_tracking = get_option( 'wpins_allow_tracking' );
376
377 // If the user has decided to opt out
378 if( $this->has_user_opted_out() ) {
379 if( isset( $allow_tracking[$plugin] ) ) {
380 unset( $allow_tracking[$plugin] );
381 }
382 } else if( $is_allowed || ! $this->require_optin ) {
383 // If the user has agreed to allow tracking or if opt-in is not required
384 if( empty( $allow_tracking ) || ! is_array( $allow_tracking ) ) {
385 // If nothing exists in the option yet, start a new array with the plugin name
386 $allow_tracking = array( $plugin => $plugin );
387 } else {
388 // Else add the plugin name to the array
389 $allow_tracking[$plugin] = $plugin;
390 }
391 } else {
392 if( isset( $allow_tracking[$plugin] ) ) {
393 unset( $allow_tracking[$plugin] );
394 }
395 }
396 update_option( 'wpins_allow_tracking', $allow_tracking );
397 }
398
399 /**
400 * Has the user opted out of allowing tracking?
401 * @since 1.1.0
402 * @return Boolean
403 */
404 public function has_user_opted_out() {
405 // Iterate through the options that are being tracked looking for wpins_opt_out setting
406 if( ! empty( $this->options ) ) {
407 foreach( $this->options as $option_name ) {
408 // Check each option
409 $options = get_option( $option_name );
410 // If we find the setting, return true
411 if( ! empty( $options['wpins_opt_out'] ) ) {
412 return true;
413 }
414 }
415 }
416 return false;
417 }
418
419 /**
420 * Check if it's time to track
421 * @since 1.1.1
422 */
423 public function get_is_time_to_track() {
424 // Let's see if we're due to track this plugin yet
425 $track_times = get_option( 'wpins_last_track_time', array() );
426 if( ! isset( $track_times[$this->plugin_name] ) ) {
427 // If we haven't set a time for this plugin yet, then we must track it
428 return true;
429 } else {
430 // If the time is set, let's see if it's more than a day ago
431 if( $track_times[$this->plugin_name] < strtotime( '-1 day' ) ) {
432 return true;
433 }
434 }
435 return false;
436 }
437
438 /**
439 * Record the time we send tracking data
440 * @since 1.1.1
441 */
442 public function set_track_time() {
443 // We've tracked, so record the time
444 $track_times = get_option( 'wpins_last_track_time', array() );
445 // Set different times according to plugin, in case we are tracking multiple plugins
446 $track_times[$this->plugin_name] = time();
447 update_option( 'wpins_last_track_time', $track_times );
448 }
449
450 /**
451 * Set if we should block the opt-in notice for this plugin
452 * Option is an array of all plugins that have received a response from the user
453 * @since 1.0.0
454 */
455 public function update_block_notice( $plugin=null ) {
456 if( empty( $plugin ) ) {
457 $plugin = $this->plugin_name;
458 }
459 $block_notice = get_option( 'wpins_block_notice' );
460 if( empty( $block_notice ) || ! is_array( $block_notice ) ) {
461 // If nothing exists in the option yet, start a new array with the plugin name
462 $block_notice = array( $plugin => $plugin );
463 } else {
464 // Else add the plugin name to the array
465 $block_notice[$plugin] = $plugin;
466 }
467 update_option( 'wpins_block_notice', $block_notice );
468 }
469
470 /**
471 * Can we collect the email address?
472 * @since 1.0.0
473 */
474 public function get_can_collect_email() {
475 // The wpins_collect_email option is an array of plugins that are being tracked
476 $collect_email = get_option( 'wpins_collect_email' );
477 // If this plugin is in the array, then we can collect the email address
478 if( isset( $collect_email[$this->plugin_name] ) ) {
479 return true;
480 }
481 return false;
482 }
483
484 /**
485 * Set if user has allowed us to collect their email address
486 * Option is an array of all plugins with email collection permitted
487 * More than one plugin may be using the tracker
488 * @since 1.0.0
489 * @param $can_collect Boolean true if collection is allowed, false if not
490 */
491 public function set_can_collect_email( $can_collect, $plugin=null ) {
492 if( empty( $plugin ) ) {
493 $plugin = $this->plugin_name;
494 }
495 // The wpins_collect_email option is an array of plugins that are being tracked
496 $collect_email = get_option( 'wpins_collect_email' );
497 // If the user has agreed to allow tracking or if opt-in is not required
498 if( $can_collect ) {
499 if( empty( $collect_email ) || ! is_array( $collect_email ) ) {
500 // If nothing exists in the option yet, start a new array with the plugin name
501 $collect_email = array( $plugin => $plugin );
502 } else {
503 // Else add the plugin name to the array
504 $collect_email[$plugin] = $plugin;
505 }
506 } else {
507 if( isset( $collect_email[$plugin] ) ) {
508 unset( $collect_email[$plugin] );
509 }
510 }
511 update_option( 'wpins_collect_email', $collect_email );
512 }
513
514 /**
515 * Get the correct email address to use
516 * @since 1.1.2
517 * @return Email address
518 */
519 public function get_admin_email() {
520 // The wpins_collect_email option is an array of plugins that are being tracked
521 $email = get_option( 'wpins_admin_emails' );
522 // If this plugin is in the array, then we can collect the email address
523 if( isset( $email[$this->plugin_name] ) ) {
524 return $email[$this->plugin_name];
525 }
526 return false;
527 }
528
529 /**
530 * Set the correct email address to use
531 * There might be more than one admin on the site
532 * So we only use the first admin's email address
533 * @param $email Email address to set
534 * @param $plugin Plugin name to set email address for
535 * @since 1.1.2
536 */
537 public function set_admin_email( $email=null, $plugin=null ) {
538 if( empty( $plugin ) ) {
539 $plugin = $this->plugin_name;
540 }
541 // If no email address passed, try to get the current user's email
542 if( empty( $email ) ) {
543 // Have to check that current user object is available
544 if( function_exists( 'wp_get_current_user' ) ) {
545 $current_user = wp_get_current_user();
546 $email = $current_user->user_email;
547 }
548 }
549 // The wpins_admin_emails option is an array of admin email addresses
550 $admin_emails = get_option( 'wpins_admin_emails' );
551 if( empty( $admin_emails ) || ! is_array( $admin_emails ) ) {
552 // If nothing exists in the option yet, start a new array with the plugin name
553 $admin_emails = array( $plugin => sanitize_email( $email ) );
554 } else if( empty( $admin_emails[$plugin] ) ) {
555 // Else add the email address to the array, if not already set
556 $admin_emails[$plugin] = sanitize_email( $email );
557 }
558 update_option( 'wpins_admin_emails', $admin_emails );
559 }
560
561 public function clicked(){
562 // Check for plugin args
563 if( isset( $_GET['plugin'] ) && isset( $_GET['plugin_action'] ) ) {
564 $plugin = sanitize_text_field( $_GET['plugin'] );
565 $action = sanitize_text_field( $_GET['plugin_action'] );
566 if( $action == 'yes' ) {
567 $this->set_is_tracking_allowed( true, $plugin );
568 $this->do_tracking( true ); // Run this straightaway
569 } else {
570 $this->set_is_tracking_allowed( false, $plugin );
571 }
572 $this->update_block_notice( $plugin );
573 }
574 }
575
576 /**
577 * Display the admin notice to users to allow them to opt in
578 *
579 * @since 1.0.0
580 */
581 public function optin_notice() {
582 // Check whether to block the notice, e.g. because we're in a local environment
583 // wpins_block_notice works the same as wpins_allow_tracking, an array of plugin names
584 $block_notice = get_option( 'wpins_block_notice' );
585 if( isset( $block_notice[$this->plugin_name] ) ) {
586 return;
587 }
588
589 if ( ! current_user_can( 'manage_options' ) ) {
590 return;
591 }
592
593 // @credit EDD
594 // Don't bother asking user to opt in if they're in local dev
595 $is_local = false;
596 if( stristr( network_site_url( '/' ), '.dev' ) !== false || stristr( network_site_url( '/' ), 'localhost' ) !== false || stristr( network_site_url( '/' ), ':8888' ) !== false ) {
597 $is_local = true;
598 }
599 $is_local = apply_filters( 'wpins_is_local_' . $this->plugin_name, $is_local );
600 if ( $is_local ) {
601 $this->update_block_notice();
602 } else {
603
604 // Display the notice requesting permission to track
605 // Retrieve current plugin information
606 $plugin = $this->plugin_data();
607 $plugin_name = $plugin['Name'];
608
609 // Args to add to query if user opts in to tracking
610 $yes_args = array(
611 'plugin' => $this->plugin_name,
612 'plugin_action' => 'yes'
613 );
614
615 // Decide how to request permission to collect email addresses
616 if( $this->marketing == 1 ) {
617 // Option 1 combines permissions to track and collect email
618 $yes_args['marketing_optin'] = 'yes';
619 } else if( $this->marketing == 2 ) {
620 // Option 2 enables a second notice that fires after the user opts in to tracking
621 $yes_args['marketing'] = 'yes';
622 }
623 $url_yes = add_query_arg( $yes_args );
624 $url_no = add_query_arg( array(
625 'plugin' => $this->plugin_name,
626 'plugin_action' => 'no'
627 ) );
628
629 // Decide on notice text
630 if( $this->marketing != 1 ) {
631 // Standard notice text
632 $notice_text = __( 'Thank you for installing our plugin. We would like to track its usage on your site. We don\'t record any sensitive data, only information regarding the WordPress environment and plugin settings, which we will use to help us make improvements to the plugin. Tracking is completely optional.', 'plugin-usage-tracker' );
633 } else {
634 // If we have option 1 for marketing, we include reference to sending product information here
635 $notice_text = __( 'Want to help make <strong>NotificationX</strong> even more awesome? You can get a <strong>25% discount coupon</strong> for Premium extensions if you allow us to track the usage. <a class="notificationx-insights-data-we-collect" href="#">What we collect.</a>', 'plugin-usage-tracker' );
636 }
637 // And we allow you to filter the text anyway
638 $notice_text = apply_filters( 'wpins_notice_text_' . esc_attr( $this->plugin_name ), $notice_text ); ?>
639
640 <div class="notice notice-info updated put-dismiss-notice">
641 <p><?php echo __( $notice_text ); ?></p>
642 <div class="notificationx-insights-data" style="display: none;">
643 <p><?php echo __( 'We collect non-sensitive diagnostic data and plugin usage information. Your site URL, WordPress & PHP version, plugins & themes and email address to send you the discount coupon. This data lets us make sure this plugin always stays compatible with the most popular plugins and themes. No spam, I promise.' ); ?></p>
644 </div>
645 <p>
646 <a href="<?php echo esc_url( $url_yes ); ?>" class="button-primary"><?php _e( 'Sure, I\'d like to help', 'plugin-usage-tracker' ); ?></a>
647 <a href="<?php echo esc_url( $url_no ); ?>" class="button-secondary"><?php _e( 'No Thanks', 'plugin-usage-tracker' ); ?></a>
648 </p>
649 <?php echo "<script type='text/javascript'>jQuery('.notificationx-insights-data-we-collect').on('click', function(e) {
650 e.preventDefault();
651 jQuery('.notificationx-insights-data').slideToggle('fast');
652 });
653 </script>";?>
654 </div>
655 <?php
656 }
657 }
658 /**
659 * Display the marketing notice to users if enabled
660 * Only displays after the user has opted in to tracking
661 *
662 * @since 1.0.0
663 */
664 public function marketing_notice() {
665 // Check if user has opted in to marketing
666 if( isset( $_GET['marketing_optin'] ) ) {
667 // Set marketing optin
668 $this->set_can_collect_email( sanitize_text_field( $_GET['marketing_optin'] ), $this->plugin_name );
669 // Do tracking
670 $this->do_tracking( true );
671 } else if( isset( $_GET['marketing'] ) && $_GET['marketing']=='yes' ) {
672 // Display the notice requesting permission to collect email address
673 // Retrieve current plugin information
674 $plugin = $this->plugin_data();
675 $plugin_name = $plugin['Name'];
676
677 $url_yes = add_query_arg( array(
678 'plugin' => $this->plugin_name,
679 'marketing_optin' => 'yes'
680 ) );
681 $url_no = add_query_arg( array(
682 'plugin' => $this->plugin_name,
683 'marketing_optin' => 'no'
684 ) );
685
686 $marketing_text = __( 'Thank you for opting in to tracking. Would you like to receive occasional news about this plugin, including details of new features and special offers?', 'plugin-usage-tracker' );
687 $marketing_text = apply_filters( 'wpins_marketing_text_' . esc_attr( $this->plugin_name ), $marketing_text ); ?>
688
689 <div class="notice notice-info updated put-dismiss-notice">
690 <p><?php echo '<strong>' . esc_html( $plugin_name ) . '</strong>'; ?></p>
691 <p><?php echo esc_html( $marketing_text ); ?></p>
692 <p>
693 <a href="<?php echo esc_url( $url_yes ); ?>" data-putnotice="yes" class="button-secondary"><?php _e( 'Yes Please', 'plugin-usage-tracker' ); ?></a>
694 <a href="<?php echo esc_url( $url_no ); ?>" data-putnotice="no" class="button-secondary"><?php _e( 'No Thank You', 'plugin-usage-tracker' ); ?></a>
695 </p>
696 </div>
697 <?php }
698 }
699
700 /**
701 * Filter the deactivation link to allow us to present a form when the user deactivates the plugin
702 * @since 1.0.0
703 */
704 public function filter_action_links( $links ) {
705 // Check to see if the user has opted in to tracking
706 if( ! $this->get_is_tracking_allowed() ) {
707 return $links;
708 }
709 if( isset( $links['deactivate'] ) && $this->include_goodbye_form ) {
710 $deactivation_link = $links['deactivate'];
711
712
713 // Insert an onClick action to allow form before deactivating
714 $deactivation_link = str_replace( '<a ', '<div class="wpdev-put-goodbye-form-wrapper-'. esc_attr( $this->plugin_name ) .'"><div class="wpdev-put-goodbye-form-bg-'. esc_attr( $this->plugin_name ) .'"></div><span class="wpdev-put-goodbye-form" id="wpdev-put-goodbye-form-' . esc_attr( $this->plugin_name ) . '"></span></div><a onclick="javascript:event.preventDefault();" id="wpdev-put-goodbye-link-' . esc_attr( $this->plugin_name ) . '" ', $deactivation_link );
715 $links['deactivate'] = $deactivation_link;
716 }
717 return $links;
718 }
719
720 /*
721 * Form text strings
722 * These are non-filterable and used as fallback in case filtered strings aren't set correctly
723 * @since 1.0.0
724 */
725 public function form_default_text() {
726 $form = array();
727 $form['heading'] = __( 'Sorry to see you go', 'plugin-usage-tracker' );
728 $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', 'plugin-usage-tracker' );
729
730 $form['options'] = array(
731 __( 'I no longer need the plugin', 'plugin-usage-tracker' ),
732 [
733 'label' => __( 'I found a better plugin', 'plugin-usage-tracker' ),
734 'extra_field' => __( 'Please share which plugin', 'plugin-usage-tracker' )
735 ],
736 __( "I couldn't get the plugin to work", 'plugin-usage-tracker' ),
737 __( 'It\'s a temporary deactivation', 'plugin-usage-tracker' ),
738 [
739 'label' => __( 'Other', 'plugin-usage-tracker' ),
740 'extra_field' => __( 'Please share the reason', 'plugin-usage-tracker' ),
741 'type' => 'textarea'
742 ]
743 );
744
745 return $form;
746 }
747
748 /**
749 * Form text strings
750 * These can be filtered
751 * The filter hook must be unique to the plugin
752 * @since 1.0.0
753 */
754 public function form_filterable_text() {
755 $form = $this->form_default_text();
756 return apply_filters( 'wpins_form_text_' . esc_attr( $this->plugin_name ), $form );
757 }
758
759 /**
760 * Form text strings
761 * These can be filtered
762 * @since 1.0.0
763 */
764 public function goodbye_ajax() {
765 // Get our strings for the form
766 $form = $this->form_filterable_text();
767 if( ! isset( $form['heading'] ) || ! isset( $form['body'] ) || ! isset( $form['options'] ) || ! is_array( $form['options'] ) || ! isset( $form['details'] ) ) {
768 // If the form hasn't been filtered correctly, we revert to the default form
769 $form = $this->form_default_text();
770 }
771 // Build the HTML to go in the form
772 $html = '<div class="wpdev-put-goodbye-form-head"><strong>' . esc_html( $form['heading'] ) . '</strong></div>';
773 $html .= '<div class="wpdev-put-goodbye-form-body"><p class="wpdev-put-goodbye-form-caption">' . esc_html( $form['body'] ) . '</p>';
774 if( is_array( $form['options'] ) ) {
775 $html .= '<div id="wpdev-'. esc_attr( $this->plugin_name ) .'-goodbye-options" class="wpdev-'. esc_attr( $this->plugin_name ) .'-goodbye-options"><ul>';
776 foreach( $form['options'] as $option ) {
777 if( is_array( $option ) ) {
778 $id = strtolower( str_replace( " ", "_", esc_attr( $option['label'] ) ) );
779 $id = $id . '_' . esc_attr( $this->plugin_name );
780 $html .= '<li class="has-goodbye-extra">';
781 $html .= '<input type="radio" name="wpdev-'. esc_attr( $this->plugin_name ) .'-goodbye-options" id="' . $id . '" value="' . esc_attr( $option['label'] ) . '">';
782 $html .= '<div><label for="' . $id . '">' . esc_attr( $option['label'] ) . '</label>';
783 if( isset( $option[ 'extra_field' ] ) && ! isset( $option['type'] )) {
784 $html .= '<input type="text" style="display: none" name="'. $id .'" id="' . str_replace( " ", "", esc_attr( $option['extra_field'] ) ) . '" placeholder="' . esc_attr( $option['extra_field'] ) . '">';
785 }
786 if( isset( $option[ 'extra_field' ] ) && isset( $option['type'] )) {
787 $html .= '<'. $option['type'] .' style="display: none" type="text" name="'. $id .'" id="' . str_replace( " ", "", esc_attr( $option['extra_field'] ) ) . '" placeholder="' . esc_attr( $option['extra_field'] ) . '"></' . $option['type'] . '>';
788 }
789 $html .= '</div></li>';
790 } else {
791 $id = strtolower( str_replace( " ", "_", esc_attr( $option ) ) );
792 $id = $id . '_' . esc_attr( $this->plugin_name );
793 $html .= '<li><input type="radio" name="wpdev-'. esc_attr( $this->plugin_name ) .'-goodbye-options" id="' . $id . '" value="' . esc_attr( $option ) . '"> <label for="' . $id . '">' . esc_attr( $option ) . '</label></li>';
794 }
795 }
796 $html .= '</ul></div><!-- .wpdev-'. esc_attr( $this->plugin_name ) .'-goodbye-options -->';
797 }
798 $html .= '</div><!-- .wpdev-put-goodbye-form-body -->';
799 $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', 'plugin-usage-tracker' ) . '</p>';
800 ?>
801 <style type="text/css">
802 .wpdev-put-form-active-<?php echo esc_attr( $this->plugin_name ); ?> .wpdev-put-goodbye-form-bg-<?php echo esc_attr( $this->plugin_name ); ?> {
803 background: rgba( 0, 0, 0, .8 );
804 position: fixed;
805 top: 0;
806 left: 0;
807 width: 100%;
808 height: 100%;
809 z-index: 9;
810 }
811 .wpdev-put-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> {
812 position: relative;
813 display: none;
814 }
815 .wpdev-put-form-active-<?php echo esc_attr( $this->plugin_name ); ?> .wpdev-put-goodbye-form-wrapper-<?php echo esc_attr( $this->plugin_name ); ?> {
816 display: flex !important;
817 align-items: center;
818 justify-content: center;
819 width: 100%;
820 height: 100%;
821 position: fixed;
822 left: 0px;
823 top: 0px;
824 }
825 .wpdev-put-goodbye-form {
826 display: none;
827 }
828 .wpdev-put-form-active-<?php echo esc_attr( $this->plugin_name ); ?> .wpdev-put-goodbye-form {
829 position: relative !important;
830 width: 550px;
831 max-width: 80%;
832 background: #fff;
833 box-shadow: 2px 8px 23px 3px rgba(0,0,0,.2);
834 border-radius: 3px;
835 white-space: normal;
836 overflow: hidden;
837 display: block;
838 z-index: 999999;
839 }
840 .wpdev-put-goodbye-form-head {
841 background: #fff;
842 color: #495157;
843 padding: 18px;
844 box-shadow: 0 0 8px rgba(0,0,0,.1);
845 font-size: 15px;
846 }
847 .wpdev-put-goodbye-form .wpdev-put-goodbye-form-head strong {
848 font-size: 15px;
849 }
850 .wpdev-put-goodbye-form-body {
851 padding: 8px 18px;
852 color: #333;
853 }
854 .wpdev-put-goodbye-form-body label {
855 color: #6d7882;
856 padding-left: 5px;
857 }
858 .wpdev-put-goodbye-form-body .wpdev-put-goodbye-form-caption {
859 font-weight: 500;
860 font-size: 15px;
861 color: #495157;
862 line-height: 1.4;
863 }
864 .wpdev-put-goodbye-form-body #wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options {
865 padding-top: 5px;
866 }
867 .wpdev-put-goodbye-form-body #wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options ul > li {
868 margin-bottom: 15px;
869 }
870 .deactivating-spinner {
871 display: none;
872 padding-bottom: 20px !important;
873 }
874 .deactivating-spinner .spinner {
875 float: none;
876 margin: 4px 4px 0 18px;
877 vertical-align: bottom;
878 visibility: visible;
879 }
880 .wpdev-put-goodbye-form-footer {
881 padding: 8px 18px;
882 margin-bottom: 15px;
883 }
884 .wpdev-put-goodbye-form-footer > .wpdev-put-goodbye-form-buttons {
885 display: flex;
886 align-items: center;
887 justify-content: space-between;
888 }
889 .wpdev-put-goodbye-form-footer .wpsp-put-submit-btn {
890 background-color: #d30c5c;
891 -webkit-border-radius: 3px;
892 border-radius: 3px;
893 color: #fff;
894 line-height: 1;
895 padding: 15px 20px;
896 font-size: 13px;
897 }
898 .wpdev-put-goodbye-form-footer .wpsp-put-deactivate-btn {
899 font-size: 13px;
900 color: #a4afb7;
901 background: none;
902 float: right;
903 padding-right: 10px;
904 width: auto;
905 text-decoration: underline;
906 }
907 #wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options ul li > div {
908 display: inline;
909 padding-left: 3px;
910 }
911 #wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options ul li > div > input, #wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options ul li > div > textarea {
912 margin: 10px 18px;
913 padding: 8px;
914 width: 80%;
915 }
916 </style>
917 <script>
918 jQuery(document).ready(function($){
919 $("#wpdev-put-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>").on("click",function(){
920 // We'll send the user to this deactivation link when they've completed or dismissed the form
921 var url = document.getElementById("wpdev-put-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>");
922 $('body').toggleClass('wpdev-put-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
923 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>").fadeIn();
924 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>").html( '<?php echo $html; ?>' + '<div class="wpdev-put-goodbye-form-footer"><div class="wpdev-put-goodbye-form-buttons"><a id="put-submit-form-<?php echo esc_attr( $this->plugin_name ); ?>" class="wpsp-put-submit-btn" href="#"><?php _e( 'Submit and Deactivate', 'plugin-usage-tracker' ); ?></a>&nbsp;<a class="wpsp-put-deactivate-btn" href="'+url+'"><?php _e( 'Just Deactivate', 'plugin-usage-tracker' ); ?></a></div></div>');
925 $('#put-submit-form-<?php echo esc_attr( $this->plugin_name ); ?>').on('click', function(e){
926 // As soon as we click, the body of the form should disappear
927 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpdev-put-goodbye-form-body").fadeOut();
928 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .wpdev-put-goodbye-form-footer").fadeOut();
929 // Fade in spinner
930 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .deactivating-spinner").fadeIn();
931 e.preventDefault();
932 var checkedInput = $("input[name='wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options']:checked"),
933 checkedInputVal, details;
934 if( checkedInput.length > 0 ) {
935 checkedInputVal = checkedInput.val();
936 details = $('input[name="'+ checkedInput[0].id +'"], textarea[name="'+ checkedInput[0].id +'"]').val();
937 }
938
939 if( typeof details === 'undefined' ) {
940 details = '';
941 }
942 if( typeof checkedInputVal === 'undefined' ) {
943 checkedInputVal = 'No Reason';
944 }
945
946 var data = {
947 'action': 'goodbye_form_<?php echo esc_attr( $this->plugin_name ); ?>',
948 'values': checkedInputVal,
949 'details': details,
950 'security': "<?php echo wp_create_nonce ( 'wpins_goodbye_form' ); ?>",
951 'dataType': "json"
952 }
953
954 $.post(
955 ajaxurl,
956 data,
957 function(response){
958 // Redirect to original deactivation URL
959 window.location.href = url;
960 }
961 );
962 });
963 $('#wpdev-<?php echo esc_attr( $this->plugin_name ); ?>-goodbye-options > ul ').on('click', 'li label, li > input', function( e ){
964 var parent = $(this).parents('li');
965 parent.siblings().find('label').next('input, textarea').css('display', 'none');
966 parent.find('label').next('input, textarea').css('display', 'block');
967 });
968 // If we click outside the form, the form will close
969 $('.wpdev-put-goodbye-form-bg-<?php echo esc_attr( $this->plugin_name ); ?>').on('click',function(){
970 $("#wpdev-put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>").fadeOut();
971 $('body').removeClass('wpdev-put-form-active-<?php echo esc_attr( $this->plugin_name ); ?>');
972 });
973 });
974
975
976 });
977 </script>
978 <?php }
979
980 /**
981 * AJAX callback when the form is submitted
982 * @since 1.0.0
983 */
984 public function goodbye_form_callback() {
985 check_ajax_referer( 'wpins_goodbye_form', 'security' );
986 if( isset( $_POST['values'] ) ) {
987 $values = $_POST['values'];
988 update_option( 'wpins_deactivation_reason_' . $this->plugin_name, $values );
989 }
990 if( isset( $_POST['details'] ) ) {
991 $details = sanitize_text_field( $_POST['details'] );
992 update_option( 'wpins_deactivation_details_' . $this->plugin_name, $details );
993 }
994 $this->do_tracking(); // Run this straightaway
995 echo 'success';
996 wp_die();
997 }
998 }
999 endif;