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