Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
25.00% covered (danger)
25.00%
14 / 56
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Manage
25.00% covered (danger)
25.00%
14 / 56
28.57% covered (danger)
28.57%
2 / 7
207.05
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
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
 add_submenu_jetpack
14.29% covered (danger)
14.29%
2 / 14
0.00% covered (danger)
0.00%
0 / 1
8.67
 could_use_jp_manage
20.00% covered (danger)
20.00%
2 / 10
0.00% covered (danger)
0.00%
0 / 1
24.43
 is_agency_account
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
72
 get_jetpack_manage_data
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Tools to manage things related to "Jetpack Manage"
4 * - Add Jetpack Manage menu item.
5 * - Check if user is an agency (used by the Jetpack Manage banner)
6 *
7 * @package automattic/my-jetpack
8 */
9
10namespace Automattic\Jetpack\My_Jetpack;
11
12use Automattic\Jetpack\Admin_UI\Admin_Menu;
13use Automattic\Jetpack\Connection\Client;
14use Automattic\Jetpack\Connection\Manager as Connection_Manager;
15use Automattic\Jetpack\Redirect;
16use WP_Error;
17use WP_Rest_Response;
18
19/**
20 * Jetpack Manage features in My Jetpack.
21 */
22class Jetpack_Manage {
23    /**
24     * Initialize the class and hooks needed.
25     */
26    public static function init() {
27        add_action( 'admin_menu', array( self::class, 'add_submenu_jetpack' ) );
28    }
29
30    /**
31     * Register the REST API routes.
32     *
33     * @return void
34     */
35    public static function register_rest_endpoints() {
36        register_rest_route(
37            'my-jetpack/v1',
38            'jetpack-manage/data',
39            array(
40                'methods'             => \WP_REST_Server::READABLE,
41                'callback'            => __CLASS__ . '::get_jetpack_manage_data',
42                'permission_callback' => __CLASS__ . '::permissions_callback',
43            )
44        );
45    }
46
47    /**
48     * Check user capabilities to access historically active modules.
49     *
50     * @access public
51     * @static
52     *
53     * @return true|WP_Error
54     */
55    public static function permissions_callback() {
56        return current_user_can( 'manage_options' );
57    }
58
59    /**
60     * The page to be added to submenu
61     *
62     * @return void|null|string The resulting page's hook_suffix
63     */
64    public static function add_submenu_jetpack() {
65        // Do not display the menu if the user has < 2 sites.
66        if ( ! self::could_use_jp_manage( 2 ) ) {
67            return;
68        }
69
70        $args = array();
71
72        $blog_id = Connection_Manager::get_site_id( true );
73        if ( $blog_id ) {
74            $args = array( 'site' => $blog_id );
75        }
76
77        return Admin_Menu::add_menu(
78            __( 'Jetpack Manage', 'jetpack-my-jetpack' ),
79            _x( 'Jetpack Manage', 'product name shown in menu', 'jetpack-my-jetpack' ) . ' <span aria-hidden="true">↗</span>',
80            'manage_options',
81            esc_url( Redirect::get_url( 'cloud-manage-dashboard-wp-menu', $args ) ),
82            null,
83            16
84        );
85    }
86
87    /**
88     * Check if the user has enough sites to be able to use Jetpack Manage.
89     *
90     * @param int $min_sites Minimum number of sites to be able to use Jetpack Manage.
91     *
92     * @return bool Return true if the user has enough sites to be able to use Jetpack Manage.
93     */
94    public static function could_use_jp_manage( $min_sites = 2 ) {
95        // Only proceed if the user is connected to WordPress.com.
96        if ( ! ( new Connection_Manager() )->is_user_connected() ) {
97            return false;
98        }
99
100        // Do not display the menu if Jetpack plugin is not installed.
101        if ( ! class_exists( 'Jetpack' ) ) {
102            return false;
103        }
104
105        // Do not display the menu on Multisite.
106        if ( is_multisite() ) {
107            return false;
108        }
109
110        // Check if the user has the minimum number of sites.
111        $user_data = ( new Connection_Manager() )->get_connected_user_data( get_current_user_id() );
112        if ( ! isset( $user_data['site_count'] ) || $user_data['site_count'] < $min_sites ) {
113            return false;
114        }
115
116        return true;
117    }
118
119    /**
120     * Check if the user is a partner/agency.
121     *
122     * @return bool Return true if the user is a partner/agency, otherwise false.
123     */
124    public static function is_agency_account() {
125        // Only proceed if the user is connected to WordPress.com.
126        if ( ! ( new Connection_Manager() )->is_user_connected() ) {
127            return false;
128        }
129
130        // Get the cached partner data.
131        $partner = get_transient( 'jetpack_partner_data' );
132
133        if ( $partner === false ) {
134            $wpcom_response = Client::wpcom_json_api_request_as_user( '/jetpack-partners' );
135
136            if ( 200 !== wp_remote_retrieve_response_code( $wpcom_response ) || is_wp_error( $wpcom_response ) ) {
137                return false;
138            }
139
140            $partner_data = json_decode( wp_remote_retrieve_body( $wpcom_response ) );
141
142            // The jetpack-partners endpoint will return only one partner data into an array, it uses Jetpack_Partner::find_by_owner.
143            if ( ! is_array( $partner_data ) || count( $partner_data ) !== 1 || ! is_object( $partner_data[0] ) ) {
144                return false;
145            }
146
147            $partner = $partner_data[0];
148
149            // Cache the partner data for 1 hour.
150            set_transient( 'jetpack_partner_data', $partner, HOUR_IN_SECONDS );
151        }
152
153        return $partner->partner_type === 'agency';
154    }
155
156    /**
157     * Get Jetpack Manage data for REST API.
158     *
159     * @return WP_Error|WP_REST_Response
160     */
161    public static function get_jetpack_manage_data() {
162        $is_enabled        = self::could_use_jp_manage();
163        $is_agency_account = self::is_agency_account();
164
165        return rest_ensure_response(
166            array(
167                'isEnabled'       => $is_enabled,
168                'isAgencyAccount' => $is_agency_account,
169            )
170        );
171    }
172}