Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.50% covered (success)
92.50%
37 / 40
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Stats_Plugin
92.50% covered (success)
92.50%
37 / 40
75.00% covered (warning)
75.00%
6 / 8
13.07
0.00% covered (danger)
0.00%
0 / 1
 bootstrap
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 configure_packages
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 initialize_other_packages
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 is_jetpack_plugin_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 plugin_page_add_links
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 handle_plugin_activation
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
3.33
 activate_stats_module
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 filter_available_modules_add_stats
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Bootstrap class for the Jetpack Stats plugin.
4 *
5 * @package automattic/jetpack-stats-plugin
6 */
7
8namespace Automattic\Jetpack\Stats_Plugin;
9
10use Automattic\Jetpack\Config;
11use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
13use Automattic\Jetpack\Modules;
14use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
15use Automattic\Jetpack\Paths;
16use Automattic\Jetpack\Stats_Admin\Dashboard as Stats_Dashboard;
17
18/**
19 * Class to bootstrap the Jetpack Stats plugin.
20 */
21class Jetpack_Stats_Plugin {
22    /**
23     * The admin page slug registered by the Stats dashboard.
24     *
25     * Owned by `Automattic\Jetpack\Stats_Admin\Dashboard`, which registers a top-level
26     * menu with this slug. Kept here so the plugin can link to the page.
27     */
28    const ADMIN_PAGE_SLUG = 'stats';
29
30    /**
31     * Register hooks to initialize the plugin.
32     */
33    public static function bootstrap() {
34        add_action( 'plugins_loaded', array( self::class, 'configure_packages' ), 1 );
35        add_action( 'plugins_loaded', array( self::class, 'initialize_other_packages' ) );
36        add_action( 'activated_plugin', array( self::class, 'handle_plugin_activation' ) );
37        add_action( 'jetpack_site_registered', array( self::class, 'activate_stats_module' ) );
38        add_filter( 'plugin_action_links_' . JETPACK_STATS_PLUGIN__FILE_RELATIVE_PATH, array( self::class, 'plugin_page_add_links' ) );
39        add_filter( 'jetpack_get_available_standalone_modules', array( self::class, 'filter_available_modules_add_stats' ) );
40
41        /**
42         * The Jetpack plugin owns the Stats admin menu while it is active, and it chooses
43         * between the legacy Stats screen and the Odyssey dashboard. Once it is deactivated
44         * that choice falls to this plugin, so re-activate the module and take over the menu.
45         */
46        add_action( 'deactivate_jetpack/jetpack.php', array( self::class, 'activate_stats_module' ) );
47    }
48
49    /**
50     * Configure packages controlled by the `Config` class.
51     *
52     * Note: the function only configures the packages, but doesn't initialize them.
53     * The actual initialization is done on 'plugins_loaded' priority 2, which is the
54     * reason the function is hooked on priority 1.
55     */
56    public static function configure_packages() {
57        $config = new Config();
58        // Connection package.
59        $config->ensure(
60            'connection',
61            array(
62                'slug'     => JETPACK_STATS_PLUGIN__SLUG,
63                'name'     => 'Jetpack Stats',
64                'url_info' => 'https://jetpack.com/stats/',
65            )
66        );
67        // Sync package.
68        $config->ensure( 'sync' );
69        // Identity crisis package.
70        $config->ensure( 'identity_crisis' );
71        // Stats package: the tracking pixel, the `view_stats` capability map and the REST provider.
72        $config->ensure( 'stats' );
73        // Stats Admin package: the REST proxy to the WPCOM stats API used by the dashboard.
74        $config->ensure( 'stats_admin' );
75    }
76
77    /**
78     * Initialize packages not controlled by the `Config` class.
79     */
80    public static function initialize_other_packages() {
81        // Set up the REST authentication hooks.
82        Connection_Rest_Authentication::init();
83        // Initialize My Jetpack.
84        My_Jetpack_Initializer::init();
85
86        /**
87         * Three places can register the `stats` menu slug, and only one of them may run.
88         * `Stats_Admin\Main` registers the dashboard itself while the site has no connection,
89         * and the Jetpack plugin registers it from `modules/stats.php` once the Stats module
90         * loads. That leaves the connected site without the Jetpack plugin to this plugin.
91         */
92        if ( self::is_jetpack_plugin_active() ) {
93            return;
94        }
95
96        if ( ( new Connection_Manager() )->is_connected() ) {
97            Stats_Dashboard::init();
98        }
99    }
100
101    /**
102     * Whether the full Jetpack plugin is running alongside this one.
103     *
104     * @return bool
105     */
106    public static function is_jetpack_plugin_active() {
107        return class_exists( 'Jetpack' );
108    }
109
110    /**
111     * Add a Stats link to the plugin actions.
112     *
113     * @param array $links the array of links.
114     * @return array
115     */
116    public static function plugin_page_add_links( $links ) {
117        $stats_link = '<a href="' . esc_url( admin_url( 'admin.php?page=' . self::ADMIN_PAGE_SLUG ) ) . '">' . esc_html__( 'Stats', 'jetpack-stats' ) . '</a>';
118        array_unshift( $links, $stats_link );
119
120        return $links;
121    }
122
123    /**
124     * Redirect to the Stats dashboard when the plugin is activated.
125     *
126     * @param string $plugin Path to the plugin file relative to the plugins directory.
127     */
128    public static function handle_plugin_activation( $plugin ) {
129        // `activated_plugin` fires for every plugin, so ignore everything but this one.
130        // Otherwise activating an unrelated plugin turns Stats back on after a user disabled it.
131        if ( JETPACK_STATS_PLUGIN__FILE_RELATIVE_PATH !== $plugin ) {
132            return;
133        }
134
135        // On a connected site the module can be switched on right away. On a site with no
136        // connection this is a no-op.
137        self::activate_stats_module();
138
139        // The Stats page is registered in both connection states, so this redirect does not
140        // branch on one. Without a connection the dashboard offers a plan and drives the
141        // connection itself.
142        if ( ( new Paths() )->is_current_request_activating_plugin_from_plugins_screen( JETPACK_STATS_PLUGIN__FILE_RELATIVE_PATH ) ) {
143            wp_safe_redirect( esc_url( admin_url( 'admin.php?page=' . self::ADMIN_PAGE_SLUG ) ) );
144            exit( 0 );
145        }
146    }
147
148    /**
149     * Activate the Stats module on a connected site.
150     *
151     * @return bool True when the module is active after the call, false when the site has no
152     *              connection or the module could not be activated.
153     */
154    public static function activate_stats_module() {
155        if ( ! ( new Connection_Manager() )->is_connected() ) {
156            return false;
157        }
158
159        return (bool) ( new Modules() )->activate( 'stats', false, false );
160    }
161
162    /**
163     * Add the Stats module to the list of modules available without the Jetpack plugin.
164     *
165     * @param array $modules The available modules.
166     * @return array
167     */
168    public static function filter_available_modules_add_stats( $modules ) {
169        return array_merge( array( 'stats' ), $modules );
170    }
171}