HEX
Server: LiteSpeed
System: Linux node612.namehero.net 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: dfwparty (1186)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/dfwparty/public_html/wp-content/plugins/ultimate-elementor/classes/class-uael-analytics.php
<?php
/**
 * UAEL Analytics.
 *
 * @package UAEL
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

if ( ! class_exists( 'UAEL_Analytics' ) ) {
	/**
	 * Class UAEL_Analytics
	 *
	 * Handles analytics-related functionality for the Ultimate Addons for Elementor plugin.
	 *
	 * @since 1.39.3
	 */
	class UAEL_Analytics {

		/**
		 * UAEL Analytics constructor.
		 *
		 * Initializing UAEL Analytics.
		 *
		 * @since 1.39.3
		 * @access public
		 */
		public function __construct() {
			add_action( 'admin_init', array( $this, 'maybe_migrate_analytics_tracking' ) );

			// Load analytics events class.
			if ( ! class_exists( 'UAEL_Analytics_Events' ) ) {
				require_once UAEL_DIR . 'classes/class-uael-analytics-events.php';
			}

			// BSF Analytics Tracker.
			if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
				require_once UAEL_DIR . 'admin/bsf-analytics/class-bsf-analytics-loader.php';
			}

			$bsf_analytics = BSF_Analytics_Loader::get_instance();

			$bsf_analytics->set_entity(
				array(
					'uae' => array(
						'product_name'        => 'Ultimate Addons for Elementor Pro',
						'path'                => UAEL_DIR . 'admin/bsf-analytics',
						'author'              => 'Ultimate Addons for Elementor',
						'time_to_display'     => '+24 hours',
						'deactivation_survey' => array(
							array(
								'id'                => 'deactivation-survey-ultimate-elementor', // 'deactivation-survey-<your-plugin-slug>'
								'popup_logo'        => UAEL_URL . 'assets/images/settings/logo.svg',
								'plugin_slug'       => 'ultimate-elementor', // <your-plugin-slug>
								'plugin_version'    => UAEL_VER,
								'popup_title'       => 'Quick Feedback',
								'support_url'       => 'https://ultimateelementor.com/contact/',
								'popup_description' => 'If you have a moment, please share why you are deactivating Ultimate Addons for Elementor Pro:',
								'show_on_screens'   => array( 'plugins' ),
							),
						),
						'hide_optin_checkbox' => true,
					),
				)
			);

			add_filter( 'bsf_core_stats', array( $this, 'add_uae_analytics_data' ) );

			// Real-time first widget detection on Elementor save.
			if ( ! UAEL_Analytics_Events::is_tracked( 'first_widget_used' ) ) {
				add_action( 'elementor/editor/after_save', array( $this, 'track_first_widget_on_save' ), 10, 2 );
			}

			// Detect state-based events only in admin context, throttled to once per day.
			if ( is_admin() && false === get_transient( 'uael_state_events_checked' ) ) {
				$this->detect_state_events();
				set_transient( 'uael_state_events_checked', 1, DAY_IN_SECONDS );
			}
		}

		/**
		 * Migrates analytics tracking option from 'bsf_usage_optin' to 'uae_usage_optin'.
		 *
		 * @since 1.39.8
		 * @access public
		 * @return void
		 */
		public function maybe_migrate_analytics_tracking() {
			// Skip if already migrated or new option already set.
			if ( false !== get_site_option( 'uae_usage_optin', false ) ) {
				return;
			}

			$old_tracking = get_site_option( 'bsf_usage_optin', false );
			if ( 'yes' === $old_tracking ) {
				update_site_option( 'uae_usage_optin', 'yes' );
				$time = get_site_option( 'bsf_usage_installed_time' );
				if ( false !== $time ) {
					update_site_option( 'uae_usage_installed_time', $time );
				}
			}
		}

		/**
		 * Callback function to add specific analytics data.
		 *
		 * @param array $stats_data existing stats_data.
		 * @since 1.39.3
		 * @return array
		 */
		public function add_uae_analytics_data( $stats_data ) {
			$stats_data['plugin_data']['uae'] = array(
				'free_version'          => ( defined( 'HFE_VER' ) ? HFE_VER : '' ),
				'pro_version'           => UAEL_VER,
				'site_language'         => get_locale(),
				'elementor_version'     => ( defined( 'ELEMENTOR_VERSION' ) ? ELEMENTOR_VERSION : '' ),
				'elementor_pro_version' => ( defined( 'ELEMENTOR_PRO_VERSION' ) ? ELEMENTOR_PRO_VERSION : '' ),
				'onboarding_triggered'  => ( 'yes' === get_option( 'uaepro_onboarding_triggered' ) ) ? 'yes' : 'no',
				'active_theme'          => get_template(),
				'is_white_labeled'      => $this->is_white_labeled(),
				'onboarding_analytics'  => get_option( 'uaepro_onboarding_analytics', array() ),
			);

			// AI Tools (MCP) adoption — the three master toggles only. No usage detail, no user data.
			$mcp_settings = get_option( 'uae_mcp_settings', array() );
			$mcp_settings = is_array( $mcp_settings ) ? $mcp_settings : array();

			$stats_data['plugin_data']['uae']['mcp'] = array(
				'abilities_enabled'  => ! empty( $mcp_settings['enable_abilities'] ) ? 'yes' : 'no',
				'mcp_server_enabled' => ! empty( $mcp_settings['dedicated_server'] ) ? 'yes' : 'no',
				'angie_enabled'      => ! empty( $mcp_settings['angie_enabled'] ) ? 'yes' : 'no',
			);

			$fetch_elementor_data = $this->uael_get_widgets_usage();
			foreach ( $fetch_elementor_data as $key => $value ) {
				$stats_data['plugin_data']['uae']['numeric_values'][ $key ] = $value;
			}

			// Add KPI tracking data.
			$kpi_data = $this->get_kpi_tracking_data();
			if ( ! empty( $kpi_data ) ) {
				$stats_data['plugin_data']['uae']['kpi_records'] = $kpi_data;
			}

			// Flush pending events into payload (only if any exist).
			$pending_events = UAEL_Analytics_Events::flush_pending();
			if ( ! empty( $pending_events ) ) {
				$stats_data['plugin_data']['uae']['events_record'] = $pending_events;
			}
			return $stats_data;
		}

		/**
		 * Track first UAE Pro widget usage on Elementor post save.
		 *
		 * Fires on elementor/editor/after_save. Checks if the saved post
		 * contains any UAE Pro widget (uael- prefix) and tracks the
		 * first_widget_used event immediately instead of waiting for the
		 * daily cron scan.
		 *
		 * @since 1.44.3
		 * @param int   $post_id     Post ID.
		 * @param array $editor_data Elementor editor data.
		 * @return void
		 */
		public function track_first_widget_on_save( $post_id, $editor_data ) {
			// Skip if already tracked — zero overhead after first detection.
			if ( UAEL_Analytics_Events::is_tracked( 'first_widget_used' ) ) {
				return;
			}

			$elementor_data = get_post_meta( $post_id, '_elementor_data', true );
			if ( empty( $elementor_data ) ) {
				return;
			}

			// _elementor_data is normally a JSON string, but some setups return it
			// as an array. preg_match() is strictly typed on PHP 8+, so coerce to string.
			if ( is_array( $elementor_data ) ) {
				$elementor_data = wp_json_encode( $elementor_data );
			}

			if ( ! is_string( $elementor_data ) || '' === $elementor_data ) {
				return;
			}

			// Extract the first UAE Pro widget name from Elementor data.
			$first_widget = '';
			if ( preg_match( '/"widgetType":"(uael-[^"]+)"/', $elementor_data, $matches ) ) {
				$first_widget = $matches[1];
			}

			if ( empty( $first_widget ) ) {
				return;
			}

			$install_time       = get_option( 'uae_usage_installed_time', 0 );
			$days_since_install = 0;
			if ( $install_time > 0 ) {
				$days_since_install = (int) floor( ( time() - (int) $install_time ) / DAY_IN_SECONDS );
			}

			UAEL_Analytics_Events::track(
				'first_widget_used',
				$first_widget,
				array(
					'days_since_install' => (string) $days_since_install,
				)
			);
		}

		/**
		 * Detect state-based events that can't use direct hooks.
		 * Uses dedup in UAEL_Analytics_Events::track() — safe to call repeatedly.
		 *
		 * @since 1.44.2
		 * @return void
		 */
		private function detect_state_events() {
			// Read pushed + pending once to avoid repeated get_option calls per event.
			$pushed  = get_option( 'uael_usage_events_pushed', array() );
			$pushed  = is_array( $pushed ) ? $pushed : array();
			$pending = get_option( 'uael_usage_events_pending', array() );
			$pending = is_array( $pending ) ? $pending : array();

			$tracked_names = array_merge( $pushed, array_column( $pending, 'event_name' ) );

			// onboarding_completed: detect completed or early-exit state from the analytics blob.
			if ( ! in_array( 'onboarding_completed', $tracked_names, true ) ) {
				$onboarding_analytics = get_option( 'uaepro_onboarding_analytics', array() );
				$onboarding_done      = 'yes' === get_option( 'uaepro_onboarding_triggered' );
				$onboarding_skipped   = ! empty( $onboarding_analytics['exited_early'] ) && empty( $onboarding_analytics['completed'] );

				if ( $onboarding_done || $onboarding_skipped ) {
					UAEL_Analytics_Events::track(
						'onboarding_completed',
						$onboarding_skipped ? 'no' : 'yes',
						array( 'skipped' => (string) (int) $onboarding_skipped )
					);
				}
			}

			// first_widget_used: tracked in real-time via elementor/editor/after_save hook.

			// white_label_enabled: fires once when white label branding is first configured.
			if ( ! in_array( 'white_label_enabled', $tracked_names, true ) ) {
				if ( $this->is_white_labeled() ) {
					$install_time       = get_option( 'uae_usage_installed_time', 0 );
					$days_since_install = 0;
					if ( $install_time > 0 ) {
						$days_since_install = (int) floor( ( time() - (int) $install_time ) / DAY_IN_SECONDS );
					}
					UAEL_Analytics_Events::track(
						'white_label_enabled',
						'yes',
						array( 'days_since_install' => (string) $days_since_install )
					);
				}
			}

			// display_conditions_used: fires once when display conditions are first configured on any element.
			if ( ! in_array( 'display_conditions_used', $tracked_names, true ) ) {
				if ( get_option( 'uael_display_conditions_ever_used', false ) ) {
					$install_time       = get_option( 'uae_usage_installed_time', 0 );
					$days_since_install = 0;
					if ( $install_time > 0 ) {
						$days_since_install = (int) floor( ( time() - (int) $install_time ) / DAY_IN_SECONDS );
					}
					UAEL_Analytics_Events::track(
						'display_conditions_used',
						'yes',
						array( 'days_since_install' => (string) $days_since_install )
					);
				}
			}

			// post_duplicator_used: fires once when the post duplicator feature is first used.
			if ( ! in_array( 'post_duplicator_used', $tracked_names, true ) ) {
				if ( (int) get_option( 'uae_duplicator_count', 0 ) > 0 ) {
					$install_time       = get_option( 'uae_usage_installed_time', 0 );
					$days_since_install = 0;
					if ( $install_time > 0 ) {
						$days_since_install = (int) floor( ( time() - (int) $install_time ) / DAY_IN_SECONDS );
					}
					UAEL_Analytics_Events::track(
						'post_duplicator_used',
						'yes',
						array( 'days_since_install' => (string) $days_since_install )
					);
				}
			}
		}

		/**
		 * Check if the plugin is white labeled.
		 *
		 * @since 1.44.2
		 * @return bool
		 */
		private function is_white_labeled() {
			$branding = get_option( '_uael_white_label', array() );
			return ! empty( $branding['plugin']['name'] );
		}

		/**
		 * Get KPI tracking data for the last 2 days (excluding today).
		 *
		 * Reads pre-recorded daily snapshots from the database.
		 * Each day's data was captured on that actual day via cron.
		 *
		 * @since 1.43.1
		 * @return array KPI data organized by date.
		 */
		private function get_kpi_tracking_data() {
			$snapshots = get_option( 'uael_kpi_daily_snapshots', array() );

			if ( empty( $snapshots ) || ! is_array( $snapshots ) ) {
				return array();
			}

			$kpi_data = array();
			$today    = current_time( 'Y-m-d' );

			// Only send data for dates that have actual per-day snapshots.
			for ( $i = 1; $i <= 2; $i++ ) {
				$date = wp_date( 'Y-m-d', strtotime( $today . ' -' . $i . ' days' ) );

				if ( ! isset( $snapshots[ $date ]['numeric_values'] ) ) {
					continue;
				}

				$kpi_data[ $date ] = $snapshots[ $date ];
			}

			return $kpi_data;
		}

		/**
		 * Fetch Elementor widget usage data.
		 *
		 * @since 1.39.3
		 * @return array Widget usage data.
		 */
		private function uael_get_widgets_usage() {
				$get_widgets = get_option( 'uaepro_widgets_usage_data_option', array() );
				return $get_widgets;
		}
	}
}
new UAEL_Analytics();