Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 100
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Woocommerce_Analytics
0.00% covered (danger)
0.00%
0 / 100
0.00% covered (danger)
0.00%
0 / 9
2352
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 should_track_store
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
272
 wp_head_top
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
42
 enqueue_tracking_script
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 enqueue_client_script
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 register_rest_routes
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 maybe_add_proxy_speed_module
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
210
 maybe_remove_proxy_speed_module
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 init_filesystem
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Main class for the WooCommerce Analytics package.
4 * Originally ported from the Jetpack_Google_Analytics code.
5 *
6 * @package automattic/woocommerce-analytics
7 */
8
9namespace Automattic;
10
11use Automattic\Jetpack\Assets;
12use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
13use Automattic\Woocommerce_Analytics\My_Account;
14use Automattic\Woocommerce_Analytics\Universal;
15use Automattic\Woocommerce_Analytics\WC_Analytics_Tracking_Proxy;
16
17/**
18 * Instantiate WooCommerce Analytics
19 */
20class Woocommerce_Analytics {
21    /**
22     * Package version.
23     */
24    const PACKAGE_VERSION = '0.15.5';
25
26    /**
27     * Proxy speed module version.
28     *
29     * @var string
30    */
31    const PROXY_SPEED_MODULE_VERSION = '1.0.0';
32
33    /**
34     * Initializer.
35     * Used to configure the WooCommerce Analytics package.
36     *
37     * @return void
38     */
39    public static function init() {
40        if ( ! self::should_track_store() || did_action( 'woocommerce_analytics_init' ) ) {
41            return;
42        }
43
44        // loading _wca.
45        add_action( 'wp_head', array( __CLASS__, 'wp_head_top' ), 1 );
46
47        // loading s.js.
48        add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_tracking_script' ) );
49
50        // loading client-side analytics script.
51        add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_client_script' ) );
52
53        // Initialize general store tracking actions.
54        add_action( 'init', array( new Universal(), 'init_hooks' ) );
55        add_action( 'init', array( new My_Account(), 'init_hooks' ) );
56
57        // Initialize REST API endpoints.
58        add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
59
60        /**
61         * Fires after the WooCommerce Analytics package is initialized
62         *
63         * @since 0.1.5
64         */
65        do_action( 'woocommerce_analytics_init' );
66    }
67
68    /**
69     * WooCommerce Analytics is only available to Jetpack connected WooCommerce stores
70     * with WooCommerce version 3.0 or higher
71     *
72     * @return bool
73     */
74    public static function should_track_store() {
75        // Ensure this is available, even with mu-plugins.
76        if ( ! function_exists( 'is_plugin_active' ) ) {
77            require_once ABSPATH . 'wp-admin/includes/plugin.php';
78        }
79
80        /**
81         * Make sure WooCommerce is installed and active
82         *
83         * This action is documented in https://docs.woocommerce.com/document/create-a-plugin
84         */
85        if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
86            return false;
87        }
88        // Ensure the WooCommerce class exists and is a valid version.
89        $minimum_woocommerce_active = class_exists( 'WooCommerce' ) && version_compare( \WC_VERSION, '3.0', '>=' );
90        if ( ! $minimum_woocommerce_active ) {
91            return false;
92        }
93
94        // Ensure the WC Tracks classes exist.
95        if ( ! class_exists( 'WC_Tracks' ) ) {
96            if ( ! defined( 'WC_ABSPATH' ) || ! file_exists( WC_ABSPATH . 'includes/tracks/class-wc-tracks.php' ) ) {
97                return false;
98            }
99
100            include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks.php';
101            include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
102            include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-client.php';
103        }
104
105        // Tracking only Site pages.
106        if ( is_admin() || wp_doing_ajax() || wp_is_xml_request() || is_login() || is_feed() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
107            return false;
108        }
109
110        // Make sure the site is connected to WordPress.com.
111        if ( ! ( new Jetpack_Connection() )->is_connected() ) {
112            return false;
113        }
114
115        return true;
116    }
117
118    /**
119     * Make _wca available to queue events
120     */
121    public static function wp_head_top() {
122        if ( is_cart() || is_checkout() || is_checkout_pay_page() || is_order_received_page() || is_add_payment_method_page() ) {
123            echo '<script>window._wca_prevent_referrer = true;</script>' . "\r\n";
124        }
125        echo '<script>window._wca = window._wca || [];</script>' . "\r\n";
126    }
127
128    /**
129     * Place script to call s.js, Store Analytics.
130     */
131    public static function enqueue_tracking_script() {
132        $url = sprintf(
133            'https://stats.wp.com/s-%d.js',
134            gmdate( 'YW' )
135        );
136
137        wp_enqueue_script(
138            'woocommerce-analytics',
139            $url,
140            array(),
141            null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- The version is set in the URL.
142            array(
143                'in_footer' => false,
144                'strategy'  => 'defer',
145            )
146        );
147    }
148
149    /**
150     * Enqueue client-side analytics script.
151     */
152    public static function enqueue_client_script() {
153        Assets::register_script(
154            'woocommerce-analytics-client',
155            '../build/woocommerce-analytics-client.js',
156            __FILE__,
157            array(
158                'in_footer' => true,
159                'strategy'  => 'defer',
160                'enqueue'   => true,
161            )
162        );
163    }
164
165    /**
166     * Register REST API routes.
167     */
168    public static function register_rest_routes() {
169        $controller = new WC_Analytics_Tracking_Proxy();
170        $controller->register_routes();
171    }
172
173    /**
174     * Maybe add proxy speed module.
175     */
176    public static function maybe_add_proxy_speed_module() {
177        /**
178         * Filter to control auto-installation of the proxy speed module mu-plugin.
179         *
180         * When this filter returns false, the mu-plugin file can't be added automatically.
181         *
182         * @since 0.15.0
183         *
184         * @param bool $auto_install Whether to auto-install the mu-plugin. Default true.
185         */
186        if ( ! apply_filters( 'woocommerce_analytics_auto_install_proxy_speed_module', true ) ) {
187            return;
188        }
189
190        if ( ! self::init_filesystem() ) {
191            return;
192        }
193
194        global $wp_filesystem;
195
196        // Create the mu-plugin directory if it doesn't exist.
197        if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
198            wp_mkdir_p( WPMU_PLUGIN_DIR );
199        }
200
201        // If the mu-plugin directory doesn't exist, we can't copy the files.
202        if ( ! is_dir( WPMU_PLUGIN_DIR ) ) {
203            return;
204        }
205
206        // Check if the mu-plugin directory is writable.
207        if ( ! $wp_filesystem->is_writable( WPMU_PLUGIN_DIR ) ) {
208            if ( function_exists( 'wc_get_logger' ) ) {
209                wc_get_logger()->debug( 'WooCommerce Analytics proxy speed module not installed: mu-plugins directory is not writable.', array( 'source' => 'woocommerce-analytics' ) );
210            }
211            return;
212        }
213
214        if ( get_option( 'woocommerce_analytics_proxy_speed_module_version' ) === self::PROXY_SPEED_MODULE_VERSION ) {
215            // No need to copy the files again.
216            return;
217        }
218
219        $mu_plugin_src_file  = __DIR__ . '/mu-plugin/woocommerce-analytics-proxy-speed-module.php';
220        $mu_plugin_dest_file = trailingslashit( WPMU_PLUGIN_DIR ) . 'woocommerce-analytics-proxy-speed-module.php';
221
222        // Verify source file exists before attempting to copy.
223        if ( ! file_exists( $mu_plugin_src_file ) ) {
224            if ( function_exists( 'wc_get_logger' ) ) {
225                wc_get_logger()->error( 'WooCommerce Analytics proxy speed module source file not found.', array( 'source' => 'woocommerce-analytics' ) );
226            }
227            return;
228        }
229
230        $content = $wp_filesystem->get_contents( $mu_plugin_src_file );
231        if ( false === $content ) {
232            if ( function_exists( 'wc_get_logger' ) ) {
233                wc_get_logger()->error( 'Failed to read the WooCommerce Analytics proxy speed module source file.', array( 'source' => 'woocommerce-analytics' ) );
234            }
235            return;
236        }
237
238        if ( ! $wp_filesystem->put_contents( $mu_plugin_dest_file, $content ) ) {
239            if ( function_exists( 'wc_get_logger' ) ) {
240                wc_get_logger()->error( 'Failed to copy the WooCommerce Analytics proxy speed module file.', array( 'source' => 'woocommerce-analytics' ) );
241            }
242            return;
243        }
244
245        update_option( 'woocommerce_analytics_proxy_speed_module_version', self::PROXY_SPEED_MODULE_VERSION );
246    }
247
248    /**
249     * Maybe removes the proxy speed module. This should be invoked when the plugin is deactivated.
250     */
251    public static function maybe_remove_proxy_speed_module() {
252        if ( ! self::init_filesystem() ) {
253            return;
254        }
255
256        global $wp_filesystem;
257
258        /**
259         * Clean up MU plugin.
260         */
261        $file_path = trailingslashit( WPMU_PLUGIN_DIR ) . 'woocommerce-analytics-proxy-speed-module.php';
262
263        if ( $wp_filesystem->exists( $file_path ) && $wp_filesystem->is_writable( $file_path ) ) {
264            $wp_filesystem->delete( $file_path );
265        }
266
267        delete_option( 'woocommerce_analytics_proxy_speed_module_version' );
268    }
269
270    /**
271     * Initialize the WP filesystem.
272     *
273     * @return bool True if filesystem is initialized, false otherwise.
274     */
275    private static function init_filesystem() {
276        if ( ! function_exists( 'WP_Filesystem' ) ) {
277            require_once ABSPATH . 'wp-admin/includes/file.php';
278        }
279
280        // Initialize the WP filesystem.
281        ob_start();
282        $initialized = WP_Filesystem();
283        ob_end_clean();
284
285        return $initialized;
286    }
287}