• File: UsageTracking.php
  • Full Path: /home/matthif/www/wp-content/plugins/wpforms-lite/src/Integrations/WPCode/UsageTracking.php
  • Date Modified: 02/16/2024 11:45 AM
  • File size: 19.45 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

namespace WPForms\Integrations\UsageTracking;

use 
WPForms\Admin\Builder\Templates;
use 
WPForms\Integrations\IntegrationInterface;
use 
WPForms\Integrations\LiteConnect\Integration;

/**
 * Usage Tracker functionality to understand what's going on on client's sites.
 *
 * @since 1.6.1
 */
class UsageTracking implements IntegrationInterface {

    
/**
     * The slug that will be used to save the option of Usage Tracker.
     *
     * @since 1.6.1
     */
    
const SETTINGS_SLUG 'usage-tracking-enabled';

    
/**
     * Indicate if current integration is allowed to load.
     *
     * @since 1.6.1
     *
     * @return bool
     */
    
public function allow_load() {

        
/**
         * Whether the Usage Tracking code is allowed to be loaded.
         *
         * @since 1.6.1
         *
         * @param bool $var Boolean value.
         */
        
return (bool) apply_filters'wpforms_usagetracking_is_allowed'true ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
    
}

    
/**
     * Whether Usage Tracking is enabled.
     *
     * @since 1.6.1
     *
     * @return bool
     */
    
public function is_enabled() {

        
/**
         * Whether the Usage Tracking is enabled.
         *
         * @since 1.6.1
         *
         * @param bool $var Boolean value taken from the DB.
         */
        
return (bool) apply_filters'wpforms_integrations_usagetracking_is_enabled'wpforms_settingself::SETTINGS_SLUG ) );  // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
    
}

    
/**
     * Load an integration.
     *
     * @since 1.6.1
     */
    
public function load() {

        
add_filter'wpforms_settings_defaults', [ $this'settings_misc_option' ], );

        
// Deregister the action if option is disabled.
        
add_action(
            
'wpforms_settings_updated',
            function () {

                if ( ! 
$this->is_enabled() ) {
                    ( new 
SendUsageTask() )->cancel();
                }
            }
        );

        
// Register the action handler only if enabled.
        
if ( $this->is_enabled() ) {
            
add_filter(
                
'wpforms_tasks_get_tasks',
                static function ( 
$tasks ) {

                    
$tasks[] = SendUsageTask::class;

                    return 
$tasks;
                }
            );
        }
    }

    
/**
     * Add "Allow Usage Tracking" to WPForms settings.
     *
     * @since 1.6.1
     *
     * @param array $settings WPForms settings.
     *
     * @return array
     */
    
public function settings_misc_option$settings ) {

        
$settings['misc'][ self::SETTINGS_SLUG ] = [
            
'id'     => self::SETTINGS_SLUG,
            
'name'   => esc_html__'Allow Usage Tracking''wpforms-lite' ),
            
'desc'   => esc_html__'By allowing us to track usage data, we can better help you, as we will know which WordPress configurations, themes, and plugins we should test.''wpforms-lite' ),
            
'type'   => 'toggle',
            
'status' => true,
        ];

        return 
$settings;
    }

    
/**
     * Get the User Agent string that will be sent to the API.
     *
     * @since 1.6.1
     *
     * @return string
     */
    
public function get_user_agent() {

        return 
'WPForms/' WPFORMS_VERSION '; ' get_bloginfo'url' );
    }

    
/**
     * Get data for sending to the server.
     *
     * @since 1.6.1
     *
     * @return array
     * @noinspection PhpUndefinedConstantInspection
     * @noinspection PhpUndefinedFunctionInspection
     */
    
public function get_data() {

        global 
$wpdb;

        
$theme_data        wp_get_theme();
        
$activated_dates   get_option'wpforms_activated', [] );
        
$first_form_date   get_option'wpforms_forms_first_created' );
        
$forms             $this->get_all_forms();
        
$forms_total       count$forms );
        
$entries_total     $this->get_entries_total();
        
$form_fields_count $this->get_form_fields_count$forms );

        
$data = [
            
// Generic data (environment).
            
'url'                            => home_url(),
            
'php_version'                    => PHP_MAJOR_VERSION '.' PHP_MINOR_VERSION,
            
'wp_version'                     => get_bloginfo'version' ),
            
'mysql_version'                  => $wpdb->db_version(),
            
'server_version'                 => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_fieldwp_unslash$_SERVER['SERVER_SOFTWARE'] ) ) : '',
            
'is_ssl'                         => is_ssl(),
            
'is_multisite'                   => is_multisite(),
            
'is_network_activated'           => $this->is_active_for_network(),
            
'is_wpcom'                       => defined'IS_WPCOM' ) && IS_WPCOM,
            
'is_wpcom_vip'                   => ( defined'WPCOM_IS_VIP_ENV' ) && WPCOM_IS_VIP_ENV ) || ( function_exists'wpcom_is_vip' ) && wpcom_is_vip() ),
            
'is_wp_cache'                    => defined'WP_CACHE' ) && WP_CACHE,
            
'is_wp_rest_api_enabled'         => $this->is_rest_api_enabled(),
            
'is_user_logged_in'              => is_user_logged_in(),
            
'sites_count'                    => $this->get_sites_total(),
            
'active_plugins'                 => $this->get_active_plugins(),
            
'theme_name'                     => $theme_data->name,
            
'theme_version'                  => $theme_data->version,
            
'locale'                         => get_locale(),
            
'timezone_offset'                => $this->get_timezone_offset(),
            
// WPForms-specific data.
            
'wpforms_version'                => WPFORMS_VERSION,
            
'wpforms_license_key'            => wpforms_get_license_key(),
            
'wpforms_license_type'           => $this->get_license_type(),
            
'wpforms_license_status'         => $this->get_license_status(),
            
'wpforms_is_pro'                 => wpforms()->is_pro(),
            
'wpforms_entries_avg'            => $this->get_entries_avg$forms_total$entries_total ),
            
'wpforms_entries_total'          => $entries_total,
            
'wpforms_entries_last_7days'     => $this->get_entries_total'7days' ),
            
'wpforms_entries_last_30days'    => $this->get_entries_total'30days' ),
            
'wpforms_forms_total'            => $forms_total,
            
'wpforms_form_fields_count'      => $form_fields_count,
            
'wpforms_challenge_stats'        => get_option'wpforms_challenge', [] ),
            
'wpforms_lite_installed_date'    => $this->get_installed$activated_dates'lite' ),
            
'wpforms_pro_installed_date'     => $this->get_installed$activated_dates'pro' ),
            
'wpforms_builder_opened_date'    => (int) get_option'wpforms_builder_opened_date'),
            
'wpforms_settings'               => $this->get_settings(),
            
'wpforms_integration_active'     => $this->get_forms_integrations$forms ),
            
'wpforms_payments_active'        => $this->get_payments_active$forms ),
            
'wpforms_multiple_confirmations' => count$this->get_forms_with_multiple_confirmations$forms ) ),
            
'wpforms_multiple_notifications' => count$this->get_forms_with_multiple_notifications$forms ) ),
            
'wpforms_ajax_form_submissions'  => count$this->get_ajax_form_submissions$forms ) ),
            
'wpforms_notification_count'     => wpforms()->get'notifications' )->get_count(),
        ];

        if ( ! empty( 
$first_form_date ) ) {
            
$data['wpforms_forms_first_created'] = $first_form_date;
        }

        if ( 
$data['is_multisite'] ) {
            
$data['url_primary'] = network_site_url();
        }

        return 
$data;
    }

    
/**
     * Get the license type.
     *
     * @since 1.6.1
     * @since 1.7.2 Clarified the license type.
     * @since 1.7.9 Return only the license type, not the status.
     *
     * @return string
     */
    
private function get_license_type() {

        return 
wpforms()->is_pro() ? wpforms_get_license_type() : 'lite';
    }

    
/**
     * Get the license status.
     *
     * @since 1.7.9
     *
     * @return string
     */
    
private function get_license_status() {

        if ( ! 
wpforms()->is_pro() ) {
            return 
'lite';
        }

        
$license_type wpforms_get_license_type();
        
$license_key  wpforms_get_license_key();

        if ( ! 
$license_type ) {
            return empty( 
$license_key ) ? 'no license' 'not verified';
        }

        if ( 
wpforms_setting'is_expired'false'wpforms_license' ) ) {
            return 
'expired';
        }

        if ( 
wpforms_setting'is_disabled'false'wpforms_license' ) ) {
            return 
'disabled';
        }

        if ( 
wpforms_setting'is_invalid'false'wpforms_license' ) ) {
            return 
'invalid';
        }

        
// The correct type is returned in get_license_type(), so we "collapse" them here to a single value.
        
if ( in_array$license_type, [ 'basic''plus''pro''elite''ultimate''agency' ], true ) ) {
            
$license_type 'correct';
        }

        return 
$license_type;
    }

    
/**
     * Get all settings, except those with sensitive data.
     *
     * @since 1.6.1
     *
     * @return array
     */
    
private function get_settings() {

        
// Remove keys with exact names that we don't need.
        
$settings array_diff_key(
            
get_option'wpforms_settings', [] ),
            
array_flip(
                [
                    
'stripe-test-secret-key',
                    
'stripe-test-publishable-key',
                    
'stripe-live-secret-key',
                    
'stripe-live-publishable-key',
                    
'stripe-webhooks-secret-test',
                    
'stripe-webhooks-secret-live',
                    
'stripe-webhooks-id-test',
                    
'stripe-webhooks-id-live',
                    
'authorize_net-test-api-login-id',
                    
'authorize_net-test-transaction-key',
                    
'authorize_net-live-api-login-id',
                    
'authorize_net-live-transaction-key',
                    
'square-location-id-sandbox',
                    
'square-location-id-production',
                    
'geolocation-google-places-api-key',
                    
'geolocation-algolia-places-application-id',
                    
'geolocation-algolia-places-search-only-api-key',
                    
'geolocation-mapbox-search-access-token',
                    
'recaptcha-site-key',
                    
'recaptcha-secret-key',
                    
'recaptcha-fail-msg',
                    
'hcaptcha-site-key',
                    
'hcaptcha-secret-key',
                    
'hcaptcha-fail-msg',
                    
'pdf-ninja-api_key',
                ]
            )
        );

        
$data = [];

        
// Remove keys with a vague names that we don't need.
        
foreach ( $settings as $key => $value ) {
            if ( 
strpos$key'validation-' ) !== false ) {
                continue;
            }

            
$data$key ] = $value;
        }

        
$lite_connect_data get_optionIntegration::get_option_name() );

        
// If lite connect has been restored, set lite connect data.
        
if (
            isset( 
$lite_connect_data['import']['status'] ) &&
            
$lite_connect_data['import']['status'] === 'done'
        
) {
            
$data['lite_connect'] = [
                
'restore_date'         => $lite_connect_data['import']['ended_at'],
                
'restored_entry_count' => Integration::get_entries_count(),
            ];
        }

        
// Add favorite templates to the settings array.
        
return array_merge$data$this->get_favorite_templates() );
    }

    
/**
     * Get timezone offset.
     * We use `wp_timezone_string()` when it's available (WP 5.3+),
     * otherwise fallback to the same code, copy-pasted.
     *
     * @see wp_timezone_string()
     *
     * @since 1.6.1
     *
     * @return string
     */
    
private function get_timezone_offset() {

        
// It was added in WordPress 5.3.
        
if ( function_exists'wp_timezone_string' ) ) {
            return 
wp_timezone_string();
        }

        
/*
         * The code below is basically a copy-paste from that function.
         */

        
$timezone_string get_option'timezone_string' );

        if ( 
$timezone_string ) {
            return 
$timezone_string;
        }

        
$offset  = (float) get_option'gmt_offset' );
        
$hours   = (int) $offset;
        
$minutes = ( $offset $hours );

        
$sign      = ( $offset ) ? '-' '+';
        
$abs_hour  abs$hours );
        
$abs_mins  abs$minutes 60 );
        
$tz_offset sprintf'%s%02d:%02d'$sign$abs_hour$abs_mins );

        return 
$tz_offset;
    }

    
/**
     * Get the list of active plugins.
     *
     * @since 1.6.1
     *
     * @return array
     */
    
private function get_active_plugins() {

        if ( ! 
function_exists'get_plugins' ) ) {
            include 
ABSPATH '/wp-admin/includes/plugin.php';
        }
        
$active  is_multisite() ?
            
array_mergeget_option'active_plugins', [] ), array_flipget_site_option'active_sitewide_plugins', [] ) ) ) :
            
get_option'active_plugins', [] );
        
$plugins array_intersect_keyget_plugins(), array_flip$active ) );

        return 
array_map(
            static function ( 
$plugin ) {

                if ( isset( 
$plugin['Version'] ) ) {
                    return 
$plugin['Version'];
                }

                return 
'Not Set';
            },
            
$plugins
        
);
    }

    
/**
     * Installed date.
     *
     * @since 1.6.1
     *
     * @param array  $activated_dates Input array with dates.
     * @param string $key             Input key what you want to get.
     *
     * @return mixed
     */
    
private function get_installed$activated_dates$key ) {

        if ( ! empty( 
$activated_dates$key ] ) ) {
            return 
$activated_dates$key ];
        }

        return 
false;
    }

    
/**
     * Number of forms with some integrations active.
     *
     * @since 1.6.1
     *
     * @param array $forms List of forms.
     *
     * @return array List of forms with active integrations count.
     */
    
private function get_forms_integrations$forms ) {

        
$integrations array_map(
            static function ( 
$form ) {

                if ( ! empty( 
$form->post_content['providers'] ) ) {
                    return 
array_keys$form->post_content['providers'] );
                }

                return 
false;
            },
            
$forms
        
);

        
$integrations array_filter$integrations );

        if ( 
count$integrations ) > ) {
            
$integrations call_user_func_array'array_merge'array_values$integrations ) );
        }

        return 
array_count_values$integrations );
    }

    
/**
     * Number of forms with active payments.
     *
     * @since 1.6.1
     *
     * @param array $forms Input forms list.
     *
     * @return array List of forms with active payments count.
     */
    
private function get_payments_active$forms ) {

        
$payments array_map(
            static function ( 
$form ) {

                if ( empty( 
$form->post_content['payments'] ) ) {
                    return 
false;
                }

                
$enabled = [];

                foreach ( 
$form->post_content['payments'] as $key => $value ) {
                    if ( ! empty( 
$value['enable'] ) ) {
                        
$enabled[] = $key;
                    }
                }

                return empty( 
$enabled ) ? false $enabled;
            },
            
$forms
        
);

        
$payments array_filter$payments );

        if ( 
count$payments ) > ) {
            
$payments call_user_func_array'array_merge'array_values$payments ) );
        }

        return 
array_count_values$payments );
    }

    
/**
     * Forms with multiple notifications.
     *
     * @since 1.6.1
     *
     * @param array $forms List of forms to check.
     *
     * @return array List of forms with multiple notifications.
     */
    
private function get_forms_with_multiple_notifications$forms ) {

        return 
array_filter(
            
$forms,
            static function ( 
$form ) {

                return ! empty( 
$form->post_content['settings']['notifications'] ) && count$form->post_content['settings']['notifications'] ) > 1;
            }
        );
    }

    
/**
     * Forms with multiple confirmations.
     *
     * @since 1.6.1
     *
     * @param array $forms List of forms to check.
     *
     * @return array List of forms with multiple confirmations.
     */
    
private function get_forms_with_multiple_confirmations$forms ) {

        return 
array_filter(
            
$forms,
            static function ( 
$form ) {

                return ! empty( 
$form->post_content['settings']['confirmations'] ) && count$form->post_content['settings']['confirmations'] ) > 1;
            }
        );
    }

    
/**
     * Forms with ajax submission option enabled.
     *
     * @since 1.6.1
     *
     * @param array $forms All forms.
     *
     * @return array
     */
    
private function get_ajax_form_submissions$forms ) {

        return 
array_filter(
            
$forms,
            static function ( 
$form ) {

                return ! empty( 
$form->post_content['settings']['ajax_submit'] );
            }
        );
    }

    
/**
     * Total number of sites.
     *
     * @since 1.6.1
     *
     * @return int
     */
    
private function get_sites_total() {

        return 
function_exists'get_blog_count' ) ? (int) get_blog_count() : 1;
    }

    
/**
     * Total number of entries.
     *
     * @since 1.6.1
     *
     * @param string $period Which period should be counted? Possible values: 7days, 30days.
     *                       Everything else will mean "all" entries.
     *
     * @return int
     */
    
private function get_entries_totalstring $period 'all' ): int {

        if ( ! 
wpforms()->is_pro() ) {
            if ( 
$period === '7days' || $period === '30days' ) {
                return 
0;
            }

            global 
$wpdb;

            
$count $wpdb->get_var// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
                
"SELECT SUM(meta_value)
                FROM 
$wpdb->postmeta
                WHERE meta_key = 'wpforms_entries_count';"
            
);

            return (int) 
$count;
        }

        
$args = [];

        switch ( 
$period ) {
            case 
'7days':
                
$args = [
                    
'date' => [
                        
gmdate'Y-m-d'strtotime'-7 days' ) ),
                        
gmdate'Y-m-d' ),
                    ],
                ];
                break;

            case 
'30days':
                
$args = [
                    
'date' => [
                        
gmdate'Y-m-d'strtotime'-30 days' ) ),
                        
gmdate'Y-m-d' ),
                    ],
                ];
                break;
        }

        
$entry_obj wpforms()->get'entry' );

        return 
$entry_obj $entry_obj->get_entries$argstrue ) : 0;
    }

    
/**
     * Forms field occurrences.
     *
     * @since 1.7.9
     *
     * @param array $forms List of forms.
     *
     * @return array List of field occurrences in all forms created.
     */
    
private function get_form_fields_count$forms ) {

        
// Bail early, in case there are no forms created yet!
        
if ( empty( $forms ) ) {
            return [];
        }

        
$fields         array_map(
            static function( 
$form ) {

                return isset( 
$form->post_content['fields'] ) ? $form->post_content['fields'] : [];
            },
            
$forms
        
);
        
$fields_flatten array_merge( [], ...$fields );
        
$field_types    array_column$fields_flatten'type' );

        return 
array_count_values$field_types );
    }

    
/**
     * Determines whether the plugin is active for the entire network.
     *
     * This is a copy of the WP core is_plugin_active_for_network() function.
     *
     * @since 1.8.2
     *
     * @return bool
     */
    
private function is_active_for_network() {

        
// Bail early, in case we are not in multisite.
        
if ( ! is_multisite() ) {
            return 
false;
        }

        
// Get all active plugins.
        
$plugins get_site_option'active_sitewide_plugins' );

        
// Bail early, in case the plugin is active for the entire network.
        
if ( isset( $pluginsplugin_basenameWPFORMS_PLUGIN_FILE ) ] ) ) {
            return 
true;
        }

        return 
false;
    }

    
/**
     * Average entries count.
     *
     * @since 1.6.1
     *
     * @param int $forms   Total forms count.
     * @param int $entries Total entries count.
     *
     * @return int
     */
    
private function get_entries_avg$forms$entries ) {

        return 
$forms round$entries $forms ) : 0;
    }

    
/**
     * Get all forms.
     *
     * @since 1.6.1
     *
     * @return array
     */
    
private function get_all_forms() {

        
$forms wpforms()->get'form' )->get'' );

        if ( ! 
is_array$forms ) ) {
            return [];
        }

        return 
array_map(
            static function( 
$form ) {

                
$form->post_content wpforms_decode$form->post_content );

                return 
$form;
            },
            
$forms
        
);
    }

    
/**
     * Get the favorite templates.
     *
     * @since 1.7.7
     *
     * @return array
     */
    
private function get_favorite_templates() {

        
$settings  = [];
        
$templates = (array) get_optionTemplates::FAVORITE_TEMPLATES_OPTION, [] );

        foreach ( 
$templates as $user_templates ) {
            foreach ( 
$user_templates as $template => $v ) {
                
$name              'fav_templates_' str_replace'-''_'$template );
                
$settings$name ] = empty( $settings$name ] ) ? : ++ $settings$name ];
            }
        }

        return 
$settings;
    }

    
/**
     * Test if the REST API is accessible.
     *
     * The REST API might be inaccessible due to various security measures,
     * or it might be completely disabled by a plugin.
     *
     * @since 1.8.2.2
     *
     * @return bool
     */
    
private function is_rest_api_enabled() {

        
// phpcs:disable WPForms.PHP.ValidateHooks.InvalidHookName
        /** This filter is documented in wp-includes/class-wp-http-streams.php */
        
$sslverify apply_filters'https_local_ssl_verify'false );
        
// phpcs:enable WPForms.PHP.ValidateHooks.InvalidHookName

        
$url      rest_url'wp/v2/types/post' );
        
$response wp_remote_get(
            
$url,
            [
                
'timeout'   => 10,
                
'cookies'   => is_user_logged_in() ? wp_unslash$_COOKIE ) : [],
                
'sslverify' => $sslverify,
                
'headers'   => [
                    
'Cache-Control' => 'no-cache',
                    
'X-WP-Nonce'    => wp_create_nonce'wp_rest' ),
                ],
            ]
        );

        
// When testing the REST API, an error was encountered, leave early.
        
if ( is_wp_error$response ) ) {
            return 
false;
        }

        
// When testing the REST API, an unexpected result was returned, leave early.
        
if ( wp_remote_retrieve_response_code$response ) !== 200 ) {
            return 
false;
        }

        
// The REST API did not behave correctly, leave early.
        
if ( ! wpforms_is_jsonwp_remote_retrieve_body$response ) ) ) {
            return 
false;
        }

        
// We are all set. Confirm the connection.
        
return true;
    }
}