Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.79% covered (warning)
78.79%
26 / 33
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Historically_Active_Modules
78.79% covered (warning)
78.79%
26 / 33
57.14% covered (warning)
57.14%
4 / 7
18.44
0.00% covered (danger)
0.00%
0 / 1
 register_rest_endpoints
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 permissions_callback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update_historically_active_jetpack_modules
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
8
 is_refresh_due
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 expire_refresh
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rest_trigger_historically_active_modules_update
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 queue_historically_active_jetpack_modules_update
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Sets up the Historically Active Modules rest api endpoint and helper functions
4 *
5 * @package automattic/my-jetpack
6 */
7
8namespace Automattic\Jetpack\My_Jetpack;
9
10use WP_Error;
11
12/**
13 * Registers REST route for updating historically active modules
14 * and includes all helper functions for triggering an update elsewhere
15 */
16class Historically_Active_Modules {
17    public const UPDATE_HISTORICALLY_ACTIVE_JETPACK_MODULES_KEY = 'jetpack_update_historically_active_modules';
18
19    /**
20     * Transient set while the list is fresh enough that a My Jetpack visit need not rebuild it.
21     */
22    public const REFRESHED_TRANSIENT_KEY = 'jetpack_my_jetpack_historically_active_modules_refreshed';
23
24    /**
25     * How long a rebuild satisfies My Jetpack visits, in seconds.
26     */
27    public const REFRESH_INTERVAL = HOUR_IN_SECONDS;
28
29    /**
30     * Register the REST API routes.
31     *
32     * @return void
33     */
34    public static function register_rest_endpoints() {
35        register_rest_route(
36            'my-jetpack/v1',
37            'site/update-historically-active-modules',
38            array(
39                'methods'             => \WP_REST_Server::EDITABLE,
40                'callback'            => __CLASS__ . '::rest_trigger_historically_active_modules_update',
41                'permission_callback' => __CLASS__ . '::permissions_callback',
42            )
43        );
44    }
45
46    /**
47     * Check user capabilities to access historically active modules.
48     *
49     * @access public
50     * @static
51     *
52     * @return true|WP_Error
53     */
54    public static function permissions_callback() {
55        return current_user_can( 'edit_posts' );
56    }
57
58    /**
59     * Update historically active Jetpack plugins
60     * Historically active is defined as the Jetpack plugins that are installed and active with the required connections
61     * This array will consist of any plugins that were active at one point in time and are still enabled on the site
62     *
63     * @return void
64     */
65    public static function update_historically_active_jetpack_modules() {
66        $historically_active_modules = \Jetpack_Options::get_option( 'historically_active_modules', array() );
67        $product_classes             = Products::get_products_classes();
68
69        foreach ( $product_classes as $product_class ) {
70            // Not the array key: the class map also lists Jetpack AI under an 'ai' alias.
71            $product_slug = $product_class::$slug;
72            $status       = $product_class::get_status();
73            // We want to leave modules in the array if they've been active in the past
74            // and were not manually disabled by the user.
75            if ( in_array( $status, Products::$broken_module_statuses, true ) ) {
76                continue;
77            }
78
79            // If the module is active and not already in the array, add it
80            if (
81                in_array( $status, Products::$active_module_statuses, true ) &&
82                ! in_array( $product_slug, $historically_active_modules, true )
83            ) {
84                    $historically_active_modules[] = $product_slug;
85            }
86
87            // If the module has been disabled due to a manual user action,
88            // or because of a missing plan error, remove it from the array.
89            // A bundle without a plan reports needs_plan rather than module_disabled, so it goes too.
90            if (
91                in_array( $status, Products::$disabled_module_statuses, true ) ||
92                ( Products::STATUS_NEEDS_PLAN === $status && $product_class::is_bundle_product() )
93            ) {
94                $historically_active_modules = array_values( array_diff( $historically_active_modules, array( $product_slug ) ) );
95            }
96        }
97
98        \Jetpack_Options::update_option( 'historically_active_modules', array_unique( $historically_active_modules ) );
99        set_transient( self::REFRESHED_TRANSIENT_KEY, 1, self::REFRESH_INTERVAL );
100    }
101
102    /**
103     * Whether My Jetpack should rebuild the list on this visit.
104     *
105     * @return bool
106     */
107    public static function is_refresh_due() {
108        return ! get_transient( self::REFRESHED_TRANSIENT_KEY );
109    }
110
111    /**
112     * Make the next My Jetpack visit rebuild the list, for changes no hook updates it on.
113     *
114     * @return void
115     */
116    public static function expire_refresh() {
117        delete_transient( self::REFRESHED_TRANSIENT_KEY );
118    }
119
120    /**
121     * REST API endpoint to trigger an update to the historically active Jetpack modules
122     *
123     * @return WP_Error|\WP_REST_Response
124     */
125    public static function rest_trigger_historically_active_modules_update() {
126        self::update_historically_active_jetpack_modules();
127        $historically_active_modules = \Jetpack_Options::get_option( 'historically_active_modules', array() );
128        return rest_ensure_response( $historically_active_modules );
129    }
130
131    /**
132     * Set transient to queue an update to the historically active Jetpack modules on the next wp-admin load
133     *
134     * @param string $plugin The plugin that triggered the update. This will be present if the function was queued by a plugin activation.
135     *
136     * @return void
137     */
138    public static function queue_historically_active_jetpack_modules_update( $plugin = null ) {
139        $plugin_filenames = Products::get_all_plugin_filenames();
140
141        if ( ! $plugin || in_array( $plugin, $plugin_filenames, true ) ) {
142            set_transient( self::UPDATE_HISTORICALLY_ACTIVE_JETPACK_MODULES_KEY, true );
143        }
144    }
145}