HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/tracks.tar
class-wc-site-tracking.php000064400000017176151542343440011554 0ustar00<?php
/**
 * Nosara Tracks for WooCommerce
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce.
 */
class WC_Site_Tracking {
	/**
	 * Check if tracking is enabled.
	 *
	 * @return bool
	 */
	public static function is_tracking_enabled() {
		/**
		 * Don't track users if a filter has been applied to turn it off.
		 * `woocommerce_apply_tracking` will be deprecated. Please use
		 * `woocommerce_apply_user_tracking` instead.
		 *
		 * @since 3.6.0
		 */
		if ( ! apply_filters( 'woocommerce_apply_user_tracking', true ) || ! apply_filters( 'woocommerce_apply_tracking', true ) ) {
			return false;
		}

		// Check if tracking is actively being opted into.
		$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

		/**
		 * Don't track users who haven't opted-in to tracking or aren't in
		 * the process of opting-in.
		 */
		if ( 'yes' !== get_option( 'woocommerce_allow_tracking' ) && ! $is_obw_opting_in ) {
			return false;
		}

		if ( ! class_exists( 'WC_Tracks' ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Register scripts required to record events from javascript.
	 */
	public static function register_scripts() {
		wp_register_script( 'woo-tracks', 'https://stats.wp.com/w.js', array( 'wp-hooks' ), gmdate( 'YW' ), false );
	}

	/**
	 * Add scripts required to record events from javascript.
	 */
	public static function enqueue_scripts() {
		wp_enqueue_script( 'woo-tracks' );
	}

	/**
	 * Adds the tracking function to the admin footer.
	 */
	public static function add_tracking_function() {
		$user            = wp_get_current_user();
		$server_details  = WC_Tracks::get_server_details();
		$blog_details    = WC_Tracks::get_blog_details( $user->ID );
		$tracks_identity = WC_Tracks_Client::get_identity( $user->ID );

		$client_tracking_properties = array_merge( $server_details, $blog_details );
		/**
		 * Add global tracks event properties.
		 *
		 * @since 6.5.0
		 */
		$filtered_properties = apply_filters( 'woocommerce_tracks_event_properties', $client_tracking_properties, false );
		$environment_type    = function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production';
		?>
		<!-- WooCommerce Tracks -->
		<script type="text/javascript">
			window.wcTracks = window.wcTracks || {};
			window.wcTracks.isEnabled = <?php echo self::is_tracking_enabled() ? 'true' : 'false'; ?>;
			window._tkq = window._tkq || [];

			<?php if ( 'anon' !== $tracks_identity['_ut'] ) { ?>
			window._tkq.push( [ 'identifyUser', '<?php echo esc_js( $tracks_identity['_ui'] ); ?>' ] );
			<?php } ?>
			window.wcTracks.validateEvent = function( eventName, props = {} ) {
				let isValid = true;
				if ( ! <?php echo esc_js( WC_Tracks_Event::EVENT_NAME_REGEX ); ?>.test( eventName ) ) {
					if ( <?php echo $environment_type !== 'production' ? 'true' : 'false'; ?> ) {
						/* eslint-disable no-console */
						console.error(
							`A valid event name must be specified. The event name: "${ eventName }" is not valid.`
						);
						/* eslint-enable no-console */
					}
					isValid = false;
				}
				for ( const prop of Object.keys( props ) ) {
					if ( ! <?php echo esc_js( WC_Tracks_Event::PROP_NAME_REGEX ); ?>.test( prop ) ) {
						if ( <?php echo $environment_type !== 'production' ? 'true' : 'false'; ?> ) {
							/* eslint-disable no-console */
							console.error(
								`A valid prop name must be specified. The property name: "${ prop }" is not valid.`
							);
							/* eslint-enable no-console */
						}
						isValid = false;
					}
				}
				return isValid;
			}
			window.wcTracks.recordEvent = function( name, properties ) {
				if ( ! window.wcTracks.isEnabled ) {
					return;
				}

				const eventName = '<?php echo esc_attr( WC_Tracks::PREFIX ); ?>' + name;
				let eventProperties = properties || {};
				eventProperties = { ...eventProperties, ...<?php echo json_encode( $filtered_properties ); ?> };
				if ( window.wp && window.wp.hooks && window.wp.hooks.applyFilters ) {
					eventProperties = window.wp.hooks.applyFilters( 'woocommerce_tracks_client_event_properties', eventProperties, eventName );
					delete( eventProperties._ui );
					delete( eventProperties._ut );
				}

				if ( ! window.wcTracks.validateEvent( eventName, eventProperties ) ) {
					return;
				}
				window._tkq.push( [ 'recordEvent', eventName, eventProperties ] );
			}
		</script>
		<?php
	}

	/**
	 * Adds a function to load tracking scripts and enable them client-side on the fly.
	 * Note that this function does not update `woocommerce_allow_tracking` in the database
	 * and will not persist enabled tracking across page loads.
	 */
	public static function add_enable_tracking_function() {
		global $wp_scripts;

		if ( ! isset( $wp_scripts->registered['woo-tracks'] ) ) {
			return;
		}

		$woo_tracks_script = $wp_scripts->registered['woo-tracks']->src;

		?>
		<script type="text/javascript">
			window.wcTracks.enable = function( callback ) {
				window.wcTracks.isEnabled = true;

				var scriptUrl = '<?php echo esc_url( $woo_tracks_script ); ?>';
				var existingScript = document.querySelector( `script[src="${ scriptUrl }"]` );
				if ( existingScript ) {
					return;
				}

				var script = document.createElement('script');
				script.src = scriptUrl;
				document.body.append(script);

				// Callback after scripts have loaded.
				script.onload = function() {
					if ( 'function' === typeof callback ) {
						callback( true );
					}
				}

				// Callback triggered if the script fails to load.
				script.onerror = function() {
					if ( 'function' === typeof callback ) {
						callback( false );
					}
				}
			}
		</script>
		<?php
	}

	/**
	 * Init tracking.
	 */
	public static function init() {
		// Define window.wcTracks.recordEvent in case it is enabled client-side.
		self::register_scripts();
		add_filter( 'admin_footer', array( __CLASS__, 'add_tracking_function' ), 24 );

		if ( ! self::is_tracking_enabled() ) {
			add_filter( 'admin_footer', array( __CLASS__, 'add_enable_tracking_function' ), 24 );
			return;
		}

		self::enqueue_scripts();

		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-admin-setup-wizard-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-extensions-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-importer-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-products-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-orders-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-settings-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-status-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-coupons-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-order-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-coupon-tracking.php';
		include_once WC_ABSPATH . 'includes/tracks/events/class-wc-theme-tracking.php';

		$tracking_classes = array(
			'WC_Extensions_Tracking',
			'WC_Importer_Tracking',
			'WC_Products_Tracking',
			'WC_Orders_Tracking',
			'WC_Settings_Tracking',
			'WC_Status_Tracking',
			'WC_Coupons_Tracking',
			'WC_Order_Tracking',
			'WC_Coupon_Tracking',
			'WC_Theme_Tracking',
		);

		foreach ( $tracking_classes as $tracking_class ) {
			$tracker_instance    = new $tracking_class();
			$tracker_init_method = array( $tracker_instance, 'init' );

			if ( is_callable( $tracker_init_method ) ) {
				call_user_func( $tracker_init_method );
			}
		}
	}


}
class-wc-tracks-client.php000064400000012347151542343440011546 0ustar00<?php
/**
 * Send Tracks events on behalf of a user.
 *
 * @package WooCommerce\Tracks
 */

use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Utilities\NumberUtil;

defined( 'ABSPATH' ) || exit;

/**
 * WC_Tracks_Client class.
 */
class WC_Tracks_Client {

	/**
	 * Pixel URL.
	 */
	const PIXEL = 'https://pixel.wp.com/t.gif';

	/**
	 * Browser type.
	 */
	const BROWSER_TYPE = 'php-agent';

	/**
	 * User agent.
	 */
	const USER_AGENT_SLUG = 'tracks-client';

	/**
	 * Initialize tracks client class
	 *
	 * @return void
	 */
	public static function init() {
		// Use wp hook for setting the identity cookie to avoid headers already sent warnings.
		add_action( 'admin_init', array( __CLASS__, 'maybe_set_identity_cookie' ) );
	}

	/**
	 * Check if identity cookie is set, if not set it.
	 *
	 * @return void
	 */
	public static function maybe_set_identity_cookie() {
		// Do not set on AJAX requests.
		if ( Constants::is_true( 'DOING_AJAX' ) ) {
			return;
		}

		// Bail if cookie already set.
		if ( isset( $_COOKIE['tk_ai'] ) ) {
			return;
		}

		$user = wp_get_current_user();

		// We don't want to track user events during unit tests/CI runs.
		if ( $user instanceof WP_User && 'wptests_capabilities' === $user->cap_key ) {
			return false;
		}
		$user_id = $user->ID;
		$anon_id = get_user_meta( $user_id, '_woocommerce_tracks_anon_id', true );

		// If an id is still not found, create one and save it.
		if ( ! $anon_id ) {
			$anon_id = self::get_anon_id();
			update_user_meta( $user_id, '_woocommerce_tracks_anon_id', $anon_id );
		}

		// Don't set cookie on API requests.
		if ( ! Constants::is_true( 'REST_REQUEST' ) && ! Constants::is_true( 'XMLRPC_REQUEST' ) ) {
			wc_setcookie( 'tk_ai', $anon_id );
		}
	}

	/**
	 * Record a Tracks event
	 *
	 * @param  array $event Array of event properties.
	 * @return bool|WP_Error         True on success, WP_Error on failure.
	 */
	public static function record_event( $event ) {
		if ( ! $event instanceof WC_Tracks_Event ) {
			$event = new WC_Tracks_Event( $event );
		}

		if ( is_wp_error( $event ) ) {
			return $event;
		}

		$pixel = $event->build_pixel_url( $event );

		if ( ! $pixel ) {
			return new WP_Error( 'invalid_pixel', 'cannot generate tracks pixel for given input', 400 );
		}

		return self::record_pixel( $pixel );
	}

	/**
	 * Synchronously request the pixel.
	 *
	 * @param string $pixel pixel url and query string.
	 * @return bool Always returns true.
	 */
	public static function record_pixel( $pixel ) {
		// Add the Request Timestamp and URL terminator just before the HTTP request.
		$pixel .= '&_rt=' . self::build_timestamp() . '&_=_';

		wp_safe_remote_get(
			$pixel,
			array(
				'blocking'    => false,
				'redirection' => 2,
				'httpversion' => '1.1',
				'timeout'     => 1,
			)
		);

		return true;
	}

	/**
	 * Create a timestamp representing milliseconds since 1970-01-01
	 *
	 * @return string A string representing a timestamp.
	 */
	public static function build_timestamp() {
		$ts = NumberUtil::round( microtime( true ) * 1000 );

		return number_format( $ts, 0, '', '' );
	}

	/**
	 * Get a user's identity to send to Tracks. If Jetpack exists, default to its implementation.
	 *
	 * @param int $user_id User id.
	 * @return array Identity properties.
	 */
	public static function get_identity( $user_id ) {
		$jetpack_lib = '/tracks/client.php';

		if ( class_exists( 'Jetpack' ) && Constants::is_defined( 'JETPACK__VERSION' ) ) {
			if ( version_compare( Constants::get_constant( 'JETPACK__VERSION' ), '7.5', '<' ) ) {
				if ( file_exists( jetpack_require_lib_dir() . $jetpack_lib ) ) {
					include_once jetpack_require_lib_dir() . $jetpack_lib;
					if ( function_exists( 'jetpack_tracks_get_identity' ) ) {
						return jetpack_tracks_get_identity( $user_id );
					}
				}
			} else {
				$tracking = new Automattic\Jetpack\Tracking();
				return $tracking->tracks_get_identity( $user_id );
			}
		}

		// Start with a previously set cookie.
		$anon_id = isset( $_COOKIE['tk_ai'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['tk_ai'] ) ) : false;

		// If there is no cookie, apply a saved id.
		if ( ! $anon_id ) {
			$anon_id = get_user_meta( $user_id, '_woocommerce_tracks_anon_id', true );
		}

		// If an id is still not found, create one and save it.
		if ( ! $anon_id ) {
			$anon_id = self::get_anon_id();

			update_user_meta( $user_id, '_woocommerce_tracks_anon_id', $anon_id );
		}

		return array(
			'_ut' => 'anon',
			'_ui' => $anon_id,
		);
	}

	/**
	 * Grabs the user's anon id from cookies, or generates and sets a new one
	 *
	 * @return string An anon id for the user
	 */
	public static function get_anon_id() {
		static $anon_id = null;

		if ( ! isset( $anon_id ) ) {

			// Did the browser send us a cookie?
			if ( isset( $_COOKIE['tk_ai'] ) ) {
				$anon_id = sanitize_text_field( wp_unslash( $_COOKIE['tk_ai'] ) );
			} else {

				$binary = '';

				// Generate a new anonId and try to save it in the browser's cookies.
				// Note that base64-encoding an 18 character string generates a 24-character anon id.
				for ( $i = 0; $i < 18; ++$i ) {
					$binary .= chr( wp_rand( 0, 255 ) );
				}

				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
				$anon_id = 'woo:' . base64_encode( $binary );
			}
		}

		return $anon_id;
	}
}

WC_Tracks_Client::init();
class-wc-tracks-event.php000064400000010115151542343440011400 0ustar00<?php
/**
 * This class represents an event used to record a Tracks event
 *
 * @package WooCommerce\Tracks
 */

use Automattic\Jetpack\Constants;

defined( 'ABSPATH' ) || exit;

/**
 * WC_Tracks_Event class.
 */
#[AllowDynamicProperties]
class WC_Tracks_Event {

	/**
	 * Event name regex.
	 */
	public const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){1}([a-z0-9_]+)$/';

	/**
	 * Property name regex.
	 */
	public const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';

	/**
	 * Error message as WP_Error.
	 *
	 * @var WP_Error
	 */
	public $error;

	/**
	 * WC_Tracks_Event constructor.
	 *
	 * @param array $event Event properties.
	 */
	public function __construct( $event ) {
		$_event = self::validate_and_sanitize( $event );
		if ( is_wp_error( $_event ) ) {
			$this->error = $_event;
			return;
		}

		foreach ( $_event as $key => $value ) {
			$this->{$key} = $value;
		}
	}

	/**
	 * Record Tracks event
	 *
	 * @return bool Always returns true.
	 */
	public function record() {
		if ( wp_doing_ajax() || Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'WP_CLI' ) || wp_doing_cron() ) {
			return WC_Tracks_Client::record_event( $this );
		}

		return WC_Tracks_Footer_Pixel::record_event( $this );
	}

	/**
	 * Annotate the event with all relevant info.
	 *
	 * @param  array $event Event arguments.
	 * @return bool|WP_Error True on success, WP_Error on failure.
	 */
	public static function validate_and_sanitize( $event ) {
		$event = (object) $event;

		// Required.
		if ( ! $event->_en ) {
			return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 );
		}

		// Delete non-routable addresses otherwise geoip will discard the record entirely.
		if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) {
			unset( $event->_via_ip );
		}

		$validated = array(
			'browser_type' => WC_Tracks_Client::BROWSER_TYPE,
		);

		$_event = (object) array_merge( (array) $event, $validated );

		// If you want to block property names, do it here.
		// Make sure we have an event timestamp.
		if ( ! isset( $_event->_ts ) ) {
			$_event->_ts = WC_Tracks_Client::build_timestamp();
		}

		if ( ! self::event_name_is_valid( $_event->_en ) ) {
			return new WP_Error( 'invalid_event_name', __( 'A valid event name must be specified.', 'woocommerce' ) );
		}

		foreach ( array_keys( (array) $_event ) as $key ) {
			if ( ! self::prop_name_is_valid( $key ) && '_en' !== $key ) {
				return new WP_Error( 'invalid_prop_name', __( 'A valid prop name must be specified', 'woocommerce' ) );
			}
		}

		return $_event;
	}

	/**
	 * Build a pixel URL that will send a Tracks event when fired.
	 * On error, returns an empty string ('').
	 *
	 * @return string A pixel URL or empty string ('') if there were invalid args.
	 */
	public function build_pixel_url() {
		if ( $this->error ) {
			return '';
		}

		$args = get_object_vars( $this );

		// Request Timestamp and URL Terminator must be added just before the HTTP request or not at all.
		unset( $args['_rt'], $args['_'] );

		$validated = self::validate_and_sanitize( $args );

		if ( is_wp_error( $validated ) ) {
			return '';
		}

		return esc_url_raw( WC_Tracks_Client::PIXEL . '?' . http_build_query( $validated ) );
	}

	/**
	 * Check if event name is valid.
	 *
	 * @param string $name Event name.
	 * @return false|int
	 */
	public static function event_name_is_valid( $name ) {
		return preg_match( self::EVENT_NAME_REGEX, $name );
	}

	/**
	 * Check if a property name is valid.
	 *
	 * @param string $name Event property.
	 * @return false|int
	 */
	public static function prop_name_is_valid( $name ) {
		return preg_match( self::PROP_NAME_REGEX, $name );
	}

	/**
	 * Check event names
	 *
	 * @param object $event An event object.
	 */
	public static function scrutinize_event_names( $event ) {
		if ( ! self::event_name_is_valid( $event->_en ) ) {
			return;
		}

		$allowed_key_names = array(
			'anonId',
			'Browser_Type',
		);

		foreach ( array_keys( (array) $event ) as $key ) {
			if ( in_array( $key, $allowed_key_names, true ) ) {
				continue;
			}
			if ( ! self::prop_name_is_valid( $key ) ) {
				return;
			}
		}
	}
}
class-wc-tracks-footer-pixel.php000064400000004562151542343450012706 0ustar00<?php
/**
 * Send Tracks events on behalf of a user using pixel images in page footer.
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * WC_Tracks_Footer_Pixel class.
 */
class WC_Tracks_Footer_Pixel {
	/**
	 * Singleton instance.
	 *
	 * @var WC_Tracks_Footer_Pixel
	 */
	protected static $instance = null;

	/**
	 * Events to send to Tracks.
	 *
	 * @var array
	 */
	protected $events = array();

	/**
	 * Instantiate the singleton.
	 *
	 * @return WC_Tracks_Footer_Pixel
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new WC_Tracks_Footer_Pixel();
		}

		return self::$instance;
	}

	/**
	 * Constructor - attach hooks to the singleton instance.
	 */
	public function __construct() {
		add_action( 'admin_footer', array( $this, 'render_tracking_pixels' ) );
		add_action( 'shutdown', array( $this, 'send_tracks_requests' ) );
	}

	/**
	 * Record a Tracks event
	 *
	 * @param  array $event Array of event properties.
	 * @return bool|WP_Error True on success, WP_Error on failure.
	 */
	public static function record_event( $event ) {
		if ( ! $event instanceof WC_Tracks_Event ) {
			$event = new WC_Tracks_Event( $event );
		}

		if ( is_wp_error( $event ) ) {
			return $event;
		}

		self::instance()->add_event( $event );

		return true;
	}

	/**
	 * Add a Tracks event to the queue.
	 *
	 * @param WC_Tracks_Event $event Event to track.
	 */
	public function add_event( $event ) {
		$this->events[] = $event;
	}

	/**
	 * Add events as tracking pixels to page footer.
	 */
	public function render_tracking_pixels() {
		if ( empty( $this->events ) ) {
			return;
		}

		foreach ( $this->events as $event ) {
			$pixel = $event->build_pixel_url();

			if ( ! $pixel ) {
				continue;
			}

			echo '<img style="position: fixed;" src="', esc_url( $pixel ), '" />';
		}

		$this->events = array();
	}

	/**
	 * Fire off API calls for events that weren't converted to pixels.
	 *
	 * This handles wp_redirect().
	 */
	public function send_tracks_requests() {
		if ( empty( $this->events ) ) {
			return;
		}

		foreach ( $this->events as $event ) {
			WC_Tracks_Client::record_event( $event );
		}
	}

	/**
	 * Get all events.
	 */
	public static function get_events() {
		return self::instance()->events;
	}

	/**
	 * Clear all queued events.
	 */
	public static function clear_events() {
		self::instance()->events = array();
	}
}
class-wc-tracks.php000064400000010325151542343450010265 0ustar00<?php
/**
 * PHP Tracks Client
 *
 * @package WooCommerce\Tracks
 */

/**
 * WC_Tracks class.
 */
class WC_Tracks {

	/**
	 * Tracks event name prefix.
	 */
	const PREFIX = 'wcadmin_';

	/**
	 * Get total product counts.
	 *
	 * @return int Number of products.
	 */
	public static function get_products_count() {
		$product_counts = WC_Tracker::get_product_counts();
		return $product_counts['total'];
	}

	/**
	 * Gather blog related properties.
	 *
	 * @param int $user_id User id.
	 * @return array Blog details.
	 */
	public static function get_blog_details( $user_id ) {
		$blog_details = get_transient( 'wc_tracks_blog_details' );
		if ( false === $blog_details ) {
			$blog_details = array(
				'url'            => home_url(),
				'blog_lang'      => get_user_locale( $user_id ),
				'blog_id'        => class_exists( 'Jetpack_Options' ) ? Jetpack_Options::get_option( 'id' ) : null,
				'products_count' => self::get_products_count(),
				'wc_version'     => WC()->version,
			);
			set_transient( 'wc_tracks_blog_details', $blog_details, DAY_IN_SECONDS );
		}
		return $blog_details;
	}

	/**
	 * Gather details from the request to the server.
	 *
	 * @return array Server details.
	 */
	public static function get_server_details() {
		$data = array();

		$data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
		$data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? wc_clean( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		$data['_lg']     = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) : '';
		$data['_dr']     = isset( $_SERVER['HTTP_REFERER'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '';

		$uri         = isset( $_SERVER['REQUEST_URI'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$host        = isset( $_SERVER['HTTP_HOST'] ) ? wc_clean( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
		$data['_dl'] = isset( $_SERVER['REQUEST_SCHEME'] ) ? wc_clean( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) . '://' . $host . $uri : '';

		return $data;
	}

	/**
	 * Record an event in Tracks - this is the preferred way to record events from PHP.
	 * Note: the event request won't be made if $properties has a member called `error`.
	 *
	 * @param string $event_name The name of the event.
	 * @param array  $event_properties Custom properties to send with the event.
	 * @return bool|WP_Error True for success or WP_Error if the event pixel could not be fired.
	 */
	public static function record_event( $event_name, $event_properties = array() ) {
		/**
		 * Don't track users who don't have tracking enabled.
		 */
		if ( ! WC_Site_Tracking::is_tracking_enabled() ) {
			return false;
		}

		$user = wp_get_current_user();

		// We don't want to track user events during unit tests/CI runs.
		if ( $user instanceof WP_User && 'wptests_capabilities' === $user->cap_key ) {
			return false;
		}
		$prefixed_event_name = self::PREFIX . $event_name;
		$properties          = self::get_properties( $prefixed_event_name, $event_properties );
		$event_obj           = new WC_Tracks_Event( $properties );

		if ( is_wp_error( $event_obj->error ) ) {
			return $event_obj->error;
		}

		return $event_obj->record();
	}

	/**
	 * Get all properties for the event including filtered and identity properties.
	 *
	 * @param string $event_name Event name.
	 * @param array  $event_properties Event specific properties.
	 * @return array
	 */
	public static function get_properties( $event_name, $event_properties ) {
		/**
		 * Allow event props to be filtered to enable adding site-wide props.
		 *
		 * @since 4.1.0
		 */
		$properties = apply_filters( 'woocommerce_tracks_event_properties', $event_properties, $event_name );
		$user       = wp_get_current_user();
		$identity   = WC_Tracks_Client::get_identity( $user->ID );

		// Delete _ui and _ut protected properties.
		unset( $properties['_ui'] );
		unset( $properties['_ut'] );

		$data = $event_name
			? array(
				'_en' => $event_name,
				'_ts' => WC_Tracks_Client::build_timestamp(),
			)
			: array();

		$server_details = self::get_server_details();
		$blog_details   = self::get_blog_details( $user->ID );

		return array_merge( $properties, $data, $server_details, $identity, $blog_details );
	}
}
events/class-wc-admin-setup-wizard-tracking.php000064400000011425151542343450015630 0ustar00<?php
/**
 * WooCommerce Admin Setup Wizard Tracking
 *
 * @package WooCommerce\Tracks
 *
 * @deprecated 4.6.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of the WooCommerce Onboarding Wizard.
 */
class WC_Admin_Setup_Wizard_Tracking {
	/**
	 * Steps for the setup wizard
	 *
	 * @var array
	 */
	private $steps = array();

	/**
	 * Init tracking.
	 *
	 * @deprecated 4.6.0
	 */
	public function init() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Get the name of the current step.
	 *
	 * @deprecated 4.6.0
	 * @return string
	 */
	public function get_current_step() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
		return isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
	}

	/**
	 * Add footer scripts to OBW via woocommerce_setup_footer
	 *
	 * @deprecated 4.6.0
	 */
	public function add_footer_scripts() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Dequeue unwanted scripts from OBW footer.
	 *
	 * @deprecated 4.6.0
	 */
	public function dequeue_non_allowed_scripts() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
		global $wp_scripts;
		$allowed = array( 'woo-tracks' );

		foreach ( $wp_scripts->queue as $script ) {
			if ( in_array( $script, $allowed, true ) ) {
				continue;
			}
			wp_dequeue_script( $script );
		}
	}

	/**
	 * Track when tracking is opted into and OBW has started.
	 *
	 * @param string $option Option name.
	 * @param string $value  Option value.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_start( $option, $value ) {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track the marketing form on submit.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_ready_next_steps() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track various events when a step is saved.
	 *
	 * @deprecated 4.6.0
	 */
	public function add_step_save_events() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track store setup and store properties on save.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_store_setup() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track payment gateways selected.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_payments() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track shipping units and whether or not labels are set.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_shipping() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track recommended plugins selected for install.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_recommended() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Tracks when Jetpack is activated through the OBW.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_jetpack_activate() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Tracks when last next_steps screen is viewed in the OBW.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_next_steps() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Track skipped steps.
	 *
	 * @deprecated 4.6.0
	 */
	public function track_skip_step() {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
	}

	/**
	 * Set the OBW steps inside this class instance.
	 *
	 * @param array $steps Array of OBW steps.
	 *
	 * @deprecated 4.6.0
	 */
	public function set_obw_steps( $steps ) {
		_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '4.6.0', __( 'Onboarding is maintained in WooCommerce Admin.', 'woocommerce' ) );
		$this->steps = $steps;

		return $steps;
	}
}
events/class-wc-coupon-tracking.php000064400000002205151542343450013403 0ustar00<?php
/**
 * WooCommerce Coupon Tracking
 *
 * @package WooCommerce\Tracks
 */

/**
 * This class adds actions to track usage of a WooCommerce Coupon.
 */
class WC_Coupon_Tracking {

	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'woocommerce_coupon_object_updated_props', array( $this, 'track_coupon_updated' ), 10, 2 );
	}

	/**
	 * Send a Tracks event when a coupon is updated.
	 *
	 * @param WC_Coupon $coupon        The coupon that has been updated.
	 * @param Array     $updated_props The props of the coupon that have been updated.
	 */
	public function track_coupon_updated( $coupon, $updated_props ) {
		$properties = array(
			'discount_code'        => $coupon->get_code(),
			'free_shipping'        => $coupon->get_free_shipping(),
			'individual_use'       => $coupon->get_individual_use(),
			'exclude_sale_items'   => $coupon->get_exclude_sale_items(),
			'usage_limits_applied' => 0 < intval( $coupon->get_usage_limit() )
									|| 0 < intval( $coupon->get_usage_limit_per_user() )
									|| 0 < intval( $coupon->get_limit_usage_to_x_items() ),
		);

		WC_Tracks::record_event( 'coupon_updated', $properties );
	}
}
events/class-wc-coupons-tracking.php000064400000003472151542343450013575 0ustar00<?php
/**
 * WooCommerce Coupons Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce Orders.
 */
class WC_Coupons_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'load-edit.php', array( $this, 'tracks_coupons_events' ), 10 );
	}

	/**
	 * Add a listener on the "Apply" button to track bulk actions.
	 */
	public function tracks_coupons_bulk_actions() {
		wc_enqueue_js(
			"
			function onApplyBulkActions( event ) {
				var id = event.data.id;
				var action = $( '#' + id ).val();
				
				if ( action && '-1' !== action ) {
					window.wcTracks.recordEvent( 'coupons_view_bulk_action', {
						action: action
					} );
				}
			}
			$( '#doaction' ).on( 'click', { id: 'bulk-action-selector-top' }, onApplyBulkActions );
			$( '#doaction2' ).on( 'click', { id: 'bulk-action-selector-bottom' }, onApplyBulkActions );
		"
		);
	}

	/**
	 * Track page view events.
	 */
	public function tracks_coupons_events() {
		if ( isset( $_GET['post_type'] ) && 'shop_coupon' === $_GET['post_type'] ) {

			$this->tracks_coupons_bulk_actions();

			WC_Tracks::record_event(
				'coupons_view',
				array(
					'status' => isset( $_GET['post_status'] ) ? sanitize_text_field( wp_unslash( $_GET['post_status'] ) ) : 'all',
				)
			);

			if ( isset( $_GET['filter_action'] ) && 'Filter' === sanitize_text_field( wp_unslash( $_GET['filter_action'] ) ) && isset( $_GET['coupon_type'] ) ) {
				WC_Tracks::record_event(
					'coupons_filter',
					array(
						'filter' => 'coupon_type',
						'value'  => sanitize_text_field( wp_unslash( $_GET['coupon_type'] ) ),
					)
				);
			}

			if ( isset( $_GET['s'] ) && 0 < strlen( sanitize_text_field( wp_unslash( $_GET['s'] ) ) ) ) {
				WC_Tracks::record_event( 'coupons_search' );
			}
		}
	}
}
events/class-wc-extensions-tracking.php000064400000007652151542343450014312 0ustar00<?php
/**
 * WooCommerce Extensions Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of the WooCommerce Extensions page.
 */
class WC_Extensions_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'load-woocommerce_page_wc-addons', array( $this, 'track_extensions_page' ) );
		add_action( 'woocommerce_helper_connect_start', array( $this, 'track_helper_connection_start' ) );
		add_action( 'woocommerce_helper_denied', array( $this, 'track_helper_connection_cancelled' ) );
		add_action( 'woocommerce_helper_connected', array( $this, 'track_helper_connection_complete' ) );
		add_action( 'woocommerce_helper_disconnected', array( $this, 'track_helper_disconnected' ) );
		add_action( 'woocommerce_helper_subscriptions_refresh', array( $this, 'track_helper_subscriptions_refresh' ) );
		add_action( 'woocommerce_addon_installed', array( $this, 'track_addon_install' ), 10, 2 );
		add_action( 'woocommerce_page_wc-addons_connection_error', array( $this, 'track_extensions_page_connection_error' ), 10, 1 );
	}

	/**
	 * Send a Tracks event when an Extensions page is viewed.
	 */
	public function track_extensions_page() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		$properties = array(
			'section' => empty( $_REQUEST['section'] ) ? '_featured' : wc_clean( wp_unslash( $_REQUEST['section'] ) ),
		);

		$event      = 'extensions_view';
		if ( 'helper' === $properties['section'] ) {
			$event = 'subscriptions_view';
		}

		if ( ! empty( $_REQUEST['search'] ) ) {
			$event                     = 'extensions_view_search';
			$properties['search_term'] = wc_clean( wp_unslash( $_REQUEST['search'] ) );
		}
		// phpcs:enable

		WC_Tracks::record_event( $event, $properties );
	}

	/**
	 * Send a Tracks event when the Extensions page gets a bad response or no response
	 * from the WCCOM extensions API.
	 *
	 * @param string $error
	 */
	public function track_extensions_page_connection_error( string $error = '' ) {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		$properties = array(
			'section' => empty( $_REQUEST['section'] ) ? '_featured' : wc_clean( wp_unslash( $_REQUEST['section'] ) ),
		);

		if ( ! empty( $_REQUEST['search'] ) ) {
			$properties['search_term'] = wc_clean( wp_unslash( $_REQUEST['search'] ) );
		}
		// phpcs:enable

		if ( ! empty( $error ) ) {
			$properties['error_data'] = $error;
		}
		WC_Tracks::record_event( 'extensions_view_connection_error', $properties );
	}

	/**
	 * Send a Tracks even when a Helper connection process is initiated.
	 */
	public function track_helper_connection_start() {
		WC_Tracks::record_event( 'extensions_subscriptions_connect' );
	}

	/**
	 * Send a Tracks even when a Helper connection process is cancelled.
	 */
	public function track_helper_connection_cancelled() {
		WC_Tracks::record_event( 'extensions_subscriptions_cancelled' );
	}

	/**
	 * Send a Tracks even when a Helper connection process completed successfully.
	 */
	public function track_helper_connection_complete() {
		WC_Tracks::record_event( 'extensions_subscriptions_connected' );
	}

	/**
	 * Send a Tracks even when a Helper has been disconnected.
	 */
	public function track_helper_disconnected() {
		WC_Tracks::record_event( 'extensions_subscriptions_disconnect' );
	}

	/**
	 * Send a Tracks even when Helper subscriptions are refreshed.
	 */
	public function track_helper_subscriptions_refresh() {
		WC_Tracks::record_event( 'extensions_subscriptions_update' );
	}

	/**
	 * Send a Tracks event when addon is installed via the Extensions page.
	 *
	 * @param string $addon_id Addon slug.
	 * @param string $section  Extensions tab.
	 */
	public function track_addon_install( $addon_id, $section ) {
		$properties = array(
			'context' => 'extensions',
			'section' => $section,
		);

		if ( 'woocommerce-payments' === $addon_id ) {
			WC_Tracks::record_event( 'woocommerce_payments_install', $properties );
		}
	}
}
events/class-wc-importer-tracking.php000064400000004571151542343450013751 0ustar00<?php
/**
 * WooCommerce Import Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce Imports.
 */
class WC_Importer_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'product_page_product_importer', array( $this, 'track_product_importer' ) );
	}

	/**
	 * Route product importer action to the right callback.
	 *
	 * @return void
	 */
	public function track_product_importer() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_REQUEST['step'] ) ) {
			return;
		}

		if ( 'import' === $_REQUEST['step'] ) {
			return $this->track_product_importer_start();
		}

		if ( 'done' === $_REQUEST['step'] ) {
			return $this->track_product_importer_complete();
		}
		// phpcs:enable
	}

	/**
	 * Send a Tracks event when the product importer is started.
	 *
	 * @return void
	 */
	public function track_product_importer_start() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_REQUEST['file'] ) || ! isset( $_REQUEST['_wpnonce'] ) ) {
			return;
		}

		$properties = array(
			'update_existing' => isset( $_REQUEST['update_existing'] ) ? (bool) $_REQUEST['update_existing'] : false,
			'delimiter'       => empty( $_REQUEST['delimiter'] ) ? ',' : wc_clean( wp_unslash( $_REQUEST['delimiter'] ) ),
		);
		// phpcs:enable

		WC_Tracks::record_event( 'product_import_start', $properties );
	}

	/**
	 * Send a Tracks event when the product importer has finished.
	 *
	 * @return void
	 */
	public function track_product_importer_complete() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_REQUEST['nonce'] ) ) {
			return;
		}

		$properties = array(
			'imported'            => isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 0,
			'imported_variations' => isset( $_GET['products-imported-variations'] ) ? absint( $_GET['products-imported-variations'] ) : 0,
			'updated'             => isset( $_GET['products-updated'] ) ? absint( $_GET['products-updated'] ) : 0,
			'failed'              => isset( $_GET['products-failed'] ) ? absint( $_GET['products-failed'] ) : 0,
			'skipped'             => isset( $_GET['products-skipped'] ) ? absint( $_GET['products-skipped'] ) : 0,
		);
		// phpcs:enable

		WC_Tracks::record_event( 'product_import_complete', $properties );
	}
}
events/class-wc-order-tracking.php000064400000001611151542343450013213 0ustar00<?php
/**
 * WooCommerce Order Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of a WooCommerce Order.
 */
class WC_Order_Tracking {

	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'woocommerce_admin_order_data_after_order_details', array( $this, 'track_order_viewed' ) );
	}

	/**
	 * Send a Tracks event when an order is viewed.
	 *
	 * @param WC_Order $order Order.
	 */
	public function track_order_viewed( $order ) {
		if ( ! $order instanceof WC_Order || ! $order->get_id() ) {
			return;
		}
		$properties = array(
			'current_status' => $order->get_status(),
			'date_created'   => $order->get_date_created() ? $order->get_date_created()->format( DateTime::ATOM ) : '',
			'payment_method' => $order->get_payment_method(),
		);

		WC_Tracks::record_event( 'single_order_view', $properties );
	}
}

events/class-wc-orders-tracking.php000064400000015225151542343450013404 0ustar00<?php
/**
 * WooCommerce Orders Tracking
 *
 * @package WooCommerce\Tracks
 */

use Automattic\WooCommerce\Utilities\OrderUtil;
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce Orders.
 */
class WC_Orders_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'woocommerce_order_status_changed', array( $this, 'track_order_status_change' ), 10, 3 );
		add_action( 'load-edit.php', array( $this, 'track_orders_view' ), 10 );
		add_action( 'pre_post_update', array( $this, 'track_created_date_change' ), 10 );
		// WC_Meta_Box_Order_Actions::save() hooks in at priority 50.
		add_action( 'woocommerce_process_shop_order_meta', array( $this, 'track_order_action' ), 51 );
		add_action( 'load-post-new.php', array( $this, 'track_add_order_from_edit' ), 10 );
		add_filter( 'woocommerce_shop_order_search_results', array( $this, 'track_order_search' ), 10, 3 );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_order_tracking_scripts' ) );
	}

	/**
	 * Send a track event when on the Order Listing page, and search results are being displayed.
	 *
	 * @param array  $order_ids Array of order_ids that are matches for the search.
	 * @param string $term The string that was used in the search.
	 * @param array  $search_fields Fields that were used in the original search.
	 */
	public function track_order_search( $order_ids, $term, $search_fields ) {
		// Since `woocommerce_shop_order_search_results` can run in the front-end context, exit if get_current_screen isn't defined.
		if ( ! function_exists( 'get_current_screen' ) ) {
			return $order_ids;
		}

		$screen = get_current_screen();

		// We only want to record this track when the filter is executed on the order listing page.
		if ( 'edit-shop_order' === $screen->id ) {
			// we are on the order listing page, and query results are being shown.
			WC_Tracks::record_event( 'orders_view_search' );
		}

		return $order_ids;
	}

	/**
	 * Send a Tracks event when the Orders page is viewed.
	 */
	public function track_orders_view() {
		if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

			// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
			$properties = array(
				'status' => isset( $_GET['post_status'] ) ? sanitize_text_field( $_GET['post_status'] ) : 'all',
			);
			// phpcs:enable

			WC_Tracks::record_event( 'orders_view', $properties );
		}
	}

	/**
	 * Send a Tracks event when an order status is changed.
	 *
	 * @param int    $id Order id.
	 * @param string $previous_status the old WooCommerce order status.
	 * @param string $next_status the new WooCommerce order status.
	 */
	public function track_order_status_change( $id, $previous_status, $next_status ) {
		$order = wc_get_order( $id );

		$properties = array(
			'order_id'        => $id,
			'next_status'     => $next_status,
			'previous_status' => $previous_status,
			'date_created'    => $order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d' ) : '',
			'payment_method'  => $order->get_payment_method(),
			'order_total'     => $order->get_total(),
		);

		WC_Tracks::record_event( 'orders_edit_status_change', $properties );
	}

	/**
	 * Send a Tracks event when an order date is changed.
	 *
	 * @param int $id Order id.
	 */
	public function track_created_date_change( $id ) {
		if ( ! OrderUtil::is_order( $id ) ) {
			return;
		}

		if ( 'auto-draft' === get_post_status( $id ) ) {
			return;
		}

		$order        = wc_get_order( $id );
		$date_created = $order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d H:i:s' ) : '';
		// phpcs:disable WordPress.Security.NonceVerification
		$new_date = sprintf(
			'%s %2d:%2d:%2d',
			isset( $_POST['order_date'] ) ? wc_clean( wp_unslash( $_POST['order_date'] ) ) : '',
			isset( $_POST['order_date_hour'] ) ? wc_clean( wp_unslash( $_POST['order_date_hour'] ) ) : '',
			isset( $_POST['order_date_minute'] ) ? wc_clean( wp_unslash( $_POST['order_date_minute'] ) ) : '',
			isset( $_POST['order_date_second'] ) ? wc_clean( wp_unslash( $_POST['order_date_second'] ) ) : ''
		);
		// phpcs:enable

		if ( $new_date !== $date_created ) {
			$properties = array(
				'order_id' => $id,
				'status'   => $order->get_status(),
			);

			WC_Tracks::record_event( 'order_edit_date_created', $properties );
		}
	}

	/**
	 * Track order actions taken.
	 *
	 * @param int $order_id Order ID.
	 */
	public function track_order_action( $order_id ) {
		// phpcs:disable WordPress.Security.NonceVerification
		if ( ! empty( $_POST['wc_order_action'] ) ) {
			$order      = wc_get_order( $order_id );
			$action     = wc_clean( wp_unslash( $_POST['wc_order_action'] ) );
			$properties = array(
				'order_id' => $order_id,
				'status'   => $order->get_status(),
				'action'   => $action,
			);

			WC_Tracks::record_event( 'order_edit_order_action', $properties );
		}
		// phpcs:enable
	}

	/**
	 * Track "add order" button on the Edit Order screen.
	 */
	public function track_add_order_from_edit() {
		// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) {
			$referer = wp_get_referer();

			if ( $referer ) {
				$referring_page = wp_parse_url( $referer );
				$referring_args = array();
				$post_edit_page = wp_parse_url( admin_url( 'post.php' ) );

				if ( ! empty( $referring_page['query'] ) ) {
					parse_str( $referring_page['query'], $referring_args );
				}

				// Determine if we arrived from an Order Edit screen.
				if (
					$post_edit_page['path'] === $referring_page['path'] &&
					isset( $referring_args['action'] ) &&
					'edit' === $referring_args['action'] &&
					isset( $referring_args['post'] ) &&
					'shop_order' === OrderUtil::get_order_type( $referring_args['post'] )
				) {
					WC_Tracks::record_event( 'order_edit_add_order' );
				}
			}
		}
	}

	/**
	 * Adds the tracking scripts for product setting pages.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_order_tracking_scripts( $hook ) {
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) ||
			( 'post.php' === $hook && isset( $_GET['post'] ) && 'shop_order' === get_post_type( intval( $_GET['post'] ) ) )
		) {
			WCAdminAssets::register_script( 'wp-admin-scripts', 'order-tracking', false );
		}
		// phpcs:enable
	}
}
events/class-wc-products-tracking.php000064400000044250151542343450013751 0ustar00<?php
/**
 * WooCommerce Import Tracking
 *
 * @package WooCommerce\Tracks
 */

use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;

defined( 'ABSPATH' ) || exit;

require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';

/**
 * This class adds actions to track usage of WooCommerce Products.
 */
class WC_Products_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'load-edit.php', array( $this, 'track_products_view' ), 10 );
		add_action( 'load-edit-tags.php', array( $this, 'track_categories_and_tags_view' ), 10, 2 );
		add_action( 'edit_post', array( $this, 'track_product_updated' ), 10, 2 );
		add_action( 'wp_after_insert_post', array( $this, 'track_product_published' ), 10, 4 );
		add_action( 'created_product_cat', array( $this, 'track_product_category_created' ) );
		add_action( 'edited_product_cat', array( $this, 'track_product_category_updated' ) );
		add_action( 'add_meta_boxes_product', array( $this, 'track_product_updated_client_side' ), 10 );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_product_tracking_scripts' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_product_import_scripts' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_attribute_tracking_scripts' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_tag_tracking_scripts' ) );
	}

	/**
	 * Send a Tracks event when the Products page is viewed.
	 */
	public function track_products_view() {
		// We only record Tracks event when no `_wp_http_referer` query arg is set, since
		// when searching, the request gets sent from the browser twice,
		// once with the `_wp_http_referer` and once without it.
		//
		// Otherwise, we would double-record the view and search events.

		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			isset( $_GET['post_type'] )
			&& 'product' === wp_unslash( $_GET['post_type'] )
			&& ! isset( $_GET['_wp_http_referer'] )
		) {
			// phpcs:enable

			WC_Tracks::record_event( 'products_view' );

			// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
			if (
				isset( $_GET['s'] )
				&& 0 < strlen( sanitize_text_field( wp_unslash( $_GET['s'] ) ) )
			) {
				// phpcs:enable

				WC_Tracks::record_event( 'products_search' );
			}
		}
	}

	/**
	 * Send a Tracks event when the Products Categories and Tags page is viewed.
	 */
	public function track_categories_and_tags_view() {
		// We only record Tracks event when no `_wp_http_referer` query arg is set, since
		// when searching, the request gets sent from the browser twice,
		// once with the `_wp_http_referer` and once without it.
		//
		// Otherwise, we would double-record the view and search events.

		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			isset( $_GET['post_type'] )
			&& 'product' === wp_unslash( $_GET['post_type'] )
			&& isset( $_GET['taxonomy'] )
			&& ! isset( $_GET['_wp_http_referer'] )
		) {
			$taxonomy = wp_unslash( $_GET['taxonomy'] );
			// phpcs:enable

			if ( 'product_cat' === $taxonomy ) {
				WC_Tracks::record_event( 'categories_view' );
			} elseif ( 'product_tag' === $taxonomy ) {
				WC_Tracks::record_event( 'tags_view' );
			}

			// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
			if (
				isset( $_GET['s'] )
				&& 0 < strlen( sanitize_text_field( wp_unslash( $_GET['s'] ) ) )
			) {
				// phpcs:enable

				if ( 'product_cat' === $taxonomy ) {
					WC_Tracks::record_event( 'categories_search' );
				} elseif ( 'product_tag' === $taxonomy ) {
					WC_Tracks::record_event( 'tags_search' );
				}
			}
		}
	}

	/**
	 * Send a Tracks event when a product is updated.
	 *
	 * @param int    $product_id Product id.
	 * @param object $post       WordPress post.
	 */
	public function track_product_updated( $product_id, $post ) {
		if ( 'product' !== $post->post_type ) {
			return;
		}

		$properties = array(
			'product_id' => $product_id,
		);

		WC_Tracks::record_event( 'product_edit', $properties );
	}

	/**
	 * Track the Update button being clicked on the client side.
	 * This is needed because `track_product_updated` (using the `edit_post`
	 * hook) is called in response to a number of other triggers.
	 *
	 * @param WP_Post $post The post, not used.
	 */
	public function track_product_updated_client_side( $post ) {
		wc_enqueue_js(
			"
			if ( $( 'h1.wp-heading-inline' ).text().trim() === '" . __( 'Edit product', 'woocommerce' ) . "') {
				var initialStockValue = $( '#_stock' ).val();
				var isBlockEditor = false;
				var child_element = '#publish';

				if ( $( '.block-editor' ).length !== 0 && $( '.block-editor' )[0] ) {
	    			isBlockEditor = true;
				}

				if ( isBlockEditor ) {
					child_element = '.editor-post-publish-button';
				}

				$( '#wpwrap' ).on( 'click', child_element, function() {
					var description_value  = '';
					var tagsText = '';
					var currentStockValue = $( '#_stock' ).val();

					function getProductTypeOptions() {
						const productTypeOptionsCheckboxes = $( 'input[type=\"checkbox\"][data-product-type-option-id]' );
						const productTypeOptions = productTypeOptionsCheckboxes.map( function() {
							return {
								id: $( this ).data( 'product-type-option-id' ),
								isEnabled: $( this ).is( ':checked' ),
							};
						} ).get();
						return productTypeOptions;
					}

					function getProductTypeOptionsString( productTypeOptions ) {
						return productTypeOptions
							.filter( productTypeOption => productTypeOption.isEnabled )
							.map( productTypeOption => productTypeOption.id )
							.join( ', ' );
					}

					const productTypeOptions = getProductTypeOptions();
					const productTypeOptionsString = getProductTypeOptionsString( productTypeOptions );

					if ( ! isBlockEditor ) {
						tagsText          = $( '[name=\"tax_input[product_tag]\"]' ).val();
						if ( $( '#content' ).is( ':visible' ) ) {
							description_value = $( '#content' ).val();
						} else if ( typeof tinymce === 'object' && tinymce.get( 'content' ) ) {
							description_value = tinymce.get( 'content' ).getContent();
						}
					} else {
						description_value  = $( '.block-editor-rich-text__editable' ).text();
					}

					// We can't just check the number of '.woocommerce_attribute' elements because
					// there might be empty ones, which get stripped out when saved. So, we'll check
					// whether the name and values have been filled out.
					var numberOfAttributes = $( '.woocommerce_attribute' ).filter( function () {
						var attributeElement = $( this );
						var attributeName = attributeElement.find( 'input.attribute_name' ).val();
						var attributeValues = attributeElement.find( 'textarea[name^=\"attribute_values\"]' ).val();

						return attributeName !== '' && attributeValues !== '';
					} ).length;

					var properties = {
						attributes:				     numberOfAttributes,
						categories:				     $( '[name=\"tax_input[product_cat][]\"]:checked' ).length,
						cross_sells:			     $( '#crosssell_ids option' ).length ? 'Yes' : 'No',
						description:			     description_value.trim() !== '' ? 'Yes' : 'No',
						enable_reviews:			     $( '#comment_status' ).is( ':checked' ) ? 'Yes' : 'No',
						is_virtual:				     $( '#_virtual' ).is( ':checked' ) ? 'Yes' : 'No',
						is_block_editor:		     isBlockEditor,
						is_downloadable:		     $( '#_downloadable' ).is( ':checked' ) ? 'Yes' : 'No',
						manage_stock:			     $( '#_manage_stock' ).is( ':checked' ) ? 'Yes' : 'No',
						menu_order:				     parseInt( $( '#menu_order' ).val(), 10 ) !== 0 ? 'Yes' : 'No',
						product_gallery:		     $( '#product_images_container .product_images > li' ).length,
						product_image:			     $( '#_thumbnail_id' ).val() > 0 ? 'Yes' : 'No',
						product_type:			     $( '#product-type' ).val(),
						product_type_options_string: productTypeOptionsString,
						purchase_note:			     $( '#_purchase_note' ).val().length ? 'yes' : 'no',
						sale_price:				     $( '#_sale_price' ).val() ? 'yes' : 'no',
						short_description:		     $( '#excerpt' ).val().length ? 'yes' : 'no',
						stock_quantity_update:	     ( initialStockValue != currentStockValue ) ? 'Yes' : 'No',
						tags:					     tagsText.length > 0 ? tagsText.split( ',' ).length : 0,
						upsells:				     $( '#upsell_ids option' ).length ? 'Yes' : 'No',
						weight:					     $( '#_weight' ).val() ? 'Yes' : 'No',
					};
					window.wcTracks.recordEvent( 'product_update', properties );
				} );
			}
			"
		);
	}

	/**
	 * Get the IDs of the possible product type options.
	 *
	 * @return array
	 */
	private static function get_possible_product_type_options_ids() {
		$product_type_options_ids =
			array_values(
				array_map(
					function ( $product_type_option ) {
						return $product_type_option['id'];
					},
					/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
					apply_filters(
						'product_type_options',
						wc_get_default_product_type_options(),
					)
					/* phpcs: enable */
				)
			);

		return $product_type_options_ids;
	}

	/**
	 * Get the product type options for a product.
	 *
	 * @param int $post_id The ID of the product.
	 *
	 * @return array
	 */
	private static function get_product_type_options( $post_id ) {
		$possible_product_type_options_ids = self::get_possible_product_type_options_ids();
		$post_meta                         = get_post_meta( $post_id );
		$product_type_options              = array();

		foreach ( $possible_product_type_options_ids as $product_type_option_id ) {
			$product_type_options[ $product_type_option_id ] = isset( $post_meta[ $product_type_option_id ] ) ? $post_meta[ $product_type_option_id ][0] : 'no';
		}

		return $product_type_options;
	}

	/**
	 * Get a comma-separated string of the product type options that are enabled.
	 *
	 * @param array $product_type_options The product type options.
	 *
	 * @return string
	 */
	private static function get_product_type_options_string( $product_type_options ) {
		return implode(
			', ',
			array_keys(
				array_filter(
					$product_type_options,
					function ( $is_enabled ) {
						return 'yes' === $is_enabled;
					}
				)
			)
		);
	}

	/**
	 * Send a Tracks event when a product is published.
	 *
	 * @param int          $post_id     Post ID.
	 * @param WP_Post      $post        Post object.
	 * @param bool         $update      Whether this is an existing post being updated.
	 * @param null|WP_Post $post_before Null for new posts, the WP_Post object prior
	 *                                  to the update for updated posts.
	 */
	public function track_product_published( $post_id, $post, $update, $post_before ) {
		if (
			'product' !== $post->post_type ||
			'publish' !== $post->post_status ||
			( $post_before && 'publish' === $post_before->post_status )
		) {
			return;
		}

		$product = wc_get_product( $post_id );

		$product_type_options        = self::get_product_type_options( $post_id );
		$product_type_options_string = self::get_product_type_options_string( $product_type_options );

		$properties = array(
			'attributes'           => count( $product->get_attributes() ),
			'categories'           => count( $product->get_category_ids() ),
			'cross_sells'          => ! empty( $product->get_cross_sell_ids() ) ? 'yes' : 'no',
			'description'          => $product->get_description() ? 'yes' : 'no',
			'dimensions'           => wc_format_dimensions( $product->get_dimensions( false ) ) !== 'N/A' ? 'yes' : 'no',
			'enable_reviews'       => $product->get_reviews_allowed() ? 'yes' : 'no',
			'is_downloadable'      => $product->is_downloadable() ? 'yes' : 'no',
			'is_virtual'           => $product->is_virtual() ? 'yes' : 'no',
			'manage_stock'         => $product->get_manage_stock() ? 'yes' : 'no',
			'menu_order'           => $product->get_menu_order() ? 'yes' : 'no',
			'product_id'           => $post_id,
			'product_gallery'      => count( $product->get_gallery_image_ids() ),
			'product_image'        => $product->get_image_id() ? 'yes' : 'no',
			'product_type'         => $product->get_type(),
			'product_type_options' => $product_type_options_string,
			'purchase_note'        => $product->get_purchase_note() ? 'yes' : 'no',
			'sale_price'           => $product->get_sale_price() ? 'yes' : 'no',
			'source'               => apply_filters( 'woocommerce_product_source', '' ),
			'short_description'    => $product->get_short_description() ? 'yes' : 'no',
			'tags'                 => count( $product->get_tag_ids() ),
			'upsells'              => ! empty( $product->get_upsell_ids() ) ? 'yes' : 'no',
			'weight'               => $product->get_weight() ? 'yes' : 'no',
		);

		WC_Tracks::record_event( 'product_add_publish', $properties );
	}

	/**
	 * Send a Tracks event when a product category is created.
	 *
	 * @param int $category_id Category ID.
	 */
	public function track_product_category_created( $category_id ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		// Only track category creation from the edit product screen or the
		// category management screen (which both occur via AJAX).
		if (
			! Constants::is_defined( 'DOING_AJAX' ) ||
			empty( $_POST['action'] ) ||
			(
				// Product Categories screen.
				'add-tag' !== $_POST['action'] &&
				// Edit Product screen.
				'add-product_cat' !== $_POST['action']
			)
		) {
			return;
		}

		$category        = get_term( $category_id, 'product_cat' );
		$parent_category = $category->parent > 0 ? 'Other' : 'None';
		if ( $category->parent > 0 ) {
			$parent = get_term( $category_id, 'product_cat' );
			if ( 'uncategorized' === $parent->name ) {
				$parent_category = 'Uncategorized';
			}
		}
		$properties = array(
			'category_id'     => $category_id,
			'parent_id'       => $category->parent,
			'parent_category' => $parent_category,
			'page'            => ( 'add-tag' === $_POST['action'] ) ? 'categories' : 'product',
			'display_type'    => isset( $_POST['display_type'] ) ? wp_unslash( $_POST['display_type'] ) : '',
			'image'           => isset( $_POST['product_cat_thumbnail_id'] ) && '' !== $_POST['product_cat_thumbnail_id'] ? 'Yes' : 'No',
		);
		// phpcs:enable

		WC_Tracks::record_event( 'product_category_add', $properties );
	}

	/**
	 * Send a Tracks event when a product category is updated.
	 *
	 * @param int $category_id Category ID.
	 */
	public function track_product_category_updated( $category_id ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing
		// Only track category creation from the edit product screen or the
		// category management screen (which both occur via AJAX).
		if (
			empty( $_POST['action'] ) ||
			( 'editedtag' !== $_POST['action'] && 'inline-save-tax' !== $_POST['action'] )
		) {
			return;
		}
		// phpcs:enable

		WC_Tracks::record_event( 'product_category_update' );
	}

	/**
	 * Adds the tracking scripts for product filtering actions.
	 *
	 * @param string $hook Hook of the current page.
	 * @return string|boolean
	 */
	protected function get_product_screen( $hook ) {
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			'edit.php' === $hook &&
			isset( $_GET['post_type'] ) &&
			'product' === wp_unslash( $_GET['post_type'] )
		) {
			return 'list';
		}

		if (
			'post-new.php' === $hook &&
			isset( $_GET['post_type'] ) &&
			'product' === wp_unslash( $_GET['post_type'] )
		) {
			return 'new';
		}

		if (
			'post.php' === $hook &&
			isset( $_GET['post'] ) &&
			'product' === get_post_type( intval( $_GET['post'] ) )
		) {
			return 'edit';
		}

		if ( 'product_page_product_importer' === $hook ) {
			return 'import';
		}

		// phpcs:enable

		return false;
	}

	/**
	 * Adds the tracking scripts for product filtering actions.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_product_tracking_scripts( $hook ) {
		$product_screen = $this->get_product_screen( $hook );
		if ( ! $product_screen ) {
			return;
		}

		WCAdminAssets::register_script( 'wp-admin-scripts', 'product-tracking', false );
		wp_localize_script(
			'wc-admin-product-tracking',
			'productScreen',
			array(
				'name' => $product_screen,
			)
		);
	}

	/**
	 * Adds the tracking scripts for product setting pages.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_product_import_scripts( $hook ) {
		$product_screen = $this->get_product_screen( $hook );

		if ( 'import' !== $product_screen ) {
			return;
		}

		WCAdminAssets::register_script( 'wp-admin-scripts', 'product-import-tracking', false );

	}

	/**
	 * Adds the tracking scripts for product attributes filtering actions.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_attribute_tracking_scripts( $hook ) {
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			'product_page_product_attributes' !== $hook ||
			! isset( $_GET['page'] ) ||
			'product_attributes' !== wp_unslash( $_GET['page'] )
		) {
			return;
		}
		// phpcs:enable

		WCAdminAssets::register_script( 'wp-admin-scripts', 'attributes-tracking', false );
	}

	/**
	 * Adds the tracking scripts for tags and categories filtering actions.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_tag_tracking_scripts( $hook ) {
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			'edit-tags.php' !== $hook ||
			! isset( $_GET['post_type'] ) ||
			'product' !== wp_unslash( $_GET['post_type'] )
		) {
			return;
		}
		// phpcs:enable

		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if (
			isset( $_GET['taxonomy'] ) &&
			'product_tag' === wp_unslash( $_GET['taxonomy'] )
		) {
			WCAdminAssets::register_script( 'wp-admin-scripts', 'tags-tracking', false );
			return;
		}

		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if (
			isset( $_GET['taxonomy'] ) &&
			'product_cat' === wp_unslash( $_GET['taxonomy'] )
		) {
			WCAdminAssets::register_script( 'wp-admin-scripts', 'category-tracking', false );
			return;
		}
		WCAdminAssets::register_script( 'wp-admin-scripts', 'add-term-tracking', false );
	}
}
events/class-wc-settings-tracking.php000064400000015517151542343450013752 0ustar00<?php
/**
 * WooCommerce Settings Tracking
 *
 * @package WooCommerce\Tracks
 */

use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce Settings.
 */
class WC_Settings_Tracking {

	/**
	 * List of allowed WooCommerce settings to potentially track updates for.
	 *
	 * @var array
	 */
	protected $allowed_options = array();

	/**
	 * WooCommerce settings that have been updated (and will be tracked).
	 *
	 * @var array
	 */
	protected $updated_options = array();

	/**
	 * List of option names that are dropdown menus.
	 *
	 * @var array
	 */
	protected $dropdown_menu_options = array();


	/**
	 * List of options that have been modified.
	 *
	 * @var array
	 */
	protected $modified_options = array();

	/**
	 * List of options that have been deleted.
	 *
	 * @var array
	 */
	protected $deleted_options = array();

	/**
	 * List of options that have been added.
	 *
	 * @var array
	 */
	protected $added_options = array();


	/**
	 * Toggled options.
	 *
	 * @var array
	 */
	protected $toggled_options = array(
		'enabled'  => array(),
		'disabled' => array(),
	);

	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'woocommerce_settings_page_init', array( $this, 'track_settings_page_view' ) );
		add_action( 'woocommerce_update_option', array( $this, 'add_option_to_list' ) );
		add_action( 'woocommerce_update_non_option_setting', array( $this, 'add_option_to_list_and_track_setting_change' ) );
		add_action( 'woocommerce_update_options', array( $this, 'send_settings_change_event' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_settings_tracking_scripts' ) );
	}

	/**
	 * Adds the option to the allowed and updated options directly.
	 * Currently used for settings that don't use update_option.
	 *
	 * @param array $option WooCommerce option that should be updated.
	 */
	public function add_option_to_list_and_track_setting_change( $option ) {
		if ( ! in_array( $option['id'], $this->allowed_options, true ) ) {
			$this->allowed_options[] = $option['id'];
		}
		if ( isset( $option['action'] ) ) {
			if ( 'add' === $option['action'] ) {
				$this->added_options[] = $option['id'];
			} elseif ( 'delete' === $option['action'] ) {
				$this->deleted_options[] = $option['id'];
			} elseif ( ! in_array( $option['id'], $this->updated_options, true ) ) {
				$this->updated_options[] = $option['id'];
			}
		} elseif ( isset( $option['value'] ) ) {
			if ( 'select' === $option['type'] ) {
				$this->modified_options[ $option['id'] ] = $option['value'];

			} elseif ( 'checkbox' === $option['type'] ) {
				$option_state                             = 'yes' === $option['value'] ? 'enabled' : 'disabled';
				$this->toggled_options[ $option_state ][] = $option['id'];
			}
			$this->updated_options[] = $option['id'];
		}

	}

	/**
	 * Add a WooCommerce option name to our allowed options list and attach
	 * the `update_option` hook. Rather than inspecting every updated
	 * option and pattern matching for "woocommerce", just build a dynamic
	 * list for WooCommerce options that might get updated.
	 *
	 * See `woocommerce_update_option` hook.
	 *
	 * @param array $option WooCommerce option (config) that might get updated.
	 */
	public function add_option_to_list( $option ) {
		$this->allowed_options[] = $option['id'];

		if ( isset( $option['options'] ) ) {
			$this->dropdown_menu_options[] = $option['id'];
		}

		// Delay attaching this action since it could get fired a lot.
		if ( false === has_action( 'update_option', array( $this, 'track_setting_change' ) ) ) {
			add_action( 'update_option', array( $this, 'track_setting_change' ), 10, 3 );
		}
	}

	/**
	 * Add WooCommerce option to a list of updated options.
	 *
	 * @param string $option_name Option being updated.
	 * @param mixed  $old_value Old value of option.
	 * @param mixed  $new_value New value of option.
	 */
	public function track_setting_change( $option_name, $old_value, $new_value ) {
		// Make sure this is a WooCommerce option.
		if ( ! in_array( $option_name, $this->allowed_options, true ) ) {
			return;
		}

		// Check to make sure the new value is truly different.
		// `woocommerce_price_num_decimals` tends to trigger this
		// because form values aren't coerced (e.g. '2' vs. 2).
		if (
			is_scalar( $old_value ) &&
			is_scalar( $new_value ) &&
			(string) $old_value === (string) $new_value
		) {
			return;
		}

		if ( in_array( $option_name, $this->dropdown_menu_options, true ) ) {
			$this->modified_options[ $option_name ] = $new_value;
		} elseif ( in_array( $new_value, array( 'yes', 'no' ), true ) && in_array( $old_value, array( 'yes', 'no' ), true ) ) {
			// Save toggled options.
			$option_state                             = 'yes' === $new_value ? 'enabled' : 'disabled';
			$this->toggled_options[ $option_state ][] = $option_name;
		}

		$this->updated_options[] = $option_name;
	}

	/**
	 * Send a Tracks event for WooCommerce options that changed values.
	 */
	public function send_settings_change_event() {
		global $current_tab, $current_section;

		if ( empty( $this->updated_options ) && empty( $this->deleted_options ) && empty( $this->added_options ) ) {
			return;
		}

		$properties = array();

		if ( ! empty( $this->updated_options ) ) {
			$properties['settings'] = implode( ',', $this->updated_options );
		}

		if ( ! empty( $this->deleted_options ) ) {
			$properties['deleted'] = implode( ',', $this->deleted_options );
		}

		if ( ! empty( $this->added_options ) ) {
			$properties['added'] = implode( ',', $this->added_options );
		}

		foreach ( $this->toggled_options as $state => $options ) {
			if ( ! empty( $options ) ) {
				$properties[ $state ] = implode( ',', $options );
			}
		}

		if ( ! empty( $this->modified_options ) ) {
			foreach ( $this->modified_options as $option_name => $selected_option ) {
				$properties[ $option_name ] = $selected_option ?? '';
			}
		}

		$properties['tab']     = $current_tab ?? '';
		$properties['section'] = $current_section ?? '';

		WC_Tracks::record_event( 'settings_change', $properties );
	}

	/**
	 * Send a Tracks event for WooCommerce settings page views.
	 */
	public function track_settings_page_view() {
		global $current_tab, $current_section;

		$properties = array(
			'tab'     => $current_tab,
			'section' => empty( $current_section ) ? null : $current_section,
		);

		WC_Tracks::record_event( 'settings_view', $properties );
	}

	/**
	 * Adds the tracking scripts for product setting pages.
	 *
	 * @param string $hook Page hook.
	 */
	public function possibly_add_settings_tracking_scripts( $hook ) {
		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
		if (
			! isset( $_GET['page'] ) ||
			'wc-settings' !== wp_unslash( $_GET['page'] )
		) {
			return;
		}
		// phpcs:enable

		WCAdminAssets::register_script( 'wp-admin-scripts', 'settings-tracking', false );
	}
}
events/class-wc-status-tracking.php000064400000002025151542343450013423 0ustar00<?php
/**
 * WooCommerce Status Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of WooCommerce Orders.
 */
class WC_Status_Tracking {
	/**
	 * Init tracking.
	 */
	public function init() {
		add_action( 'admin_init', array( $this, 'track_status_view' ), 10 );
	}

	/**
	 * Add Tracks events to the status page.
	 */
	public function track_status_view() {
		if ( isset( $_GET['page'] ) && 'wc-status' === sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) {

			$tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'status';

			WC_Tracks::record_event(
				'status_view',
				array(
					'tab'       => $tab,
					'tool_used' => isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : null,
				)
			);

			if ( 'status' === $tab ) {
				wc_enqueue_js(
					"
					$( 'a.debug-report' ).on( 'click', function() {
						window.wcTracks.recordEvent( 'status_view_reports' );
					} );
				"
				);
			}
		}
	}
}
events/class-wc-theme-tracking.php000064400000002453151542343450013207 0ustar00<?php
/**
 * WooCommerce Theme Tracking
 *
 * @package WooCommerce\Tracks
 */

defined( 'ABSPATH' ) || exit;

/**
 * This class adds actions to track usage of themes on a WooCommerce store.
 */
class WC_Theme_Tracking {

	/**
	 * Init tracking.
	 */
	public function init() {
		$this->track_initial_theme();
		add_action( 'switch_theme', array( $this, 'track_activated_theme' ) );
	}

	/**
	 * Tracks the sites current theme the first time this code is run, and will only be run once.
	 */
	public function track_initial_theme() {
		$has_been_initially_tracked = get_option( 'wc_has_tracked_default_theme' );

		if ( $has_been_initially_tracked ) {
			return;
		}

		$this->track_activated_theme();
		add_option( 'wc_has_tracked_default_theme', 1 );
	}

	/**
	 * Send a Tracks event when a theme is activated so that we can track active block themes.
	 */
	public function track_activated_theme() {
		$is_block_theme = false;
		$theme_object = wp_get_theme();

		if ( function_exists( 'wc_current_theme_is_fse_theme' ) ) {
			$is_block_theme = wc_current_theme_is_fse_theme();
		}

		$properties = array(
			'block_theme'   => $is_block_theme,
			'theme_name'    => $theme_object->get( 'Name' ),
			'theme_version' => $theme_object->get( 'Version' ),
		);

		WC_Tracks::record_event( 'activated_theme', $properties );
	}
}
index.asset.php000064400000000137151545533410007511 0ustar00<?php return array('dependencies' => array(), 'version' => 'cd86283fcfb425dea9da318c0ada3755');index.js000064400000017424151545533410006227 0ustar00(()=>{var e={25516:(e,t,n)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let r=0,o=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(r++,"%c"===e&&(o=r))})),t.splice(o,0,n)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=n(90480)(t);const{formatters:r}=e.exports;r.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},90480:(e,t,n)=>{e.exports=function(e){function t(e){let n,o,s,a=null;function c(...e){if(!c.enabled)return;const r=c,o=Number(new Date),s=o-(n||o);r.diff=s,r.prev=n,r.curr=o,n=o,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let a=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((n,o)=>{if("%%"===n)return"%";a++;const s=t.formatters[o];if("function"==typeof s){const t=e[a];n=s.call(r,t),e.splice(a,1),a--}return n})),t.formatArgs.call(r,e),(r.log||t.log).apply(r,e)}return c.namespace=e,c.useColors=t.useColors(),c.color=t.selectColor(e),c.extend=r,c.destroy=t.destroy,Object.defineProperty(c,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==a?a:(o!==t.namespaces&&(o=t.namespaces,s=t.enabled(e)),s),set:e=>{a=e}}),"function"==typeof t.init&&t.init(c),c}function r(e,n){const r=t(this.namespace+(void 0===n?":":n)+e);return r.log=this.log,r}function o(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(o),...t.skips.map(o).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let n;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),o=r.length;for(n=0;n<o;n++)r[n]&&("-"===(e=r[n].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.substr(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let n,r;for(n=0,r=t.skips.length;n<r;n++)if(t.skips[n].test(e))return!1;for(n=0,r=t.names.length;n<r;n++)if(t.names[n].test(e))return!0;return!1},t.humanize=n(44247),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((n=>{t[n]=e[n]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let n=0;for(let t=0;t<e.length;t++)n=(n<<5)-n+e.charCodeAt(t),n|=0;return t.colors[Math.abs(n)%t.colors.length]},t.enable(t.load()),t}},44247:e=>{var t=1e3,n=60*t,r=60*n,o=24*r;function s(e,t,n,r){var o=t>=1.5*n;return Math.round(e/n)+" "+r+(o?"s":"")}e.exports=function(e,a){a=a||{};var c,i,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var s=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(s){var a=parseFloat(s[1]);switch((s[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*a;case"weeks":case"week":case"w":return 6048e5*a;case"days":case"day":case"d":return a*o;case"hours":case"hour":case"hrs":case"hr":case"h":return a*r;case"minutes":case"minute":case"mins":case"min":case"m":return a*n;case"seconds":case"second":case"secs":case"sec":case"s":return a*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return a;default:return}}}}(e);if("number"===u&&isFinite(e))return a.long?(c=e,(i=Math.abs(c))>=o?s(c,i,o,"day"):i>=r?s(c,i,r,"hour"):i>=n?s(c,i,n,"minute"):i>=t?s(c,i,t,"second"):c+" ms"):function(e){var s=Math.abs(e);return s>=o?Math.round(e/o)+"d":s>=r?Math.round(e/r)+"h":s>=n?Math.round(e/n)+"m":s>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,n),s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};(()=>{"use strict";n.r(r),n.d(r,{queueRecordEvent:()=>c,recordEvent:()=>s,recordPageView:()=>i});var e=n(25516);const t=n.n(e)()("wc-admin:tracks"),o=e=>2===e.length&&"string"==typeof e[0];function s(e,n){if(t("recordevent %s %o","wcadmin_"+e,n,{_tqk:window._tkq,shouldRecord:!!window._tkq&&!!window.wcTracks&&!!window.wcTracks.isEnabled}),!window.wcTracks||"function"!=typeof window.wcTracks.recordEvent)return!1;window.wcTracks.recordEvent(e,n)}const a={localStorageKey:()=>"tracksQueue",clear(){window.localStorage&&window.localStorage.removeItem(a.localStorageKey())},get(){if(!window.localStorage)return[];const e=window.localStorage.getItem(a.localStorageKey()),t=e?JSON.parse(e):[];return Array.isArray(t)?t:[]},add(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];if(!window.localStorage)return t("Unable to queue, running now",{args:n}),void(o(n)?s(...n):t("Invalid args",n));let c=a.get();const i={args:n};c.push(i),c=c.slice(-100),t("Adding new item to queue.",i),window.localStorage.setItem(a.localStorageKey(),JSON.stringify(c))},process(){if(!window.localStorage)return;const e=a.get();a.clear(),t("Processing items in queue.",e),e.forEach((e=>{if("object"==typeof e){t("Processing item in queue.",e);const n=e.args;o(n)?s(...n):t("Invalid item args",e.args)}}))}};function c(e,t){a.add(e,t)}function i(e,t){e&&(s("page_view",{path:e,...t}),a.process())}})(),(window.wc=window.wc||{}).tracks=r})();