Skip to content

Commit

Permalink
Tracks: Don't track users in dev mode or when opted out (#13698)
Browse files Browse the repository at this point in the history
 Don't track users in dev mode or when opted out

* Fix all phpcs issues

This will avoid having to bypass the pre-commit hook for all commits to this file

* [not verified] Opt out of tracking in dev mode in legacy branch.

* PHPCS fixes

* Include development mode check within Jetpack::jetpack_tos_agreed.

The TOS check is used for confirming that a site is "Trackable". For sites in development mode or are not active, we should not track them.

* Docblock for is_active_and_not_development function

* Bring cookie check back

Just to be sure. See Automattic/jetpack#13698 (comment)
  • Loading branch information
scruffian authored and kraftbj committed Oct 11, 2019
1 parent 93f34c7 commit ac97b59
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 40 deletions.
61 changes: 40 additions & 21 deletions legacy/class.tracks-client.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<?php
/**
* Legacy Jetpack Tracks Client
*
* @package Jetpack
*/

/**
* Jetpack_Tracks_Client
Expand Down Expand Up @@ -38,21 +43,20 @@
}
```
*/

class Jetpack_Tracks_Client {
const PIXEL = 'https://pixel.wp.com/t.gif';
const BROWSER_TYPE = 'php-agent';
const USER_AGENT_SLUG = 'tracks-client';
const VERSION = '0.3';

/**
* record_event
* Record an event.
*
* @param mixed $event Event object to send to Tracks. An array will be cast to object. Required.
* Properties are included directly in the pixel query string after light validation.
* @return mixed True on success, WP_Error on failure
*/
static function record_event( $event ) {
public static function record_event( $event ) {
if ( ! Jetpack::jetpack_tos_agreed() || ! empty( $_COOKIE['tk_opt-out'] ) ) {
return false;
}
Expand All @@ -74,16 +78,19 @@ static function record_event( $event ) {
}

/**
* Synchronously request the pixel
* Synchronously request the pixel.
*
* @param string $pixel The wp.com tracking pixel.
* @return array|bool|WP_Error True if successful. wp_remote_get response or WP_Error if not.
*/
static function record_pixel( $pixel ) {
public static function record_pixel( $pixel ) {
// Add the Request Timestamp and URL terminator just before the HTTP request.
$pixel .= '&_rt=' . self::build_timestamp() . '&_=_';

$response = wp_remote_get(
$pixel,
array(
'blocking' => true, // The default, but being explicit here :)
'blocking' => true, // The default, but being explicit here :).
'timeout' => 1,
'redirection' => 2,
'httpversion' => '1.1',
Expand All @@ -97,25 +104,30 @@ static function record_pixel( $pixel ) {

$code = isset( $response['response']['code'] ) ? $response['response']['code'] : 0;

if ( $code !== 200 ) {
if ( 200 !== $code ) {
return new WP_Error( 'request_failed', 'Tracks pixel request failed', $code );
}

return true;
}

static function get_user_agent() {
/**
* Get the user agent.
*
* @return string The user agent.
*/
public static function get_user_agent() {
return self::USER_AGENT_SLUG . '-v' . self::VERSION;
}

/**
* Build an event and return its tracking URL
*
* @deprecated Call the `build_pixel_url` method on a Jetpack_Tracks_Event object instead.
* @param array $event Event keys and values
* @return string URL of a tracking pixel
* @param array $event Event keys and values.
* @return string URL of a tracking pixel.
*/
static function build_pixel_url( $event ) {
public static function build_pixel_url( $event ) {
$_event = new Jetpack_Tracks_Event( $event );
return $_event->build_pixel_url();
}
Expand All @@ -124,7 +136,7 @@ static function build_pixel_url( $event ) {
* Validate input for a tracks event.
*
* @deprecated Instantiate a Jetpack_Tracks_Event object instead
* @param array $event Event keys and values
* @param array $event Event keys and values.
* @return mixed Validated keys and values or WP_Error on failure
*/
private static function validate_and_sanitize( $event ) {
Expand All @@ -135,8 +147,14 @@ private static function validate_and_sanitize( $event ) {
return get_object_vars( $_event );
}

// Milliseconds since 1970-01-01
static function build_timestamp() {
/**
* Builds a timestamp.
*
* Milliseconds since 1970-01-01.
*
* @return string
*/
public static function build_timestamp() {
$ts = round( microtime( true ) * 1000 );
return number_format( $ts, 0, '', '' );
}
Expand All @@ -146,7 +164,7 @@ static function build_timestamp() {
*
* @return string An anon id for the user
*/
static function get_anon_id() {
public static function get_anon_id() {
static $anon_id = null;

if ( ! isset( $anon_id ) ) {
Expand All @@ -158,13 +176,13 @@ static function get_anon_id() {

$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
// 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( mt_rand( 0, 255 ) );
$binary .= chr( wp_rand( 0, 255 ) );
}

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

if ( ! headers_sent()
&& ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
Expand All @@ -183,8 +201,9 @@ static function get_anon_id() {
*
* @return array|bool
*/
static function get_connected_user_tracks_identity() {
if ( ! $user_data = Jetpack::get_connected_user_data() ) {
public static function get_connected_user_tracks_identity() {
$user_data = Jetpack::get_connected_user_data();
if ( ! $user_data ) {
return false;
}

Expand Down
64 changes: 45 additions & 19 deletions src/Tracking.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@
* The Tracking class, used to record events in wpcom
*/
class Tracking {
/**
* Slug of the product that we are tracking.
*
* @var string
*/
private $product_name;

/**
* Connection manager object.
*
* @var Object
*/
private $connection;

/**
Expand All @@ -20,7 +31,7 @@ class Tracking {
* @param String $product_name the slug of the product that we are tracking.
* @param Automattic\Jetpack\Connection\Manager $connection the connection manager object.
*/
function __construct( $product_name = 'jetpack', $connection = null ) {
public function __construct( $product_name = 'jetpack', $connection = null ) {
$this->product_name = $product_name;
$this->connection = $connection;
if ( is_null( $this->connection ) ) {
Expand All @@ -29,7 +40,10 @@ function __construct( $product_name = 'jetpack', $connection = null ) {
}
}

function enqueue_tracks_scripts() {
/**
* Enqueue script necessary for tracking.
*/
public function enqueue_tracks_scripts() {
wp_enqueue_script( 'jptracks', plugins_url( '_inc/lib/tracks/tracks-ajax.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true );
wp_localize_script(
'jptracks',
Expand All @@ -41,7 +55,14 @@ function enqueue_tracks_scripts() {
);
}

function record_user_event( $event_type, $data = array(), $user = null ) {
/**
* Send an event in Tracks.
*
* @param string $event_type Type of the event.
* @param array $data Data to send with the event.
* @param mixed $user username, user_id, or WP_user object.
*/
public function record_user_event( $event_type, $data = array(), $user = null ) {
if ( ! $user ) {
$user = wp_get_current_user();
}
Expand All @@ -53,8 +74,8 @@ function record_user_event( $event_type, $data = array(), $user = null ) {
$data['blog_url'] = $site_url;
$data['blog_id'] = \Jetpack_Options::get_option( 'id' );

// Top level events should not be namespaced
if ( '_aliasUser' != $event_type ) {
// Top level events should not be namespaced.
if ( '_aliasUser' !== $event_type ) {
$event_type = $this->product_name . '_' . $event_type;
}

Expand All @@ -66,20 +87,25 @@ function record_user_event( $event_type, $data = array(), $user = null ) {
/**
* Record an event in Tracks - this is the preferred way to record events from PHP.
*
* @param mixed $identity username, user_id, or WP_user object
* @param string $event_name The name of the event
* @param array $properties Custom properties to send with the event
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred
* @param mixed $user username, user_id, or WP_user object.
* @param string $event_name The name of the event.
* @param array $properties Custom properties to send with the event.
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
*
* @return bool true for success | \WP_Error if the event pixel could not be fired
*/
function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
public function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {

// 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;
}

// Don't track users who have opted out or not agreed to our TOS, or are not running an active Jetpack.
if ( ! \Jetpack::jetpack_tos_agreed() ) {
return false;
}

$event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );

if ( is_wp_error( $event_obj->error ) ) {
Expand All @@ -93,14 +119,14 @@ function tracks_record_event( $user, $event_name, $properties = array(), $event_
* Procedurally build a Tracks Event Object.
* NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
*
* @param $identity WP_user object
* @param string $event_name The name of the event
* @param array $properties Custom properties to send with the event
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred
* @param WP_user $user WP_user object.
* @param string $event_name The name of the event.
* @param array $properties Custom properties to send with the event.
* @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
*
* @return \Jetpack_Tracks_Event|\WP_Error
*/
function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
$identity = $this->tracks_get_identity( $user->ID );

$properties['user_lang'] = $user->get( 'WPLANG' );
Expand All @@ -109,7 +135,7 @@ function tracks_build_event_obj( $user, $event_name, $properties = array(), $eve
'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
);

$timestamp = ( $event_timestamp_millis !== false ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
$timestamp = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
$timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );

return new \Jetpack_Tracks_Event(
Expand All @@ -128,13 +154,13 @@ function tracks_build_event_obj( $user, $event_name, $properties = array(), $eve
/**
* Get the identity to send to tracks.
*
* @param int $user_id The user id of the local user
* @param int $user_id The user id of the local user.
*
* @return array $identity
*/
function tracks_get_identity( $user_id ) {
public function tracks_get_identity( $user_id ) {

// Meta is set, and user is still connected. Use WPCOM ID
// Meta is set, and user is still connected. Use WPCOM ID.
$wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
return array(
Expand Down

0 comments on commit ac97b59

Please sign in to comment.