Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
5.05% covered (danger)
5.05%
5 / 99
0.00% covered (danger)
0.00%
0 / 10
CRAP
n/a
0 / 0
wpcomsh_is_admin_menu_api_request
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_mimic_admin_page_load
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
wpcomsh_admin_color_scheme_picker_disabled
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
wpcomsh_set_connected_user_data_as_user_options
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
56
wpcomsh_activate_nav_unification
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
wpcomsh_is_site_sticker_active
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
wpcomsh_update_plugin_link_destination
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
wpcomsh_update_plugin_add_filter
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_get_wpcom_admin_interface_option
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcomsh_unsync_color_schemes_on_save
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Customizations to the Masterbar module available in Jetpack.
4 * We want that feature to always be available on Atomic sites.
5 *
6 * @package wpcomsh
7 */
8
9use Automattic\Jetpack\Connection\Manager as Connection_Manager;
10
11/**
12 * Check if the current request is an API request to the `wpcom/v2/admin-menu` endpoint.
13 *
14 * @return bool
15 */
16function wpcomsh_is_admin_menu_api_request() {
17    $route = wp_unslash( $_GET['rest_route'] ?? '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
18    return str_starts_with( wp_unslash( $_SERVER['REQUEST_URI'] ), '/?rest_route=%2Fwpcom%2Fv2%2Fadmin-menu' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
19        && is_string( $route ) && str_starts_with( $route, '/wpcom/v2/admin-menu' );
20}
21
22/**
23 * Sets WP_ADMIN constant on API requests for admin menus.
24 *
25 * Attempt to increase our chances that third-party plugins will
26 * register their menu items based on `is_admin()` returning true.
27 *
28 * This has to run before plugins are loaded.
29 */
30function wpcomsh_mimic_admin_page_load() {
31    if ( wpcomsh_is_admin_menu_api_request() ) {
32        // Display errors can cause the API request to fail due to the PHP notice
33        // triggered by `$pagenow` not being correctly determined when `WP_ADMIN`
34        // is forced on a non-WP Admin page.
35        @ini_set( 'display_errors', false ); // phpcs:ignore
36
37        define( 'WP_ADMIN', true );
38        add_action(
39            'wp_loaded',
40            function () {
41                require_once ABSPATH . 'wp-admin/includes/admin.php';
42            }
43        );
44
45        // Valid requests will exit before here, but ensure custom code doesn't handle things poorly.
46        add_action(
47            'wp',
48            /**
49             * Rejects the request.
50             *
51             * @return never
52             */
53            function () {
54                status_header( 400 );
55                exit;
56            },
57            PHP_INT_MIN
58        );
59    }
60}
61add_action( 'muplugins_loaded', 'wpcomsh_mimic_admin_page_load' );
62
63/**
64 * Prints the calypso page link for changing a color scheme.
65 **/
66function wpcomsh_admin_color_scheme_picker_disabled() {
67    printf(
68        '<a target="_blank" href="%1$s">%2$s</a>',
69        esc_url( 'https://wordpress.com/me/account' ),
70        esc_html( __( 'Set your color scheme on WordPress.com.', 'wpcomsh' ) )
71    );
72}
73
74/**
75 * Gets data from the `wpcom.getUser` XMLRPC response and set it as user options. This is hooked
76 * into the `set_transient` action that is triggered everytime the XMLRPC response is read.
77 *
78 * @see https://github.com/Automattic/jetpack/blob/57ca1d524a6f6e446c5a3891d3024c71a6b0684b/projects/packages/connection/src/class-manager.php#L676
79 *
80 * @param string $transient  The name of the transient.
81 * @param mixed  $value      Transient value.
82 */
83function wpcomsh_set_connected_user_data_as_user_options( $transient, $value ) {
84    if ( 0 !== strpos( $transient, 'jetpack_connected_user_data_' . get_current_user_id() ) ) {
85        return;
86    }
87
88    if ( ! $value || ! is_array( $value ) ) {
89        return;
90    }
91
92    if ( isset( $value['site_count'] ) ) {
93        update_user_option( get_current_user_id(), 'wpcom_site_count', $value['site_count'] );
94    }
95
96    if ( isset( $value['sidebar_collapsed'] ) ) {
97        set_user_setting( 'mfold', $value['sidebar_collapsed'] ? 'f' : 'o' );
98    }
99}
100
101add_action( 'set_transient', 'wpcomsh_set_connected_user_data_as_user_options', 10, 2 );
102
103/**
104 * Determines whether Nav Unification should be enabled (pbAPfg-Ou-p2).
105 *
106 * This function is hooked into the `jetpack_load_admin_menu_class` filter that lives in Jetpack.
107 * See https://github.com/Automattic/jetpack/blob/507142b09bae12b58e84c0c2b7d20024563f170d/modules%2Fmasterbar.php#L29.
108 *
109 * @return bool Whether Nav Unification should be enabled.
110 */
111function wpcomsh_activate_nav_unification() {
112    remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
113    $is_wp_admin_menu = get_option( 'wpcom_admin_interface' ) === 'wp-admin';
114
115    if ( function_exists( 'wpcom_admin_interface_pre_get_option' ) ) {
116        add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 );
117    }
118
119    // Disable when in the redesigned nav.
120    if ( $is_wp_admin_menu ) {
121        return false;
122    }
123
124    $user_id = get_current_user_id();
125
126    // Loads for all API requests to the admin-menu endpoint (i.e. Calypso).
127    if ( wpcomsh_is_admin_menu_api_request() ) {
128        return true;
129    }
130
131    // Disable when explicitly requested. This is an escape hatch for HEs. See paYJgx-1p8-p2.
132    if ( isset( $_GET['disable-nav-unification'] ) ) { // phpcs:ignore WordPress.Security
133        return false;
134    }
135
136    // Disable for users not connected to WP.com.
137    if ( ! class_exists( 'Automattic\Jetpack\Connection\Manager' ) ) {
138        return false;
139    }
140    $connection_manager = new Connection_Manager( 'jetpack' );
141    if ( ! $connection_manager->is_user_connected( $user_id ) ) {
142        return false;
143    }
144
145    // Enabled by default.
146    return true;
147}
148add_filter( 'jetpack_load_admin_menu_class', 'wpcomsh_activate_nav_unification' );
149
150/**
151 * Checks if site sticker is toggled on/off.
152 * For further information/context on Atomic_Persistent_Data and site_stickers please also see this diff: D66496-code
153 *
154 * @param string $sticker_name Name of the site sticker to check.
155 * @return boolean
156 */
157function wpcomsh_is_site_sticker_active( $sticker_name ) {
158    if ( ! class_exists( '\Atomic_Persistent_Data' ) ) {
159        return false;
160    }
161
162    $persistent_data = new \Atomic_Persistent_Data();
163
164    if ( $persistent_data->{"site_sticker_{$sticker_name}"} ) {
165        return true;
166    }
167
168    return false;
169}
170
171/**
172 * Forces the Add New (plugin install) link to be Calypso.
173 *
174 * @param string $url  The complete URL including scheme and path.
175 * @param string $path Path relative to the URL. Blank string if no path is specified.
176 *
177 * @return string
178 */
179function wpcomsh_update_plugin_link_destination( $url, $path ) {
180    // Run only for plugin-install.php links.
181    if ( ! strpos( $url, '/plugin-install.php' ) ) {
182        return $url;
183    }
184
185    // If Jetpack doesn't exist, stay in wp-admin
186    if ( ! class_exists( 'Jetpack' ) ) {
187        return $url;
188    }
189
190    // Run only if SSO is active
191    if ( ! Jetpack::is_module_active( 'sso' ) ) {
192        return $url;
193    }
194
195    // Return WP Admin url for example: plugin-install.php?tab=plugin-information&plugin=classic-editor&TB_iframe=true&width=600&height=550
196    if ( strpos( $path, '?' ) ) {
197        return $url;
198    }
199
200    return 'https://wordpress.com/plugins/' . ( new Automattic\Jetpack\Status() )->get_site_suffix();
201}
202
203/**
204 * Adds filters so that Add New menu & button
205 * redirect to Calypso.
206 *
207 * @return void
208 */
209function wpcomsh_update_plugin_add_filter() {
210    // If site is not connected or nav redesign is enabled, return early.
211    if ( ! class_exists( 'Automattic\Jetpack\Status' ) || get_option( 'wpcom_admin_interface' ) === 'wp-admin' ) {
212        return;
213    }
214
215    // Update any plugin-install.php links appearing in /wp-admin/plugins.php or elsewhere.
216    add_filter( 'self_admin_url', 'wpcomsh_update_plugin_link_destination', 10, 2 );
217}
218add_action( 'admin_menu', 'wpcomsh_update_plugin_add_filter' );
219
220/**
221 * Enable the mailbox in WPCOMSH sites.
222 */
223add_filter( 'jetpack_show_wpcom_inbox_menu', '__return_true' );
224
225/**
226 * Ensure that when getting the wpcom_admin_interface option, we return a value from the persistent data store if
227 * available, otherwise fall back to the option value.
228 *
229 * @return false|string|null
230 */
231function wpcomsh_get_wpcom_admin_interface_option() {
232
233    if ( class_exists( '\Atomic_Persistent_Data' ) ) {
234        $persistent_data = new \Atomic_Persistent_Data();
235
236        // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
237        if ( isset( $persistent_data->WPCOM_ADMIN_INTERFACE ) ) {
238            // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
239            return $persistent_data->WPCOM_ADMIN_INTERFACE;
240        }
241    }
242
243    // Fallback to the current option value.
244    return false;
245}
246// This pre_option_{$option} hook runs before the option is retrieved from the database with get_option().
247add_filter( 'pre_option_wpcom_admin_interface', 'wpcomsh_get_wpcom_admin_interface_option' );
248
249/**
250 * Color scheme from Calypso (/me/account) is stored as user option,
251 * while color scheme from /wp-admin/profile.php is stored as user meta.
252 *
253 * When saving the color scheme from /wp-admin/profile.php, we want to unsync them:
254 *
255 * 1. Read the current color scheme from Calypso (if there's still any).
256 * 2. Delete the color scheme from Calypso, effectively only using color scheme
257 *    from wp-admin going forward.
258 */
259function wpcomsh_unsync_color_schemes_on_save() {
260    // Returns Calypso color scheme (if still exists),
261    // or wp-admin color scheme otherwise.
262    $maybe_synced_color_scheme = get_user_option( 'admin_color' );
263
264    // Always return the (maybe) synced color scheme above.
265    add_filter(
266        'get_user_metadata',
267        function ( $value, $user_id, $meta_key ) use ( $maybe_synced_color_scheme ) {
268            if ( $meta_key === 'admin_color' ) {
269                return $maybe_synced_color_scheme;
270            }
271        },
272        999999,
273        3
274    );
275
276    // Delete the synced color scheme.
277    add_filter(
278        'update_user_metadata',
279        function ( $check, $user_id, $meta_key ) {
280            if ( $meta_key === 'admin_color' ) {
281                delete_user_option( $user_id, 'admin_color' );
282            }
283        },
284        10,
285        3
286    );
287}
288add_action( 'wp_ajax_save-user-color-scheme', 'wpcomsh_unsync_color_schemes_on_save', 1 );