Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.15% covered (success)
98.15%
53 / 54
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Google_Analytics_Settings
98.15% covered (success)
98.15%
53 / 54
80.00% covered (warning)
80.00%
4 / 5
19
0.00% covered (danger)
0.00%
0 / 1
 is_available
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 register
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 update
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
8.01
 get_option_name
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * The Google Analytics setting the dashboard's Settings tab edits.
4 *
5 * @package automattic/jetpack-premium-analytics
6 */
7
8namespace Automattic\Jetpack\PremiumAnalytics;
9
10use Automattic\Jetpack\Feature_Flags\Feature_Flags;
11use Automattic\Jetpack\Google_Analytics\GA_Manager;
12use WP_Error;
13
14/**
15 * Reads and writes the Google Analytics option that `GA_Manager` owns, for core's `wp/v2/settings` route.
16 */
17final class Google_Analytics_Settings {
18
19    /**
20     * The setting's name on the settings route. The option behind it is `jetpack_wga` or `wga`, as `GA_Manager` decides.
21     *
22     * @var string
23     */
24    public const REST_NAME = 'google_analytics';
25
26    /**
27     * The flag that moves the Google Analytics setting off Jetpack › Settings.
28     *
29     * @var string
30     */
31    private const FLAG = 'my-jetpack-settings-consolidation';
32
33    /**
34     * The plan feature that includes Google Analytics on WordPress.com.
35     *
36     * @var string
37     */
38    private const FEATURE = 'google-analytics';
39
40    /**
41     * The Measurement IDs `GA_Manager` accepts, or an empty string to clear it.
42     *
43     * @var string
44     */
45    private const CODE_PATTERN = '/^(|UA-\d+-\d+|G-[A-Z0-9]+)$/i';
46
47    /**
48     * Whether the site can set up Google Analytics here: WordPress.com loads `GA_Manager`, and the plan includes it.
49     *
50     * @return bool
51     */
52    public static function is_available() {
53        return Feature_Flags::is_enabled( self::FLAG )
54            // Only jetpack-mu-wpcom loads it, so self-hosted sites never get the setting.
55            && class_exists( GA_Manager::class )
56            // jetpack-mu-wpcom ships GA_Manager on its own release schedule.
57            && method_exists( GA_Manager::class, 'get_google_analytics_option_name' )
58            && function_exists( 'wpcom_site_has_feature' )
59            && \wpcom_site_has_feature( self::FEATURE );
60    }
61
62    /**
63     * Declare the setting so core's settings route exposes it.
64     *
65     * No `default` or `sanitize_callback`: core would apply them to every read and write of the option, including GA_Manager's own.
66     *
67     * @param string $group The settings group.
68     * @return void
69     */
70    public static function register( $group ) {
71        register_setting(
72            $group,
73            self::get_option_name(),
74            array(
75                'type'         => 'object',
76                'show_in_rest' => array(
77                    'name'   => self::REST_NAME,
78                    'schema' => array(
79                        'type'                 => 'object',
80                        'properties'           => array(
81                            'is_active'    => array( 'type' => 'boolean' ),
82                            'code'         => array( 'type' => 'string' ),
83                            'anonymize_ip' => array( 'type' => 'boolean' ),
84                        ),
85                        'additionalProperties' => false,
86                    ),
87                ),
88                'description'  => __( 'Google Analytics settings.', 'jetpack-premium-analytics-pkg' ),
89            )
90        );
91    }
92
93    /**
94     * The fields the Settings tab edits, defaults included.
95     *
96     * @return array{is_active: bool, code: string, anonymize_ip: bool}
97     */
98    public static function get() {
99        $stored = get_option( self::get_option_name(), array() );
100        $stored = is_array( $stored ) ? $stored : array();
101
102        return array(
103            'is_active'    => ! empty( $stored['is_active'] ),
104            'code'         => isset( $stored['code'] ) && is_string( $stored['code'] ) ? $stored['code'] : '',
105            'anonymize_ip' => ! empty( $stored['anonymize_ip'] ),
106        );
107    }
108
109    /**
110     * Merge a write into the stored option, keeping the fields this tab does not edit.
111     *
112     * @param mixed $value The fields sent.
113     * @return true|WP_Error
114     */
115    public static function update( $value ) {
116        $value = is_array( $value ) ? $value : array();
117
118        if ( isset( $value['code'] ) ) {
119            $value['code'] = trim( (string) $value['code'] );
120            if ( ! preg_match( self::CODE_PATTERN, $value['code'] ) ) {
121                return new WP_Error(
122                    'invalid_google_analytics_code',
123                    __( 'Enter a Measurement ID, such as G-XXXXXXX.', 'jetpack-premium-analytics-pkg' ),
124                    array( 'status' => 400 )
125                );
126            }
127        }
128
129        $option = self::get_option_name();
130        $stored = get_option( $option, array() );
131        $merged = array_merge( is_array( $stored ) ? $stored : array(), $value );
132
133        /** This filter is documented in projects/packages/google-analytics/src/class-ga-manager.php */
134        $merged = apply_filters( 'site_settings_update_wga', $merged, $value ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- GA_Manager's filter, applied as its own save path does.
135        if ( $merged === $stored ) {
136            return true;
137        }
138
139        if ( ! update_option( $option, $merged ) ) {
140            return new WP_Error( 'google_analytics_not_saved', __( 'Google Analytics settings could not be saved.', 'jetpack-premium-analytics-pkg' ) );
141        }
142
143        /** This action is documented in projects/packages/google-analytics/src/class-ga-manager.php */
144        do_action( 'jetpack_bump_stats_extras', 'google-analytics', empty( $merged['code'] ) ? 'disabled' : 'enabled' );
145
146        return true;
147    }
148
149    /**
150     * The option `GA_Manager` reads: `jetpack_wga` where Jetpack runs, `wga` on Simple.
151     *
152     * @return string
153     */
154    private static function get_option_name() {
155        return GA_Manager::get_instance()->get_google_analytics_option_name();
156    }
157}