PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / trunk
Pods – Custom Content Types and Fields vtrunk
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / src / Pods / Wisdom_Tracker.php
pods / src / Pods Last commit date
API 4 months ago Admin 4 months ago Blocks 4 months ago CLI 4 months ago Container 4 months ago Data 4 months ago Integrations 4 months ago REST 4 months ago Theme 4 months ago Tools 4 months ago WP 4 months ago Whatsit 4 months ago Config_Handler.php 4 months ago Integration.php 4 months ago Permissions.php 4 months ago Pod_Manager.php 4 months ago Service_Provider.php 4 months ago Service_Provider_Base.php 4 months ago Static_Cache.php 4 months ago Whatsit.php 4 months ago Wisdom_Tracker.php 4 months ago
Wisdom_Tracker.php
1117 lines
1 <?php
2
3 namespace Pods;
4
5 // Don't load directly.
6 if ( ! defined( 'ABSPATH' ) ) {
7 die( '-1' );
8 }
9
10 /**
11 * Wisdom Tracker class.
12 *
13 * @link https://wisdomplugin.com/
14 *
15 * @since 2.8.0
16 */
17 class Wisdom_Tracker {
18
19 private $wisdom_version = '1.2.5';
20 private $home_url = '';
21 private $plugin_file = '';
22 private $plugin_name = '';
23 private $options = [];
24 private $require_optin = true;
25 private $include_goodbye_form = true;
26 private $marketing = false;
27 private $collect_email = false;
28 private $what_am_i = 'plugin';
29 private $theme_allows_tracking = 0;
30
31 /**
32 * Class constructor
33 *
34 * @param $_home_url The URL to the site we're sending data to
35 * @param $_plugin_slug The slug for this plugin (SKC modification for Pods).
36 * @param $_plugin_file The file path for this plugin
37 * @param $_options Plugin options to track
38 * @param $_require_optin Whether user opt-in is required (always required on WordPress.org)
39 * @param $_include_goodbye_form Whether to include a form when the user deactivates
40 * @param $_marketing Marketing method:
41 * 0: Don't collect email addresses
42 * 1: Request permission same time as tracking opt-in
43 * 2: Request permission after opt-in
44 */
45 public function __construct(
46 $_plugin_file, $_plugin_slug, $_home_url, $_options, $_require_optin = true, $_include_goodbye_form = true, $_marketing = false
47 ) {
48 $this->plugin_file = $_plugin_file;
49 $this->home_url = trailingslashit( $_home_url );
50
51 // If the filename is 'functions' then we're tracking a theme
52 if ( basename( $this->plugin_file, '.php' ) != 'functions' ) {
53 $this->plugin_name = basename( $this->plugin_file, '.php' );
54
55 // SKC modification for Pods.
56 if ( ! empty( $_plugin_slug ) ) {
57 $this->plugin_name = $_plugin_slug;
58 }
59 } else {
60 $this->what_am_i = 'theme';
61 $theme = wp_get_theme();
62 if ( $theme->Name ) {
63 $this->plugin_name = sanitize_text_field( $theme->Name );
64 }
65 }
66
67 $this->options = $_options;
68 $this->require_optin = $_require_optin;
69 $this->include_goodbye_form = $_include_goodbye_form;
70 $this->marketing = $_marketing;
71
72 // Only use this on switching theme
73 $this->theme_allows_tracking = get_theme_mod( 'wisdom-allow-tracking', 0 );
74
75 // Schedule / deschedule tracking when activated / deactivated
76 if ( $this->what_am_i == 'theme' ) {
77 // Need to think about scheduling for sites that have already activated the theme
78 add_action( 'after_switch_theme', [ $this, 'schedule_tracking' ] );
79 add_action( 'switch_theme', [ $this, 'deactivate_this_plugin' ] );
80 } else {
81 register_activation_hook( $this->plugin_file, [ $this, 'schedule_tracking' ] );
82 register_deactivation_hook( $this->plugin_file, [ $this, 'deactivate_this_plugin' ] );
83 }
84
85 // Get it going
86 $this->init();
87 }
88
89 public function init() {
90 // Check marketing
91 if ( $this->marketing == 3 ) {
92 $this->set_can_collect_email( true, $this->plugin_name );
93 }
94
95 // Check whether opt-in is required
96 // If not, then tracking is allowed
97 if ( ! $this->require_optin ) {
98 $this->set_can_collect_email( true, $this->plugin_name );
99 $this->set_is_tracking_allowed( true );
100 $this->update_block_notice();
101 $this->do_tracking();
102 }
103
104 // Hook our do_tracking function to the weekly action
105 add_filter( 'cron_schedules', [ $this, 'schedule_weekly_event' ] );
106 // It's called weekly, but in fact it could be daily, weekly or monthly
107 add_action( 'put_do_weekly_action', [ $this, 'do_tracking' ] );
108
109 // Use this action for local testing
110 // add_action( 'admin_init', array( $this, 'do_tracking' ) );
111
112 // Display the admin notice on activation
113 add_action( 'admin_init', [ $this, 'set_notification_time' ] );
114 add_action( 'admin_notices', [ $this, 'optin_notice' ] );
115 add_action( 'admin_notices', [ $this, 'marketing_notice' ] );
116
117 // Deactivation
118 add_filter( 'plugin_action_links_' . plugin_basename( $this->plugin_file ), [ $this, 'filter_action_links' ] );
119 add_action( 'admin_footer-plugins.php', [ $this, 'goodbye_ajax' ] );
120 add_action( 'wp_ajax_goodbye_form', [ $this, 'goodbye_form_callback' ] );
121 }
122
123 /**
124 * When the plugin is activated
125 * Create scheduled event
126 * And check if tracking is enabled - perhaps the plugin has been reactivated
127 *
128 * @since 1.0.0
129 */
130 public function schedule_tracking() {
131 if ( ! wp_next_scheduled( 'put_do_weekly_action' ) ) {
132 $schedule = $this->get_schedule();
133 wp_schedule_event( time(), $schedule, 'put_do_weekly_action' );
134 }
135 $this->do_tracking( true );
136 }
137
138 /**
139 * Create weekly schedule
140 *
141 * @since 1.2.3
142 */
143 public function schedule_weekly_event( $schedules ) {
144 $schedules['weekly'] = [
145 'interval' => 604800,
146 'display' => __( 'Once Weekly', 'pods' ),
147 ];
148 $schedules['monthly'] = [
149 'interval' => 2635200,
150 'display' => __( 'Once Monthly', 'pods' ),
151 ];
152
153 return $schedules;
154 }
155
156 /**
157 * Get how frequently data is tracked back
158 *
159 * @since 1.2.3
160 */
161 public function get_schedule() {
162 // Could be daily, weekly or monthly
163 $schedule = apply_filters( 'wisdom_filter_schedule_' . $this->plugin_name, 'monthly' );
164
165 return $schedule;
166 }
167
168 /**
169 * This is our function to get everything going
170 * Check that user has opted in
171 * Collect data
172 * Then send it back
173 *
174 * @since 1.0.0
175 *
176 * @param $force Force tracking if it's not time
177 */
178 public function do_tracking( $force = false ) {
179 // If the home site hasn't been defined, we just drop out. Nothing much we can do.
180 if ( ! $this->home_url ) {
181 return;
182 }
183
184 // Check to see if the user has opted in to tracking
185 $allow_tracking = $this->get_is_tracking_allowed();
186 if ( ! $allow_tracking ) {
187 return;
188 }
189
190 // Check to see if it's time to track
191 $track_time = $this->get_is_time_to_track();
192 if ( ! $track_time && ! $force ) {
193 return;
194 }
195
196 $this->set_admin_email();
197
198 // Get our data
199 $body = $this->get_data();
200
201 // Send the data
202 $this->send_data( $body );
203 }
204
205 /**
206 * We hook this to admin_init when the user accepts tracking
207 * Ensures the email address is available
208 *
209 * @since 1.2.1
210 */
211 public function force_tracking() {
212 $this->do_tracking( true ); // Run this straightaway
213 }
214
215 /**
216 * Send the data to the home site
217 *
218 * @since 1.0.0
219 */
220 public function send_data( $body ) {
221 $request = wp_remote_post( esc_url( $this->home_url . '?usage_tracker=hello' ), [
222 'method' => 'POST',
223 'timeout' => 20,
224 'redirection' => 5,
225 'httpversion' => '1.1',
226 'blocking' => true,
227 'body' => $body,
228 'user-agent' => 'PUT/1.0.0; ' . home_url(),
229 ] );
230
231 $this->set_track_time();
232
233 if ( is_wp_error( $request ) ) {
234 return $request;
235 }
236 }
237
238 /**
239 * Here we collect most of the data
240 *
241 * @since 1.0.0
242 */
243 public function get_data() {
244 // Use this to pass error messages back if necessary
245 $body['message'] = '';
246
247 // Use this array to send data back
248 $body = [
249 'plugin_slug' => sanitize_text_field( $this->plugin_name ),
250 'url' => home_url(),
251 'site_name' => get_bloginfo( 'name' ),
252 'site_version' => get_bloginfo( 'version' ),
253 'site_language' => get_bloginfo( 'language' ),
254 'charset' => get_bloginfo( 'charset' ),
255 'wisdom_version' => $this->wisdom_version,
256 'php_version' => phpversion(),
257 'multisite' => is_multisite(),
258 'file_location' => __FILE__,
259 'product_type' => esc_html( $this->what_am_i ),
260 ];
261
262 // Collect the email if the correct option has been set
263 if ( $this->get_can_collect_email() ) {
264 $body['email'] = $this->get_admin_email();
265 }
266 $body['marketing_method'] = $this->marketing;
267
268 $body['server'] = isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '';
269
270 // Extra PHP fields
271 $body['memory_limit'] = ini_get( 'memory_limit' );
272 $body['upload_max_size'] = ini_get( 'upload_max_size' );
273 $body['post_max_size'] = ini_get( 'post_max_size' );
274 $body['upload_max_filesize'] = ini_get( 'upload_max_filesize' );
275 $body['max_execution_time'] = ini_get( 'max_execution_time' );
276 $body['max_input_time'] = ini_get( 'max_input_time' );
277
278 // Retrieve current plugin information
279 if ( ! function_exists( 'get_plugins' ) ) {
280 include ABSPATH . '/wp-admin/includes/plugin.php';
281 }
282
283 $plugins = array_keys( get_plugins() );
284 // @todo SKC customization: Contribute back to Wisdom project.
285 $active_plugins = (array) get_option( 'active_plugins', [] );
286
287 foreach ( $plugins as $key => $plugin ) {
288 if ( in_array( $plugin, $active_plugins ) ) {
289 // Remove active plugins from list so we can show active and inactive separately
290 unset( $plugins[ $key ] );
291 }
292 }
293
294 $body['active_plugins'] = $active_plugins;
295 $body['inactive_plugins'] = $plugins;
296
297 // Check text direction
298 $body['text_direction'] = 'LTR';
299 if ( function_exists( 'is_rtl' ) ) {
300 if ( is_rtl() ) {
301 $body['text_direction'] = 'RTL';
302 }
303 } else {
304 $body['text_direction'] = 'not set';
305 }
306
307 /**
308 * Get our plugin data
309 * Currently we grab plugin name and version
310 * Or, return a message if the plugin data is not available
311 *
312 * @since 1.0.0
313 */
314 $plugin = $this->plugin_data();
315 $body['status'] = 'Active'; // Never translated
316 if ( empty( $plugin ) ) {
317 // We can't find the plugin data
318 // Send a message back to our home site
319 $body['message'] .= __( 'We can\'t detect any product information. This is most probably because you have not included the code snippet.', 'pods' );
320 $body['status'] = 'Data not found'; // Never translated
321 } else {
322 if ( isset( $plugin['Name'] ) ) {
323 $body['plugin'] = sanitize_text_field( $plugin['Name'] );
324 }
325 if ( isset( $plugin['Version'] ) ) {
326 $body['version'] = sanitize_text_field( $plugin['Version'] );
327 }
328 }
329
330 /**
331 * Get our plugin options
332 *
333 * @since 1.0.0
334 */
335 $options = $this->options;
336 $plugin_options = [];
337 if ( ! empty( $options ) && is_array( $options ) ) {
338 foreach ( $options as $option ) {
339 $fields = get_option( $option );
340 // Check for permission to send this option
341 if ( isset( $fields['wisdom_registered_setting'] ) ) {
342 foreach ( $fields as $key => $value ) {
343 $plugin_options[ $key ] = $value;
344 }
345 }
346 }
347 }
348 $body['plugin_options'] = $this->options; // Returns array
349 $body['plugin_options_fields'] = $plugin_options; // Returns object
350
351 /**
352 * Get our theme data
353 * Currently we grab theme name and version
354 *
355 * @since 1.0.0
356 */
357 $theme = wp_get_theme();
358 if ( $theme->Name ) {
359 $body['theme'] = sanitize_text_field( $theme->Name );
360 }
361 if ( $theme->Version ) {
362 $body['theme_version'] = sanitize_text_field( $theme->Version );
363 }
364 if ( $theme->Template ) {
365 $body['theme_parent'] = sanitize_text_field( $theme->Template );
366 }
367
368 // Return the data
369 return $body;
370 }
371
372 /**
373 * Return plugin data
374 *
375 * @since 1.0.0
376 */
377 public function plugin_data() {
378 // Being cautious here
379 if ( ! function_exists( 'get_plugin_data' ) ) {
380 include ABSPATH . '/wp-admin/includes/plugin.php';
381 }
382 // Retrieve current plugin information
383 $plugin = get_plugin_data( $this->plugin_file, true, false );
384
385 return $plugin;
386 }
387
388 /**
389 * Deactivating plugin
390 *
391 * @since 1.0.0
392 */
393 public function deactivate_this_plugin() {
394 // Check to see if the user has opted in to tracking
395 if ( $this->what_am_i == 'theme' ) {
396 $allow_tracking = $this->theme_allows_tracking;
397 } else {
398 $allow_tracking = $this->get_is_tracking_allowed();
399 }
400
401 if ( ! $allow_tracking ) {
402 return;
403 }
404
405 $body = $this->get_data();
406 $body['status'] = 'Deactivated'; // Never translated
407 $body['deactivated_date'] = time();
408
409 // Add deactivation form data
410 if ( false !== get_option( 'wisdom_deactivation_reason_' . $this->plugin_name ) ) {
411 $body['deactivation_reason'] = get_option( 'wisdom_deactivation_reason_' . $this->plugin_name );
412 }
413 if ( false !== get_option( 'wisdom_deactivation_details_' . $this->plugin_name ) ) {
414 $body['deactivation_details'] = get_option( 'wisdom_deactivation_details_' . $this->plugin_name );
415 }
416
417 $this->send_data( $body );
418 // Clear scheduled update
419 wp_clear_scheduled_hook( 'put_do_weekly_action' );
420
421 // Clear the wisdom_last_track_time value for this plugin
422 // @since 1.2.2
423 $track_time = get_option( 'wisdom_last_track_time' );
424 if ( isset( $track_time[ $this->plugin_name ] ) ) {
425 unset( $track_time[ $this->plugin_name ] );
426 }
427 update_option( 'wisdom_last_track_time', $track_time, 'yes' );
428 }
429
430 /**
431 * Is tracking allowed?
432 *
433 * @since 1.0.0
434 */
435 public function get_is_tracking_allowed() {
436 // First, check if the user has changed their mind and opted out of tracking
437 if ( $this->has_user_opted_out() ) {
438 $this->set_is_tracking_allowed( false, $this->plugin_name );
439
440 // SKC modification for Pods.
441 $this->set_can_collect_email( false, $this->plugin_name );
442
443 return false;
444 }
445
446 if ( $this->what_am_i == 'theme' ) {
447 $mod = get_theme_mod( 'wisdom-allow-tracking', 0 );
448 if ( $mod ) {
449 return true;
450 }
451 } else {
452 // The wisdom_allow_tracking option is an array of plugins that are being tracked
453 $allow_tracking = get_option( 'wisdom_allow_tracking' );
454 // If this plugin is in the array, then tracking is allowed
455 if ( isset( $allow_tracking[ $this->plugin_name ] ) ) {
456 return true;
457 }
458 }
459
460 return false;
461 }
462
463 /**
464 * Set if tracking is allowed
465 * Option is an array of all plugins with tracking permitted
466 * More than one plugin may be using the tracker
467 *
468 * @since 1.0.0
469 *
470 * @param $is_allowed Boolean true if tracking is allowed, false if not
471 */
472 public function set_is_tracking_allowed( $is_allowed, $plugin = null ) {
473 if ( empty( $plugin ) ) {
474 $plugin = $this->plugin_name;
475 }
476
477 // The wisdom_allow_tracking option is an array of plugins that are being tracked
478 $allow_tracking = get_option( 'wisdom_allow_tracking' );
479
480 // If the user has decided to opt out
481 if ( $this->has_user_opted_out() ) {
482 if ( $this->what_am_i == 'theme' ) {
483 set_theme_mod( 'wisdom-allow-tracking', 0 );
484 } else {
485 if ( isset( $allow_tracking[ $plugin ] ) ) {
486 unset( $allow_tracking[ $plugin ] );
487 }
488 }
489 } elseif ( $is_allowed || ! $this->require_optin ) {
490 // If the user has agreed to allow tracking or if opt-in is not required
491
492 if ( $this->what_am_i == 'theme' ) {
493 set_theme_mod( 'wisdom-allow-tracking', 1 );
494 } else {
495 if ( empty( $allow_tracking ) || ! is_array( $allow_tracking ) ) {
496 // If nothing exists in the option yet, start a new array with the plugin name
497 $allow_tracking = [ $plugin => $plugin ];
498 } else {
499 // Else add the plugin name to the array
500 $allow_tracking[ $plugin ] = $plugin;
501 }
502 }
503 } else {
504 if ( $this->what_am_i == 'theme' ) {
505 set_theme_mod( 'wisdom-allow-tracking', 0 );
506 } else {
507 if ( isset( $allow_tracking[ $plugin ] ) ) {
508 unset( $allow_tracking[ $plugin ] );
509 }
510 }
511 }
512
513 update_option( 'wisdom_allow_tracking', $allow_tracking, 'yes' );
514 }
515
516 /**
517 * Has the user opted out of allowing tracking?
518 * Note that themes are opt in / plugins are opt out
519 *
520 * @since 1.1.0
521 * @return Boolean
522 */
523 public function has_user_opted_out() {
524 // Different opt-out methods for plugins and themes
525 if ( $this->what_am_i == 'theme' ) {
526 // Look for the theme mod
527 $mod = get_theme_mod( 'wisdom-allow-tracking', 0 );
528 if ( false === $mod ) {
529 // If the theme mod is not set, then return true - the user has opted out
530 return true;
531 }
532 } else {
533 // Iterate through the options that are being tracked looking for wisdom_opt_out setting
534 if ( ! empty( $this->options ) ) {
535 foreach ( $this->options as $option_name ) {
536 // Check each option
537 $options = get_option( $option_name );
538 // If we find the setting, return true
539 if ( ! empty( $options['wisdom_opt_out'] ) ) {
540 return true;
541 }
542 }
543 }
544 }
545
546 return false;
547 }
548
549 /**
550 * Check if it's time to track
551 *
552 * @since 1.1.1
553 */
554 public function get_is_time_to_track() {
555 // Let's see if we're due to track this plugin yet
556 // @todo SKC customization: Contribute back to Wisdom project.
557 $track_times = (array) get_option( 'wisdom_last_track_time', [] );
558 if ( ! isset( $track_times[ $this->plugin_name ] ) ) {
559 // If we haven't set a time for this plugin yet, then we must track it
560 return true;
561 } else {
562 // If the time is set, let's get our schedule and check if it's time to track
563 $schedule = $this->get_schedule();
564 if ( $schedule == 'daily' ) {
565 $period = 'day';
566 } elseif ( $schedule == 'weekly' ) {
567 $period = 'week';
568 } else {
569 $period = 'month';
570 }
571 if ( $track_times[ $this->plugin_name ] < strtotime( '-1 ' . $period ) ) {
572 return true;
573 }
574 }
575
576 return false;
577 }
578
579 /**
580 * Record the time we send tracking data
581 *
582 * @since 1.1.1
583 */
584 public function set_track_time() {
585 // We've tracked, so record the time
586 // @todo SKC customization: Contribute back to Wisdom project.
587 $track_times = (array) get_option( 'wisdom_last_track_time', [] );
588 // Set different times according to plugin, in case we are tracking multiple plugins
589 $track_times[ $this->plugin_name ] = time();
590 update_option( 'wisdom_last_track_time', $track_times, 'yes' );
591 }
592
593 /**
594 * Set the time when we can display the opt-in notification
595 * Will display now unless filtered
596 *
597 * @since 1.2.4
598 */
599 public function set_notification_time() {
600 // @todo SKC customization: Contribute back to Wisdom project.
601 $notification_times = (array) get_option( 'wisdom_notification_times', [] );
602
603 // Set different times according to plugin, in case we are tracking multiple plugins
604 if ( ! isset( $notification_times[ $this->plugin_name ] ) ) {
605 $delay_notification = apply_filters( 'wisdom_delay_notification_' . $this->plugin_name, 0 );
606 // We can delay the notification time
607 $notification_time = time() + absint( $delay_notification );
608 $notification_times[ $this->plugin_name ] = $notification_time;
609 update_option( 'wisdom_notification_times', $notification_times, 'yes' );
610 }
611 }
612
613 /**
614 * Get whether it's time to display the notification
615 *
616 * @since 1.2.4
617 * @return Boolean
618 */
619 public function get_is_notification_time() {
620 // @todo SKC customization: Contribute back to Wisdom project.
621 $notification_times = (array) get_option( 'wisdom_notification_times', [] );
622 $time = time();
623 // Set different times according to plugin, in case we are tracking multiple plugins
624 if ( isset( $notification_times[ $this->plugin_name ] ) ) {
625 $notification_time = $notification_times[ $this->plugin_name ];
626 if ( $time >= $notification_time ) {
627 return true;
628 }
629 }
630
631 return false;
632 }
633
634 /**
635 * Set if we should block the opt-in notice for this plugin
636 * Option is an array of all plugins that have received a response from the user
637 *
638 * @since 1.0.0
639 */
640 public function update_block_notice( $plugin = null ) {
641 if ( empty( $plugin ) ) {
642 $plugin = $this->plugin_name;
643 }
644 $block_notice = get_option( 'wisdom_block_notice' );
645 if ( empty( $block_notice ) || ! is_array( $block_notice ) ) {
646 // If nothing exists in the option yet, start a new array with the plugin name
647 $block_notice = [ $plugin => $plugin ];
648 } else {
649 // Else add the plugin name to the array
650 $block_notice[ $plugin ] = $plugin;
651 }
652 update_option( 'wisdom_block_notice', $block_notice, 'yes' );
653 }
654
655 /**
656 * Can we collect the email address?
657 *
658 * @since 1.0.0
659 */
660 public function get_can_collect_email() {
661 // The wisdom_collect_email option is an array of plugins that are being tracked
662 $collect_email = get_option( 'wisdom_collect_email' );
663 // If this plugin is in the array, then we can collect the email address
664 if ( isset( $collect_email[ $this->plugin_name ] ) ) {
665 return true;
666 }
667
668 return false;
669 }
670
671 /**
672 * Set if user has allowed us to collect their email address
673 * Option is an array of all plugins with email collection permitted
674 * More than one plugin may be using the tracker
675 *
676 * @since 1.0.0
677 *
678 * @param $can_collect Boolean true if collection is allowed, false if not
679 */
680 public function set_can_collect_email( $can_collect, $plugin = null ) {
681 if ( empty( $plugin ) ) {
682 $plugin = $this->plugin_name;
683 }
684 // The wisdom_collect_email option is an array of plugins that are being tracked
685 $collect_email = get_option( 'wisdom_collect_email' );
686 // If the user has agreed to allow tracking or if opt-in is not required
687 if ( $can_collect ) {
688 if ( empty( $collect_email ) || ! is_array( $collect_email ) ) {
689 // If nothing exists in the option yet, start a new array with the plugin name
690 $collect_email = [ $plugin => $plugin ];
691 } else {
692 // Else add the plugin name to the array
693 $collect_email[ $plugin ] = $plugin;
694 }
695 } else {
696 if ( isset( $collect_email[ $plugin ] ) ) {
697 unset( $collect_email[ $plugin ] );
698 }
699 }
700 update_option( 'wisdom_collect_email', $collect_email, 'yes' );
701 }
702
703 /**
704 * Get the correct email address to use
705 *
706 * @since 1.1.2
707 * @return Email address
708 */
709 public function get_admin_email() {
710 // The wisdom_collect_email option is an array of plugins that are being tracked
711 $email = get_option( 'wisdom_admin_emails' );
712 // If this plugin is in the array, then we can collect the email address
713 if ( isset( $email[ $this->plugin_name ] ) ) {
714 return $email[ $this->plugin_name ];
715 }
716
717 return false;
718 }
719
720 /**
721 * Set the correct email address to use
722 * There might be more than one admin on the site
723 * So we only use the first admin's email address
724 *
725 * @since 1.1.2
726 *
727 * @param $plugin Plugin name to set email address for
728 * @param $email Email address to set
729 */
730 public function set_admin_email( $email = null, $plugin = null ) {
731 if ( empty( $plugin ) ) {
732 $plugin = $this->plugin_name;
733 }
734 // If no email address passed, try to get the current user's email
735 if ( empty( $email ) ) {
736 // Have to check that current user object is available
737 if ( function_exists( 'wp_get_current_user' ) ) {
738 $current_user = wp_get_current_user();
739 $email = $current_user->user_email;
740 }
741 }
742 // The wisdom_admin_emails option is an array of admin email addresses
743 $admin_emails = get_option( 'wisdom_admin_emails' );
744 if ( empty( $admin_emails ) || ! is_array( $admin_emails ) ) {
745 // If nothing exists in the option yet, start a new array with the plugin name
746 $admin_emails = [ $plugin => sanitize_email( $email ) ];
747 } elseif ( empty( $admin_emails[ $plugin ] ) ) {
748 // Else add the email address to the array, if not already set
749 $admin_emails[ $plugin ] = sanitize_email( $email );
750 }
751 update_option( 'wisdom_admin_emails', $admin_emails );
752 }
753
754 /**
755 * Display the admin notice to users to allow them to opt in
756 *
757 * @since 1.0.0
758 */
759 public function optin_notice() {
760 // Check for plugin args
761 if ( isset( $_GET['plugin'] ) && isset( $_GET['plugin_action'] ) ) {
762 $plugin = sanitize_text_field( $_GET['plugin'] );
763 $action = sanitize_text_field( $_GET['plugin_action'] );
764 if ( $action == 'yes' ) {
765 $this->set_is_tracking_allowed( true, $plugin );
766 // Run this straightaway
767 add_action( 'admin_init', [ $this, 'force_tracking' ] );
768 } else {
769 $this->set_is_tracking_allowed( false, $plugin );
770 }
771 $this->update_block_notice( $plugin );
772 }
773
774 // Is it time to display the notification?
775 $is_time = $this->get_is_notification_time();
776 if ( ! $is_time ) {
777 return false;
778 }
779
780 // Check whether to block the notice, e.g. because we're in a local environment
781 // wisdom_block_notice works the same as wisdom_allow_tracking, an array of plugin names
782 $block_notice = get_option( 'wisdom_block_notice' );
783
784 if ( isset( $block_notice[ $this->plugin_name ] ) ) {
785 return;
786 }
787
788 if ( ! current_user_can( 'manage_options' ) ) {
789 return;
790 }
791
792 // @credit EDD
793 // Don't bother asking user to opt in if they're in local dev
794 $is_local = false;
795 if ( stristr( network_site_url( '/' ), '.dev' ) !== false || stristr( network_site_url( '/' ), 'localhost' ) !== false || stristr( network_site_url( '/' ), ':8888' ) !== false ) {
796 $is_local = true;
797 }
798 $is_local = apply_filters( 'wisdom_is_local_' . $this->plugin_name, $is_local );
799 if ( $is_local ) {
800 $this->update_block_notice();
801
802 // SKC modification for Pods.
803 if ( $this->marketing ) {
804 $this->set_can_collect_email( false );
805 }
806 } else {
807 // Display the notice requesting permission to track
808 // Retrieve current plugin information
809 $plugin = $this->plugin_data();
810 $plugin_name = $plugin['Name'];
811
812 // Args to add to query if user opts in to tracking
813 $yes_args = [
814 'plugin' => $this->plugin_name,
815 'plugin_action' => 'yes',
816 ];
817
818 // Decide how to request permission to collect email addresses
819 if ( $this->marketing == 1 ) {
820 // Option 1 combines permissions to track and collect email
821 $yes_args['marketing_optin'] = 'yes';
822 } elseif ( $this->marketing == 2 ) {
823 // Option 2 enables a second notice that fires after the user opts in to tracking
824 $yes_args['marketing'] = 'yes';
825 }
826 $url_yes = add_query_arg( $yes_args );
827 $url_no = add_query_arg( [
828 'plugin' => $this->plugin_name,
829 'plugin_action' => 'no',
830 ] );
831
832 // Decide on notice text
833 if ( $this->marketing != 1 ) {
834 // Standard notice text
835 // translators: %1$s is the type of plugin/theme being tracked.
836 $notice_text = sprintf( __( 'Thank you for installing our %1$s. We would like to track its usage on your site. We don\'t record any sensitive data, only information regarding the WordPress environment and %1$s settings, which we will use to help us make improvements to the %1$s. Tracking is completely optional.', 'pods' ), $this->what_am_i );
837 } else {
838 // If we have option 1 for marketing, we include reference to sending product information here
839 // translators: %1$s is the type of plugin/theme being tracked.
840 $notice_text = sprintf( __( 'Thank you for installing our %1$s. We\'d like your permission to track its usage on your site and subscribe you to our newsletter. We won\'t record any sensitive data, only information regarding the WordPress environment and %1$s settings, which we will use to help us make improvements to the %1$s. Tracking is completely optional.', 'pods' ), $this->what_am_i );
841 }
842 // And we allow you to filter the text anyway
843 $notice_text = apply_filters( 'wisdom_notice_text_' . esc_attr( $this->plugin_name ), $notice_text ); ?>
844
845 <div class="notice notice-info updated put-dismiss-notice">
846 <p><?php echo '<strong>' . esc_html( $plugin_name ) . '</strong>'; ?></p>
847 <p><?php echo esc_html( $notice_text ); ?></p>
848 <p>
849 <a href="<?php echo esc_url( $url_yes ); ?>"
850 class="button-secondary"><?php esc_html_e( 'Allow', 'pods' ); ?></a>
851 <a href="<?php echo esc_url( $url_no ); ?>"
852 class="button-secondary"><?php esc_html_e( 'Do Not Allow', 'pods' ); ?></a>
853 </p>
854 </div>
855 <?php
856 }
857 }
858
859 /**
860 * Display the marketing notice to users if enabled
861 * Only displays after the user has opted in to tracking
862 *
863 * @since 1.0.0
864 */
865 public function marketing_notice() {
866 // Check if user has opted in to marketing
867 if ( isset( $_GET['marketing_optin'] ) ) {
868 // Set marketing optin
869 $this->set_can_collect_email( sanitize_text_field( $_GET['marketing_optin'] ), $this->plugin_name );
870 // Do tracking
871 $this->do_tracking( true );
872 } elseif ( isset( $_GET['marketing'] ) && $_GET['marketing'] == 'yes' ) {
873 // Display the notice requesting permission to collect email address
874 // Retrieve current plugin information
875 $plugin = $this->plugin_data();
876 $plugin_name = $plugin['Name'];
877
878 $url_yes = add_query_arg( [
879 'plugin' => $this->plugin_name,
880 'marketing_optin' => 'yes',
881 ] );
882 $url_no = add_query_arg( [
883 'plugin' => $this->plugin_name,
884 'marketing_optin' => 'no',
885 ] );
886
887 // translators: %s is the type of plugin/theme being tracked.
888 $marketing_text = sprintf( __( 'Thank you for opting in to tracking. Would you like to receive occasional news about this %s, including details of new features and special offers?', 'pods' ), $this->what_am_i );
889 $marketing_text = apply_filters( 'wisdom_marketing_text_' . esc_attr( $this->plugin_name ), $marketing_text ); ?>
890
891 <div class="notice notice-info updated put-dismiss-notice">
892 <p><?php echo '<strong>' . esc_html( $plugin_name ) . '</strong>'; ?></p>
893 <p><?php echo esc_html( $marketing_text ); ?></p>
894 <p>
895 <a href="<?php echo esc_url( $url_yes ); ?>" data-putnotice="yes"
896 class="button-secondary"><?php esc_html_e( 'Yes Please', 'pods' ); ?></a>
897 <a href="<?php echo esc_url( $url_no ); ?>" data-putnotice="no"
898 class="button-secondary"><?php esc_html_e( 'No Thank You', 'pods' ); ?></a>
899 </p>
900 </div>
901 <?php }
902 }
903
904 /**
905 * Filter the deactivation link to allow us to present a form when the user deactivates the plugin
906 *
907 * @since 1.0.0
908 */
909 public function filter_action_links( $links ) {
910 // Check to see if the user has opted in to tracking
911 if ( ! $this->get_is_tracking_allowed() ) {
912 return $links;
913 }
914 if ( isset( $links['deactivate'] ) && $this->include_goodbye_form ) {
915 $deactivation_link = $links['deactivate'];
916 // Insert an onClick action to allow form before deactivating
917 $deactivation_link = str_replace( '<a ', '<div class="put-goodbye-form-wrapper"><span class="put-goodbye-form" id="put-goodbye-form-' . esc_attr( $this->plugin_name ) . '"></span></div><a onclick="javascript:event.preventDefault();" id="put-goodbye-link-' . esc_attr( $this->plugin_name ) . '" ', $deactivation_link );
918 $links['deactivate'] = $deactivation_link;
919 }
920
921 return $links;
922 }
923
924 /*
925 * Form text strings
926 * These are non-filterable and used as fallback in case filtered strings aren't set correctly
927 * @since 1.0.0
928 */
929 public function form_default_text() {
930 $form = [];
931 $form['heading'] = __( 'Sorry to see you go', 'pods' );
932 $form['body'] = __( 'Before you deactivate the plugin, would you quickly give us your reason for doing so?', 'pods' );
933 $form['options'] = [
934 __( 'Set up is too difficult', 'pods' ),
935 __( 'Lack of documentation', 'pods' ),
936 __( 'Not the features I wanted', 'pods' ),
937 __( 'Found a better plugin', 'pods' ),
938 __( 'Installed by mistake', 'pods' ),
939 __( 'Only required temporarily', 'pods' ),
940 __( 'Didn\'t work', 'pods' ),
941 ];
942 $form['details'] = __( 'Details (optional)', 'pods' );
943
944 return $form;
945 }
946
947 /**
948 * Form text strings
949 * These can be filtered
950 * The filter hook must be unique to the plugin
951 *
952 * @since 1.0.0
953 */
954 public function form_filterable_text() {
955 $form = $this->form_default_text();
956
957 return apply_filters( 'wisdom_form_text_' . esc_attr( $this->plugin_name ), $form );
958 }
959
960 /**
961 * Form text strings
962 * These can be filtered
963 *
964 * @since 1.0.0
965 */
966 public function goodbye_ajax() {
967 // Get our strings for the form
968 $form = $this->form_filterable_text();
969 if ( ! isset( $form['heading'] ) || ! isset( $form['body'] ) || ! isset( $form['options'] ) || ! is_array( $form['options'] ) || ! isset( $form['details'] ) ) {
970 // If the form hasn't been filtered correctly, we revert to the default form
971 $form = $this->form_default_text();
972 }
973 // Build the HTML to go in the form
974 $html = '<div class="put-goodbye-form-head"><strong>' . esc_html( $form['heading'] ) . '</strong></div>';
975 $html .= '<div class="put-goodbye-form-body"><p>' . esc_html( $form['body'] ) . '</p>';
976 if ( is_array( $form['options'] ) ) {
977 $html .= '<div class="put-goodbye-options"><p>';
978 foreach ( $form['options'] as $option ) {
979 $html .= '<input type="checkbox" name="put-goodbye-options[]" id="' . str_replace( " ", "", esc_attr( $option ) ) . '" value="' . esc_attr( $option ) . '"> <label for="' . str_replace( " ", "", esc_attr( $option ) ) . '">' . esc_attr( $option ) . '</label><br>';
980 }
981 $html .= '</p><label for="put-goodbye-reasons">' . esc_html( $form['details'] ) . '</label><textarea name="put-goodbye-reasons" id="put-goodbye-reasons" rows="2" style="width:100%"></textarea>';
982 $html .= '</div><!-- .put-goodbye-options -->';
983 }
984 $html .= '</div><!-- .put-goodbye-form-body -->';
985 $html .= '<p class="deactivating-spinner"><span class="spinner"></span> ' . __( 'Submitting form', 'pods' ) . '</p>';
986 ?>
987 <div class="put-goodbye-form-bg"></div>
988 <style type="text/css">
989 .put-form-active .put-goodbye-form-bg {
990 background: rgba(0, 0, 0, .5);
991 position: fixed;
992 top: 0;
993 left: 0;
994 width: 100%;
995 height: 100%;
996 }
997
998 .put-goodbye-form-wrapper {
999 position: relative;
1000 z-index: 999;
1001 display: none;
1002 }
1003
1004 .put-form-active .put-goodbye-form-wrapper {
1005 display: block;
1006 }
1007
1008 .put-goodbye-form {
1009 display: none;
1010 }
1011
1012 .put-form-active .put-goodbye-form {
1013 position: absolute;
1014 bottom: 30px;
1015 left: 0;
1016 max-width: 400px;
1017 background: #fff;
1018 white-space: normal;
1019 }
1020
1021 .put-goodbye-form-head {
1022 background: #0073aa;
1023 color: #fff;
1024 padding: 8px 18px;
1025 }
1026
1027 .put-goodbye-form-body {
1028 padding: 8px 18px;
1029 color: #444;
1030 }
1031
1032 .deactivating-spinner {
1033 display: none;
1034 }
1035
1036 .deactivating-spinner .spinner {
1037 float: none;
1038 margin: 4px 4px 0 18px;
1039 vertical-align: bottom;
1040 visibility: visible;
1041 }
1042
1043 .put-goodbye-form-footer {
1044 padding: 8px 18px;
1045 }
1046 </style>
1047 <script>
1048 jQuery( document ).ready( function( $ ) {
1049 $( "#put-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>" ).on( 'click', function() {
1050 // We'll send the user to this deactivation link when they've completed or dismissed the form
1051 var url = document.getElementById( "put-goodbye-link-<?php echo esc_attr( $this->plugin_name ); ?>" );
1052 $( 'body' ).toggleClass( 'put-form-active' );
1053 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>" ).fadeIn();
1054 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>" )
1055 .html( '<?php echo wp_kses_post( $html ); ?>'
1056 + '<div class="put-goodbye-form-footer"><p><a id="put-submit-form" class="button primary" href="#"><?php esc_html_e( 'Submit and Deactivate', 'pods' ); ?></a>&nbsp;<a class="secondary button" href="'
1057 + url
1058 + '"><?php esc_html_e( 'Just Deactivate', 'pods' ); ?></a></p></div>' );
1059 $( '#put-submit-form' ).on( 'click', function( e ) {
1060 // As soon as we click, the body of the form should disappear
1061 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .put-goodbye-form-body" )
1062 .fadeOut();
1063 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .put-goodbye-form-footer" )
1064 .fadeOut();
1065 // Fade in spinner
1066 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?> .deactivating-spinner" )
1067 .fadeIn();
1068 e.preventDefault();
1069 var values = new Array();
1070 $.each( $( 'input[name=\'put-goodbye-options[]\']:checked' ), function() {
1071 values.push( $( this ).val() );
1072 } );
1073 var details = $( '#put-goodbye-reasons' ).val();
1074 var data = {
1075 'action': 'goodbye_form',
1076 'values': values,
1077 'details': details,
1078 'security': "<?php echo esc_js( wp_create_nonce( 'wisdom_goodbye_form' ) ); ?>",
1079 'dataType': 'json',
1080 };
1081 $.post( ajaxurl, data, function( response ) {
1082 // Redirect to original deactivation URL
1083 window.location.href = url;
1084 } );
1085 } );
1086 // If we click outside the form, the form will close
1087 $( '.put-goodbye-form-bg' ).on( 'click', function() {
1088 $( "#put-goodbye-form-<?php echo esc_attr( $this->plugin_name ); ?>" ).fadeOut();
1089 $( 'body' ).removeClass( 'put-form-active' );
1090 } );
1091 } );
1092 } );
1093 </script>
1094 <?php }
1095
1096 /**
1097 * AJAX callback when the form is submitted
1098 *
1099 * @since 1.0.0
1100 */
1101 public function goodbye_form_callback() {
1102 check_ajax_referer( 'wisdom_goodbye_form', 'security' );
1103 if ( isset( $_POST['values'] ) ) {
1104 $values = json_encode( wp_unslash( $_POST['values'] ) );
1105 update_option( 'wisdom_deactivation_reason_' . $this->plugin_name, $values );
1106 }
1107 if ( isset( $_POST['details'] ) ) {
1108 $details = sanitize_text_field( $_POST['details'] );
1109 update_option( 'wisdom_deactivation_details_' . $this->plugin_name, $details );
1110 }
1111 $this->do_tracking(); // Run this straightaway
1112 echo 'success';
1113 wp_die();
1114 }
1115
1116 }
1117