events
1 month ago
class-wc-site-tracking.php
4 months ago
class-wc-tracks-client.php
5 months ago
class-wc-tracks-event.php
5 months ago
class-wc-tracks-footer-pixel.php
1 year ago
class-wc-tracks.php
5 months ago
class-wc-site-tracking.php
280 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Nosara Tracks for WooCommerce |
| 4 | * |
| 5 | * @package WooCommerce\Tracks |
| 6 | */ |
| 7 | |
| 8 | defined( 'ABSPATH' ) || exit; |
| 9 | |
| 10 | /** |
| 11 | * This class adds actions to track usage of WooCommerce. |
| 12 | */ |
| 13 | class WC_Site_Tracking { |
| 14 | /** |
| 15 | * Check if tracking is enabled. |
| 16 | * |
| 17 | * @return bool |
| 18 | */ |
| 19 | public static function is_tracking_enabled() { |
| 20 | /** |
| 21 | * Don't track users if a filter has been applied to turn it off. |
| 22 | * `woocommerce_apply_tracking` will be deprecated. Please use |
| 23 | * `woocommerce_apply_user_tracking` instead. |
| 24 | * |
| 25 | * @since 3.6.0 |
| 26 | */ |
| 27 | if ( ! apply_filters( 'woocommerce_apply_user_tracking', true ) || ! apply_filters( 'woocommerce_apply_tracking', true ) ) { |
| 28 | return false; |
| 29 | } |
| 30 | |
| 31 | // Check if tracking is actively being opted into. |
| 32 | $is_obw_opting_in = isset( $_POST['wc_tracker_checkbox'] ) && 'yes' === sanitize_text_field( $_POST['wc_tracker_checkbox'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput |
| 33 | |
| 34 | /** |
| 35 | * Don't track users who haven't opted-in to tracking or aren't in |
| 36 | * the process of opting-in. |
| 37 | */ |
| 38 | if ( 'yes' !== get_option( 'woocommerce_allow_tracking' ) && ! $is_obw_opting_in ) { |
| 39 | return false; |
| 40 | } |
| 41 | |
| 42 | if ( ! class_exists( 'WC_Tracks' ) ) { |
| 43 | return false; |
| 44 | } |
| 45 | |
| 46 | return true; |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Register scripts required to record events from javascript. |
| 51 | */ |
| 52 | public static function register_scripts() { |
| 53 | wp_register_script( 'woo-tracks', 'https://stats.wp.com/w.js', array( 'wp-hooks' ), gmdate( 'YW' ), false ); |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * Add scripts required to record events from javascript. |
| 58 | */ |
| 59 | public static function enqueue_scripts() { |
| 60 | wp_enqueue_script( 'woo-tracks' ); |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Adds the tracking function to the admin footer. |
| 65 | */ |
| 66 | public static function add_tracking_function() { |
| 67 | $user = wp_get_current_user(); |
| 68 | $server_details = WC_Tracks::get_server_details(); |
| 69 | $blog_details = WC_Tracks::get_blog_details( $user->ID ); |
| 70 | $tracks_identity = WC_Tracks_Client::get_identity( $user->ID ); |
| 71 | $role_details = WC_Tracks::get_role_details( $user ); |
| 72 | |
| 73 | $client_tracking_properties = array_merge( $server_details, $blog_details, $role_details ); |
| 74 | /** |
| 75 | * Add global tracks event properties. |
| 76 | * |
| 77 | * @since 6.5.0 |
| 78 | */ |
| 79 | $filtered_properties = apply_filters( 'woocommerce_tracks_event_properties', $client_tracking_properties, false ); |
| 80 | $environment_type = function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production'; |
| 81 | ?> |
| 82 | <!-- WooCommerce Tracks --> |
| 83 | <script type="text/javascript"> |
| 84 | window.wcTracks = window.wcTracks || {}; |
| 85 | window.wcTracks.isEnabled = <?php echo self::is_tracking_enabled() ? 'true' : 'false'; ?>; |
| 86 | window._tkq = window._tkq || []; |
| 87 | |
| 88 | <?php if ( 'anon' !== $tracks_identity['_ut'] ) { ?> |
| 89 | window._tkq.push( [ 'identifyUser', '<?php echo esc_js( $tracks_identity['_ui'] ); ?>' ] ); |
| 90 | <?php } ?> |
| 91 | window.wcTracks.validateEvent = function( eventName, props = {} ) { |
| 92 | let isValid = true; |
| 93 | if ( ! <?php echo esc_js( WC_Tracks_Event::EVENT_NAME_REGEX ); ?>.test( eventName ) ) { |
| 94 | if ( <?php echo $environment_type !== 'production' ? 'true' : 'false'; ?> ) { |
| 95 | /* eslint-disable no-console */ |
| 96 | console.error( |
| 97 | `A valid event name must be specified. The event name: "${ eventName }" is not valid.` |
| 98 | ); |
| 99 | /* eslint-enable no-console */ |
| 100 | } |
| 101 | isValid = false; |
| 102 | } |
| 103 | for ( const prop of Object.keys( props ) ) { |
| 104 | if ( ! <?php echo esc_js( WC_Tracks_Event::PROP_NAME_REGEX ); ?>.test( prop ) ) { |
| 105 | if ( <?php echo $environment_type !== 'production' ? 'true' : 'false'; ?> ) { |
| 106 | /* eslint-disable no-console */ |
| 107 | console.error( |
| 108 | `A valid prop name must be specified. The property name: "${ prop }" is not valid.` |
| 109 | ); |
| 110 | /* eslint-enable no-console */ |
| 111 | } |
| 112 | isValid = false; |
| 113 | } |
| 114 | } |
| 115 | return isValid; |
| 116 | } |
| 117 | window.wcTracks.recordEvent = function( name, properties ) { |
| 118 | if ( ! window.wcTracks.isEnabled ) { |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | const eventName = '<?php echo esc_attr( WC_Tracks::PREFIX ); ?>' + name; |
| 123 | let eventProperties = properties || {}; |
| 124 | eventProperties = { ...eventProperties, ...<?php echo json_encode( $filtered_properties, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode ?> }; |
| 125 | if ( window.wp && window.wp.hooks && window.wp.hooks.applyFilters ) { |
| 126 | eventProperties = window.wp.hooks.applyFilters( 'woocommerce_tracks_client_event_properties', eventProperties, eventName ); |
| 127 | delete( eventProperties._ui ); |
| 128 | delete( eventProperties._ut ); |
| 129 | } |
| 130 | |
| 131 | if ( ! window.wcTracks.validateEvent( eventName, eventProperties ) ) { |
| 132 | return; |
| 133 | } |
| 134 | window._tkq.push( [ 'recordEvent', eventName, eventProperties ] ); |
| 135 | } |
| 136 | </script> |
| 137 | <?php |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * Adds a function to load tracking scripts and enable them client-side on the fly. |
| 142 | * Note that this function does not update `woocommerce_allow_tracking` in the database |
| 143 | * and will not persist enabled tracking across page loads. |
| 144 | */ |
| 145 | public static function add_enable_tracking_function() { |
| 146 | global $wp_scripts; |
| 147 | |
| 148 | if ( ! isset( $wp_scripts->registered['woo-tracks'] ) ) { |
| 149 | return; |
| 150 | } |
| 151 | |
| 152 | $woo_tracks_script = $wp_scripts->registered['woo-tracks']->src; |
| 153 | |
| 154 | ?> |
| 155 | <script type="text/javascript"> |
| 156 | window.wcTracks.enable = function( callback ) { |
| 157 | window.wcTracks.isEnabled = true; |
| 158 | |
| 159 | var scriptUrl = '<?php echo esc_url( $woo_tracks_script ); ?>'; |
| 160 | var existingScript = document.querySelector( `script[src="${ scriptUrl }"]` ); |
| 161 | if ( existingScript ) { |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | var script = document.createElement('script'); |
| 166 | script.src = scriptUrl; |
| 167 | document.body.append(script); |
| 168 | |
| 169 | // Callback after scripts have loaded. |
| 170 | script.onload = function() { |
| 171 | if ( 'function' === typeof callback ) { |
| 172 | callback( true ); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | // Callback triggered if the script fails to load. |
| 177 | script.onerror = function() { |
| 178 | if ( 'function' === typeof callback ) { |
| 179 | callback( false ); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | </script> |
| 184 | <?php |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Init tracking. |
| 189 | */ |
| 190 | public static function init() { |
| 191 | // Define window.wcTracks.recordEvent in case it is enabled client-side. |
| 192 | self::register_scripts(); |
| 193 | add_filter( 'admin_footer', array( __CLASS__, 'add_tracking_function' ), 24 ); |
| 194 | |
| 195 | if ( ! self::is_tracking_enabled() ) { |
| 196 | add_filter( 'admin_footer', array( __CLASS__, 'add_enable_tracking_function' ), 24 ); |
| 197 | return; |
| 198 | } |
| 199 | |
| 200 | self::enqueue_scripts(); |
| 201 | |
| 202 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-admin-setup-wizard-tracking.php'; |
| 203 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-extensions-tracking.php'; |
| 204 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-importer-tracking.php'; |
| 205 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-products-tracking.php'; |
| 206 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-orders-tracking.php'; |
| 207 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-settings-tracking.php'; |
| 208 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-status-tracking.php'; |
| 209 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-coupons-tracking.php'; |
| 210 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-order-tracking.php'; |
| 211 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-coupon-tracking.php'; |
| 212 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-theme-tracking.php'; |
| 213 | include_once WC_ABSPATH . 'includes/tracks/events/class-wc-product-collection-block-tracking.php'; |
| 214 | |
| 215 | $tracking_classes = array( |
| 216 | 'WC_Extensions_Tracking', |
| 217 | 'WC_Importer_Tracking', |
| 218 | 'WC_Products_Tracking', |
| 219 | 'WC_Orders_Tracking', |
| 220 | 'WC_Settings_Tracking', |
| 221 | 'WC_Status_Tracking', |
| 222 | 'WC_Coupons_Tracking', |
| 223 | 'WC_Order_Tracking', |
| 224 | 'WC_Coupon_Tracking', |
| 225 | 'WC_Theme_Tracking', |
| 226 | 'WC_Product_Collection_Block_Tracking', |
| 227 | ); |
| 228 | |
| 229 | foreach ( $tracking_classes as $tracking_class ) { |
| 230 | $tracker_instance = new $tracking_class(); |
| 231 | $tracker_init_method = array( $tracker_instance, 'init' ); |
| 232 | |
| 233 | if ( is_callable( $tracker_init_method ) ) { |
| 234 | call_user_func( $tracker_init_method ); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | add_filter( 'pre_update_option_woocommerce_allow_tracking', array( __CLASS__, 'maybe_unschedule_deferred_tracks' ) ); |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * When the tracking is getting disabled, unschedules all deferred tracks. |
| 243 | * |
| 244 | * @internal |
| 245 | * @since 10.6.0 |
| 246 | * |
| 247 | * @param string $new_option_value The new, unserialized option value. |
| 248 | * @return string |
| 249 | */ |
| 250 | public static function maybe_unschedule_deferred_tracks( $new_option_value ) { |
| 251 | if ( 'yes' !== $new_option_value ) { |
| 252 | as_unschedule_all_actions( '', array(), 'woocommerce-tracks' ); |
| 253 | } |
| 254 | return $new_option_value; |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Sets a cookie for tracking purposes, but only if tracking is enabled/allowed. |
| 259 | * |
| 260 | * @internal |
| 261 | * @since 9.2.0 |
| 262 | * |
| 263 | * @param string $cookie_key The key of the cookie. |
| 264 | * @param string $cookie_value The value of the cookie. |
| 265 | * @param int $expire Expiry of the cookie. |
| 266 | * @param bool $secure Whether the cookie should be served only over https. |
| 267 | * @param bool $http_only Whether the cookie is only accessible over HTTP. |
| 268 | * |
| 269 | * @return bool If setting the cookie was attempted (will be false if tracking is not allowed). |
| 270 | */ |
| 271 | public static function set_tracking_cookie( string $cookie_key, string $cookie_value, int $expire = 0, bool $secure = false, bool $http_only = false ): bool { |
| 272 | if ( self::is_tracking_enabled() ) { |
| 273 | wc_setcookie( $cookie_key, $cookie_value, $expire, $secure, $http_only ); |
| 274 | return true; |
| 275 | } |
| 276 | |
| 277 | return false; |
| 278 | } |
| 279 | } |
| 280 |