PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.2.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.2.0
2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 All 96 releases
sureforms / admin / analytics.php

analytics.php in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 2.2.0, at admin/analytics.php

543 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Analytics class helps to connect BSFAnalytics.
4 *
5 * @package sureforms.
6 */
7
8 namespace SRFM\Admin;
9
10 use SRFM\Inc\Database\Tables\Entries;
11 use SRFM\Inc\Helper;
12 use SRFM\Inc\Traits\Get_Instance;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit; // Exit if accessed directly.
16 }
17 /**
18 * Analytics class.
19 *
20 * @since 1.4.0
21 */
22 class Analytics {
23 use Get_Instance;
24
25 /**
26 * Class constructor.
27 *
28 * @return void
29 * @since 1.4.0
30 */
31 public function __construct() {
32 /*
33 * BSF Analytics.
34 */
35 if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
36 require_once SRFM_DIR . 'inc/lib/bsf-analytics/class-bsf-analytics-loader.php';
37 }
38
39 if ( ! class_exists( 'Astra_Notices' ) ) {
40 require_once SRFM_DIR . 'inc/lib/astra-notices/class-astra-notices.php';
41 }
42
43 add_filter(
44 'uds_survey_allowed_screens',
45 static function () {
46 return [ 'plugins' ];
47 }
48 );
49
50 $srfm_bsf_analytics = \BSF_Analytics_Loader::get_instance();
51
52 $srfm_bsf_analytics->set_entity(
53 [
54 'sureforms' => [
55 'product_name' => 'SureForms',
56 'path' => SRFM_DIR . 'inc/lib/bsf-analytics',
57 'author' => 'SureForms',
58 'time_to_display' => '+24 hours',
59 'deactivation_survey' => apply_filters(
60 'srfm_deactivation_survey_data',
61 [
62 [
63 'id' => 'deactivation-survey-sureforms',
64 'popup_logo' => SRFM_URL . 'admin/assets/sureforms-logo.png',
65 'plugin_slug' => 'sureforms',
66 'popup_title' => 'Quick Feedback',
67 'support_url' => 'https://sureforms.com/contact/',
68 'popup_description' => 'If you have a moment, please share why you are deactivating SureForms:',
69 'show_on_screens' => [ 'plugins' ],
70 'plugin_version' => SRFM_VER,
71 ],
72 ]
73 ),
74 'hide_optin_checkbox' => true,
75 ],
76 ]
77 );
78
79 add_filter( 'bsf_core_stats', [ $this, 'add_srfm_analytics_data' ] );
80 }
81
82 /**
83 * Callback function to add SureForms specific analytics data.
84 *
85 * @param array $stats_data existing stats_data.
86 * @since 1.4.0
87 * @return array
88 */
89 public function add_srfm_analytics_data( $stats_data ) {
90 $stats_data['plugin_data']['sureforms'] = [
91 'free_version' => SRFM_VER,
92 'site_language' => get_locale(),
93 'most_used_anti_spam' => $this->most_used_anti_spam(),
94 'user_status' => $this->user_status(),
95 'pointer_popup_clicked' => $this->pointer_popup_clicked(),
96 ];
97 $stats_data['plugin_data']['sureforms']['numeric_values'] = [
98 'total_forms' => wp_count_posts( SRFM_FORMS_POST_TYPE )->publish ?? 0,
99 'instant_forms_enabled' => $this->instant_forms_enabled(),
100 'forms_using_custom_css' => $this->forms_using_custom_css(),
101 'ai_generated_forms' => $this->ai_generated_forms(),
102 'ai_generated_payment_forms' => $this->ai_generated_forms( 'payments' ),
103 'payment_forms' => $this->get_payment_forms_count(),
104 'total_entries' => Entries::get_total_entries_by_status(),
105 'restricted_forms' => $this->get_restricted_forms(),
106 ];
107
108 $stats_data['plugin_data']['sureforms'] = array_merge_recursive( $stats_data['plugin_data']['sureforms'], $this->global_settings_data() );
109 // Add onboarding analytics data.
110 $stats_data['plugin_data']['sureforms'] = array_merge_recursive( $stats_data['plugin_data']['sureforms'], $this->onboarding_analytics_data() );
111 return $stats_data;
112 }
113
114 /**
115 * Return total number of forms using instant forms.
116 *
117 * @since 1.4.0
118 * @return int
119 */
120 public function instant_forms_enabled() {
121 $meta_query = [
122 [
123 'key' => '_srfm_instant_form_settings',
124 'value' => '"enable_instant_form";b:1;',
125 'compare' => 'LIKE',
126 ],
127 ];
128
129 return $this->custom_wp_query_total_posts( $meta_query );
130 }
131
132 /**
133 * Return total number of ai generated forms.
134 *
135 * @param string $form_type Form type to check.
136 *
137 * @since 1.4.0
138 * @return int
139 */
140 public function ai_generated_forms( $form_type = '' ) {
141 $form_type = empty( $form_type ) || ! is_string( $form_type ) ? '' : $form_type;
142 $meta_query = [
143 [
144 'key' => '_srfm_is_ai_generated',
145 'value' => '',
146 'compare' => '!=', // Checks if the value is NOT empty.
147 ],
148 ];
149
150 if ( 'payments' === $form_type ) {
151 $search = 'wp:srfm/payment';
152 return $this->custom_wp_query_total_posts_with_search( $meta_query, $search );
153 }
154
155 return $this->custom_wp_query_total_posts( $meta_query );
156 }
157
158 /**
159 * Return most used anti-spam type on this site.
160 *
161 * @since 1.4.4
162 * @return int
163 */
164 public function most_used_anti_spam() {
165 global $wpdb;
166
167 // Attempt to get from cache first.
168 $cache_key = 'most_used_anti_spam';
169 $cached_result = wp_cache_get( $cache_key, 'sureforms' );
170
171 if ( false !== $cached_result ) {
172 return $cached_result;
173 }
174
175 $meta_key = '_srfm_captcha_security_type';
176
177 // Query to get the most used captcha type.
178 // PHPCS: Ignore direct database query warning, as there is no built-in alternative.
179 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
180 $result = $wpdb->get_row(
181 $wpdb->prepare(
182 "
183 SELECT meta_value, COUNT(meta_value) as count
184 FROM {$wpdb->postmeta}
185 WHERE meta_key = %s
186 AND meta_value != ''
187 GROUP BY meta_value
188 ORDER BY count DESC
189 LIMIT 1
190 ",
191 $meta_key
192 ),
193 ARRAY_A
194 );
195
196 $output = '';
197 if ( $result && ! empty( $result['meta_value'] ) ) {
198 switch ( $result['meta_value'] ) {
199 case 'g-recaptcha':
200 $output = 'Google reCAPTCHA';
201 break;
202
203 case 'cf-turnstile':
204 $output = 'CloudFlare Turnstile';
205 break;
206
207 case 'hcaptcha':
208 $output = 'hCaptcha';
209 break;
210
211 default:
212 $output = '';
213 break;
214 }
215 }
216
217 // Store result in cache for 1 hour.
218 wp_cache_set( $cache_key, $output, 'sureforms', HOUR_IN_SECONDS );
219
220 return $output;
221 }
222
223 /**
224 * Returns total number of forms using custom css.
225 *
226 * @since 1.4.0
227 * @return int
228 */
229 public function forms_using_custom_css() {
230 $meta_query = [
231 [
232 'key' => '_srfm_form_custom_css',
233 'value' => '',
234 'compare' => '!=', // Checks if the value is NOT empty.
235 ],
236 ];
237
238 return $this->custom_wp_query_total_posts( $meta_query );
239 }
240
241 /**
242 * Return total number of restricted forms.
243 *
244 * @since 1.10.1
245 * @return int
246 */
247 public function get_restricted_forms() {
248 $meta_query = [
249 [
250 'key' => '_srfm_form_restriction',
251 'value' => '"status":true',
252 'compare' => 'LIKE',
253 ],
254 ];
255
256 return $this->custom_wp_query_total_posts( $meta_query );
257 }
258
259 /**
260 * Generates global setting data for analytics
261 *
262 * @since 1.4.0
263 * @return array
264 */
265 public function global_settings_data() {
266 $global_data = [];
267
268 $security_settings = get_option( 'srfm_security_settings_options', [] );
269 $global_data['boolean_values']['honeypot_enabled'] = isset( $security_settings['srfm_honeypot'] ) && true === $security_settings['srfm_honeypot'];
270
271 $email_summary_data = get_option( 'srfm_email_summary_settings_options', [] );
272 $global_data['boolean_values']['email_summary_enabled'] = isset( $email_summary_data['srfm_email_summary'] ) && true === $email_summary_data['srfm_email_summary'];
273
274 $global_data['boolean_values']['suretriggers_active'] = is_plugin_active( 'suretriggers/suretriggers.php' );
275
276 $bsf_internal_referrer = get_option( 'bsf_product_referers', [] );
277 if ( ! empty( $bsf_internal_referrer['sureforms'] ) ) {
278 $global_data['internal_referer'] = $bsf_internal_referrer['sureforms'];
279 } else {
280 $global_data['internal_referer'] = '';
281 }
282
283 $general_settings = get_option( 'srfm_general_settings_options', [] );
284 $global_data['boolean_values']['ip_logging_enabled'] = ! empty( $general_settings['srfm_ip_log'] );
285
286 $validation_messages = get_option( 'srfm_default_dynamic_block_option', [] );
287 $global_data['boolean_values']['custom_validation_message'] = ! empty( $validation_messages ) && is_array( $validation_messages );
288
289 // Payment analytics - check if any payment method is enabled.
290 $global_data['boolean_values']['stripe_enabled'] = $this->is_stripe_enabled();
291
292 return $global_data;
293 }
294
295 /**
296 * Generates onboarding analytics data
297 *
298 * @since 1.9.1
299 * @return array
300 */
301 public function onboarding_analytics_data() {
302 $onboarding_data = [];
303 $analytics_option = Helper::get_srfm_option( 'onboarding_analytics', [] );
304
305 if ( empty( $analytics_option ) ) {
306 return $onboarding_data;
307 }
308
309 // Process skipped steps - store as an array.
310 if ( ! empty( $analytics_option['skippedSteps'] ) && is_array( $analytics_option['skippedSteps'] ) ) {
311 // Map step keys to more descriptive names.
312 $step_mapping = [
313 'welcome' => 'Welcome',
314 'connect' => 'Connect',
315 'emailDelivery' => 'SureMail',
316 'premiumFeatures' => 'Features',
317 'done' => 'Done',
318 ];
319
320 // Transform the step keys to their descriptive names.
321 $mapped_steps = array_map(
322 static function( $step ) use ( $step_mapping ) {
323 return $step_mapping[ $step ] ?? $step;
324 },
325 $analytics_option['skippedSteps']
326 );
327
328 // Store as an array.
329 $onboarding_data['onboarding_skipped_steps'] = $mapped_steps;
330 }
331
332 // SureMail Installation Status.
333 if ( isset( $analytics_option['suremailInstalled'] ) ) {
334 $onboarding_data['boolean_values']['onboarding_suremail_installed'] = (bool) $analytics_option['suremailInstalled'];
335 }
336
337 // Account Connection Status.
338 if ( isset( $analytics_option['accountConnected'] ) ) {
339 $onboarding_data['boolean_values']['onboarding_account_connected'] = (bool) $analytics_option['accountConnected'];
340 }
341
342 // Onboarding Completion Status.
343 if ( isset( $analytics_option['completed'] ) ) {
344 $onboarding_data['boolean_values']['onboarding_completed'] = (bool) $analytics_option['completed'];
345 }
346
347 // Onboarding Early Exit Status.
348 if ( isset( $analytics_option['exitedEarly'] ) ) {
349 $onboarding_data['boolean_values']['onboarding_exited_early'] = (bool) $analytics_option['exitedEarly'];
350 }
351
352 // Onboarding Selected Premium Features.
353 if ( ! empty( $analytics_option['premiumFeatures'] ) && ! empty( $analytics_option['premiumFeatures']['selectedFeatures'] ) ) {
354 // Map feature IDs to more descriptive names - exclude free features.
355 $feature_mapping = [
356 // Starter features.
357 'multi_step_form' => 'Multi-step Forms',
358 'conditional_logic' => 'Conditional Fields',
359 'webhooks' => 'Webhooks',
360 'advanced_fields' => 'Advanced Fields',
361
362 // Pro features.
363 'conversational_forms' => 'Conversational Forms',
364 'digital_signatures' => 'Digital Signatures',
365
366 // Business features.
367 'calculations' => 'Calculators',
368 'user_registration' => 'User Registration and Login',
369 'custom_app' => 'Custom App',
370 'pdf_generation' => 'PDF Generation',
371 ];
372
373 // Filter out any free features that might have been included.
374 $premium_features = array_filter(
375 $analytics_option['premiumFeatures']['selectedFeatures'],
376 static function( $feature ) {
377 // Exclude free features (ai-form-generation and entries).
378 return 'ai-form-generation' !== $feature && 'entries' !== $feature;
379 }
380 );
381
382 // Transform the feature IDs to their descriptive names.
383 $mapped_features = array_map(
384 static function( $feature ) use ( $feature_mapping ) {
385 return $feature_mapping[ $feature ] ?? $feature;
386 },
387 $premium_features
388 );
389
390 // Store as an array.
391 $onboarding_data['onboarding_selected_premium_features'] = $mapped_features;
392 }
393
394 return $onboarding_data;
395 }
396
397 /**
398 * Returns user status.
399 *
400 * @since 1.8.0
401 * @return string
402 */
403 public function user_status() {
404 // First, check if user_active is already set in srfm_options.
405 if ( Helper::get_srfm_option( 'user_active', false ) ) {
406 return 'active';
407 }
408 // Get up to 10 published SureForms.
409 $forms = get_posts(
410 [
411 'post_type' => SRFM_FORMS_POST_TYPE,
412 'posts_per_page' => 10,
413 'post_status' => 'publish',
414 ]
415 );
416 if ( empty( $forms ) ) {
417 return 'inactive';
418 }
419 foreach ( $forms as $form ) {
420 if ( ! get_post_meta( $form->ID, '_astra_sites_imported_post', true ) ) {
421 // Mark user as active in srfm_options.
422 Helper::update_srfm_option( 'user_active', true );
423 return 'active';
424 }
425 }
426 return 'inactive';
427 }
428
429 /**
430 * Return pointer popup clicked status.
431 *
432 * @return string pointer click status.
433 * @since 1.8.0
434 */
435 public function pointer_popup_clicked() {
436 // Get both values from srfm_options.
437 $accepted = Helper::get_srfm_option( 'pointer_popup_accepted', false );
438 $dismissed = Helper::get_srfm_option( 'pointer_popup_dismissed', false );
439 // If neither action has occurred.
440 if ( ! $accepted && ! $dismissed ) {
441 return '';
442 }
443
444 // If both are set, return the most recent one.
445 if ( $accepted && $dismissed ) {
446 return $accepted > $dismissed ? 'accepted' : 'dismissed';
447 }
448
449 // If only one is set, return it.
450 return $accepted ? 'accepted' : 'dismissed';
451 }
452
453 /**
454 * Runs a custom WP_Query to fetch the total number of posts matching the given meta query and optional search string.
455 *
456 * This function is used to count SureForms posts based on specific meta query conditions.
457 * Optionally, a search string can be included to further filter results by keyword match.
458 *
459 * @since 2.0.0
460 *
461 * @param array $meta_query Meta query array for WP_Query.
462 * @param string $search Optional. Search string for WP_Query. Default empty.
463 * @return int The number of matching posts.
464 */
465 public function custom_wp_query_total_posts_with_search( $meta_query = [], $search = '' ) {
466 $args = [
467 'post_type' => SRFM_FORMS_POST_TYPE,
468 'post_status' => 'publish',
469 'posts_per_page' => -1,
470 ];
471
472 if ( ! empty( $meta_query ) && is_array( $meta_query ) ) {
473 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query required as we need to fetch count of nested data.
474 $args['meta_query'] = $meta_query;
475 }
476
477 // If search string is provided, add it to the query.
478 if ( ! empty( $search ) ) {
479 $args['s'] = sanitize_text_field( $search );
480 }
481
482 $query = new \WP_Query( $args );
483 $posts_count = $query->found_posts;
484
485 wp_reset_postdata();
486
487 return $posts_count;
488 }
489
490 /**
491 * Get the total number of forms that utilize payment blocks.
492 *
493 * This function searches for forms containing the payment block identifier
494 * ('wp:srfm/payment') to determine how many forms include payment capabilities.
495 *
496 * @since 2.0.0
497 * @return int The number of forms that contain payment blocks.
498 */
499 public function get_payment_forms_count() {
500 $search = 'wp:srfm/payment';
501 // Runs a custom WP_Query to find the count of forms with payment block.
502 return $this->custom_wp_query_total_posts_with_search( [], $search );
503 }
504
505 /**
506 * Check if any payment method is enabled.
507 *
508 * This function checks if any payment gateway is connected and enabled.
509 * Currently supports Stripe, but can be extended for other payment methods in the future.
510 *
511 * @since 2.0.0
512 * @return bool True if any payment method is enabled, false otherwise.
513 */
514 private function is_stripe_enabled() {
515 // Check if Stripe is connected.
516 return class_exists( 'SRFM\Inc\Payments\Stripe\Stripe_Helper' ) && \SRFM\Inc\Payments\Stripe\Stripe_Helper::is_stripe_connected();
517 }
518
519 /**
520 * Runs custom WP_Query to fetch data as per requirement
521 *
522 * @param array $meta_query meta query array for WP_Query.
523 * @since 1.4.0
524 * @return int
525 */
526 private function custom_wp_query_total_posts( $meta_query ) {
527
528 $args = [
529 'post_type' => SRFM_FORMS_POST_TYPE,
530 'post_status' => 'publish',
531 'posts_per_page' => -1,
532 'meta_query' => $meta_query, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query required as we need to fetch count of nested data.
533 ];
534
535 $query = new \WP_Query( $args );
536 $posts_count = $query->found_posts;
537
538 wp_reset_postdata();
539
540 return $posts_count;
541 }
542 }
543