Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
45.83% covered (danger)
45.83%
121 / 264
36.36% covered (danger)
36.36%
4 / 11
CRAP
n/a
0 / 0
maybe_add_origin_site_id_to_url
n/a
0 / 0
n/a
0 / 0
1
add_origin_admin_bar_to_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcom_enqueue_admin_bar_assets
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_always_use_user_locale
16.67% covered (danger)
16.67%
2 / 12
0.00% covered (danger)
0.00%
0 / 1
13.26
wpcom_replace_wp_logo_with_wpcom_logo_menu
100.00% covered (success)
100.00%
50 / 50
100.00% covered (success)
100.00%
1 / 1
8
wpcom_add_shopping_cart
6.67% covered (danger)
6.67%
2 / 30
0.00% covered (danger)
0.00%
0 / 1
25.33
wpcom_replace_edit_profile_menu_to_me
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
4.77
wpcom_add_my_wpcom_account_submenu
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
wpcom_custom_wpcom_admin_bar_class
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_edit_site_menu_override
16.67% covered (danger)
16.67%
1 / 6
0.00% covered (danger)
0.00%
0 / 1
4.31
wpcom_add_site_badges_and_plan
50.63% covered (warning)
50.63%
40 / 79
0.00% covered (danger)
0.00%
0 / 1
100.20
1<?php
2/**
3 * WordPress.com admin bar
4 *
5 * Modifies the WordPress admin bar with WordPress.com-specific stuff.
6 *
7 * @package automattic/jetpack-mu-wpcom
8 */
9
10use Automattic\Jetpack\Connection\Urls;
11use Automattic\Jetpack\Current_Plan;
12use Automattic\Jetpack\Jetpack_Mu_Wpcom;
13use Automattic\Jetpack\Status;
14
15// The $icon-color variable for admin color schemes.
16// See: https://github.com/WordPress/wordpress-develop/blob/679cc0c4a261a77bd8fdb140cd9b0b2ff80ebf37/src/wp-admin/css/colors/_variables.scss#L9
17// Only Core schemes are listed here. Calypso schemes all use #ffffff.
18const WPCOM_ADMIN_ICON_COLORS = array(
19    'blue'      => '#e5f8ff',
20    'coffee'    => '#f3f2f1',
21    'ectoplasm' => '#ece6f6',
22    'midnight'  => '#f3f2f1',
23    'fresh'     => '#a7aaad',
24    'ocean'     => '#f2fcff',
25    'light'     => '#999',
26    'modern'    => '#f3f1f1',
27    'sunrise'   => '#f3f1f1',
28);
29
30/**
31 * Adds the origin_site_id query parameter to a URL.
32 *
33 * @deprecated 6.10.0 Use Automattic\Jetpack\Connection\Urls::maybe_add_origin_site_id instead.
34 *
35 * @param string $url The URL to add the query param to.
36 * @return string The URL with the origin_site_id query parameter maybe added.
37 */
38function maybe_add_origin_site_id_to_url( $url ) {
39    _deprecated_function( __FUNCTION__, 'jetpack-mu-wpcom-6.10.0', 'Automattic\Jetpack\Connection\Urls::maybe_add_origin_site_id' );
40    return Urls::maybe_add_origin_site_id( $url );
41}
42
43/**
44 * Adds the origin_admin_bar query parameter to a URL.
45 * Calypso can use this parameter to know that the user is coming from wp-admin
46 * and to check whether it should do custom routing based on user preferences.
47 * E.g. redirecting to my.wordpress.com depending on their preference.
48 *
49 * @param string $url The URL to add the query param to.
50 * @return string The URL with the origin_admin_bar query parameter mey be added.
51 */
52function add_origin_admin_bar_to_url( $url ) {
53    return add_query_arg( 'origin_admin_bar', 'wpcom', $url );
54}
55
56/**
57 * Enqueue assets needed by the WordPress.com admin bar.
58 */
59function wpcom_enqueue_admin_bar_assets() {
60    $asset_file = include Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.asset.php';
61
62    wp_enqueue_script(
63        'wpcom-admin-bar',
64        plugins_url( 'build/wpcom-admin-bar/wpcom-admin-bar.js', Jetpack_Mu_Wpcom::BASE_FILE ),
65        $asset_file['dependencies'] ?? array(),
66        $asset_file['version'] ?? filemtime( Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.js' ),
67        array(
68            'strategy'  => 'defer',
69            'in_footer' => true,
70        )
71    );
72
73    wp_enqueue_style(
74        'wpcom-admin-bar',
75        plugins_url( 'build/wpcom-admin-bar/wpcom-admin-bar.css', Jetpack_Mu_Wpcom::BASE_FILE ),
76        array(),
77        $asset_file['version'] ?? filemtime( Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.css' )
78    );
79
80    /**
81     * Force the Atomic debug bar menu to be the first menu at the top-right.
82     */
83    if ( defined( 'AT_PROXIED_REQUEST' ) && AT_PROXIED_REQUEST ) {
84        wp_add_inline_style(
85            'wpcom-admin-bar',
86            <<<'CSS'
87                #wpadminbar .quicklinks #wp-admin-bar-top-secondary {
88                    display: flex;
89                }
90
91                #wpadminbar .quicklinks #wp-admin-bar-top-secondary #wp-admin-bar-debug-bar {
92                    order: -1;
93                }
94CSS
95        );
96    }
97
98    $admin_color      = is_admin() ? get_user_option( 'admin_color' ) : 'fresh';
99    $admin_icon_color = WPCOM_ADMIN_ICON_COLORS[ $admin_color ] ?? '#ffffff';
100
101    // Force the icon colors to have desktop color even on mobile viewport.
102    wp_add_inline_style(
103        'wpcom-admin-bar',
104        <<<CSS
105            #wpadminbar.mobile .quicklinks li:not(#wpwrap.wp-responsive-open #wp-admin-bar-menu-toggle) .ab-icon::before,
106            #wpadminbar.mobile .quicklinks li:not(#wpwrap.wp-responsive-open #wp-admin-bar-menu-toggle) .ab-item::before {
107                color: $admin_icon_color !important;
108            }
109CSS
110    );
111
112    // Force wpcom icons to have consistent color.
113    wp_add_inline_style(
114        'wpcom-admin-bar',
115        <<<CSS
116            :where(#wpadminbar .ab-icon) {
117                color: $admin_icon_color;
118            }
119CSS
120    );
121}
122add_action( 'wp_enqueue_scripts', 'wpcom_enqueue_admin_bar_assets' );
123add_action( 'admin_enqueue_scripts', 'wpcom_enqueue_admin_bar_assets' );
124
125/**
126 * Render the admin bar in user locale even on frontend screens.
127 */
128function wpcom_always_use_user_locale() {
129    if ( is_admin() || ! is_admin_bar_showing() ) {
130        return;
131    }
132
133    $site_locale = get_locale();
134    $user_locale = get_user_locale();
135
136    if ( $site_locale !== $user_locale ) {
137        switch_to_locale( $user_locale );
138        add_action(
139            'wp_after_admin_bar_render',
140            function () use ( $site_locale ) {
141                switch_to_locale( $site_locale );
142            }
143        );
144    }
145}
146add_action( 'admin_bar_menu', 'wpcom_always_use_user_locale', -1 );
147
148/**
149 * Replaces the WP logo with WP.com logo.
150 *
151 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
152 */
153function wpcom_replace_wp_logo_with_wpcom_logo_menu( $wp_admin_bar ) {
154    $about_node      = $wp_admin_bar->get_node( 'about' );
155    $contribute_node = $wp_admin_bar->get_node( 'contribute' );
156
157    foreach ( $wp_admin_bar->get_nodes() as $node ) {
158        if ( $node->parent === 'wp-logo' || $node->parent === 'wp-logo-external' ) {
159            $wp_admin_bar->remove_node( $node->id );
160        }
161    }
162    $wp_admin_bar->remove_node( 'wp-logo' );
163    $wp_admin_bar->add_node(
164        array(
165            'id'    => 'wp-logo',
166            'title' => '<span class="ab-icon" aria-hidden="true"></span><span class="screen-reader-text">' .
167                        /* translators: Hidden accessibility text. */
168                        'WordPress.com' .
169                        '</span>',
170            'href'  => add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/sites' ) ),
171            'meta'  => array(
172                'menu_title' => 'WordPress.com',
173            ),
174        )
175    );
176
177    $wp_admin_bar->add_node(
178        array(
179            'parent' => 'wp-logo',
180            'id'     => 'wpcom-sites',
181            'title'  => __( 'Sites', 'jetpack-mu-wpcom' ),
182            'href'   => add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/sites' ) ),
183        )
184    );
185
186    $wp_admin_bar->add_node(
187        array(
188            'parent' => 'wp-logo',
189            'id'     => 'wpcom-domains',
190            'title'  => __( 'Domains', 'jetpack-mu-wpcom' ),
191            'href'   => add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/domains/manage' ) ),
192        )
193    );
194
195    if ( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
196        $wp_admin_bar->add_group(
197            array(
198                'parent' => 'wp-logo',
199                'id'     => 'wp-logo-external',
200                'meta'   => array(
201                    'class' => 'ab-sub-secondary',
202                ),
203            )
204        );
205
206        if ( $about_node ) {
207            $about_node->parent = 'wp-logo-external';
208            $wp_admin_bar->add_node( (array) $about_node );
209        }
210        if ( $contribute_node ) {
211            $contribute_node->parent = 'wp-logo-external';
212            $wp_admin_bar->add_node( (array) $contribute_node );
213        }
214    }
215}
216add_action( 'admin_bar_menu', 'wpcom_replace_wp_logo_with_wpcom_logo_menu', 11 );
217
218/**
219 * Adds the Cart menu to the WordPress admin bar.
220 *
221 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
222 */
223function wpcom_add_shopping_cart( $wp_admin_bar ) {
224    // Return if the site isn't a simple site
225    if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
226        return;
227    }
228
229    // Include the shopping cart functionality from the specified path.
230    require_once WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/shopping-cart.php';
231
232    // Get the current blog ID.
233    $blog_id = get_current_blog_id();
234
235    $is_empty = \Store_Shopping_Cart::is_cart_empty(
236        array(
237            'blog_id' => $blog_id,
238            'user_id' => get_current_user_id(),
239        )
240    );
241
242    // If the cart is empty (no products), do not add the cart menu.
243    if ( $is_empty ) {
244        return;
245    }
246
247    // Get the Calypso site slug for the current blog.
248    $calypso_site_slug = \WPCOM_Masterbar::get_calypso_site_slug( $blog_id );
249
250    // If no Calypso site slug is found, return early.
251    if ( ! $calypso_site_slug ) {
252        return;
253    }
254
255    // Add the cart menu item to the WordPress admin bar.
256    $wp_admin_bar->add_menu(
257        array(
258            'id'     => 'cart', // Unique ID for the cart menu item.
259            'title'  => '<span class="ab-item cart-icon" aria-hidden="true"></span>' .
260                        '<div class="cart-icon__dot"></div>' .
261                        '<span class="screen-reader-text">' .
262                        /* translators: Hidden accessibility text. */
263                        __( 'Cart', 'jetpack-mu-wpcom' ) .
264                        '</span>',
265            'href'   => 'https://wordpress.com/checkout/' . esc_attr( $calypso_site_slug ), // Link to the checkout page.
266            'meta'   => array(
267                'class' => 'wp-admin-bar-cart', // Custom class for styling the cart menu item.
268            ),
269            'parent' => 'top-secondary', // Position the cart in the 'top-secondary' section of the admin bar.
270        )
271    );
272}
273
274// Hook the cart icon to the admin bar menu, placing it before the reader icon (same as Calypso).
275add_action( 'admin_bar_menu', 'wpcom_add_shopping_cart', 11 );
276
277// Add the reader icon to the admin bar before the help center icon.
278add_action( 'wp_loaded', array( 'Automattic\Jetpack\Newsletter\Reader_Link', 'init' ) );
279
280/**
281 * Points the "Edit Profile" and "Howdy,..." to /me if the user is not member of the blog.
282 *
283 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
284 */
285function wpcom_replace_edit_profile_menu_to_me( $wp_admin_bar ) {
286    if ( is_user_member_of_blog() ) {
287        return;
288    }
289
290    $edit_profile_node = $wp_admin_bar->get_node( 'user-info' );
291    if ( $edit_profile_node ) {
292        $edit_profile_node->href  = add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/me' ) );
293        $edit_profile_node->title = preg_replace( "/(<span class='display-name edit-profile'>)(.*?)(<\/span>)/", '$1' . __( 'My Profile', 'jetpack-mu-wpcom' ) . '$3', $edit_profile_node->title );
294        $wp_admin_bar->add_node( (array) $edit_profile_node );
295    }
296    $my_account_node = $wp_admin_bar->get_node( 'my-account' );
297    if ( $my_account_node ) {
298        $my_account_node->href = add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/me' ) );
299        $wp_admin_bar->add_node( (array) $my_account_node );
300    }
301}
302// Run this function later than Core: https://github.com/WordPress/wordpress-develop/blob/5a30482419f1b0bcc713a7fdee3a14afd67a1bca/src/wp-includes/class-wp-admin-bar.php#L651
303add_action( 'admin_bar_menu', 'wpcom_replace_edit_profile_menu_to_me', 9999 );
304
305/**
306 * Adds "Howdy,..." -> My WP.com Account submenu pointing to /me/account.
307 *
308 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
309 */
310function wpcom_add_my_wpcom_account_submenu( $wp_admin_bar ) {
311    $wp_admin_bar->add_group(
312        array(
313            'parent' => 'my-account',
314            'id'     => 'wpcom-account',
315            'meta'   => array(
316                'class' => 'ab-sub-secondary',
317            ),
318        )
319    );
320
321    /* translators: %s: WordPress.com logo */
322    $button_text = sprintf( __( 'My %s WordPress.com Account', 'jetpack-mu-wpcom' ), '<span class="wpcom-logo"></span>' );
323
324    $wp_admin_bar->add_node(
325        array(
326            'parent' => 'wpcom-account',
327            'id'     => 'my-wpcom-account',
328            'title'  => '<span class="button wpcom-button">' . $button_text . '</span>',
329            'href'   => add_origin_admin_bar_to_url( Urls::maybe_add_origin_site_id( 'https://wordpress.com/me/account' ) ),
330        )
331    );
332}
333add_action( 'admin_bar_menu', 'wpcom_add_my_wpcom_account_submenu' );
334
335/**
336 * Replaces the default admin bar class with our own.
337 *
338 * @param string $wp_admin_bar_class Admin bar class to use. Default 'WP_Admin_Bar'.
339 * @return string Name of the admin bar class.
340 */
341function wpcom_custom_wpcom_admin_bar_class( $wp_admin_bar_class ) {
342    remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
343    $is_wp_admin = get_option( 'wpcom_admin_interface' ) === 'wp-admin';
344    add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 );
345
346    if ( $is_wp_admin ) {
347        return $wp_admin_bar_class;
348    }
349
350    require_once __DIR__ . '/class-wpcom-admin-bar.php';
351    return '\Automattic\Jetpack\Jetpack_Mu_Wpcom\WPCOM_Admin_Bar';
352}
353add_filter( 'wp_admin_bar_class', 'wpcom_custom_wpcom_admin_bar_class' );
354
355/**
356 * Changes the edit site menu to point to the top-level site editor.
357 *
358 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
359 */
360function wpcom_edit_site_menu_override( $wp_admin_bar ) {
361    if ( $wp_admin_bar->get_node( 'site-editor' ) ) {
362        $args = array(
363            'id'   => 'site-editor',
364            'href' => admin_url( 'site-editor.php' ),
365        );
366
367        $wp_admin_bar->add_node( $args );
368    }
369}
370add_action( 'admin_bar_menu', 'wpcom_edit_site_menu_override', 41 );
371
372/**
373 * Adds site badges and plan information to the site title dropdown menu.
374 *
375 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
376 */
377function wpcom_add_site_badges_and_plan( $wp_admin_bar ) {
378    // Get the current blog ID
379    $blog_id = get_current_blog_id();
380    $status  = new Status();
381
382    // Check for various site types
383    $badge_text = '';
384
385    // Check if this is a P2 site
386    if ( str_contains( get_stylesheet(), 'pub/p2' ) ||
387        ( function_exists( '\WPForTeams\is_wpforteams_site' ) &&
388        \WPForTeams\is_wpforteams_site( $blog_id ) ) ) {
389        $badge_text = 'P2';
390    } elseif ( (bool) get_option( 'wpcom_is_staging_site' ) ) {
391        // Check for staging site
392        $badge_text = __( 'Staging', 'jetpack-mu-wpcom' );
393    } elseif ( function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'trial' ) ) {
394        // Check for trial site
395        $badge_text = __( 'Trial', 'jetpack-mu-wpcom' );
396    } elseif ( get_option( 'launch-status' ) === 'unlaunched' || $status->is_coming_soon() ) {
397        // Check for Coming Soon site
398        $badge_text = __( 'Coming Soon', 'jetpack-mu-wpcom' );
399    } elseif ( $status->is_private_site() ) {
400        // Check for private site
401        $badge_text = __( 'Private', 'jetpack-mu-wpcom' );
402    } elseif ( ( function_exists( 'has_blog_sticker' ) && has_blog_sticker( 'difm-lite-in-progress' ) ) ||
403        ( function_exists( 'wpcomsh_is_site_sticker_active' ) && wpcomsh_is_site_sticker_active( 'difm-lite-in-progress' ) ) ) {
404        // Check for Express service
405        $badge_text = __( 'Express', 'jetpack-mu-wpcom' );
406    } elseif ( function_exists( 'is_simple_site_redirect' ) && is_simple_site_redirect( $status->get_site_suffix() ) ) {
407        // Check for Redirect site
408        $badge_text = __( 'Redirect', 'jetpack-mu-wpcom' );
409    } elseif ( ! empty( get_option( 'options' )['is_domain_only'] ) ) {
410        // Check for Domain Only site
411        $badge_text = __( 'Domain Only', 'jetpack-mu-wpcom' );
412    }
413
414    // Add badge to the site name dropdown if a badge is applicable
415    $status_text = '';
416    if ( $badge_text ) {
417        $status_text = '<div class="wp-admin-bar__site-info">
418                            <span class="wp-admin-bar__site-info-label">' . __( 'Status', 'jetpack-mu-wpcom' ) . '</span>
419                            <span class="wp-admin-bar__info-badges">' . esc_html( $badge_text ) . '</span>
420                        </div>';
421    }
422
423    // Add plan information for non-staging sites
424    $plan_text  = '';
425    $is_staging = (bool) get_option( 'wpcom_is_staging_site' );
426    if ( ! $is_staging ) {
427        if ( class_exists( '\WPCOM_Store_API' ) ) {
428            $current_plan = WPCOM_Store_API::get_current_plan( get_current_blog_id() );
429        } else {
430            $current_plan = Current_Plan::get();
431        }
432        $plan_name = $current_plan['product_name_short'] ?? '';
433
434        if ( $plan_name ) {
435            $site_slug = method_exists( '\WPCOM_Masterbar', 'get_calypso_site_slug' )
436                ? WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() )
437                : '';
438
439            if ( $site_slug ) {
440                $plan_text = '<a class="wp-admin-bar__site-info" href="https://wordpress.com/plans/' . esc_attr( $site_slug ) . '">
441                                <span class="wp-admin-bar__site-info-label">' . __( 'Plan', 'jetpack-mu-wpcom' ) . '</span>
442                                <span class="wp-admin-bar__info-badges">' . esc_html( $plan_name ) . '</span>
443                            </a>';
444            } else {
445                $plan_text = '<div class="wp-admin-bar__site-info">
446                                <span class="wp-admin-bar__site-info-label">' . __( 'Plan', 'jetpack-mu-wpcom' ) . '</span>
447                                <span class="wp-admin-bar__info-badges">' . esc_html( $plan_name ) . '</span>
448                            </div>';
449            }
450        }
451    }
452
453    if ( $plan_text ) {
454        $wp_admin_bar->add_group(
455            array(
456                'parent' => 'site-name',
457                'id'     => 'site-plan',
458            )
459        );
460        $wp_admin_bar->add_node(
461            array(
462                'parent' => 'site-plan',
463                'id'     => 'site-plan-badge',
464                'title'  => $plan_text,
465            )
466        );
467    }
468
469    if ( $status_text ) {
470        $wp_admin_bar->add_group(
471            array(
472                'parent' => 'site-name',
473                'id'     => 'site-status',
474                'meta'   => array(
475                    'class' => 'ab-sub-secondary',
476                ),
477            )
478        );
479        $wp_admin_bar->add_node(
480            array(
481                'parent' => 'site-status',
482                'id'     => 'site-status-badge',
483                'title'  => $status_text,
484            )
485        );
486    }
487}
488add_action( 'admin_bar_menu', 'wpcom_add_site_badges_and_plan', 35 );