khaihihi
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<?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.
|
||||
*/
|
||||
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.NoNonceVerification, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add scripts required to record events from javascript.
|
||||
*/
|
||||
public static function enqueue_scripts() {
|
||||
|
||||
// Add w.js to the page.
|
||||
wp_enqueue_script( 'woo-tracks', 'https://stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
|
||||
|
||||
// Expose tracking via a function in the wcTracks global namespace directly before wc_print_js.
|
||||
add_filter( 'admin_footer', array( __CLASS__, 'add_tracking_function' ), 24 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the tracking function to the admin footer.
|
||||
*/
|
||||
public static function add_tracking_function() {
|
||||
?>
|
||||
<!-- WooCommerce Tracks -->
|
||||
<script type="text/javascript">
|
||||
window.wcTracks = window.wcTracks || {};
|
||||
window.wcTracks.recordEvent = function( name, properties ) {
|
||||
var eventName = '<?php echo esc_attr( WC_Tracks::PREFIX ); ?>' + name;
|
||||
var eventProperties = properties || {};
|
||||
eventProperties.url = '<?php echo esc_html( home_url() ); ?>'
|
||||
eventProperties.products_count = '<?php echo intval( WC_Tracks::get_products_count() ); ?>';
|
||||
window._tkq = window._tkq || [];
|
||||
window._tkq.push( [ 'recordEvent', eventName, eventProperties ] );
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Add empty tracking function to admin footer when tracking is disabled in case
|
||||
* it's called without checking if it's defined beforehand.
|
||||
*/
|
||||
public static function add_empty_tracking_function() {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.wcTracks = window.wcTracks || {};
|
||||
window.wcTracks.recordEvent = function() {};
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Init tracking.
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
if ( ! self::is_tracking_enabled() ) {
|
||||
|
||||
// Define window.wcTracks.recordEvent in case there is an attempt to use it when tracking is turned off.
|
||||
add_filter( 'admin_footer', array( __CLASS__, 'add_empty_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';
|
||||
|
||||
$tracking_classes = array(
|
||||
'WC_Admin_Setup_Wizard_Tracking',
|
||||
'WC_Extensions_Tracking',
|
||||
'WC_Importer_Tracking',
|
||||
'WC_Products_Tracking',
|
||||
'WC_Orders_Tracking',
|
||||
'WC_Settings_Tracking',
|
||||
'WC_Status_Tracking',
|
||||
'WC_Coupons_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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/**
|
||||
* Send Tracks events on behalf of a user.
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
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 identiy cookie is set, if not set it.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_set_identity_cookie() {
|
||||
// Do not set on AJAX requests.
|
||||
if ( defined( 'DOING_AJAX' ) && 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 (
|
||||
! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) &&
|
||||
! ( defined( 'XMLRPC_REQUEST' ) && 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 timestap representing milliseconds since 1970-01-01
|
||||
*
|
||||
* @return string A string representing a timestamp.
|
||||
*/
|
||||
public static function build_timestamp() {
|
||||
$ts = 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' ) && defined( 'JETPACK__VERSION' ) ) {
|
||||
if ( version_compare( 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 ) );
|
||||
}
|
||||
|
||||
$anon_id = 'woo:' . base64_encode( $binary );
|
||||
}
|
||||
}
|
||||
|
||||
return $anon_id;
|
||||
}
|
||||
}
|
||||
|
||||
WC_Tracks_Client::init();
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
/**
|
||||
* This class represents an event used to record a Tracks event
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* WC_Tracks_Event class.
|
||||
*/
|
||||
class WC_Tracks_Event {
|
||||
|
||||
/**
|
||||
* Event name regex.
|
||||
*/
|
||||
const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/';
|
||||
|
||||
/**
|
||||
* Property name regex.
|
||||
*/
|
||||
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() ||
|
||||
( defined( 'REST_REQUEST' ) && REST_REQUEST )
|
||||
) {
|
||||
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 blacklist property names, do it here.
|
||||
// Make sure we have an event timestamp.
|
||||
if ( ! isset( $_event->_ts ) ) {
|
||||
$_event->_ts = WC_Tracks_Client::build_timestamp();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$whitelisted_key_names = array(
|
||||
'anonId',
|
||||
'Browser_Type',
|
||||
);
|
||||
|
||||
foreach ( array_keys( (array) $event ) as $key ) {
|
||||
if ( in_array( $key, $whitelisted_key_names, true ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! self::prop_name_is_valid( $key ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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(),
|
||||
);
|
||||
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.
|
||||
*
|
||||
* @param string $event_name The name of the event.
|
||||
* @param array $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, $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;
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'_en' => self::PREFIX . $event_name,
|
||||
'_ts' => WC_Tracks_Client::build_timestamp(),
|
||||
);
|
||||
|
||||
$server_details = self::get_server_details();
|
||||
$identity = WC_Tracks_Client::get_identity( $user->ID );
|
||||
$blog_details = self::get_blog_details( $user->ID );
|
||||
|
||||
$event_obj = new WC_Tracks_Event( array_merge( $data, $server_details, $identity, $blog_details, $properties ) );
|
||||
|
||||
if ( is_wp_error( $event_obj->error ) ) {
|
||||
return $event_obj->error;
|
||||
}
|
||||
|
||||
return $event_obj->record();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Admin Setup Wizard Tracking
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
public function init() {
|
||||
if ( empty( $_GET['page'] ) || 'wc-setup' !== $_GET['page'] ) { // WPCS: CSRF ok, input var ok.
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'woocommerce_setup_wizard_steps', array( $this, 'set_obw_steps' ) );
|
||||
add_action( 'shutdown', array( $this, 'track_skip_step' ), 1 );
|
||||
add_action( 'add_option_woocommerce_allow_tracking', array( $this, 'track_start' ), 10, 2 );
|
||||
add_action( 'admin_init', array( $this, 'track_ready_next_steps' ), 1 );
|
||||
add_action( 'wp_print_scripts', array( $this, 'dequeue_non_whitelisted_scripts' ) );
|
||||
$this->add_step_save_events();
|
||||
add_action( 'woocommerce_setup_footer', array( $this, 'add_footer_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the current step.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_current_step() {
|
||||
return isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
|
||||
}
|
||||
|
||||
/**
|
||||
* Add footer scripts to OBW via woocommerce_setup_footer
|
||||
*/
|
||||
public function add_footer_scripts() {
|
||||
wp_print_scripts();
|
||||
WC_Site_Tracking::add_tracking_function();
|
||||
wc_print_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue unwanted scripts from OBW footer.
|
||||
*/
|
||||
public function dequeue_non_whitelisted_scripts() {
|
||||
global $wp_scripts;
|
||||
$whitelist = array( 'woo-tracks' );
|
||||
|
||||
foreach ( $wp_scripts->queue as $script ) {
|
||||
if ( in_array( $script, $whitelist, 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.
|
||||
* @return void
|
||||
*/
|
||||
public function track_start( $option, $value ) {
|
||||
if ( 'yes' !== $value || empty( $_GET['page'] ) || 'wc-setup' !== $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
|
||||
return;
|
||||
}
|
||||
|
||||
WC_Tracks::record_event( 'obw_start' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track the marketing form on submit.
|
||||
*/
|
||||
public function track_ready_next_steps() {
|
||||
if ( 'next_steps' !== $this->get_current_step() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wc_enqueue_js(
|
||||
"
|
||||
jQuery( '#mc-embedded-subscribe' ).click( function() {
|
||||
window.wcTracks.recordEvent( 'obw_marketing_signup' );
|
||||
} );
|
||||
jQuery( '.wc-setup-content a' ).click( function trackNextScreen( e ) {
|
||||
var properties = {
|
||||
next_url: e.target.href,
|
||||
button: e.target.textContent && e.target.textContent.trim()
|
||||
};
|
||||
window.wcTracks.recordEvent( 'obw_ready_next_step', properties );
|
||||
} );
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track various events when a step is saved.
|
||||
*/
|
||||
public function add_step_save_events() {
|
||||
// Always record a track on this page view.
|
||||
if ( 'next_steps' === $this->get_current_step() ) {
|
||||
add_action( 'admin_init', array( $this, 'track_next_steps' ), 1 );
|
||||
}
|
||||
|
||||
if ( empty( $_POST['save_step'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
|
||||
return;
|
||||
}
|
||||
|
||||
update_option( 'woocommerce_obw_last_completed_step', $this->get_current_step() );
|
||||
|
||||
switch ( $this->get_current_step() ) {
|
||||
case '':
|
||||
case 'store_setup':
|
||||
add_action( 'admin_init', array( $this, 'track_store_setup' ), 1 );
|
||||
break;
|
||||
case 'payment':
|
||||
add_action( 'admin_init', array( $this, 'track_payments' ), 1 );
|
||||
break;
|
||||
case 'shipping':
|
||||
add_action( 'admin_init', array( $this, 'track_shipping' ), 1 );
|
||||
break;
|
||||
case 'recommended':
|
||||
add_action( 'admin_init', array( $this, 'track_recommended' ), 1 );
|
||||
break;
|
||||
case 'activate':
|
||||
add_action( 'admin_init', array( $this, 'track_jetpack_activate' ), 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track store setup and store properties on save.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_store_setup() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput
|
||||
$properties = array(
|
||||
'country' => isset( $_POST['store_country'] ) ? sanitize_text_field( $_POST['store_country'] ) : '',
|
||||
'currency_code' => isset( $_POST['currency_code'] ) ? sanitize_text_field( $_POST['currency_code'] ) : '',
|
||||
'product_type' => isset( $_POST['product_type'] ) ? sanitize_text_field( $_POST['product_type'] ) : '',
|
||||
'sell_in_person' => isset( $_POST['sell_in_person'] ) && ( 'yes' === sanitize_text_field( $_POST['sell_in_person'] ) ),
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
WC_Tracks::record_event( 'obw_store_setup', $properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track payment gateways selected.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_payments() {
|
||||
$selected_gateways = array();
|
||||
$created_accounts = array();
|
||||
$wc_admin_setup_wizard = new WC_Admin_Setup_Wizard();
|
||||
$gateways = array_merge( $wc_admin_setup_wizard->get_wizard_in_cart_payment_gateways(), $wc_admin_setup_wizard->get_wizard_manual_payment_gateways() );
|
||||
|
||||
foreach ( $gateways as $gateway_id => $gateway ) {
|
||||
if ( ! empty( $_POST[ 'wc-wizard-service-' . $gateway_id . '-enabled' ] ) ) { // WPCS: CSRF ok, input var ok.
|
||||
$selected_gateways[] = $gateway_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Stripe account being created.
|
||||
if (
|
||||
! empty( $_POST['wc-wizard-service-stripe-enabled'] ) && // WPCS: CSRF ok, input var ok.
|
||||
! empty( $_POST['stripe_create_account'] ) // WPCS: CSRF ok, input var ok.
|
||||
) {
|
||||
$created_accounts[] = 'stripe';
|
||||
}
|
||||
// PayPal account being created.
|
||||
if (
|
||||
! empty( $_POST['wc-wizard-service-ppec_paypal-enabled'] ) && // WPCS: CSRF ok, input var ok.
|
||||
! empty( $_POST['ppec_paypal_reroute_requests'] ) // WPCS: CSRF ok, input var ok.
|
||||
) {
|
||||
$created_accounts[] = 'ppec_paypal';
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput
|
||||
$properties = array(
|
||||
'selected_gateways' => implode( ', ', $selected_gateways ),
|
||||
'created_accounts' => implode( ', ', $created_accounts ),
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
WC_Tracks::record_event( 'obw_payments', $properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track shipping units and whether or not labels are set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_shipping() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput
|
||||
$properties = array(
|
||||
'weight_unit' => isset( $_POST['weight_unit'] ) ? sanitize_text_field( wp_unslash( $_POST['weight_unit'] ) ) : '',
|
||||
'dimension_unit' => isset( $_POST['dimension_unit'] ) ? sanitize_text_field( wp_unslash( $_POST['dimension_unit'] ) ) : '',
|
||||
'setup_wcs_labels' => isset( $_POST['setup_woocommerce_services'] ) && 'yes' === $_POST['setup_woocommerce_services'],
|
||||
'setup_shipstation' => isset( $_POST['setup_shipstation'] ) && 'yes' === $_POST['setup_shipstation'],
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
WC_Tracks::record_event( 'obw_shipping', $properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track recommended plugins selected for install.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_recommended() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
|
||||
$properties = array(
|
||||
'setup_storefront' => isset( $_POST['setup_storefront_theme'] ) && 'yes' === $_POST['setup_storefront_theme'],
|
||||
'setup_automated_tax' => isset( $_POST['setup_automated_taxes'] ) && 'yes' === $_POST['setup_automated_taxes'],
|
||||
'setup_mailchimp' => isset( $_POST['setup_mailchimp'] ) && 'yes' === $_POST['setup_mailchimp'],
|
||||
'setup_facebook' => isset( $_POST['setup_facebook'] ) && 'yes' === $_POST['setup_facebook'],
|
||||
'setup_wc_admin' => isset( $_POST['setup_wc_admin'] ) && 'yes' === $_POST['setup_wc_admin'],
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
WC_Tracks::record_event( 'obw_recommended', $properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks when Jetpack is activated through the OBW.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_jetpack_activate() {
|
||||
WC_Tracks::record_event( 'obw_activate' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks when last next_steps screen is viewed in the OBW.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_next_steps() {
|
||||
WC_Tracks::record_event( 'obw_ready_view' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Track skipped steps.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function track_skip_step() {
|
||||
$previous_step = get_option( 'woocommerce_obw_last_completed_step' );
|
||||
$current_step = $this->get_current_step();
|
||||
if ( ! $previous_step || ! $current_step ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$steps = array_keys( $this->steps );
|
||||
$current_step_index = array_search( $current_step, $steps, true );
|
||||
$previous_step_index = array_search( $previous_step, $steps, true );
|
||||
|
||||
// If we're going forward more than 1 completed step.
|
||||
if ( $current_step_index > $previous_step_index + 1 ) {
|
||||
$properties = array(
|
||||
'step' => $steps[ $current_step_index - 1 ],
|
||||
);
|
||||
WC_Tracks::record_event( 'obw_skip_step', $properties );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OBW steps inside this class instance.
|
||||
*
|
||||
* @param array $steps Array of OBW steps.
|
||||
*/
|
||||
public function set_obw_steps( $steps ) {
|
||||
$this->steps = $steps;
|
||||
|
||||
return $steps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Tracks event when an Extensions page is viewed.
|
||||
*/
|
||||
public function track_extensions_page() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
|
||||
$event = 'extensions_view';
|
||||
$properties = array(
|
||||
'section' => empty( $_REQUEST['section'] ) ? '_featured' : wc_clean( wp_unslash( $_REQUEST['section'] ) ),
|
||||
);
|
||||
|
||||
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 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' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?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.NoNonceVerification
|
||||
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.NoNonceVerification
|
||||
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.NoNonceVerification
|
||||
if ( ! isset( $_REQUEST['nonce'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'imported' => isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Orders Tracking
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(),
|
||||
);
|
||||
|
||||
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 ) {
|
||||
$post_type = get_post_type( $id );
|
||||
|
||||
if ( 'shop_order' !== $post_type ) {
|
||||
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' === get_post_type( $referring_args['post'] )
|
||||
) {
|
||||
WC_Tracks::record_event( 'order_edit_add_order' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Import Tracking
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* This class adds actions to track usage of WooCommerce Products.
|
||||
*/
|
||||
class WC_Products_Tracking {
|
||||
/**
|
||||
* Init tracking.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'edit_post', array( $this, 'track_product_updated' ), 10, 2 );
|
||||
add_action( 'transition_post_status', array( $this, 'track_product_published' ), 10, 3 );
|
||||
add_action( 'created_product_cat', array( $this, 'track_product_category_created' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Tracks event when a product is published.
|
||||
*
|
||||
* @param string $new_status New post_status.
|
||||
* @param string $old_status Previous post_status.
|
||||
* @param object $post WordPress post.
|
||||
*/
|
||||
public function track_product_published( $new_status, $old_status, $post ) {
|
||||
if (
|
||||
'product' !== $post->post_type ||
|
||||
'publish' !== $new_status ||
|
||||
'publish' === $old_status
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'product_id' => $post->ID,
|
||||
);
|
||||
|
||||
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.NoNonceVerification
|
||||
// Only track category creation from the edit product screen or the
|
||||
// category management screen (which both occur via AJAX).
|
||||
if (
|
||||
! 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' );
|
||||
$properties = array(
|
||||
'category_id' => $category_id,
|
||||
'parent_id' => $category->parent,
|
||||
'page' => ( 'add-tag' === $_POST['action'] ) ? 'categories' : 'product',
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
WC_Tracks::record_event( 'product_category_add', $properties );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Settings Tracking
|
||||
*
|
||||
* @package WooCommerce\Tracks
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* This class adds actions to track usage of WooCommerce Settings.
|
||||
*/
|
||||
class WC_Settings_Tracking {
|
||||
/**
|
||||
* Whitelisted WooCommerce settings to potentially track updates for.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $whitelist = array();
|
||||
|
||||
/**
|
||||
* WooCommerce settings that have been updated (and will be tracked).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $updated_options = 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_whitelist' ) );
|
||||
add_action( 'woocommerce_update_options', array( $this, 'send_settings_change_event' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a WooCommerce option name to our whitelist and attach
|
||||
* the `update_option` hook. Rather than inspecting every updated
|
||||
* option and pattern matching for "woocommerce", just build a dynamic
|
||||
* whitelist 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_whitelist( $option ) {
|
||||
$this->whitelist[] = $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->whitelist, 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;
|
||||
}
|
||||
|
||||
$this->updated_options[] = $option_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Tracks event for WooCommerce options that changed values.
|
||||
*/
|
||||
public function send_settings_change_event() {
|
||||
global $current_tab;
|
||||
|
||||
if ( empty( $this->updated_options ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'settings' => implode( ',', $this->updated_options ),
|
||||
);
|
||||
|
||||
if ( isset( $current_tab ) ) {
|
||||
$properties['tab'] = $current_tab;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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' ).click( function() {
|
||||
window.wcTracks.recordEvent( 'status_view_reports' );
|
||||
} );
|
||||
"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user