Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Activity_Log
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 7
240
0.00% covered (danger)
0.00%
0 / 1
 initialize
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 add_wp_admin_submenu
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 is_available
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 admin_init
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 enqueue_admin_scripts
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 render_page
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 register_rest_routes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Primary class for the Jetpack Activity Log package.
4 *
5 * @package automattic/jetpack-activity-log
6 */
7
8namespace Automattic\Jetpack\Activity_Log;
9
10if ( ! defined( 'ABSPATH' ) ) {
11    exit( 0 );
12}
13
14use Automattic\Jetpack\Activity_Log\Initial_State as Activity_Log_Initial_State;
15use Automattic\Jetpack\Admin_UI\Admin_Menu;
16use Automattic\Jetpack\Assets;
17use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
18use Automattic\Jetpack\Connection\Manager as Connection_Manager;
19use function add_action;
20use function add_filter;
21use function current_user_can;
22use function did_action;
23use function do_action;
24use function is_multisite;
25use function sanitize_text_field;
26use function wp_add_inline_script;
27use function wp_unslash;
28use function wp_verify_nonce;
29
30/**
31 * Class Jetpack_Activity_Log
32 *
33 * Registers the Activity Log admin page and its REST routes inside the
34 * main Jetpack plugin.
35 */
36class Jetpack_Activity_Log {
37
38    /**
39     * Admin page slug.
40     *
41     * @var string
42     */
43    const PAGE_SLUG = 'jetpack-activity-log';
44
45    /**
46     * Script handle for the admin bundle.
47     *
48     * @var string
49     */
50    const SCRIPT_HANDLE = 'jetpack-activity-log';
51
52    /**
53     * Nonce action for refreshing the access flag after a checkout
54     * return. Used by `admin_init()` below and exposed to the client via
55     * Initial_State so the upsell CTA can embed a valid nonce in its
56     * `redirect_to`. Same shape as `Social_Admin_Page::REFRESH_PLAN_NONCE_ACTION`.
57     *
58     * @var string
59     */
60    const REFRESH_ACCESS_NONCE_ACTION = 'jetpack_activity_log_refresh_access';
61
62    /**
63     * Entry point. Idempotent: safe to call from multiple bootstraps.
64     */
65    public static function initialize() {
66        if ( did_action( 'jetpack_activity_log_initialized' ) ) {
67            return;
68        }
69
70        add_action( 'admin_menu', array( __CLASS__, 'add_wp_admin_submenu' ) );
71        add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
72        add_filter( 'jetpack_package_versions', array( Package_Version::class, 'send_package_version_to_tracker' ) );
73
74        /**
75         * Fires once the Jetpack Activity Log package has wired its hooks.
76         *
77         * @since 0.1.0
78         */
79        do_action( 'jetpack_activity_log_initialized' );
80    }
81
82    /**
83     * Register the Activity Log submenu under Jetpack.
84     *
85     * Mirrors the gating used by the legacy my-jetpack "Activity Log" menu
86     * item (connected user + non-multisite).
87     *
88     * @return string|null The resulting page's hook suffix, if registered.
89     */
90    public static function add_wp_admin_submenu() {
91        if ( ! self::is_available() ) {
92            return null;
93        }
94
95        $page_suffix = Admin_Menu::add_menu(
96            /** "Activity Log" is a product name, do not translate. */
97            'Activity Log',
98            'Activity Log',
99            'manage_options',
100            self::PAGE_SLUG,
101            array( __CLASS__, 'render_page' ),
102            12
103        );
104
105        if ( $page_suffix ) {
106            add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
107        }
108
109        return $page_suffix;
110    }
111
112    /**
113     * Whether the Activity Log page should be shown to the current user.
114     *
115     * @return bool
116     */
117    public static function is_available() {
118        if ( is_multisite() ) {
119            return false;
120        }
121
122        if ( ! current_user_can( 'manage_options' ) ) {
123            return false;
124        }
125
126        return ( new Connection_Manager() )->is_user_connected();
127    }
128
129    /**
130     * Fires when the admin page is loaded.
131     *
132     * When the user is returning from a successful checkout, the upsell
133     * CTA appends `?refresh_access=1&_wpnonce=…` to the `redirect_to`
134     * value it hands off to WordPress.com. Detect that here, verify the
135     * nonce, and drop the cached paid-plan signal so
136     * `Initial_State::get_data()` (which runs later in the same request,
137     * when the bundle is enqueued) rehydrates from WPCOM instead of
138     * re-serving the pre-checkout value. Mirrors the pattern in
139     * `Automattic\Jetpack\Publicize\Social_Admin_Page::admin_init()`.
140     */
141    public static function admin_init() {
142        if ( isset( $_GET['refresh_access'] ) && isset( $_GET['_wpnonce'] ) ) {
143            $nonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) );
144            if ( wp_verify_nonce( $nonce, self::REFRESH_ACCESS_NONCE_ACTION ) ) {
145                REST_Controller::clear_access_cache();
146            }
147        }
148
149        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_scripts' ) );
150    }
151
152    /**
153     * Enqueue the admin bundle and seed initial state.
154     */
155    public static function enqueue_admin_scripts() {
156        Assets::register_script(
157            self::SCRIPT_HANDLE,
158            '../build/index.js',
159            __FILE__,
160            array(
161                'in_footer'  => true,
162                'textdomain' => 'jetpack-activity-log',
163            )
164        );
165        Assets::enqueue_script( self::SCRIPT_HANDLE );
166
167        wp_add_inline_script( self::SCRIPT_HANDLE, ( new Activity_Log_Initial_State() )->render(), 'before' );
168        Connection_Initial_State::render_script( self::SCRIPT_HANDLE );
169    }
170
171    /**
172     * Render the admin page root node. React mounts into this element.
173     */
174    public static function render_page() {
175        ?>
176            <div id="jetpack-activity-log-root"></div>
177        <?php
178    }
179
180    /**
181     * Register the REST routes backing the Activity Log UI.
182     *
183     * Routes are added in Phase 2. This method exists now so that the
184     * `jetpack/v4/activity-log` namespace is reserved and the hook is wired.
185     */
186    public static function register_rest_routes() {
187        REST_Controller::register_rest_routes();
188    }
189}