Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.00% covered (danger)
12.00%
75 / 625
16.00% covered (danger)
16.00%
8 / 50
CRAP
n/a
0 / 0
wpcom_launchpad_get_task_list_definitions
0.00% covered (danger)
0.00%
0 / 333
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_get_task_list
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_launchpad_get_task_lists
18.18% covered (danger)
18.18%
2 / 11
0.00% covered (danger)
0.00%
0 / 1
18.69
wpcom_register_default_launchpad_checklists
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
wpcom_launchpad_get_retired_site_intents
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcom_launchpad_clear_retired_site_intents
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
wpcom_launchpad_clear_retired_site_intents_on_correct_action
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
wpcom_add_active_task_listener_hooks_to_correct_action
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
wpcom_launchpad_add_active_task_listeners
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcom_can_update_design_selected_task
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_is_checklist_task_complete
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_get_launchpad_checklist_by_checklist_slug
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_register_launchpad_task_list
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_get_required_tasks
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_get_launch_tasks
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_launchpad_are_all_tasks_completed
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_default_launchpad_task_list_completed
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
wpcom_launchpad_is_task_list_completed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_register_launchpad_tasks
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_register_launchpad_task
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_checklists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcom_hacky_track_video_uploaded_task
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_maybe_disable_for_difm
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_maybe_disable_launchpad_screen_for_site_setup
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
wpcom_maybe_preview_with_no_interactions
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_log_launchpad_being_enabled_for_test_sites
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_launchpad_get_fullscreen_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_get_launchpad_task_list_is_enabled
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_launchpad_is_task_list_dismissed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
wpcom_launchpad_is_task_list_dismissible
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_launchpad_task_list_dismissed_until
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_set_task_list_dismissed
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_launchpad_is_fse_next_steps_modal_hidden
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_launchpad_set_fse_next_steps_modal_hidden
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
110
wpcom_launchpad_navigator_get_checklists
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_launchpad_navigator_update_checklists
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
wpcom_launchpad_navigator_remove_checklist
71.43% covered (warning)
71.43%
20 / 28
0.00% covered (danger)
0.00%
0 / 1
8.14
wpcom_launchpad_navigator_add_checklist
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
wpcom_launchpad_get_active_checklist
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
6.22
wpcom_launchpad_set_current_active_checklist
66.67% covered (warning)
66.67%
12 / 18
0.00% covered (danger)
0.00%
0 / 1
13.70
wpcom_launchpad_is_keep_building_enabled
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_launchpad_is_hosting_flow_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_is_intent_write_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
wpcom_launchpad_is_free_newsletter_enabled
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_launchpad_is_paid_newsletter_enabled
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_launchpad_is_newsletter_goal_enabled
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
add_launchpad_options_to_jetpack_sync
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
wpcom_get_launchpad_checklist_title_by_checklist_slug
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
wpcom_get_launchpad_config_option
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
wpcom_set_launchpad_config_option
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Launchpad Helpers
4 *
5 * @package automattic/jetpack-mu-wpcom
6 * @since 1.4.0
7 */
8
9/**
10 * This file provides helpers that return the appropriate Launchpad
11 * checklist and tasks for a given checklist id.
12 *
13 * @package automattic/jetpack-mu-wpcom
14 * @since 1.4.0
15 */
16
17// Type aliases used in a bunch of places in this file. Unfortunately Phan doesn't have a way to set these more globally than copy-pasting them into each file needing them.
18<<<'PHAN'
19@phan-type Task_List = array{id:string, task_ids:string[], required_task_ids?:string[], visible_tasks_callback?:callable, require_last_task_completion?:bool, get_title?:callable, is_dismissible?:bool, is_enabled_callback?:callable}
20@phan-type Task = array{id:string, title?:string, get_title?:callable, id_map?:string, add_listener_callback?:callable, badge_text_callback?:callable, extra_data_callback?:callable, get_calypso_path?:callable, is_complete_callback?:callable, is_disabled_callback?:callable, isLaunchTask?:bool, is_visible_callback?:callable, target_repetitions?:int, repetition_count_callback?:callable, subtitle?:callable, completed?:bool}
21PHAN;
22
23require_once __DIR__ . '/../../utils.php';
24require_once __DIR__ . '/../../common/launchpad-no-guidance.php';
25require_once __DIR__ . '/class-launchpad-task-lists.php';
26require_once __DIR__ . '/launchpad-task-definitions.php';
27
28/**
29 * Registers all default launchpad checklists
30 *
31 * @return Task[]
32 */
33function wpcom_launchpad_get_task_list_definitions() {
34    $core_task_list_definitions = array(
35        'build'                   => array(
36            'get_title'           => function () {
37                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
38            },
39            'task_ids'            => array(
40                'plan_selected',
41                'site_theme_selected',
42                'domain_upsell',
43                'mobile_app_installed',
44                'site_launched',
45            ),
46            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
47        ),
48        'free'                    => array(
49            'get_title'           => function () {
50                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
51            },
52            'task_ids'            => array(
53                'plan_selected',
54                'setup_free',
55                'design_completed',
56                'site_theme_selected',
57                'domain_upsell',
58                'first_post_published',
59                'site_launched',
60            ),
61            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
62        ),
63        'link-in-bio'             => array(
64            'get_title'           => function () {
65                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
66            },
67            'task_ids'            => array(
68                'design_selected',
69                'plan_selected',
70                'links_added',
71                'link_in_bio_launched',
72            ),
73            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
74        ),
75        'link-in-bio-tld'         => array(
76            'get_title'           => function () {
77                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
78            },
79            'task_ids'            => array(
80                'design_selected',
81                'plan_selected',
82                'links_added',
83                'link_in_bio_launched',
84            ),
85            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
86        ),
87        'newsletter'              => array(
88            'get_title'           => function () {
89                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
90            },
91            'task_ids'            => array(
92                'setup_newsletter',
93                'plan_selected',
94                'verify_email',
95                'migrate_content',
96                'subscribers_added',
97                'set_up_payments',
98                'newsletter_plan_created',
99                'first_post_published_newsletter',
100            ),
101            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
102        ),
103        'intent-newsletter-goal'  => array(
104            'get_title'           => function () {
105                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
106            },
107            'task_ids'            => array(
108                'verify_email',
109                'site_title',
110                'start_building_your_audience',
111                'customize_welcome_message',
112                'first_post_published',
113                'site_launched',
114            ),
115            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
116        ),
117        'create-course-goal'      => array(
118            'get_title'           => function () {
119                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
120            },
121            'task_ids'            => array(
122                'site_title',
123                'verify_email',
124                'domain_customize',
125                'site_launched',
126            ),
127            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
128        ),
129        'videopress'              => array(
130            'get_title'           => function () {
131                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
132            },
133            'task_ids'            => array(
134                'videopress_setup',
135                'plan_selected',
136                'videopress_upload',
137                'videopress_launched',
138            ),
139            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
140        ),
141        'write'                   => array(
142            'get_title'           => function () {
143                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
144            },
145            'task_ids'            => array(
146                'verify_email',
147                'site_title',
148                'start_building_your_audience',
149                'complete_profile',
150                'first_post_published',
151                'site_launched',
152            ),
153            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
154        ),
155        'design-first'            => array(
156            'get_title'           => function () {
157                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
158            },
159            'task_ids'            => array(
160                'design_completed',
161                'setup_blog',
162                'domain_upsell',
163                'plan_completed',
164                'first_post_published',
165                'blog_launched',
166            ),
167            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
168        ),
169        'intent-build'            => array(
170            'get_title'           => function () {
171                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
172            },
173            'task_ids'            => array(
174                'site_title',
175                'domain_claim',
176                'verify_email',
177                'domain_customize',
178                'add_new_page',
179                'drive_traffic',
180                'edit_page',
181                'share_site',
182                'update_about_page',
183            ),
184            'is_enabled_callback' => 'wpcom_launchpad_is_keep_building_enabled',
185        ),
186        'intent-write'            => array(
187            'get_title'           => function () {
188                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
189            },
190            'task_ids'            => array(
191                'site_title',
192                'domain_claim',
193                'verify_email',
194                'domain_customize',
195                'drive_traffic',
196                'write_3_posts',
197            ),
198            'is_enabled_callback' => 'wpcom_launchpad_is_intent_write_enabled',
199        ),
200        'intent-free-newsletter'  => array(
201            'get_title'           => function () {
202                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
203            },
204            'task_ids'            => array(
205                'enable_subscribers_modal',
206                'verify_email',
207                'migrate_content',
208                'subscribers_added',
209                'share_site',
210                'manage_subscribers',
211                'update_about_page',
212                'add_about_page',
213            ),
214            'is_enabled_callback' => 'wpcom_launchpad_is_free_newsletter_enabled',
215        ),
216        'intent-paid-newsletter'  => array(
217            'get_title'           => function () {
218                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
219            },
220            'task_ids'            => array(
221                'enable_subscribers_modal',
222                'verify_email',
223                'migrate_content',
224                'subscribers_added',
225                'share_site',
226                'set_up_payments',
227                'manage_subscribers',
228                'manage_paid_newsletter_plan',
229                'update_about_page',
230                'add_about_page',
231            ),
232            'is_enabled_callback' => 'wpcom_launchpad_is_paid_newsletter_enabled',
233        ),
234        'earn'                    => array(
235            'task_ids'            => array(
236                'stripe_connected',
237                'paid_offer_created',
238            ),
239            'is_enabled_callback' => '__return_true',
240        ),
241        'host-site'               => array(
242            'task_ids'            => array(
243                'site_theme_selected',
244                'install_custom_plugin',
245                'setup_ssh',
246                'verify_email',
247                'site_monitoring_page',
248                'site_launched',
249            ),
250            'is_enabled_callback' => 'wpcom_launchpad_is_hosting_flow_enabled',
251        ),
252        'subscribers'             => array(
253            'task_ids' => array(
254                'import_subscribers',
255                'add_subscribe_block',
256                'share_site',
257            ),
258        ),
259        'assembler-first'         => array(
260            'get_title'           => function () {
261                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
262            },
263            'task_ids'            => array(
264                'verify_domain_email',
265                'plan_completed',
266                'setup_free',
267                'design_selected',
268                'site_theme_selected',
269                'domain_upsell',
270                'first_post_published',
271                'site_launched',
272            ),
273            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
274        ),
275        'readymade-template'      => array(
276            'get_title'           => function () {
277                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
278            },
279            'task_ids'            => array(
280                'verify_domain_email',
281                'design_selected',
282                'setup_general',
283                'generate_content',
284                'plan_completed',
285                'site_theme_selected',
286                'domain_upsell',
287                'site_launched',
288            ),
289            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
290        ),
291        'ai-assembler'            => array(
292            'get_title'           => function () {
293                return __( 'Next steps for your site', 'jetpack-mu-wpcom' );
294            },
295            'task_ids'            => array(
296                'verify_domain_email',
297                'plan_completed',
298                'setup_free',
299                'design_selected',
300                'site_theme_selected',
301                'domain_upsell',
302                'site_launched',
303            ),
304            'is_enabled_callback' => 'wpcom_launchpad_get_fullscreen_enabled',
305        ),
306        'legacy-site-setup'       => array(
307            'get_title'      => function () {
308                return __( 'Site setup', 'jetpack-mu-wpcom' );
309            },
310            'is_dismissible' => true,
311            'task_ids'       => array(
312                'woocommerce_setup',
313                'sensei_setup',
314                'site_title',
315                'front_page_updated',
316                'verify_domain_email',
317                'verify_email',
318                'mobile_app_installed',
319                'post_sharing_enabled',
320                'site_launched',
321            ),
322        ),
323        'sell'                    => array(
324            'get_title'      => function () {
325                return __( 'Site setup', 'jetpack-mu-wpcom' );
326            },
327            'is_dismissible' => true,
328            'task_ids'       => array(
329                'woocommerce_setup',
330                'sensei_setup',
331                'site_title',
332                'front_page_updated',
333                'verify_domain_email',
334                'verify_email',
335                'mobile_app_installed',
336                'post_sharing_enabled',
337                'site_launched',
338            ),
339        ),
340        'entrepreneur-site-setup' => array(
341            'task_ids' => array(
342                'woo_customize_store',
343                'woo_products',
344                'woo_woocommerce_payments',
345                'woo_tax',
346                'woo_marketing',
347                'woo_add_domain',
348                'woo_launch_site',
349            ),
350        ),
351        'post-migration'          => array(
352            'get_title' => function () {
353                return __( 'Site migration', 'jetpack-mu-wpcom' );
354            },
355            'task_ids'  => array(
356                'migrating_site',
357                'review_site',
358                'review_plugins',
359                'connect_migration_domain',
360                'domain_dns_mapped',
361                'check_ssl_status',
362            ),
363        ),
364    );
365
366    $extended_task_list_definitions = apply_filters( 'wpcom_launchpad_extended_task_list_definitions', array() );
367
368    // As for tasks, we can decide what overrides we allow later.
369    return array_merge( $extended_task_list_definitions, $core_task_list_definitions );
370}
371
372/**
373 * Get a registered task list.
374 *
375 * @param string $checklist_slug The checklist slug to get the task list for.
376 *
377 * @return Task_List
378 */
379function wpcom_launchpad_get_task_list( $checklist_slug = null ) {
380    // If we don't have a checklist slug, fall back to the site intent option.
381    $checklist_slug = $checklist_slug ? $checklist_slug : get_option( 'site_intent' );
382    if ( ! $checklist_slug ) {
383        return array();
384    }
385
386    return wpcom_launchpad_checklists()->get_task_list( $checklist_slug );
387}
388
389/**
390 * Register all tasks and task lists from definitions
391 *
392 * @param bool $rebuild Whether to rebuild the task lists or not.
393 *
394 * @return Task_List[]
395 */
396function wpcom_launchpad_get_task_lists( $rebuild = false ) {
397    // If we already have task lists registered and we don't want to rebuild, return all task lists.
398    if ( ! $rebuild && wpcom_launchpad_checklists()->has_task_lists() ) {
399        return wpcom_launchpad_checklists()->get_all_task_lists();
400    }
401
402    $task_definitions = wpcom_launchpad_get_task_definitions();
403
404    // Register all tasks.
405    foreach ( $task_definitions as $task_id => $task_definition ) {
406        $task_data = array_merge( $task_definition, array( 'id' => $task_id ) );
407        wpcom_register_launchpad_task( $task_data );
408    }
409
410    $task_list_definitions = wpcom_launchpad_get_task_list_definitions();
411
412    // Register all task lists.
413    foreach ( $task_list_definitions as $task_list_id => $task_list_definition ) {
414        $task_list_data = array_merge( $task_list_definition, array( 'id' => $task_list_id ) );
415        wpcom_register_launchpad_task_list( $task_list_data );
416    }
417
418    // Assuming the reference is good, just return all checklists.
419    return wpcom_launchpad_checklists()->get_all_task_lists();
420}
421
422/**
423 * Register all tasks and task lists on init.
424 */
425function wpcom_register_default_launchpad_checklists() {
426    wpcom_launchpad_get_task_lists();
427    wpcom_add_active_task_listener_hooks_to_correct_action();
428}
429add_action( 'init', 'wpcom_register_default_launchpad_checklists', 11 );
430
431/**
432 * The site intents of retired onboarding flows.
433 *
434 * These no longer have a checklist, but sites created under them still carry
435 * the value, and clients still pass it to the launchpad endpoint as a checklist
436 * slug. The endpoint keeps accepting these so such a request degrades to an
437 * empty checklist rather than a 400. Empty this list once no site carries one.
438 *
439 * @return string[] Retired site intents.
440 */
441function wpcom_launchpad_get_retired_site_intents() {
442    return array( 'start-writing' );
443}
444
445/**
446 * Clears the site intent for a retired onboarding flow.
447 *
448 * A retired flow no longer accepts new sites, so the sites left on one sit in
449 * an onboarding they can never be guided through. Clearing the intent returns
450 * them to the same state as a site that never picked one: no Launchpad, and no
451 * onboarding email campaign.
452 *
453 * Writes once per site, since the intent stops matching afterwards. Delete this
454 * once no site carries one of these values.
455 */
456function wpcom_launchpad_clear_retired_site_intents() {
457    if ( ! in_array( get_option( 'site_intent' ), wpcom_launchpad_get_retired_site_intents(), true ) ) {
458        return;
459    }
460
461    update_option( 'site_intent', '' );
462    update_option( 'launchpad_screen', 'off' );
463}
464
465/**
466 * Clears the retired site intent once the target site's options are readable.
467 *
468 * On `public-api.wordpress.com`, `init` fires before the request switches to
469 * the blog, so reading `site_intent` there would read the wrong site.
470 *
471 * Runs ahead of the checklist registration, on both paths, so that the active
472 * task listeners are never registered for an intent that is about to be gone.
473 *
474 * @return void
475 */
476function wpcom_launchpad_clear_retired_site_intents_on_correct_action() {
477    if ( wp_parse_url( home_url(), PHP_URL_HOST ) === 'public-api.wordpress.com' ) {
478        add_action( 'rest_api_switched_to_blog', 'wpcom_launchpad_clear_retired_site_intents' );
479        return;
480    }
481
482    wpcom_launchpad_clear_retired_site_intents();
483}
484add_action( 'init', 'wpcom_launchpad_clear_retired_site_intents_on_correct_action', 10 );
485
486/**
487 * Adds hooks to the correct action to add active task listeners.
488 * Handles REST API requests vs non-REST API requests.
489 *
490 * @return null
491 */
492function wpcom_add_active_task_listener_hooks_to_correct_action() {
493    $url = wp_parse_url( home_url(), PHP_URL_HOST );
494    if ( $url === 'public-api.wordpress.com' ) {
495        return add_action( 'rest_api_switched_to_blog', 'wpcom_launchpad_add_active_task_listeners' );
496    }
497
498    // If we're not deferring to REST API blog switch, just run now
499    return wpcom_launchpad_add_active_task_listeners();
500}
501
502/**
503 * Adds task-defined `add_listener_callback` hooks for incomplete tasks.
504 *
505 * @return void
506 */
507function wpcom_launchpad_add_active_task_listeners() {
508    wpcom_launchpad_checklists()->add_hooks_for_active_tasks();
509}
510
511/**
512 * Determines whether or not design selected task is enabled
513 *
514 * @return boolean True if design selected task is enabled
515 */
516function wpcom_can_update_design_selected_task() {
517    $site_intent = get_option( 'site_intent' );
518    return $site_intent === 'free' || $site_intent === 'build' || $site_intent === 'write' || $site_intent === 'design-first';
519}
520
521/**
522 * Returns launchpad checklist task by task id.
523 *
524 * @param string $task_id Task id.
525 *
526 * @return array Associative array with task data
527 *               or false if task id is not found.
528 */
529function wpcom_is_checklist_task_complete( $task_id ) {
530    return wpcom_launchpad_checklists()->is_task_id_complete( $task_id );
531}
532
533/**
534 * Returns launchpad checklist by checklist slug.
535 *
536 * @param string      $checklist_slug Checklist slug.
537 * @param string|null $launchpad_context Optional. Screen where Launchpad is loading.
538 *
539 * @return Task[] Collection of tasks for a given checklist
540 */
541function wpcom_get_launchpad_checklist_by_checklist_slug( $checklist_slug, $launchpad_context = null ) {
542    if ( ! $checklist_slug ) {
543        return array();
544    }
545
546    return wpcom_launchpad_checklists()->build( $checklist_slug, $launchpad_context );
547}
548
549// TODO: Write code p2 post or dotcom post
550/**
551 * Wrapper that registers a launchpad checklist.
552 *
553 * @param Task_List $task_list Task list definition.
554 *
555 * @return bool True if successful, false otherwise.
556 */
557function wpcom_register_launchpad_task_list( $task_list ) {
558    return wpcom_launchpad_checklists()->register_task_list( $task_list );
559}
560
561/**
562 * Retrieves the required tasks from a given set of tasks.
563 *
564 * This function filters the provided tasks array and returns only the tasks that are marked as required,
565 * based on the specified array of required task IDs. It's worth noting that some tasks may not be
566 * visible to the user, so we are going to return all tasks that are marked as required, regardless
567 *
568 * @param array  $required_task_ids An array of task IDs that are considered required.
569 * @param Task[] $tasks An array of tasks to filter.
570 * @return array An array containing only the required tasks from the provided task list.
571 */
572function wpcom_launchpad_get_required_tasks( $required_task_ids, $tasks ) {
573    return array_filter(
574        $tasks,
575        function ( $task ) use ( $required_task_ids ) {
576            return in_array( $task['id'], $required_task_ids, true );
577        }
578    );
579}
580
581/**
582 * Get all tasks that are marked as launch tasks.
583 *
584 * @param Task[] $tasks Array of tasks.
585 * @return Task[] Array of launch tasks.
586 */
587function wpcom_launchpad_get_launch_tasks( $tasks ) {
588    return array_filter(
589        $tasks,
590        function ( $task ) {
591            if ( isset( $task['isLaunchTask'] ) ) {
592                return $task['isLaunchTask'];
593            }
594            return false;
595        }
596    );
597}
598
599/**
600 * Check if all the given tasks are completed.
601 *
602 * @param Task[] $tasks Array of tasks to check if they are completed.
603 * @return bool True if all tasks are completed, false otherwise.
604 */
605function wpcom_launchpad_are_all_tasks_completed( $tasks ) {
606    return array_reduce(
607        $tasks,
608        function ( $carry, $launch_task ) {
609            return $carry && $launch_task['completed'];
610        },
611        true
612    );
613}
614
615/**
616 * Determine the completion status of a task list based on its tasks.
617 *
618 * This function identifies any required tasks within the task list. If there are required tasks, the task list is considered complete
619 * only if all required tasks are completed. In the absence of required tasks, a secondary check is performed on tasks flagged with
620 * "isLaunchTask" to determine completion based on whether all of those tasks are complete. If there are no required tasks and no
621 * launch tasks, the task list's completion is determined by checking whether all tasks are complete. However, if there are incomplete
622 * tasks in this scenario, the completion status is based on whether the last task was completed. It is possible to control this behavior
623 * using a flag, as the default approach might be confusing for task lists where the steps may not be sequential. By default, the task
624 * list is marked as incomplete to align with most use cases that focus on user guidance and next steps rather than a specific end goal.
625 *
626 * @param Task_List $task_list Task list.
627 * @return bool True if the task list is considered complete, false otherwise.
628 */
629function wpcom_default_launchpad_task_list_completed( $task_list ) {
630    $task_list_id      = $task_list['id'];
631    $required_task_ids = wpcom_launchpad_checklists()->get_required_task_ids( $task_list_id );
632    $all_visible_tasks = wpcom_launchpad_checklists()->build( $task_list_id );
633
634    // If there are required tasks, check if they are all completed.
635    if ( ! empty( $required_task_ids ) ) {
636        $required_tasks = wpcom_launchpad_get_required_tasks( $required_task_ids, $all_visible_tasks );
637        return wpcom_launchpad_are_all_tasks_completed( $required_tasks );
638    }
639
640    // If there are no required tasks, check if there are any launch tasks.
641    $launch_tasks = wpcom_launchpad_get_launch_tasks( $all_visible_tasks );
642    if ( ! empty( $launch_tasks ) ) {
643        return wpcom_launchpad_are_all_tasks_completed( $launch_tasks );
644    }
645
646    // If there are no required tasks and no launch tasks, check if all tasks are completed.
647    if ( wpcom_launchpad_are_all_tasks_completed( $all_visible_tasks ) ) {
648        return true;
649    }
650
651    // If there are incomplete tasks, check if the last task was completed.
652    $require_last_task_completion = wpcom_launchpad_checklists()->get_require_last_task_completion( $task_list_id );
653    $last_task                    = end( $all_visible_tasks );
654    if ( $require_last_task_completion && $last_task['completed'] ) {
655        return true;
656    }
657
658    return false;
659}
660
661/**
662 * Callback to determine the completion status of a task list.
663 *
664 * @param string $task_list_id The task list ID.
665 * @return bool True if the task list is considered complete, false otherwise.
666 */
667function wpcom_launchpad_is_task_list_completed( $task_list_id ) {
668    return wpcom_launchpad_checklists()->is_task_list_completed( $task_list_id );
669}
670
671/**
672 * Wrapper that registers a launchpad checklist.
673 *
674 * @param Task $tasks Collection of Task definitions.
675 *
676 * @return bool True if successful, false otherwise.
677 */
678function wpcom_register_launchpad_tasks( $tasks ) {
679    return wpcom_launchpad_checklists()->register_tasks( $tasks );
680}
681
682/**
683 * Wrapper that registers a launchpad checklist.
684 *
685 * @param Task $task Task definition.
686 *
687 * @return bool True if successful, false otherwise.
688 */
689function wpcom_register_launchpad_task( $task ) {
690    return wpcom_launchpad_checklists()->register_task( $task );
691}
692
693/**
694 * Helper function to return a `Launchpad_Task_Lists` instance.
695 *
696 * @return object Launchpad_Task_Lists instance.
697 */
698function wpcom_launchpad_checklists() {
699    return Launchpad_Task_Lists::get_instance();
700}
701
702/**
703 * The `/rest/v1.1/video-uploads` endpoint operates without calling the `rest_api_switched_to_blog` hook.
704 * Which prevents our listeners from being added.
705 * This mimics the legacy mu-plugin logic of always adding the listener.
706 *
707 * @param int $post_id The attachment ID.
708 * @return void
709 */
710function wpcom_hacky_track_video_uploaded_task( $post_id ) {
711    if ( get_option( 'site_intent' ) !== 'videopress' ) {
712        return;
713    }
714    if ( get_option( 'launchpad_screen' ) !== 'full' ) {
715        return;
716    }
717    if ( has_action( 'add_attachment', 'wpcom_launchpad_track_video_uploaded_task' ) ) {
718        return;
719    }
720
721    wpcom_launchpad_track_video_uploaded_task( $post_id );
722}
723add_action( 'add_attachment', 'wpcom_hacky_track_video_uploaded_task' );
724
725//
726// Misc other Launchpad-related functionality below.
727//
728
729/**
730 * A filter for `get_option( 'launchpad_screen' )`
731 *
732 * @param mixed $value The filterable option value, retrieved from the DB.
733 * @return mixed       false if DIFM is active, the unaltered value otherwise.
734 */
735function wpcom_maybe_disable_for_difm( $value ) {
736    // If it's already false I don't care
737    if ( $value === false ) {
738        return $value;
739    }
740
741    // We want to disable for Built By Express aka DIFM, in case they've
742    // 1) Entered the Launchpad during signup, then 2) Purchased DIFM
743    if ( has_blog_sticker( 'difm-lite-in-progress' ) ) {
744        return false;
745    }
746    // Just in case, always return from a filter!
747    return $value;
748}
749// only WPCOM has blog stickers.
750if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
751    add_filter( 'option_launchpad_screen', 'wpcom_maybe_disable_for_difm' );
752}
753
754/**
755 * Filter for `get_option( 'launchpad_screen' )`: hide the old Launchpad on Site Setup and no-guidance sites.
756 *
757 * @param mixed $value The filterable option value, retrieved from the DB.
758 * @return mixed 'off' for those sites, the unaltered value otherwise.
759 */
760function wpcom_maybe_disable_launchpad_screen_for_site_setup( $value ) {
761    if ( false === $value ) {
762        return $value;
763    }
764
765    if ( get_option( 'wpcom_ai_launchpad_enabled' ) || wpcom_launchpad_is_no_guidance() ) {
766        return 'off';
767    }
768
769    return $value;
770}
771add_filter( 'option_launchpad_screen', 'wpcom_maybe_disable_launchpad_screen_for_site_setup' );
772
773add_action( 'wp_head', 'wpcom_maybe_preview_with_no_interactions', PHP_INT_MAX );
774/**
775 * Add CSS that disallows interaction with the Launchpad preview.
776 *
777 * @return void|string
778 */
779function wpcom_maybe_preview_with_no_interactions() {
780    // phpcs:ignore
781    if ( empty( $_GET['do_preview_no_interactions'] ) || $_GET['do_preview_no_interactions'] !== 'true' ) {
782        return;
783    }
784
785    ?>
786        <style type="text/css">
787                body {
788                    pointer-events: none !important;
789                }
790        </style>
791    <?php
792}
793
794// Temporarily log information to debug intermittent launchpad errors for e2e tests
795if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
796    add_action( 'add_option_launchpad_screen', 'wpcom_log_launchpad_being_enabled_for_test_sites', 10, 2 );
797}
798
799/**
800 * Logs data when e2eflowtesting5.wordpress.com has launchpad enabled
801 *
802 * @param string $option The previous option value.
803 * @param string $value The new option value.
804 * @return void
805 */
806function wpcom_log_launchpad_being_enabled_for_test_sites( $option, $value ) {
807    // e2eflowtesting5.wordpress.com
808    if ( get_current_blog_id() !== 208860881 || $value !== 'full' ) {
809        return;
810    }
811
812    require_once WP_CONTENT_DIR . '/lib/log2logstash/log2logstash.php';
813    require_once WP_CONTENT_DIR . '/admin-plugins/wpcom-billing.php';
814    $current_plan = WPCOM_Store_API::get_current_plan( get_current_blog_id() );
815    $extra        = array(
816        'is_free'     => $current_plan['is_free'],
817        'site_intent' => get_option( 'site_intent' ),
818    );
819
820    log2logstash(
821        array(
822            'feature' => 'launchpad',
823            'message' => 'Launchpad enabled for e2e test site.',
824            'extra'   => wp_json_encode( $extra, JSON_UNESCAPED_SLASHES ),
825        )
826    );
827}
828
829/**
830 * Checks if the overall launchpad is enabled. Used with `is_enabled_callback`
831 * for backwards compatibility with established task lists
832 * that relied on the old `launchpad_screen` option.
833 *
834 * @return bool True if the launchpad is enabled, false otherwise.
835 */
836function wpcom_launchpad_get_fullscreen_enabled() {
837    return wpcom_launchpad_checklists()->is_fullscreen_launchpad_enabled();
838}
839
840/**
841 * Checks if a specific launchpad task list is enabled.
842 *
843 * @param string $checklist_slug The slug of the launchpad task list to check.
844 * @return bool True if the task list is enabled, false otherwise.
845 */
846function wpcom_get_launchpad_task_list_is_enabled( $checklist_slug ) {
847    if ( false !== $checklist_slug ) {
848        return wpcom_launchpad_checklists()->is_task_list_enabled( $checklist_slug );
849    }
850
851    return false;
852}
853
854/**
855 * Checks if a specific task list is dismissed.
856 *
857 * @param string $checklist_slug The slug of the launchpad task list to check.
858 * @return bool True if the task list is dismissed, false otherwise.
859 */
860function wpcom_launchpad_is_task_list_dismissed( $checklist_slug ) {
861    return wpcom_launchpad_checklists()->is_task_list_dismissed( $checklist_slug );
862}
863
864/**
865 * Checks if the Launchpad is dismissible.
866 *
867 * @param string $checklist_slug The slug of the launchpad task list to check.
868 * @return bool True if the Launchpad is dismissible, false otherwise.
869 */
870function wpcom_launchpad_is_task_list_dismissible( $checklist_slug ) {
871    if ( false === $checklist_slug ) {
872        return false;
873    }
874    return wpcom_launchpad_checklists()->is_task_list_dismissible( $checklist_slug );
875}
876
877/**
878 * Returns the the temporary dismissed timestamps for a specific task list.
879 *
880 * @param string $checklist_slug The slug of the launchpad task list to check.
881 * @return int The timestamp until which the task list is dismissed.
882 */
883function wpcom_launchpad_task_list_dismissed_until( $checklist_slug ) {
884    return wpcom_launchpad_checklists()->get_task_list_dismissed_until( $checklist_slug );
885}
886
887/**
888 * Sets a specific task list dismissed state.
889 *
890 * @param string        $checklist_slug The slug of the launchpad task list to check.
891 * @param bool          $is_dismissed True if the task list is dismissed, false otherwise.
892 * @param string | null $dismissed_until The date until which the task list is dismissed.
893 */
894function wpcom_launchpad_set_task_list_dismissed( $checklist_slug, $is_dismissed, $dismissed_until ) {
895
896    if ( isset( $dismissed_until ) ) {
897        wpcom_launchpad_checklists()->set_task_list_dismissed_until( $checklist_slug, $dismissed_until );
898    } else {
899        wpcom_launchpad_checklists()->set_task_list_dismissed( $checklist_slug, $is_dismissed );
900    }
901}
902
903/**
904 * Helper function to indicate whether the Next Steps modal in
905 * the Full Site Editor should be hidden.
906 *
907 * @return bool
908 */
909function wpcom_launchpad_is_fse_next_steps_modal_hidden() {
910    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config' );
911
912    if ( ! $wpcom_launchpad_config || ! is_array( $wpcom_launchpad_config ) ) {
913        return false;
914    }
915
916    if ( ! isset( $wpcom_launchpad_config['hide_fse_next_steps_modal'] ) ) {
917        return false;
918    }
919
920    return true === $wpcom_launchpad_config['hide_fse_next_steps_modal'];
921}
922
923/**
924 * Helper function to hide and show the Next Steps modal we show in the
925 * Full Site Editor.
926 *
927 * @param bool $should_hide Should the modal be hidden (true) or displayed (false).
928 * @return bool Whether the option update succeeded.
929 */
930function wpcom_launchpad_set_fse_next_steps_modal_hidden( $should_hide ) {
931    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config' );
932
933    // If we want to show the modal, we don't need to do anything if either the main
934    // wpcom_launchpad_config option OR the hide sub-option aren't set.
935    if ( ! $should_hide ) {
936        if ( ! $wpcom_launchpad_config || ! is_array( $wpcom_launchpad_config ) ) {
937            return true;
938        }
939
940        if ( ! isset( $wpcom_launchpad_config['hide_fse_next_steps_modal'] ) ) {
941            return true;
942        }
943
944        unset( $wpcom_launchpad_config['hide_fse_next_steps_modal'] );
945    } else {
946        // Make sure we have an array for the main option.
947        if ( ! $wpcom_launchpad_config || ! is_array( $wpcom_launchpad_config ) ) {
948            $wpcom_launchpad_config = array();
949        }
950
951        // If we already have the option set, we can return early.
952        if ( isset( $wpcom_launchpad_config['hide_fse_next_steps_modal'] ) && true === $wpcom_launchpad_config['hide_fse_next_steps_modal'] ) {
953            return true;
954        }
955
956        $wpcom_launchpad_config['hide_fse_next_steps_modal'] = true;
957    }
958
959    if ( empty( $wpcom_launchpad_config ) ) {
960        return delete_option( 'wpcom_launchpad_config' );
961    }
962
963    return update_option( 'wpcom_launchpad_config', $wpcom_launchpad_config );
964}
965
966/**
967 * Returns a list of all the checklists that are currently available for the navigator.
968 *
969 * @return array Array of strings representing the checklist slugs.
970 */
971function wpcom_launchpad_navigator_get_checklists() {
972    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
973
974    if ( ! isset( $wpcom_launchpad_config['navigator_checklists'] ) ) {
975        return array();
976    }
977    $all_checklists  = wpcom_launchpad_checklists()->get_all_task_lists();
978    $checklist_slugs = $wpcom_launchpad_config['navigator_checklists'];
979
980    $results = array();
981    foreach ( $checklist_slugs as $slug ) {
982        if ( ! isset( $all_checklists[ $slug ] ) ) {
983            continue;
984        }
985
986        $results[ $slug ] = array(
987            'slug'  => $slug,
988            'title' => $all_checklists[ $slug ]['title'],
989        );
990    }
991
992    return $results;
993}
994
995/**
996 * Updates the list of checklists that are currently available for the navigator.
997 *
998 * @param array $new_checklists Array of strings representing the checklist slugs.
999 * @return bool Whether the option update succeeded.
1000 */
1001function wpcom_launchpad_navigator_update_checklists( $new_checklists ) {
1002    if ( ! is_array( $new_checklists ) ) {
1003        return false;
1004    }
1005
1006    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
1007
1008    $wpcom_launchpad_config['navigator_checklists'] = $new_checklists;
1009
1010    return update_option( 'wpcom_launchpad_config', $wpcom_launchpad_config );
1011}
1012
1013/**
1014 * Removes a checklist from the list of checklists that are currently available for the navigator.
1015 *
1016 * @param string $checklist_slug The slug of the checklist to remove.
1017 * @return array Array with two values: whether the option update succeeded, and the new active checklist slug.
1018 */
1019function wpcom_launchpad_navigator_remove_checklist( $checklist_slug ) {
1020    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
1021
1022    if ( ! isset( $wpcom_launchpad_config['navigator_checklists'] ) ) {
1023        return array(
1024            'updated'              => false,
1025            'new_active_checklist' => null,
1026        );
1027    }
1028
1029    $checklists = $wpcom_launchpad_config['navigator_checklists'];
1030    // Find if $checklist_slug is in the checklists array. If it is, remove it.
1031    $key = array_search( $checklist_slug, $checklists, true );
1032    if ( $key === false ) {
1033        return array(
1034            'updated'              => false,
1035            'new_active_checklist' => wpcom_launchpad_get_active_checklist(),
1036        );
1037    }
1038
1039    unset( $checklists[ $key ] );
1040
1041    // Compare against the raw stored slug, not the getter: a retired active checklist
1042    // reads as null there, which would otherwise hide that it was the one removed.
1043    $stored_active_slug = $wpcom_launchpad_config['active_checklist_slug'] ?? null;
1044
1045    $new_active_checklist = wpcom_launchpad_get_active_checklist();
1046    if ( $stored_active_slug === $checklist_slug ) {
1047        // The active checklist was removed. Promote the most recently added one that
1048        // is still registered, skipping any other retired slugs, or clear it.
1049        $registered_checklists = wpcom_launchpad_checklists()->get_all_task_lists();
1050        $new_active_checklist  = null;
1051        foreach ( array_reverse( $checklists ) as $remaining_slug ) {
1052            if ( is_string( $remaining_slug ) && array_key_exists( $remaining_slug, $registered_checklists ) ) {
1053                $new_active_checklist = $remaining_slug;
1054                break;
1055            }
1056        }
1057        wpcom_launchpad_set_current_active_checklist( $new_active_checklist );
1058    }
1059
1060    return array(
1061        'updated'              => wpcom_launchpad_navigator_update_checklists( $checklists ),
1062        'new_active_checklist' => $new_active_checklist,
1063    );
1064}
1065
1066/**
1067 * Adds a new checklist to the list of checklists that are currently available for the navigator.
1068 *
1069 * @param string $new_checklist_slug The slug of the launchpad task list to add.
1070 */
1071function wpcom_launchpad_navigator_add_checklist( $new_checklist_slug ) {
1072    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
1073    $checklists             = array();
1074
1075    if ( isset( $wpcom_launchpad_config['navigator_checklists'] ) ) {
1076        $checklists = $wpcom_launchpad_config['navigator_checklists'];
1077    }
1078
1079    // add the new_checklist_slug to the checklists array if it's not already there.
1080    if ( ! in_array( $new_checklist_slug, $checklists, true ) ) {
1081        $checklists[] = $new_checklist_slug;
1082    }
1083
1084    wpcom_launchpad_navigator_update_checklists( $checklists );
1085}
1086
1087/**
1088 * Helper function to indicate what's the current active checklist
1089 * in the context of the navigator.
1090 * It will try to read the key 'active_checklist_slug' from the 'wpcom_launchpad_config' option.
1091 *
1092 * @return string|null The active checklist slug, null if none is set.
1093 */
1094function wpcom_launchpad_get_active_checklist() {
1095    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config' );
1096
1097    if ( ! $wpcom_launchpad_config || ! is_array( $wpcom_launchpad_config ) ) {
1098        return null;
1099    }
1100
1101    if ( ! isset( $wpcom_launchpad_config['active_checklist_slug'] ) ) {
1102        return null;
1103    }
1104
1105    $active_checklist_slug = $wpcom_launchpad_config['active_checklist_slug'];
1106
1107    // This repairs persisted state, so it cannot assume a well-formed value: legacy
1108    // or custom code may have stored a non-string, which would fatal below.
1109    if ( ! is_string( $active_checklist_slug ) ) {
1110        return null;
1111    }
1112
1113    // A retired checklist can linger here from before it was unregistered. Treat it
1114    // as unset, matching how the available list drops unregistered slugs and how the
1115    // setter refuses them, so the Navigator never reports a checklist that is gone.
1116    if ( ! array_key_exists( $active_checklist_slug, wpcom_launchpad_checklists()->get_all_task_lists() ) ) {
1117        return null;
1118    }
1119
1120    return $active_checklist_slug;
1121}
1122
1123/**
1124 * Helper function to set the current active checklist in the navigator context.
1125 *
1126 * @param string|null $checklist_slug The slug of the launchpad task list to mark as active, or null to clear it.
1127 * @return bool Whether the option update succeeded.
1128 */
1129function wpcom_launchpad_set_current_active_checklist( $checklist_slug ) {
1130    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config' );
1131
1132    if ( null !== $checklist_slug ) {
1133        $checklists = wpcom_launchpad_checklists()->get_all_task_lists();
1134        if ( ! array_key_exists( $checklist_slug, $checklists ) ) {
1135            return false;
1136        }
1137    }
1138
1139    if ( ! $wpcom_launchpad_config || ! is_array( $wpcom_launchpad_config ) ) {
1140        $wpcom_launchpad_config = array();
1141    }
1142
1143    if ( null === $checklist_slug ) {
1144        if ( ! isset( $wpcom_launchpad_config['active_checklist_slug'] ) ) {
1145            return true;
1146        }
1147        unset( $wpcom_launchpad_config['active_checklist_slug'] );
1148    } else {
1149        if ( isset( $wpcom_launchpad_config['active_checklist_slug'] ) && $checklist_slug === $wpcom_launchpad_config['active_checklist_slug'] ) {
1150            return true;
1151        }
1152        $wpcom_launchpad_config['active_checklist_slug'] = $checklist_slug;
1153    }
1154
1155    $return_value = update_option( 'wpcom_launchpad_config', $wpcom_launchpad_config );
1156    // add to available checklists if not null
1157    if ( $checklist_slug !== null ) {
1158        wpcom_launchpad_navigator_add_checklist( $checklist_slug );
1159    }
1160
1161    return $return_value;
1162}
1163
1164/**
1165 * Checks if the Keep building task list is enabled.
1166 *
1167 * @return bool True if the task list is enabled, false otherwise.
1168 */
1169function wpcom_launchpad_is_keep_building_enabled() {
1170    $intent  = get_option( 'site_intent', false );
1171    $blog_id = get_current_blog_id();
1172
1173    if ( 'build' === $intent && $blog_id > 220443356 ) {
1174        return true;
1175    }
1176
1177    return false;
1178}
1179
1180/**
1181 * Checks if the hosting flow task list is enabled.
1182 *
1183 * @return bool True if the task list is enabled, false otherwise.
1184 */
1185function wpcom_launchpad_is_hosting_flow_enabled() {
1186    return apply_filters( 'is_launchpad_intent_hosting_enabled', false );
1187}
1188
1189/**
1190 * Checks if the Blog flow task list is enabled.
1191 *
1192 * @return bool True if the task list is enabled, false otherwise.
1193 */
1194function wpcom_launchpad_is_intent_write_enabled() {
1195    return apply_filters( 'is_launchpad_intent_write_enabled', false );
1196}
1197
1198/**
1199 * Checks if the Free Newsletter flow task list is enabled.
1200 *
1201 * @return bool True if the task list is enabled, false otherwise.
1202 */
1203function wpcom_launchpad_is_free_newsletter_enabled() {
1204    $intent = get_option( 'site_intent', false );
1205    if ( 'newsletter' !== $intent ) {
1206        return false;
1207    }
1208
1209    return ! wpcom_launchpad_has_goal_paid_subscribers() && apply_filters( 'wpcom_launchpad_intent_free_newsletter_enabled', false );
1210}
1211
1212/**
1213 * Checks if the Paid Newsletter flow task list is enabled.
1214 *
1215 * @return bool True if the task list is enabled, false otherwise.
1216 */
1217function wpcom_launchpad_is_paid_newsletter_enabled() {
1218    $intent = get_option( 'site_intent', false );
1219    if ( 'newsletter' !== $intent ) {
1220        return false;
1221    }
1222
1223    return wpcom_launchpad_has_goal_paid_subscribers() && apply_filters( 'wpcom_launchpad_intent_paid_newsletter_enabled', false );
1224}
1225
1226/**
1227 * Checks if the Newsletter goal flow task list is enabled.
1228 *
1229 * @return bool True if the task list is enabled, false otherwise.
1230 */
1231function wpcom_launchpad_is_newsletter_goal_enabled() {
1232    $intent = get_option( 'site_intent', false );
1233    if ( 'intent-newsletter-goal' !== $intent ) {
1234        return false;
1235    }
1236
1237    return apply_filters( 'wpcom_launchpad_intent_newsletter_goal_enabled', false );
1238}
1239/**
1240 * Add launchpad options to Jetpack Sync.
1241 *
1242 * @param array $allowed_options The allowed options.
1243 */
1244function add_launchpad_options_to_jetpack_sync( $allowed_options ) {
1245    // We are not either in Simple or Atomic
1246    if ( ! class_exists( 'Automattic\Jetpack\Status\Host' ) ) {
1247        return $allowed_options;
1248    }
1249
1250    if ( ! ( new Automattic\Jetpack\Status\Host() )->is_woa_site() ) {
1251        return $allowed_options;
1252    }
1253
1254    if ( ! is_array( $allowed_options ) ) {
1255        return $allowed_options;
1256    }
1257
1258    $launchpad_options = array(
1259        'site_intent',
1260        'launchpad_checklist_tasks_statuses',
1261        'site_goals',
1262        'sm_enabled',
1263    );
1264
1265    return array_merge( $allowed_options, $launchpad_options );
1266}
1267add_filter( 'jetpack_sync_options_whitelist', 'add_launchpad_options_to_jetpack_sync', 10, 1 );
1268
1269/**
1270 * Get the title of a checklist by its slug.
1271 *
1272 * @param string $checklist_slug The slug of the checklist.
1273 * @return string The title of the checklist.
1274 */
1275function wpcom_get_launchpad_checklist_title_by_checklist_slug( $checklist_slug ) {
1276    if ( ! $checklist_slug ) {
1277        return array();
1278    }
1279
1280    return wpcom_launchpad_checklists()->get_task_list_title( $checklist_slug );
1281}
1282
1283/**
1284 * Gets a launchpad config option.
1285 *
1286 * @param string $option The option to get.
1287 * @param mixed  $default The default value to return if the option is not set.
1288 */
1289function wpcom_get_launchpad_config_option( $option, $default = null ) {
1290    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
1291
1292    if ( ! is_array( $wpcom_launchpad_config ) || ! isset( $wpcom_launchpad_config[ $option ] ) ) {
1293        return $default;
1294    }
1295
1296    return $wpcom_launchpad_config[ $option ];
1297}
1298
1299/**
1300 * Sets a launchpad config option.
1301 *
1302 * @param string $option The option to set.
1303 * @param mixed  $value The value to set.
1304 */
1305function wpcom_set_launchpad_config_option( $option, $value ) {
1306    $wpcom_launchpad_config = get_option( 'wpcom_launchpad_config', array() );
1307
1308    $wpcom_launchpad_config[ $option ] = $value;
1309
1310    return update_option( 'wpcom_launchpad_config', $wpcom_launchpad_config );
1311}