Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
49.06% covered (danger)
49.06%
157 / 320
25.00% covered (danger)
25.00%
3 / 12
CRAP
n/a
0 / 0
wpcom_enqueue_admin_bar_assets
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_always_use_user_locale
33.33% covered (danger)
33.33%
4 / 12
0.00% covered (danger)
0.00%
0 / 1
8.74
wpcom_replace_wp_logo_with_wpcom_logo_menu
100.00% covered (success)
100.00%
67 / 67
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
70.00% covered (warning)
70.00%
7 / 10
0.00% covered (danger)
0.00%
0 / 1
4.43
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
57.14% covered (warning)
57.14%
44 / 77
0.00% covered (danger)
0.00%
0 / 1
69.34
wpcom_add_stats_to_site_menu
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
wpcom_admin_bar_upsell_get_source
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
wpcom_add_free_domain_upsell
8.33% covered (danger)
8.33%
2 / 24
0.00% covered (danger)
0.00%
0 / 1
5.08
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\Current_Plan;
11use Automattic\Jetpack\Jetpack_Mu_Wpcom;
12use Automattic\Jetpack\Jetpack_Mu_Wpcom\Free_Domain_Upsell_Experiment;
13use Automattic\Jetpack\Modules;
14use Automattic\Jetpack\Status;
15
16/**
17 * Enqueue assets needed by the WordPress.com admin bar.
18 */
19function wpcom_enqueue_admin_bar_assets() {
20    $asset_file = include Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.asset.php';
21
22    wp_enqueue_script(
23        'wpcom-admin-bar',
24        plugins_url( 'build/wpcom-admin-bar/wpcom-admin-bar.js', Jetpack_Mu_Wpcom::BASE_FILE ),
25        $asset_file['dependencies'] ?? array(),
26        $asset_file['version'] ?? filemtime( Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.js' ),
27        array(
28            'strategy'  => 'defer',
29            'in_footer' => true,
30        )
31    );
32
33    wp_enqueue_style(
34        'wpcom-admin-bar',
35        plugins_url( 'build/wpcom-admin-bar/wpcom-admin-bar.css', Jetpack_Mu_Wpcom::BASE_FILE ),
36        array(),
37        $asset_file['version'] ?? filemtime( Jetpack_Mu_Wpcom::BASE_DIR . 'build/wpcom-admin-bar/wpcom-admin-bar.css' )
38    );
39
40    if ( Free_Domain_Upsell_Experiment::should_show_upsell() ) {
41        wp_add_inline_script(
42            'wpcom-admin-bar',
43            'var wpcomAdminBarUpsellSource = ' . wp_json_encode( wpcom_admin_bar_upsell_get_source(), JSON_HEX_TAG | JSON_HEX_AMP ) . ';'
44            . 'var wpcomAdminBarFreeDomainUpsellSource = ' . wp_json_encode( Free_Domain_Upsell_Experiment::get_upsell_source(), JSON_HEX_TAG | JSON_HEX_AMP ) . ';',
45            'before'
46        );
47    }
48
49    /**
50     * Force the Atomic debug bar menu to be the first menu at the top-right.
51     */
52    if ( defined( 'AT_PROXIED_REQUEST' ) && AT_PROXIED_REQUEST ) {
53        wp_add_inline_style(
54            'wpcom-admin-bar',
55            <<<'CSS'
56                #wpadminbar .quicklinks #wp-admin-bar-top-secondary {
57                    display: flex;
58                }
59
60                #wpadminbar .quicklinks #wp-admin-bar-top-secondary #wp-admin-bar-debug-bar {
61                    order: -1;
62                }
63CSS
64        );
65    }
66}
67add_action( 'wp_enqueue_scripts', 'wpcom_enqueue_admin_bar_assets' );
68add_action( 'admin_enqueue_scripts', 'wpcom_enqueue_admin_bar_assets' );
69
70/**
71 * Render the admin bar in user locale even on frontend screens.
72 */
73function wpcom_always_use_user_locale() {
74    if ( is_admin() || ! is_admin_bar_showing() ) {
75        return;
76    }
77
78    $site_locale = get_locale();
79    $user_locale = get_user_locale();
80
81    if ( $site_locale !== $user_locale ) {
82        switch_to_locale( $user_locale );
83        add_action(
84            'wp_after_admin_bar_render',
85            function () use ( $site_locale ) {
86                switch_to_locale( $site_locale );
87            }
88        );
89    }
90}
91add_action( 'admin_bar_menu', 'wpcom_always_use_user_locale', -1 );
92
93/**
94 * Replaces the WP logo with WP.com logo.
95 *
96 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
97 */
98function wpcom_replace_wp_logo_with_wpcom_logo_menu( $wp_admin_bar ) {
99    $about_node      = $wp_admin_bar->get_node( 'about' );
100    $contribute_node = $wp_admin_bar->get_node( 'contribute' );
101
102    foreach ( $wp_admin_bar->get_nodes() as $node ) {
103        if ( $node->parent === 'wp-logo' || $node->parent === 'wp-logo-external' ) {
104            $wp_admin_bar->remove_node( $node->id );
105        }
106    }
107    $wp_admin_bar->remove_node( 'wp-logo' );
108    $wp_admin_bar->add_node(
109        array(
110            'id'    => 'wp-logo',
111            // WordPress 7.2+ renders the icon slot from `icon`; older versions ignore it and use the empty `.ab-icon` in the title.
112            'icon'  => 'core-admin/wordpress',
113            'title' => '<span class="ab-icon" aria-hidden="true"></span><span class="screen-reader-text">' .
114                        /* translators: Hidden accessibility text. */
115                        'WordPress.com' .
116                        '</span>',
117            'href'  => 'https://my.wordpress.com/sites',
118            'meta'  => array(
119                'menu_title' => 'WordPress.com',
120            ),
121        )
122    );
123
124    $wp_admin_bar->add_node(
125        array(
126            'parent' => 'wp-logo',
127            'id'     => 'wpcom-sites',
128            'title'  => __( 'Sites', 'jetpack-mu-wpcom' ),
129            'href'   => 'https://my.wordpress.com/sites',
130        )
131    );
132
133    $wp_admin_bar->add_node(
134        array(
135            'parent' => 'wp-logo',
136            'id'     => 'wpcom-domains',
137            'title'  => __( 'Domains', 'jetpack-mu-wpcom' ),
138            'href'   => 'https://my.wordpress.com/domains',
139        )
140    );
141
142    $wp_admin_bar->add_node(
143        array(
144            'parent' => 'wp-logo',
145            'id'     => 'wpcom-emails',
146            'title'  => __( 'Emails', 'jetpack-mu-wpcom' ),
147            'href'   => 'https://my.wordpress.com/emails',
148        )
149    );
150
151    $wp_admin_bar->add_node(
152        array(
153            'parent' => 'wp-logo',
154            'id'     => 'wpcom-plugins',
155            'title'  => __( 'Plugins', 'jetpack-mu-wpcom' ),
156            'href'   => 'https://my.wordpress.com/plugins',
157        )
158    );
159
160    if ( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
161        $wp_admin_bar->add_group(
162            array(
163                'parent' => 'wp-logo',
164                'id'     => 'wp-logo-external',
165                'meta'   => array(
166                    'class' => 'ab-sub-secondary',
167                ),
168            )
169        );
170
171        if ( $about_node ) {
172            $about_node->parent = 'wp-logo-external';
173            $wp_admin_bar->add_node( (array) $about_node );
174        }
175        if ( $contribute_node ) {
176            $contribute_node->parent = 'wp-logo-external';
177            $wp_admin_bar->add_node( (array) $contribute_node );
178        }
179    }
180}
181add_action( 'admin_bar_menu', 'wpcom_replace_wp_logo_with_wpcom_logo_menu', 11 );
182
183/**
184 * Adds the Cart menu to the WordPress admin bar.
185 *
186 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
187 */
188function wpcom_add_shopping_cart( $wp_admin_bar ) {
189    // Return if the site isn't a simple site
190    if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
191        return;
192    }
193
194    // Include the shopping cart functionality from the specified path.
195    require_once WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/shopping-cart.php';
196
197    // Get the current blog ID.
198    $blog_id = get_current_blog_id();
199
200    $is_empty = \Store_Shopping_Cart::is_cart_empty(
201        array(
202            'blog_id' => $blog_id,
203            'user_id' => get_current_user_id(),
204        )
205    );
206
207    // If the cart is empty (no products), do not add the cart menu.
208    if ( $is_empty ) {
209        return;
210    }
211
212    // Get the Calypso site slug for the current blog.
213    $calypso_site_slug = \WPCOM_Masterbar::get_calypso_site_slug( $blog_id );
214
215    // If no Calypso site slug is found, return early.
216    if ( ! $calypso_site_slug ) {
217        return;
218    }
219
220    // Add the cart menu item to the WordPress admin bar.
221    $wp_admin_bar->add_menu(
222        array(
223            'id'     => 'cart', // Unique ID for the cart menu item.
224            'title'  => '<span class="ab-item cart-icon" aria-hidden="true"></span>' .
225                        '<div class="cart-icon__dot"></div>' .
226                        '<span class="screen-reader-text">' .
227                        /* translators: Hidden accessibility text. */
228                        __( 'Cart', 'jetpack-mu-wpcom' ) .
229                        '</span>',
230            'href'   => 'https://wordpress.com/checkout/' . esc_attr( $calypso_site_slug ), // Link to the checkout page.
231            'meta'   => array(
232                'class' => 'wp-admin-bar-cart', // Custom class for styling the cart menu item.
233            ),
234            'parent' => 'top-secondary', // Position the cart in the 'top-secondary' section of the admin bar.
235        )
236    );
237}
238
239// Hook the cart icon to the admin bar menu, placing it before the reader icon (same as Calypso).
240add_action( 'admin_bar_menu', 'wpcom_add_shopping_cart', 11 );
241
242// Add the reader icon to the admin bar before the help center icon.
243add_action(
244    'wp_loaded',
245    function () {
246        if ( class_exists( '\Automattic\Jetpack\Newsletter\Reader_Link' ) ) {
247            // @phan-suppress-next-line PhanUndeclaredClassMethod -- class_exists guarded above; provided by sibling autoloader.
248            \Automattic\Jetpack\Newsletter\Reader_Link::init();
249        }
250    }
251);
252
253/**
254 * Points the "Edit Profile" and "Howdy,..." to /me if the user is not member of the blog.
255 *
256 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
257 */
258function wpcom_replace_edit_profile_menu_to_me( $wp_admin_bar ) {
259    if ( is_user_member_of_blog() ) {
260        return;
261    }
262
263    $edit_profile_node = $wp_admin_bar->get_node( 'user-info' );
264    if ( $edit_profile_node ) {
265        $edit_profile_node->href = 'https://my.wordpress.com/me/account';
266        $wp_admin_bar->add_node( (array) $edit_profile_node );
267    }
268    $my_account_node = $wp_admin_bar->get_node( 'my-account' );
269    if ( $my_account_node ) {
270        $my_account_node->href = 'https://my.wordpress.com/me/account';
271        $wp_admin_bar->add_node( (array) $my_account_node );
272    }
273}
274// Run this function later than Core: https://github.com/WordPress/wordpress-develop/blob/5a30482419f1b0bcc713a7fdee3a14afd67a1bca/src/wp-includes/class-wp-admin-bar.php#L651
275add_action( 'admin_bar_menu', 'wpcom_replace_edit_profile_menu_to_me', 9999 );
276
277/**
278 * Adds "Howdy,..." -> My WP.com Account submenu pointing to /me/account.
279 *
280 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
281 */
282function wpcom_add_my_wpcom_account_submenu( $wp_admin_bar ) {
283    $wp_admin_bar->add_group(
284        array(
285            'parent' => 'my-account',
286            'id'     => 'wpcom-account',
287            'meta'   => array(
288                'class' => 'ab-sub-secondary',
289            ),
290        )
291    );
292
293    /* translators: %s: WordPress.com logo */
294    $button_text = sprintf( __( 'My %s WordPress.com Account', 'jetpack-mu-wpcom' ), '<span class="wpcom-logo"></span>' );
295
296    $wp_admin_bar->add_node(
297        array(
298            'parent' => 'wpcom-account',
299            'id'     => 'my-wpcom-account',
300            'title'  => '<span class="button wpcom-button">' . $button_text . '</span>',
301            'href'   => 'https://my.wordpress.com/me/account',
302        )
303    );
304}
305add_action( 'admin_bar_menu', 'wpcom_add_my_wpcom_account_submenu' );
306
307/**
308 * Replaces the default admin bar class with our own.
309 *
310 * @param string $wp_admin_bar_class Admin bar class to use. Default 'WP_Admin_Bar'.
311 * @return string Name of the admin bar class.
312 */
313function wpcom_custom_wpcom_admin_bar_class( $wp_admin_bar_class ) {
314    remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
315    $is_wp_admin = get_option( 'wpcom_admin_interface' ) === 'wp-admin';
316    add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option', 10 );
317
318    if ( $is_wp_admin ) {
319        return $wp_admin_bar_class;
320    }
321
322    require_once __DIR__ . '/class-wpcom-admin-bar.php';
323    return '\Automattic\Jetpack\Jetpack_Mu_Wpcom\WPCOM_Admin_Bar';
324}
325add_filter( 'wp_admin_bar_class', 'wpcom_custom_wpcom_admin_bar_class' );
326
327/**
328 * Changes the edit site menu to point to the top-level site editor.
329 *
330 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
331 */
332function wpcom_edit_site_menu_override( $wp_admin_bar ) {
333    if ( $wp_admin_bar->get_node( 'site-editor' ) ) {
334        $args = array(
335            'id'   => 'site-editor',
336            'href' => admin_url( 'site-editor.php' ),
337        );
338
339        $wp_admin_bar->add_node( $args );
340    }
341}
342add_action( 'admin_bar_menu', 'wpcom_edit_site_menu_override', 41 );
343
344/**
345 * Adds site badges and plan information to the site title dropdown menu.
346 *
347 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
348 */
349function wpcom_add_site_badges_and_plan( $wp_admin_bar ) {
350    // Get the current blog ID
351    $blog_id = get_current_blog_id();
352    $status  = new Status();
353
354    // Check for various site types
355    $badge_text = '';
356
357    // Check if this is a P2 site
358    if ( str_contains( get_stylesheet(), 'pub/p2' ) ||
359        ( function_exists( '\WPForTeams\is_wpforteams_site' ) &&
360        \WPForTeams\is_wpforteams_site( $blog_id ) ) ) {
361        $badge_text = 'P2';
362    } elseif ( (bool) get_option( 'wpcom_is_staging_site' ) ) {
363        // Check for staging site
364        $badge_text = __( 'Staging', 'jetpack-mu-wpcom' );
365    } elseif ( function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'trial' ) ) {
366        // Check for trial site
367        $badge_text = __( 'Trial', 'jetpack-mu-wpcom' );
368    } elseif ( get_option( 'launch-status' ) === 'unlaunched' || $status->is_coming_soon() ) {
369        // Check for Coming Soon site
370        $badge_text = __( 'Coming Soon', 'jetpack-mu-wpcom' );
371    } elseif ( $status->is_private_site() ) {
372        // Check for private site
373        $badge_text = __( 'Private', 'jetpack-mu-wpcom' );
374    } elseif ( ( function_exists( 'has_blog_sticker' ) && has_blog_sticker( 'difm-lite-in-progress' ) ) ||
375        ( function_exists( 'wpcomsh_is_site_sticker_active' ) && wpcomsh_is_site_sticker_active( 'difm-lite-in-progress' ) ) ) {
376        // Check for Express service
377        $badge_text = __( 'Express', 'jetpack-mu-wpcom' );
378    } elseif ( function_exists( 'is_simple_site_redirect' ) && is_simple_site_redirect( $status->get_site_suffix() ) ) {
379        // Check for Redirect site
380        $badge_text = __( 'Redirect', 'jetpack-mu-wpcom' );
381    } elseif ( ! empty( get_option( 'options' )['is_domain_only'] ) ) {
382        // Check for Domain Only site
383        $badge_text = __( 'Domain Only', 'jetpack-mu-wpcom' );
384    }
385
386    // Add badge to the site name dropdown if a badge is applicable
387    $status_text = '';
388    if ( $badge_text ) {
389        $status_text = '<div class="wp-admin-bar__site-info">
390                            <span class="wp-admin-bar__site-info-label">' . __( 'Status', 'jetpack-mu-wpcom' ) . '</span>
391                            <span class="wp-admin-bar__info-badges">' . esc_html( $badge_text ) . '</span>
392                        </div>';
393    }
394
395    // Add plan information for non-staging sites
396    $plan_text  = '';
397    $is_staging = (bool) get_option( 'wpcom_is_staging_site' );
398    if ( ! $is_staging ) {
399        if ( class_exists( '\WPCOM_Store_API' ) ) {
400            $current_plan = WPCOM_Store_API::get_current_plan( get_current_blog_id() );
401        } else {
402            $current_plan = Current_Plan::get();
403        }
404        $plan_name = $current_plan['product_name_short'] ?? '';
405
406        if ( $plan_name ) {
407            // wpcom_get_site_slug() resolves the Calypso slug on both Simple and
408            // Atomic (where \WPCOM_Masterbar is absent), falling back to the site
409            // URL so the link still renders.
410            $site_slug = wpcom_get_site_slug();
411
412            if ( $site_slug ) {
413                $plan_text = '<a class="wp-admin-bar__site-info" href="https://wordpress.com/plans/' . esc_attr( $site_slug ) . '">
414                                <span class="wp-admin-bar__site-info-label">' . __( 'Plan', 'jetpack-mu-wpcom' ) . '</span>
415                                <span class="wp-admin-bar__info-badges">' . esc_html( $plan_name ) . '</span>
416                            </a>';
417            } else {
418                $plan_text = '<div class="wp-admin-bar__site-info">
419                                <span class="wp-admin-bar__site-info-label">' . __( 'Plan', 'jetpack-mu-wpcom' ) . '</span>
420                                <span class="wp-admin-bar__info-badges">' . esc_html( $plan_name ) . '</span>
421                            </div>';
422            }
423        }
424    }
425
426    if ( $plan_text ) {
427        $wp_admin_bar->add_group(
428            array(
429                'parent' => 'site-name',
430                'id'     => 'site-plan',
431            )
432        );
433        $wp_admin_bar->add_node(
434            array(
435                'parent' => 'site-plan',
436                'id'     => 'site-plan-badge',
437                'title'  => $plan_text,
438            )
439        );
440    }
441
442    if ( $status_text ) {
443        $wp_admin_bar->add_group(
444            array(
445                'parent' => 'site-name',
446                'id'     => 'site-status',
447                'meta'   => array(
448                    'class' => 'ab-sub-secondary',
449                ),
450            )
451        );
452        $wp_admin_bar->add_node(
453            array(
454                'parent' => 'site-status',
455                'id'     => 'site-status-badge',
456                'title'  => $status_text,
457            )
458        );
459    }
460}
461add_action( 'admin_bar_menu', 'wpcom_add_site_badges_and_plan', 35 );
462
463/**
464 * Adds a "Stats" link to the site-name submenu, alongside "Dashboard"/"Visit Site" (STATS-287).
465 *
466 * Hooks `admin_bar_menu` at priority 40, after core's `wp_admin_bar_site_menu`
467 * (priority 30) has added the `site-name` node and either `dashboard` (front end)
468 * or `view-site` (wp-admin), so this shows in both contexts.
469 *
470 * `view_stats` is not a proxy for the module being on: the Stats package registers
471 * its `map_meta_cap` handler on any wp-admin request, so an administrator keeps the
472 * capability after the module is switched off, while `admin.php?page=stats` stops
473 * being registered. Linking there then renders "Sorry, you are not allowed to access
474 * this page", so check the module too. `is_active()` short-circuits to true on Simple,
475 * where the module can't be switched off; it only narrows anything on Atomic.
476 *
477 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance.
478 */
479function wpcom_add_stats_to_site_menu( $wp_admin_bar ) {
480    if (
481        ( ! $wp_admin_bar->get_node( 'dashboard' ) && ! $wp_admin_bar->get_node( 'view-site' ) )
482        || ! current_user_can( 'view_stats' )
483        || ! ( new Modules() )->is_active( 'stats' )
484    ) {
485        return;
486    }
487
488    $wp_admin_bar->add_node(
489        array(
490            'parent' => 'site-name',
491            'id'     => 'wpcom-stats',
492            'title'  => __( 'Stats', 'jetpack-mu-wpcom' ),
493            /** This filter is documented in projects/packages/stats-admin/src/class-admin-bar.php */
494            'href'   => apply_filters( 'jetpack_stats_url', admin_url( 'admin.php?page=stats' ), array( 'view' => 'dashboard' ) ),
495        )
496    );
497}
498add_action( 'admin_bar_menu', 'wpcom_add_stats_to_site_menu', 40 );
499
500/**
501 * Which source the admin bar upsell is rendering on, for tracking.
502 *
503 * @return string One of 'site-frontend', 'site-editor', 'post-editor', 'wp-admin'.
504 */
505function wpcom_admin_bar_upsell_get_source() {
506    $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
507    if ( ! $screen ) {
508        return 'site-frontend';
509    }
510    if ( 'site-editor' === $screen->id ) {
511        return 'site-editor';
512    }
513    if ( $screen->is_block_editor() ) {
514        return 'post-editor';
515    }
516    return 'wp-admin';
517}
518
519/**
520 * Adds a "Free domain" upsell chip to the admin bar for users in the treatment
521 * of the omnibar upsell experiment.
522 *
523 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
524 */
525function wpcom_add_free_domain_upsell( $wp_admin_bar ) {
526    if ( ! Free_Domain_Upsell_Experiment::should_show_upsell() ) {
527        return;
528    }
529
530    $icon = '<svg width="16" height="16" viewBox="4 5 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
531        <path d="M18.9397 9.87999L15.4197 6.06999L15.3597 6.00999C15.2897 5.93999 15.1997 5.89999 15.0997 5.89999H8.87973C8.77973 5.89999 8.68973 5.93999 8.61973 6.00999L5.05973 9.87999C4.93973 10.01 4.93973 10.21 5.05973 10.34L11.5397 17.86C11.6497 17.99 11.8197 18.07 11.9997 18.07C12.1797 18.07 12.3397 17.99 12.4597 17.86L18.9397 10.34C19.0597 10.21 19.0497 10.01 18.9397 9.87999ZM15.4097 7.53999L17.3297 9.63999H15.1697L15.4097 7.53999ZM14.4297 6.83999L14.1097 9.63999H10.2897L9.64973 6.83999H14.4297ZM8.68973 7.42999L9.19973 9.63999H6.66973L8.68973 7.42999ZM6.61973 10.6H9.42973L10.8397 15.49L6.61973 10.6ZM12.0397 15.87L10.5297 10.6H13.8597L12.0397 15.87ZM14.9697 10.6H17.3797L13.3697 15.24L14.9697 10.6Z" fill="currentColor" />
532    </svg>';
533
534    $wp_admin_bar->add_menu(
535        array(
536            'id'     => 'free-domain-upsell',
537            'parent' => null,
538            'title'  => '<span class="ab-icon" aria-hidden="true">' . $icon . '</span><span class="ab-label">' . esc_html__( 'Free domain', 'jetpack-mu-wpcom' ) . '</span>',
539            'href'   => add_query_arg(
540                array(
541                    'siteSlug' => wpcom_get_site_slug(),
542                    'ref'      => 'omnibar-free-domain',
543                ),
544                'https://wordpress.com/setup/domain-and-plan'
545            ),
546            'meta'   => array(
547                'class'         => 'free-domain-upsell',
548                'title'         => __( 'Free domain with an annual plan', 'jetpack-mu-wpcom' ),
549                'menu_title'    => __( 'Free domain', 'jetpack-mu-wpcom' ),
550                'upsell_source' => Free_Domain_Upsell_Experiment::get_upsell_source(),
551            ),
552        )
553    );
554}
555add_action( 'admin_bar_menu', 'wpcom_add_free_domain_upsell', 81 );