PluginProbe ʕ •ᴥ•ʔ
Starter Templates – AI-Powered Templates for Elementor & Gutenberg / 4.4.45
Starter Templates – AI-Powered Templates for Elementor & Gutenberg v4.4.45
4.6.1 4.6.0 4.5.4 4.5.3 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.5.0 2.5.1 2.6.0 2.6.1 2.6.10 2.6.11 2.6.12 2.6.13 2.6.14 2.6.15 2.6.16 2.6.17 2.6.18 2.6.19 2.6.2 2.6.20 2.6.21 2.6.22 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6.8 2.6.9 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 3.0.0 3.0.1 3.0.10 3.0.11 3.0.12 3.0.13 3.0.14 3.0.15 3.0.16 3.0.17 3.0.18 3.0.19 3.0.2 3.0.20 3.0.21 3.0.22 3.0.23 3.0.24 3.0.25 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.20 3.1.21 3.1.22 3.1.23 3.1.24 3.1.25 3.1.26 3.1.27 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.10 4.4.11 4.4.12 4.4.13 4.4.14 4.4.16 4.4.17 4.4.18 4.4.19 4.4.2 4.4.20 4.4.21 4.4.22 4.4.23 4.4.24 trunk 4.4.25 0.4.4.0 4.4.26 0.4.4.15 4.4.27 1.0.0 4.4.28 1.0.1 4.4.29 1.0.10 4.4.3 1.0.11 4.4.30 1.0.12 4.4.31 1.0.13 4.4.32 1.0.14 4.4.33 1.0.2 4.4.34 1.0.3 4.4.35 1.0.4 4.4.36 1.0.5 4.4.37 1.0.6 4.4.38 1.0.7 4.4.39 1.0.8 4.4.4 1.0.9 4.4.40 1.1.0 4.4.41 1.1.1 4.4.42 1.1.2 4.4.43 1.1.3 4.4.44 1.1.4 4.4.45 1.1.5 4.4.46 1.1.6 4.4.47 1.1.7 4.4.48 1.1.8 4.4.49 1.1.9 4.4.5 1.2.0 4.4.50 1.2.1 4.4.51 1.2.10 4.4.52 1.2.11 4.4.6 1.2.12 4.4.7 1.2.13 4.4.8 1.2.14 4.4.9 1.2.15 4.5.0 1.2.2 4.5.1 1.2.3 4.5.2 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.10 1.3.11 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.19 1.3.2 1.3.20 1.3.21 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 2.0.0 2.0.1 2.0.2 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5
astra-sites / admin / bsf-analytics / class-bsf-analytics.php
astra-sites / admin / bsf-analytics Last commit date
assets 4 years ago classes 1 year ago modules 10 months ago changelog.txt 10 months ago class-bsf-analytics-loader.php 5 years ago class-bsf-analytics-stats.php 1 year ago class-bsf-analytics.php 1 year ago version.json 10 months ago
class-bsf-analytics.php
603 lines
1 <?php
2 /**
3 * BSF analytics class file.
4 *
5 * @version 1.0.0
6 *
7 * @package bsf-analytics
8 */
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13
14 if ( ! class_exists( 'BSF_Analytics' ) ) {
15
16 /**
17 * BSF analytics
18 */
19 class BSF_Analytics {
20
21 /**
22 * Member Variable
23 *
24 * @var array Entities data.
25 */
26 private $entities;
27
28 /**
29 * Member Variable
30 *
31 * @var string Usage tracking document URL
32 */
33 public $usage_doc_link = 'https://store.brainstormforce.com/usage-tracking/?utm_source=wp_dashboard&utm_medium=general_settings&utm_campaign=usage_tracking';
34
35 /**
36 * Setup actions, load files.
37 *
38 * @param array $args entity data for analytics.
39 * @param string $analytics_path directory path to analytics library.
40 * @param float $analytics_version analytics library version.
41 * @since 1.0.0
42 */
43 public function __construct( $args, $analytics_path, $analytics_version ) {
44
45 // Bail when no analytics entities are registered.
46 if ( empty( $args ) ) {
47 return;
48 }
49
50 $this->entities = $args;
51
52 define( 'BSF_ANALYTICS_VERSION', $analytics_version );
53 define( 'BSF_ANALYTICS_URI', $this->get_analytics_url( $analytics_path ) );
54
55 add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
56 add_action( 'admin_init', array( $this, 'option_notice' ) );
57 add_action( 'init', array( $this, 'maybe_track_analytics' ), 99 );
58
59 $this->set_actions();
60
61 add_action( 'admin_init', array( $this, 'register_usage_tracking_setting' ) );
62
63 $this->includes();
64
65 $this->load_deactivation_survey_actions();
66 }
67
68 /**
69 * Function to load the deactivation survey form actions.
70 *
71 * @since 1.1.6
72 * @return void
73 */
74 public function load_deactivation_survey_actions() {
75
76 // If not in a admin area then return it.
77 if ( ! is_admin() ) {
78 return;
79 }
80
81 add_filter( 'uds_survey_vars', array( $this, 'add_slugs_to_uds_vars' ) );
82 add_action( 'admin_footer', array( $this, 'load_deactivation_survey_form' ) );
83 }
84
85 /**
86 * Setup actions for admin notice style and analytics cron event.
87 *
88 * @since 1.0.4
89 */
90 public function set_actions() {
91
92 foreach ( $this->entities as $key => $data ) {
93 add_action( 'astra_notice_before_markup_' . $key . '-optin-notice', array( $this, 'enqueue_assets' ) );
94 add_action( 'update_option_' . $key . '_analytics_optin', array( $this, 'update_analytics_option_callback' ), 10, 3 );
95 add_action( 'add_option_' . $key . '_analytics_optin', array( $this, 'add_analytics_option_callback' ), 10, 2 );
96 }
97 }
98
99 /**
100 * BSF Analytics URL
101 *
102 * @param string $analytics_path directory path to analytics library.
103 * @return String URL of bsf-analytics directory.
104 * @since 1.0.0
105 */
106 public function get_analytics_url( $analytics_path ) {
107
108 $content_dir_path = wp_normalize_path( WP_CONTENT_DIR );
109
110 $analytics_path = wp_normalize_path( $analytics_path );
111
112 return str_replace( $content_dir_path, content_url(), $analytics_path );
113 }
114
115 /**
116 * Enqueue Scripts.
117 *
118 * @since 1.0.0
119 * @return void
120 */
121 public function enqueue_assets() {
122
123 /**
124 * Load unminified if SCRIPT_DEBUG is true.
125 *
126 * Directory and Extensions.
127 */
128 $dir_name = ( SCRIPT_DEBUG ) ? 'unminified' : 'minified';
129 $file_rtl = ( is_rtl() ) ? '-rtl' : '';
130 $css_ext = ( SCRIPT_DEBUG ) ? '.css' : '.min.css';
131
132 $css_uri = BSF_ANALYTICS_URI . '/assets/css/' . $dir_name . '/style' . $file_rtl . $css_ext;
133
134 wp_enqueue_style( 'bsf-analytics-admin-style', $css_uri, false, BSF_ANALYTICS_VERSION, 'all' );
135 }
136
137 /**
138 * Send analytics API call.
139 *
140 * @since 1.0.0
141 */
142 public function send() {
143
144 $api_url = BSF_Analytics_Helper::get_api_url();
145
146 wp_remote_post(
147 $api_url . 'api/analytics/',
148 array(
149 'body' => BSF_Analytics_Stats::instance()->get_stats(),
150 'timeout' => 5,
151 'blocking' => false,
152 )
153 );
154 }
155
156 /**
157 * Check if usage tracking is enabled.
158 *
159 * @return bool
160 * @since 1.0.0
161 */
162 public function is_tracking_enabled() {
163
164 foreach ( $this->entities as $key => $data ) {
165
166 $is_enabled = get_site_option( $key . '_analytics_optin' ) === 'yes' ? true : false;
167 $is_enabled = $this->is_white_label_enabled( $key ) ? false : $is_enabled;
168
169 if ( apply_filters( $key . '_tracking_enabled', $is_enabled ) ) {
170 return true;
171 }
172 }
173
174 return false;
175 }
176
177 /**
178 * Check if WHITE label is enabled for BSF products.
179 *
180 * @param string $source source of analytics.
181 * @return bool
182 * @since 1.0.0
183 */
184 public function is_white_label_enabled( $source ) {
185
186 $options = apply_filters( $source . '_white_label_options', array() );
187 $is_enabled = false;
188
189 if ( is_array( $options ) ) {
190 foreach ( $options as $option ) {
191 if ( true === $option ) {
192 $is_enabled = true;
193 break;
194 }
195 }
196 }
197
198 return $is_enabled;
199 }
200
201 /**
202 * Display admin notice for usage tracking.
203 *
204 * @since 1.0.0
205 */
206 public function option_notice() {
207
208 if ( ! current_user_can( 'manage_options' ) ) {
209 return;
210 }
211
212 if( $this->is_tracking_enabled() ) {
213 return; // Don't need to display notice if any of our plugin already have the permission.
214 }
215
216 // If the user has opted out of tracking, don't show the notice till 7 days.
217 if ( get_site_option( 'bsf_analytics_last_displayed_time' ) > time() - ( 7 * DAY_IN_SECONDS ) ) {
218 return; // Don't display the notice if it was displayed recently.
219 }
220
221 foreach ( $this->entities as $key => $data ) {
222
223 $time_to_display = isset( $data['time_to_display'] ) ? $data['time_to_display'] : '+24 hours';
224 $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
225
226 // Don't display the notice if tracking is disabled or White Label is enabled for any of our plugins.
227 if ( false !== get_site_option( $key . '_analytics_optin', false ) || $this->is_white_label_enabled( $key ) ) {
228 continue;
229 }
230
231 // Show tracker consent notice after 24 hours from installed time.
232 if ( strtotime( $time_to_display, $this->get_analytics_install_time( $key ) ) > time() ) {
233 continue;
234 }
235
236 /* translators: %s product name */
237 $notice_string = sprintf(
238 __(
239 'Help us improve %1$s and our other products!<br><br>With your permission, we\'d like to collect <strong>non-sensitive information</strong> from your website — like your PHP version and which features you use — so we can fix bugs faster, make smarter decisions, and build features that actually matter to you. <em>No personal info. Ever.</em>', 'astra-sites' ),
240 '<strong>' . esc_html( $data['product_name'] ) . '</strong>'
241 );
242
243 if ( is_multisite() ) {
244 $notice_string .= __( 'This will be applicable for all sites from the network.', 'astra-sites' );
245 }
246
247 $language_dir = is_rtl() ? 'rtl' : 'ltr';
248
249 Astra_Notices::add_notice(
250 array(
251 'id' => $key . '-optin-notice',
252 'type' => '',
253 'message' => sprintf(
254 '<div class="notice-content">
255 <div class="notice-heading">
256 %1$s
257 </div>
258 <div class="astra-notices-container">
259 <a href="%2$s" class="astra-notices button-primary">
260 %3$s
261 </a>
262 <a href="%4$s" data-repeat-notice-after="%5$s" class="astra-notices button-secondary">
263 %6$s
264 </a>
265 </div>
266 </div>',
267 /* translators: %s usage doc link */
268 sprintf( $notice_string . '<span dir="%1s"><a href="%2s" target="_blank" rel="noreferrer noopener">%3s</a><span><br><br>', $language_dir, esc_url( $usage_doc_link ), __( ' Know More.', 'astra-sites' ) ),
269 esc_url(
270 add_query_arg(
271 array(
272 $key . '_analytics_optin' => 'yes',
273 $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
274 'bsf_analytics_source' => $key,
275 )
276 )
277 ),
278 __( 'Yes! Allow it', 'astra-sites' ),
279 esc_url(
280 add_query_arg(
281 array(
282 $key . '_analytics_optin' => 'no',
283 $key . '_analytics_nonce' => wp_create_nonce( $key . '_analytics_optin' ),
284 'bsf_analytics_source' => $key,
285 )
286 )
287 ),
288 MONTH_IN_SECONDS,
289 __( 'No Thanks', 'astra-sites' )
290 ),
291 'show_if' => true,
292 'repeat-notice-after' => false,
293 'priority' => 18,
294 'display-with-other-notices' => true,
295 )
296 );
297
298 return;
299 }
300 }
301
302 /**
303 * Process usage tracking opt out.
304 *
305 * @since 1.0.0
306 */
307 public function handle_optin_optout() {
308
309 if ( ! current_user_can( 'manage_options' ) ) {
310 return;
311 }
312
313 $source = isset( $_GET['bsf_analytics_source'] ) ? sanitize_text_field( wp_unslash( $_GET['bsf_analytics_source'] ) ) : '';
314
315 if ( ! isset( $_GET[ $source . '_analytics_nonce' ] ) ) {
316 return;
317 }
318
319 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_nonce' ] ) ), $source . '_analytics_optin' ) ) {
320 return;
321 }
322
323 $optin_status = isset( $_GET[ $source . '_analytics_optin' ] ) ? sanitize_text_field( wp_unslash( $_GET[ $source . '_analytics_optin' ] ) ) : '';
324
325 if ( 'yes' === $optin_status ) {
326 $this->optin( $source );
327 } elseif ( 'no' === $optin_status ) {
328 $this->optout( $source );
329 }
330
331 wp_safe_redirect(
332 esc_url_raw(
333 remove_query_arg(
334 array(
335 $source . '_analytics_optin',
336 $source . '_analytics_nonce',
337 'bsf_analytics_source',
338 )
339 )
340 )
341 );
342 }
343
344 /**
345 * Opt in to usage tracking.
346 *
347 * @param string $source source of analytics.
348 * @since 1.0.0
349 */
350 private function optin( $source ) {
351 update_site_option( $source . '_analytics_optin', 'yes' );
352 }
353
354 /**
355 * Opt out to usage tracking.
356 *
357 * @param string $source source of analytics.
358 * @since 1.0.0
359 */
360 private function optout( $source ) {
361 update_site_option( $source . '_analytics_optin', 'no' );
362 update_site_option( 'bsf_analytics_last_displayed_time', time() );
363 }
364
365 /**
366 * Load analytics stat class.
367 *
368 * @since 1.0.0
369 */
370 private function includes() {
371 require_once __DIR__ . '/classes/class-bsf-analytics-helper.php';
372 require_once __DIR__ . '/class-bsf-analytics-stats.php';
373
374 // Loads all the modules.
375 require_once __DIR__ . '/modules/deactivation-survey/classes/class-deactivation-survey-feedback.php';
376 require_once __DIR__ . '/modules/utm-analytics.php';
377 }
378
379 /**
380 * Register usage tracking option in General settings page.
381 *
382 * @since 1.0.0
383 */
384 public function register_usage_tracking_setting() {
385
386 foreach ( $this->entities as $key => $data ) {
387
388 if ( ! apply_filters( $key . '_tracking_enabled', true ) || $this->is_white_label_enabled( $key ) ) {
389 return;
390 }
391
392 /**
393 * Introducing a new key 'hide_optin_checkbox, which allows individual plugin to hide optin checkbox
394 * If they are providing providing in-plugin option to manage this option.
395 * from General > Settings page.
396 *
397 * @since 1.1.14
398 */
399 if( ! empty( $data['hide_optin_checkbox'] ) && true === $data['hide_optin_checkbox'] ) {
400 continue;
401 }
402
403 $usage_doc_link = isset( $data['usage_doc_link'] ) ? $data['usage_doc_link'] : $this->usage_doc_link;
404 $author = isset( $data['author'] ) ? $data['author'] : 'Brainstorm Force';
405
406 register_setting(
407 'general', // Options group.
408 $key . '_analytics_optin', // Option name/database.
409 array( 'sanitize_callback' => array( $this, 'sanitize_option' ) ) // sanitize callback function.
410 );
411
412 add_settings_field(
413 $key . '-analytics-optin', // Field ID.
414 __( 'Usage Tracking', 'astra-sites' ), // Field title.
415 array( $this, 'render_settings_field_html' ), // Field callback function.
416 'general',
417 'default', // Settings page slug.
418 array(
419 'type' => 'checkbox',
420 'title' => $author,
421 'name' => $key . '_analytics_optin',
422 'label_for' => $key . '-analytics-optin',
423 'id' => $key . '-analytics-optin',
424 'usage_doc_link' => $usage_doc_link,
425 )
426 );
427 }
428 }
429
430 /**
431 * Sanitize Callback Function
432 *
433 * @param bool $input Option value.
434 * @since 1.0.0
435 */
436 public function sanitize_option( $input ) {
437
438 if ( ! $input || 'no' === $input ) {
439 return 'no';
440 }
441
442 return 'yes';
443 }
444
445 /**
446 * Print settings field HTML.
447 *
448 * @param array $args arguments to field.
449 * @since 1.0.0
450 */
451 public function render_settings_field_html( $args ) {
452 ?>
453 <fieldset>
454 <label for="<?php echo esc_attr( $args['label_for'] ); ?>">
455 <input id="<?php echo esc_attr( $args['id'] ); ?>" type="checkbox" value="1" name="<?php echo esc_attr( $args['name'] ); ?>" <?php checked( get_site_option( $args['name'], 'no' ), 'yes' ); ?>>
456 <?php
457 /* translators: %s Product title */
458 echo esc_html( sprintf( __( 'Allow %s products to track non-sensitive usage tracking data.', 'astra-sites' ), $args['title'] ) );// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
459
460 if ( is_multisite() ) {
461 esc_html_e( ' This will be applicable for all sites from the network.', 'astra-sites' );
462 }
463 ?>
464 </label>
465 <?php
466 echo wp_kses_post( sprintf( '<a href="%1s" target="_blank" rel="noreferrer noopener">%2s</a>', esc_url( $args['usage_doc_link'] ), __( 'Learn More.', 'astra-sites' ) ) );
467 ?>
468 </fieldset>
469 <?php
470 }
471
472 /**
473 * Set analytics installed time in option.
474 *
475 * @param string $source source of analytics.
476 * @return string $time analytics installed time.
477 * @since 1.0.0
478 */
479 private function get_analytics_install_time( $source ) {
480
481 $time = get_site_option( $source . '_analytics_installed_time' );
482
483 if ( ! $time ) {
484 $time = time();
485 update_site_option( $source . '_analytics_installed_time', time() );
486 }
487
488 return $time;
489 }
490
491 /**
492 * Schedule/unschedule cron event on updation of option.
493 *
494 * @param string $old_value old value of option.
495 * @param string $value value of option.
496 * @param string $option Option name.
497 * @since 1.0.0
498 */
499 public function update_analytics_option_callback( $old_value, $value, $option ) {
500 if ( is_multisite() ) {
501 $this->add_option_to_network( $option, $value );
502 }
503 }
504
505 /**
506 * Analytics option add callback.
507 *
508 * @param string $option Option name.
509 * @param string $value value of option.
510 * @since 1.0.0
511 */
512 public function add_analytics_option_callback( $option, $value ) {
513 if ( is_multisite() ) {
514 $this->add_option_to_network( $option, $value );
515 }
516 }
517
518 /**
519 * Send analytics track event if tracking is enabled.
520 *
521 * @since 1.0.0
522 */
523 public function maybe_track_analytics() {
524
525 if ( ! $this->is_tracking_enabled() ) {
526 return;
527 }
528
529 $analytics_track = get_site_transient( 'bsf_analytics_track' );
530
531 // If the last data sent is 2 days old i.e. transient is expired.
532 if ( ! $analytics_track ) {
533 $this->send();
534 set_site_transient( 'bsf_analytics_track', true, 2 * DAY_IN_SECONDS );
535 }
536 }
537
538 /**
539 * Save analytics option to network.
540 *
541 * @param string $option name of option.
542 * @param string $value value of option.
543 * @since 1.0.0
544 */
545 public function add_option_to_network( $option, $value ) {
546
547 // If action coming from general settings page.
548 if ( isset( $_POST['option_page'] ) && 'general' === $_POST['option_page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
549
550 if ( get_site_option( $option ) ) {
551 update_site_option( $option, $value );
552 } else {
553 add_site_option( $option, $value );
554 }
555 }
556 }
557
558 /**
559 * Function to load the deactivation survey form on the admin footer.
560 *
561 * This function checks if the Deactivation_Survey_Feedback class exists and if so, it loads the deactivation survey form.
562 * The form is configured with specific settings for plugin. Example: For CartFlows, including the source, logo, plugin slug, title, support URL, description, and the screen on which to show the form.
563 *
564 * @since 1.1.6
565 * @return void
566 */
567 public function load_deactivation_survey_form() {
568
569 if ( class_exists( 'Deactivation_Survey_Feedback' ) ) {
570 foreach ( $this->entities as $key => $data ) {
571 // If the deactivation_survey info in available then only add the form.
572 if ( ! empty( $data['deactivation_survey'] ) && is_array( $data['deactivation_survey'] ) ) {
573 foreach ( $data['deactivation_survey'] as $key => $survey_args ) {
574 Deactivation_Survey_Feedback::show_feedback_form(
575 $survey_args
576 );
577 }
578 }
579 }
580 }
581 }
582
583 /**
584 * Function to add plugin slugs to Deactivation Survey vars for JS operations.
585 *
586 * @param array $vars UDS vars array.
587 * @return array Modified UDS vars array with plugin slugs.
588 * @since 1.1.6
589 */
590 public function add_slugs_to_uds_vars( $vars ) {
591 foreach ( $this->entities as $key => $data ) {
592 if ( ! empty( $data['deactivation_survey'] ) && is_array( $data['deactivation_survey'] ) ) {
593 foreach ( $data['deactivation_survey'] as $key => $survey_args ) {
594 $vars['_plugin_slug'] = isset( $vars['_plugin_slug'] ) ? array_merge( $vars['_plugin_slug'], array( $survey_args['plugin_slug'] ) ) : array( $survey_args['plugin_slug'] );
595 }
596 }
597 }
598
599 return $vars;
600 }
601 }
602 }
603