Skip to content

Commit

Permalink
Google Analytics: extract API into the package (#37358)
Browse files Browse the repository at this point in the history
* Move Google Analytics API code from Jetpack into the package.
* Add the `is_active` flag to indicate whether GA is active (currently module activation status is used).
* Modify the Status package to check activation status of an unavailable module to ease transition to settings when GA module gets removed from Jetpack.
  • Loading branch information
sergeymitr authored Jun 19, 2024
1 parent 831e679 commit 25eb8fa
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 112 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add the GA API handling moved from Jetpack.
5 changes: 3 additions & 2 deletions projects/packages/google-analytics/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.0"
"php": ">=7.0",
"automattic/jetpack-status": "@dev"
},
"require-dev": {
"yoast/phpunit-polyfills": "1.1.0",
Expand Down Expand Up @@ -42,7 +43,7 @@
"extra": {
"autotagger": true,
"branch-alias": {
"dev-trunk": "0.1.x-dev"
"dev-trunk": "0.2.x-dev"
},
"changelogger": {
"link-template": "https://github.com/Automattic/jetpack-google-analytics/compare/v${old}...v${new}"
Expand Down
2 changes: 1 addition & 1 deletion projects/packages/google-analytics/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@automattic/jetpack-google-analytics",
"version": "0.1.0",
"version": "0.2.0-alpha",
"description": "Set up Google Analytics without touching a line of code.",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/google-analytics/#readme",
"bugs": {
Expand Down
192 changes: 191 additions & 1 deletion projects/packages/google-analytics/src/class-ga-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@

namespace Automattic\Jetpack\Google_Analytics;

use Automattic\Jetpack\Modules;
use WP_Error;

/**
* The Facade class of the package.
*/
class GA_Manager {

const PACKAGE_VERSION = '0.1.0';
const PACKAGE_VERSION = '0.2.0-alpha';

/**
* Jetpack_Google_Analytics singleton instance.
Expand All @@ -31,6 +34,34 @@ class GA_Manager {
*/
public static $analytics = false;

/**
* Defaults for the API version >=1.3.
*
* @var array
*/
private $api_defaults = array(
'1.3' => array(
'code' => '',
'anonymize_ip' => false,
'ec_track_purchases' => false,
'ec_track_add_to_cart' => false,
),
'1.4' => array(
'is_active' => false, // This default value will most likely be overwritten by the current status of the GA module.
'code' => '',
'anonymize_ip' => false,
'honor_dnt' => false,
'ec_track_purchases' => false,
'ec_track_add_to_cart' => false,
'enh_ec_tracking' => false,
'enh_ec_track_remove_from_cart' => false,
'enh_ec_track_prod_impression' => false,
'enh_ec_track_prod_click' => false,
'enh_ec_track_prod_detail_view' => false,
'enh_ec_track_checkout_started' => false,
),
);

/**
* This is our constructor, which is private to force the use of get_instance()
*
Expand All @@ -46,6 +77,12 @@ private function __construct() {
} else {
self::$analytics = new Legacy();
}

add_filter( 'site_settings_endpoint_get', array( $this, 'site_settings_fetch' ), 10, 2 );
add_filter( 'site_settings_endpoint_update_wga', array( $this, 'site_settings_update' ), 10, 2 );
add_filter( 'site_settings_endpoint_update_jetpack_wga', array( $this, 'site_settings_update' ) );
add_action( 'jetpack_activate_module_google-analytics', array( $this, 'set_status_from_module' ) );
add_action( 'jetpack_deactivate_module_google-analytics', array( $this, 'set_status_from_module' ) );
}

/**
Expand All @@ -59,6 +96,159 @@ public static function get_instance() {
return self::$instance;
}

/**
* Includes the GA settings into site settings during a fetch request.
*
* @phan-suppress PhanUndeclaredTypeParameter,PhanUndeclaredClassInstanceof,PhanUndeclaredClassProperty
*
* @param array $settings The fetched settings.
* @param \WPCOM_JSON_API_Site_Settings_Endpoint $api_handler The API handler object.
*
* @return array|mixed
*/
public function site_settings_fetch( $settings = array(), $api_handler = null ) {
if ( ! is_array( $settings ) || ! $api_handler instanceof \WPCOM_JSON_API_Site_Settings_Endpoint ) {
// Safeguard against something that should never happen.
return $settings;
}

$settings['wga'] = $this->get_google_analytics_settings();

if ( array_key_exists( $api_handler->min_version, $this->api_defaults ) ) {
$settings['wga'] = wp_parse_args( $settings['wga'], $this->api_defaults[ $api_handler->min_version ] );
}

return $settings;
}

/**
* Modifies the GA settings into site settings during an update request.
*
* @phan-suppress PhanUndeclaredTypeParameter,PhanUndeclaredClassInstanceof,PhanUndeclaredClassProperty
*
* @param array $value The settings to update.
* @param \WPCOM_JSON_API_Site_Settings_Endpoint $api_handler The API handler object.
*
* @return array|mixed
*/
public function site_settings_update( $value, $api_handler = null ) {
if ( ! is_array( $value ) || ! $api_handler instanceof \WPCOM_JSON_API_Site_Settings_Endpoint ) {
// This should never happen.
return $value;
}

if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^(UA-\d+-\d+)|(G-[A-Z0-9]+)$/i', $value['code'] ) ) {
return new WP_Error( 'invalid_code', 'Invalid UA ID' );
}

$option_name = $this->get_google_analytics_option_name();

$wga = get_option( $option_name, array() );
$wga['code'] = $value['code'];

if ( ! array_key_exists( 'is_active', $wga ) ) {
// The `is_active` flag is missing from the settings, add a default value based on the module status.
$wga['is_active'] = ( new Modules() )->is_active( 'google-analytics', false );
}

/**
* Allow newer versions of this endpoint to filter in additional fields for Google Analytics
*
* @since Jetpack 5.4.0
* @since $$next-version$$
*
* @param array $wga Associative array of existing Google Analytics settings.
* @param array $value Associative array of new Google Analytics settings passed to the endpoint.
*/
$wga = apply_filters( 'site_settings_update_wga', $wga, $value );

if ( array_key_exists( $api_handler->min_version, $this->api_defaults ) ) {
$wga_keys = array_keys( $this->api_defaults[ $api_handler->min_version ] );
foreach ( $wga_keys as $wga_key ) {
// Skip code since it's already handled.
if ( 'code' === $wga_key ) {
continue;
}

// All our new keys are booleans, so let's coerce each key's value
// before updating the value in settings
if ( array_key_exists( $wga_key, $value ) ) {
$wga[ $wga_key ] = Utils::is_truthy( $value[ $wga_key ] );
}
}
}

$is_updated = update_option( $option_name, $wga );

$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';

/**
* Fires for each settings update.
*
* @since Jetpack 3.6.0
* @since $$next-version$$
*
* @param string $action_type Type of settings to track.
* @param string $val The settings value.
*/
do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );

return $is_updated ? $wga : null;
}

/**
* Update the `is_active` settings flag depending on the Google Analytics module status.
*
* @return void
*/
public function set_status_from_module() {
$option_name = $this->get_google_analytics_option_name();

$wga = get_option( $option_name, array() );
$is_active = ( new Modules() )->is_active( 'google-analytics', false );

if ( $is_active !== $wga['is_active'] ) {
$wga['is_active'] = $is_active;
}

update_option( $option_name, $wga );
}

/**
* Get the GA settings option name.
*
* @return string
*/
public function get_google_analytics_option_name() {
/**
* Filter whether the current site is a Jetpack site.
*
* @since Jetpack 3.3.0
* @since $$next-version$$
*
* @param bool $is_jetpack Is the current site a Jetpack site. Default to false.
* @param int $blog_id Blog ID.
*/
$is_jetpack = true === apply_filters( 'is_jetpack_site', false, get_current_blog_id() );
return $is_jetpack ? 'jetpack_wga' : 'wga';
}

/**
* Get GA settings.
*
* @return array
*/
public function get_google_analytics_settings() {
$settings = get_option( $this->get_google_analytics_option_name() );

// The `is_active` flag is missing from the settings, add a value based on the module status.
if ( is_array( $settings ) && ! array_key_exists( 'is_active', $settings ) ) {
$settings['is_active'] = ( new Modules() )->is_active( 'google-analytics', false );
}

return $settings;
}

/**
* Add amp-analytics tags.
*
Expand Down
32 changes: 32 additions & 0 deletions projects/packages/google-analytics/src/class-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,36 @@ public static function is_dnt_enabled() {

return false;
}

/**
* Determine if a string is truthy. If it's not a string, which can happen with
* not well-formed data coming from Jetpack sites, we still consider it a truthy value.
*
* @since $$next-version$$
*
* @param mixed $value true, 1, "1", "t", and "true" (case insensitive) are truthy, everything else isn't.
* @return bool
*/
public static function is_truthy( $value ) {
if ( true === $value ) {
return true;
}

if ( 1 === $value ) {
return true;
}

if ( ! is_string( $value ) ) {
return false;
}

switch ( strtolower( $value ) ) {
case '1':
case 't':
case 'true':
return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Check for active modules among the unavailable ones.
20 changes: 14 additions & 6 deletions projects/packages/status/src/class-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ class Modules {
* Check whether or not a Jetpack module is active.
*
* @param string $module The slug of a Jetpack module.
* @param bool $available_only Whether to only check among available modules.
*
* @return bool
*/
public function is_active( $module ) {
public function is_active( $module, $available_only = true ) {
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
return true;
}

return in_array( $module, self::get_active(), true );
return in_array( $module, self::get_active( $available_only ), true );
}

/**
Expand Down Expand Up @@ -184,8 +186,12 @@ public function get_file_data( $file, $headers ) {

/**
* Get a list of activated modules as an array of module slugs.
*
* @param bool $available_only Filter out the unavailable (deleted) modules.
*
* @return array
*/
public function get_active() {
public function get_active( $available_only = true ) {
$active = \Jetpack_Options::get_option( 'active_modules' );

if ( ! is_array( $active ) ) {
Expand All @@ -206,9 +212,11 @@ public function get_active() {
$active[] = 'protect';
}

// If it's not available, it shouldn't be active.
// We don't delete it from the options though, as it will be active again when a plugin gets reactivated.
$active = array_intersect( $active, $this->get_available() );
if ( $available_only ) {
// If it's not available, it shouldn't be active.
// We don't delete it from the options though, as it will be active again when a plugin gets reactivated.
$active = array_intersect( $active, $this->get_available() );
}

/**
* Allow filtering of the active modules.
Expand Down
2 changes: 1 addition & 1 deletion projects/plugins/jetpack/.phan/baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
'json-endpoints/class.wpcom-json-api-render-endpoint.php' => ['PhanPluginSimplifyExpressionBool', 'PhanTypeMismatchArgument'],
'json-endpoints/class.wpcom-json-api-render-shortcode-endpoint.php' => ['PhanNoopNew', 'PhanTypeMismatchReturn'],
'json-endpoints/class.wpcom-json-api-sharing-buttons-endpoint.php' => ['PhanNoopNew', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchArgument', 'PhanTypeMismatchReturn', 'PhanTypePossiblyInvalidDimOffset'],
'json-endpoints/class.wpcom-json-api-site-settings-endpoint.php' => ['PhanDeprecatedFunction', 'PhanNoopNew', 'PhanParamTooMany', 'PhanRedundantCondition', 'PhanRedundantConditionInLoop', 'PhanTypeMismatchArgument', 'PhanTypeMismatchReturn', 'PhanTypeMismatchReturnProbablyReal', 'PhanTypePossiblyInvalidDimOffset'],
'json-endpoints/class.wpcom-json-api-site-settings-endpoint.php' => ['PhanDeprecatedFunction', 'PhanNoopNew', 'PhanParamTooMany', 'PhanRedundantCondition', 'PhanRedundantConditionInLoop', 'PhanTypeMismatchArgument', 'PhanTypeMismatchReturn', 'PhanTypePossiblyInvalidDimOffset'],
'json-endpoints/class.wpcom-json-api-site-settings-v1-2-endpoint.php' => ['PhanNoopNew'],
'json-endpoints/class.wpcom-json-api-site-settings-v1-3-endpoint.php' => ['PhanNoopNew'],
'json-endpoints/class.wpcom-json-api-site-settings-v1-4-endpoint.php' => ['PhanNoopNew'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: other

Extract the Google Analytics API into the package.
5 changes: 3 additions & 2 deletions projects/plugins/jetpack/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 25eb8fa

Please sign in to comment.