Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 3
CRAP
n/a
0 / 0
load_wpcom_dashboard_widgets
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 1
462
enqueue_wpcom_dashboard_widgets
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
2
render_wpcom_dashboard_widget
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Load the widgets of Dashboard for WordPress.com sites.
4 *
5 * @package automattic/jetpack-mu-plugins
6 */
7
8use Automattic\Jetpack\Connection\Client;
9
10/**
11 * Load all wpcom dashboard widgets.
12 */
13function load_wpcom_dashboard_widgets() {
14    if ( ! current_user_can( 'manage_options' ) ) {
15        return;
16    }
17
18    // wpcom_json_api_request_as_user does not support internal requests.
19    $request = defined( 'IS_WPCOM' ) && IS_WPCOM ? 'wpcom_json_api_request_as_blog' : 'wpcom_json_api_request_as_user';
20
21    $layout_response = Client::$request(
22        '/sites/' . get_wpcom_blog_id() . '/home/layout',
23        'v2',
24        array(),
25        null,
26        'wpcom'
27    );
28
29    $tasks = array();
30
31    if ( ! is_wp_error( $layout_response ) ) {
32        $layout = json_decode( $layout_response['body'], true );
33        if ( isset( $layout['secondary'] ) && is_array( $layout['secondary'] ) ) {
34            // If there's an array within the secondary section, it's the task
35            // list.
36            foreach ( $layout['secondary'] as $item ) {
37                if ( is_array( $item ) ) {
38                    // Delete any tasks that don't have a corresponding PHP file.
39                    foreach ( $item as $task ) {
40                        if ( file_exists( __DIR__ . '/wpcom-general-tasks-widget/tasks/' . $task ) ) {
41                            $tasks[] = $task;
42                        }
43                    }
44                    break;
45                }
46            }
47        }
48    }
49
50    enqueue_wpcom_dashboard_widgets( array( 'tasks' => $tasks ) );
51
52    $wpcom_dashboard_widgets = array(
53        array(
54            'id'       => 'wpcom_site_preview_widget',
55            'name'     => __( 'Site', 'jetpack-mu-wpcom' ),
56            'context'  => 'side',
57            'priority' => 'high',
58        ),
59    );
60
61    $launchpad_context = 'wpadmin-dashboard-widget';
62    $checklist_slug    = get_option( 'site_intent' );
63
64    // The AI Launchpad's Site Setup screen supersedes this widget: showing both surfaces two
65    // different checklists under the same name. Eligibility covers dismissal, so a user who
66    // dismissed the AI Launchpad gets this widget back.
67    $has_ai_launchpad = function_exists( 'wpcom_ai_launchpad_is_eligible' )
68        && wpcom_ai_launchpad_is_eligible();
69
70    // The no_guidance launchpad-personalization variation gets no launchpad surface at all.
71    // Unlike the ai_launchpad arm above, there is no dismissal fallback: dismissing nothing
72    // cannot bring the widget back.
73    require_once __DIR__ . '/../../common/class-launchpad-personalization-experiment.php';
74    $is_no_guidance =
75        'no_guidance' === \Automattic\Jetpack\Jetpack_Mu_Wpcom\Launchpad_Personalization_Experiment::get_variation();
76
77    if (
78        defined( 'IS_WPCOM' ) && IS_WPCOM &&
79        ! $has_ai_launchpad &&
80        ! $is_no_guidance &&
81        get_option( 'launch-status', 'launched' ) !== 'launched' &&
82        ! empty( wpcom_get_launchpad_checklist_by_checklist_slug( $checklist_slug, $launchpad_context ) ) &&
83        ! wpcom_launchpad_is_task_list_dismissed( $checklist_slug )
84    ) {
85        $wpcom_dashboard_widgets[] = array(
86            'id'       => 'wpcom_launchpad_widget',
87            'name'     => __( 'Site Setup', 'jetpack-mu-wpcom' ),
88            'context'  => 'normal',
89            'priority' => 'high',
90        );
91    }
92
93    if ( ! empty( $tasks ) ) {
94        $wpcom_dashboard_widgets[] = array(
95            'id'       => 'wpcom_general_tasks_widget',
96            'name'     => __( 'Suggestions', 'jetpack-mu-wpcom' ),
97            'context'  => 'normal',
98            'priority' => 'high',
99        );
100    }
101
102    foreach ( $wpcom_dashboard_widgets as $wpcom_dashboard_widget ) {
103        wp_add_dashboard_widget(
104            $wpcom_dashboard_widget['id'],
105            $wpcom_dashboard_widget['name'],
106            'render_wpcom_dashboard_widget',
107            null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. See https://core.trac.wordpress.org/ticket/52539.
108            array(
109                'id'   => $wpcom_dashboard_widget['id'],
110                'name' => $wpcom_dashboard_widget['name'],
111            ),
112            $wpcom_dashboard_widget['context'],
113            $wpcom_dashboard_widget['priority']
114        );
115    }
116}
117add_action( 'wp_dashboard_setup', 'load_wpcom_dashboard_widgets' );
118
119/**
120 * Enqueue the assets of the wpcom dashboard widgets.
121 *
122 * @param array $args Settings to pass.
123 */
124function enqueue_wpcom_dashboard_widgets( $args = array() ) {
125    $handle = jetpack_mu_wpcom_enqueue_assets( 'wpcom-dashboard-widgets', array( 'js', 'css' ) );
126
127    $bundles      = wp_list_filter( wpcom_get_site_purchases(), array( 'product_type' => 'bundle' ) );
128    $current_plan = array_pop( $bundles );
129
130    $data = wp_json_encode(
131        array(
132            'siteName'        => wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ),
133            'siteUrl'         => home_url(),
134            'siteIconUrl'     => get_site_icon_url( 38 ),
135            'isBlockTheme'    => wp_is_block_theme(),
136            'siteDomain'      => wp_parse_url( home_url(), PHP_URL_HOST ),
137            'siteIntent'      => get_option( 'site_intent' ),
138            'sitePlan'        => $current_plan,
139            'hasCustomDomain' => wpcom_site_has_feature( 'custom-domain' ),
140            'tasks'           => $args['tasks'],
141            'blogId'          => get_current_blog_id(),
142        ),
143        JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
144    );
145
146    wp_add_inline_script(
147        $handle,
148        "var JETPACK_MU_WPCOM_DASHBOARD_WIDGETS = $data;var configData = {};",
149        'before'
150    );
151}
152
153/**
154 * Render the container of the wpcom dashboard widget.
155 *
156 * @param WP_Post $post The post object.
157 * @param array   $callback_args The callback args of the render function.
158 */
159function render_wpcom_dashboard_widget( $post, $callback_args ) {
160    $args         = $callback_args['args'];
161    $widget_id    = $args['id'] . '_main';
162    $widget_class = $args['class'] ?? $args['id'];
163    $widget_name  = $args['name'];
164
165    $warning = sprintf(
166        /* translators: The name of the widget. */
167        __( 'Your %s widget requires JavaScript to function properly.', 'jetpack-mu-wpcom' ),
168        $widget_name
169    );
170
171    ?>
172    <div>
173        <div class="hide-if-js">
174            <?php echo esc_html( $warning ); ?>
175        </div>
176        <div
177            id="<?php echo esc_attr( $widget_id ); ?>"
178            class="<?php echo esc_attr( $widget_class ); ?> hide-if-no-js"
179            style="height: 100%">
180        </div>
181    </div>
182    <?php
183}