Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 359
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
WP_Admin
0.00% covered (danger)
0.00%
0 / 359
0.00% covered (danger)
0.00%
0 / 16
3906
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 load_php_textdomain
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 register_page
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 page_title
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 is_loading
0.00% covered (danger)
0.00%
0 / 92
0.00% covered (danger)
0.00%
0 / 1
2
 render
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 register_assets
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 1
20
 enqueue_block_assets
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exit_to_dashboard
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 initialize
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 mark_sitegen_generated_themes
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
12
 set_onboarding_restart_option
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 can_restart_onboarding
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
12
 hide_onboarding_restart_card
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 enqueue_site_editor_assets
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
30
 enqueue_preview_fonts
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 1
650
1<?php
2namespace NewfoldLabs\WP\Module\Onboarding;
3
4use NewfoldLabs\WP\Module\Onboarding\Data\Data;
5use NewfoldLabs\WP\Module\Onboarding\Data\Options;
6use NewfoldLabs\WP\Module\Onboarding\Services\PluginService;
7use NewfoldLabs\WP\Module\Onboarding\Services\ThemeService;
8use NewfoldLabs\WP\Module\Onboarding\Data\Services\FlowService;
9use NewfoldLabs\WP\Module\Onboarding\Data\Services\SiteGenService;
10use NewfoldLabs\WP\Module\Onboarding\Data\Themes;
11use NewfoldLabs\WP\Module\Onboarding\Services\I18nService;
12use NewfoldLabs\WP\Module\Onboarding\Services\ReduxStateService;
13use NewfoldLabs\WP\Module\Onboarding\Services\StatusService;
14
15/**
16 * Register Admin Page, Assets & Admin functionality with WordPress.
17 */
18final class WP_Admin {
19
20    /**
21     * Identifier for page and assets.
22     *
23     * @var string
24     */
25    public static $slug = 'nfd-onboarding';
26
27    /**
28     * Array of allowed referrers
29     *
30     * @var array
31     */
32    protected static $allowed_referrers = array(
33        'nfd-onboarding',
34        'nfd-plugin',
35    );
36
37    /**
38     * Tap WordPress Hooks
39     *
40     * @return void
41     */
42    public function __construct() {
43        \add_action( 'init', array( __CLASS__, 'load_php_textdomain' ) );
44        \add_action( 'admin_menu', array( __CLASS__, 'register_page' ) );
45        \add_action( 'load-dashboard_page_' . self::$slug, array( __CLASS__, 'page_title' ), 9, 1 );
46        \add_action( 'load-dashboard_page_' . self::$slug, array( __CLASS__, 'initialize' ) );
47        /**
48         * We're disabling the restart onboarding feature for now.
49         */
50        \add_action( 'load-toplevel_page_bluehost', array( __CLASS__, 'hide_onboarding_restart_card' ) );
51        // \add_action( 'load-themes.php', array( __CLASS__, 'can_restart_onboarding' ) );
52        \add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_site_editor_assets' ) );
53        if ( 'sitegen' === Data::current_flow() ) {
54            \add_action( 'load-themes.php', array( __CLASS__, 'mark_sitegen_generated_themes' ) );
55            SiteGenService::pre_set_filter_wonder_blocks_transients();
56        }
57    }
58
59    /**
60     * Loads the textdomain for the module. This applies only to PHP strings.
61     *
62     * @return boolean
63     */
64    public static function load_php_textdomain() {
65        return I18nService::load_php_translations(
66            'wp-module-onboarding',
67            NFD_ONBOARDING_PLUGIN_DIRNAME . '/vendor/newfold-labs/wp-module-onboarding/languages'
68        );
69    }
70
71    /**
72     * Register WordPress Admin Page.
73     *
74     * By passing null to add_submenu_page, the page isn't displayed in the left Admin Menu,
75     * but is available from /wp-admin/index.php?page=nfd-onboarding.
76     *
77     * @return void
78     */
79    public static function register_page() {
80        \add_submenu_page(
81            '',
82            \__( 'Onboarding', 'wp-module-onboarding' ),
83            \__( 'Onboarding', 'wp-module-onboarding' ),
84            Permissions::ADMIN,
85            self::$slug,
86            array( __CLASS__, 'render' ),
87            100
88        );
89    }
90
91    /**
92     * Set the page title for the Onboarding page.
93     *
94     * @return void
95     * */
96    public static function page_title() {
97        if ( isset( $_GET['page'] ) && \sanitize_text_field( wp_unslash( $_GET['page'] ) ) === self::$slug ) {
98            global $title;
99            $title = \__( 'Onboarding', 'wp-module-onboarding' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
100        }
101    }
102
103    /**
104     * Render a loading screen.
105     *
106     * @return string
107     */
108    public static function is_loading() {
109        ob_start();
110        ?>
111        <style>
112            body.wp-admin {
113                overflow: hidden !important;
114            }
115            .nfd-onboarding-loading-app__skeleton {
116                position: relative;
117                overflow: hidden;
118                background-color: #DDE7F0;
119            }
120            .nfd-onboarding-loading-app__skeleton::after {
121                position: absolute;
122                top: 0;
123                right: 0;
124                bottom: 0;
125                left: 0;
126                transform: translateX(-100%);
127                background-image: linear-gradient(
128                    90deg,
129                    rgba(255, 255, 255, 0) 0,
130                    rgba(255, 255, 255, 0.2) 20%,
131                    rgba(255, 255, 255, 0.5) 60%,
132                    rgba(255, 255, 255, 0)
133                );
134                animation: nfd-skeleton-animation 2.5s infinite;
135                content: "";
136            }
137            @keyframes nfd-skeleton-animation {
138                100% {
139                    transform: translateX(100%);
140                }
141            }
142            #nfd-onboarding {
143                z-index: 100000;
144            }
145            .nfd-onboarding-loading-app {
146                background-color: #F1F5F9;
147                width: 100%;
148                opacity: 1;
149                transition: opacity 0.3s ease-in-out;
150            }
151            .nfd-onboarding-loading-app.fade-out {
152                opacity: 0;
153            }
154            .nfd-onboarding-loading-app__header {
155                background-color: #FFF;
156                border-bottom: 1px solid #ECEEFE;
157                display: flex;
158                justify-content: space-between;
159                align-items: center;
160                padding: 0 1.5rem;
161                min-height: 4rem;
162            }
163            .nfd-onboarding-loading-app__header__logo {
164                width: 135px;
165                height: 24px;
166                border-radius: 6px;
167            }
168            .nfd-onboarding-loading-app__header__exit {
169                width: 24px;
170                height: 24px;
171                border-radius: 6px;
172            }
173            .nfd-onboarding-loading-app__body {
174                position: absolute;
175                top: 0;
176                left: 0;
177                right: 0;
178                bottom: 0;
179                display: flex;
180                justify-content: center;
181                align-items: center;
182            }
183            .nfd-onboarding-loading-app__body__spinner {
184                width: 100px;
185                height: 100px;
186                margin-top: -65px;
187                border-radius: 12px;
188                opacity: 0.8;
189            }
190        </style>
191        <div class="nfd-onboarding-loading-app">
192            <header class="nfd-onboarding-loading-app__header">
193                <div class="nfd-onboarding-loading-app__header__logo nfd-onboarding-loading-app__skeleton"></div>
194                <div class="nfd-onboarding-loading-app__header__exit nfd-onboarding-loading-app__skeleton"></div>
195            </header>
196            <div class="nfd-onboarding-loading-app__body">
197                <div class="nfd-onboarding-loading-app__body__spinner nfd-onboarding-loading-app__skeleton"></div>
198            </div>
199        </div>
200        <?php
201        return ob_get_clean();
202    }
203
204    /**
205     * Render DOM element for React SPA mount.
206     *
207     * @return void
208     */
209    public static function render() {
210        echo PHP_EOL;
211        echo '<!-- NFD:ONBOARDING -->';
212        echo PHP_EOL;
213        echo '<div id="nfd-onboarding" class="nfd-onboarding-container">' . self::is_loading() . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
214        echo PHP_EOL;
215        echo '<!-- /NFD:ONBOARDING -->';
216        echo PHP_EOL;
217    }
218
219    /**
220     * Register built assets with WP_Dependency system.
221     *
222     * @return void
223     */
224    public static function register_assets() {
225        global $current_screen;
226
227        $current_screen->is_block_editor( true );
228
229        $asset_file = NFD_ONBOARDING_BUILD_DIR . '/onboarding.asset.php';
230
231        if ( is_readable( $asset_file ) ) {
232            $asset = include_once $asset_file;
233
234            \wp_register_script(
235                self::$slug,
236                NFD_ONBOARDING_BUILD_URL . '/onboarding.js',
237                array_merge( $asset['dependencies'], array() ),
238                $asset['version'],
239                true
240            );
241
242            I18nService::load_js_translations(
243                'wp-module-onboarding',
244                self::$slug,
245                NFD_ONBOARDING_DIR . '/languages'
246            );
247
248            $nfd_onboarding_data = array(
249                'runtime'    => Data::runtime(),
250                'input'      => ReduxStateService::get( 'input' ),
251                'sitegen'    => ReduxStateService::get( 'sitegen' ),
252                'logogen'    => ReduxStateService::get( 'logogen' ),
253                'blueprints' => ReduxStateService::get( 'blueprints' ),
254            );
255            \wp_add_inline_script(
256                self::$slug,
257                'var nfdOnboarding =' . wp_json_encode( $nfd_onboarding_data ) . ';',
258                'before'
259            );
260
261            \wp_register_style(
262                self::$slug,
263                NFD_ONBOARDING_BUILD_URL . '/onboarding.css',
264                array( 'wp-components', 'wp-editor', 'wp-edit-blocks' ),
265                $asset['version']
266            );
267
268            wp_add_inline_script(
269                'wp-blocks',
270                'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
271            );
272
273            // Preload server-registered block bindings sources.
274            $registered_sources = get_all_registered_block_bindings_sources();
275            if ( ! empty( $registered_sources ) ) {
276                $filtered_sources = array();
277                foreach ( $registered_sources as $source ) {
278                    $filtered_sources[] = array(
279                        'name'        => $source->name,
280                        'label'       => $source->label,
281                        'usesContext' => $source->uses_context,
282                    );
283                }
284                $script = sprintf( 'for ( const source of %s ) { wp.blocks.registerBlockBindingsSource( source ); }', wp_json_encode( $filtered_sources ) );
285                wp_add_inline_script(
286                    'wp-blocks',
287                    $script
288                );
289            }
290
291            \wp_enqueue_script( self::$slug );
292            \wp_enqueue_style( self::$slug );
293
294            \add_action( 'enqueue_block_assets', array( __CLASS__, 'enqueue_block_assets' ) );
295        }
296    }
297
298    /**
299     * Enqueues the block assets for the live previews.
300     *
301     * @return void
302     */
303    public static function enqueue_block_assets() {
304        // This hook exists in the patterns module.
305        \do_action( 'enqueue_nfd_wonder_blocks_utilities' );
306    }
307
308    /**
309     * Redirects to the brand plugin page or the WordPress admin dashboard.
310     *
311     * @return void
312     */
313    public static function exit_to_dashboard(): bool {
314        $runtime_data              = Data::runtime();
315        $brand_plugin_url          = '';
316        $dashboard_redirect_params = 'referrer=' . self::$slug;
317
318        // Get the brand plugin page URL from the runtime data.
319        if (
320            isset( $runtime_data['currentBrand'], $runtime_data['currentBrand']['pluginDashboardPage'] ) &&
321            is_string( $runtime_data['currentBrand']['pluginDashboardPage'] )
322            ) {
323                // Set the brand plugin page URL.
324                $brand_plugin_url = $runtime_data['currentBrand']['pluginDashboardPage'];
325        }
326
327        // If the brand plugin page URL is not found in the runtime, redirect to the WordPress admin.
328        if ( empty( $brand_plugin_url ) ) {
329            wp_redirect( apply_filters( 'nfd_build_url', admin_url() . '?' . $dashboard_redirect_params ) );
330            exit;
331        }
332
333        // If the brand plugin page URL is found in the runtime, redirect to the brand plugin page.
334        wp_redirect( apply_filters( 'nfd_build_url', $brand_plugin_url . '&' . $dashboard_redirect_params ) );
335        exit;
336    }
337
338    /**
339     * Initialize Plugins and Themes if necessary.
340     *
341     * @return void
342     */
343    public static function initialize() {
344        if ( ! empty( $_GET['nfd_plugins'] ) && 'true' === sanitize_text_field( $_GET['nfd_plugins'] ) ) {
345            PluginService::initialize();
346        }
347
348        // Install and activate the default theme.
349        $default_theme_installation_result = ThemeService::initialize();
350        if ( ! $default_theme_installation_result ) {
351            self::exit_to_dashboard();
352        }
353
354        self::register_assets();
355
356        self::set_onboarding_restart_option();
357    }
358
359    /**
360     * Enqueue scripts that mark Sitegen flow generated themes.
361     *
362     * @return void
363     */
364    public static function mark_sitegen_generated_themes() {
365        $flow_data = get_option( Options::get_option_name( 'flow' ), false );
366
367        if ( ! $flow_data || ! isset( $flow_data['sitegen']['homepages'] ) ) {
368            return;
369        }
370
371        \wp_register_script(
372            'sitegen-theme-marker',
373            NFD_ONBOARDING_BUILD_URL . '/sitegen-theme-marker.js',
374            array(),
375            '1.0.0',
376            true
377        );
378
379        \wp_add_inline_script(
380            'sitegen-theme-marker',
381            'var nfdOnboarding =' . wp_json_encode(
382                array(
383                    'homepages' => $flow_data['sitegen']['homepages'],
384                    'active'    => Themes::get_active_theme(),
385                )
386            ) . ';',
387            'before'
388        );
389
390        \wp_register_style(
391            'sitegen-theme-marker',
392            NFD_ONBOARDING_BUILD_URL . '/sitegen-theme-marker.css.css',
393            array(),
394            '1.0.0',
395            'all'
396        );
397
398        \wp_enqueue_script( 'sitegen-theme-marker' );
399        \wp_enqueue_style( 'sitegen-theme-marker' );
400    }
401
402    /**
403     * Sets the option in DB for the Initial Load of Onboarding
404     *
405     * @return void
406     */
407    public static function set_onboarding_restart_option(): void {
408        // Check if the customer is eligible for onboarding restart
409        if ( StatusService::is_onboarding_restart_eligible() ) {
410            // Get the option name for 'can_restart'
411            $option_name = Options::get_option_name( 'can_restart' );
412
413            // Check if the option doesn't exist before adding it
414            if ( ! get_option( $option_name ) ) {
415                // Add the option if it doesn't exist
416                add_option( $option_name, true );
417            }
418        } else {
419            // Get the option name for 'can_restart'
420            $option_name = Options::get_option_name( 'can_restart' );
421
422            // Add the option if it doesn't exist
423            update_option( $option_name, false );
424        }
425    }
426
427    /**
428     * Enqueue scripts that adds a new button to Restart Onboarding in themes.php
429     *
430     * @return void
431     */
432    public static function can_restart_onboarding(): void {
433        $can_restart = get_option( Options::get_option_name( 'can_restart' ), false );
434
435        // If the customer in ineligible for restart don't enqueue scripts
436        if ( ! $can_restart || ! StatusService::is_onboarding_restart_eligible() ) {
437            return;
438        }
439
440        \wp_register_script(
441            'onboarding-restart-button',
442            NFD_ONBOARDING_BUILD_URL . '/onboarding-restart-button.js',
443            array(),
444            '1.0.0',
445            true
446        );
447
448        \wp_add_inline_script(
449            'onboarding-restart-button',
450            'var nfdOnboardingRestartMeta =' . wp_json_encode(
451                array(
452                    'buttonText' => \__( 'Build with AI', 'wp-module-onboarding' ),
453                    'buttonHref' => \apply_filters( 'nfd_build_url', admin_url( 'index.php?page=' . self::$slug ) ),
454                )
455            ) . ';',
456            'before'
457        );
458
459        \wp_register_style(
460            'onboarding-restart-button',
461            NFD_ONBOARDING_BUILD_URL . '/onboarding-restart-button.css.css',
462            array(),
463            '1.0.0',
464            'all'
465        );
466
467        /**
468         * Temporary: hide the build with ai button
469         */
470        wp_add_inline_style(
471            'onboarding-restart-button',
472            '.themes .theme.build-with-ai {
473                display: none !important;
474            }'
475        );
476
477        \wp_enqueue_script( 'onboarding-restart-button' );
478        \wp_enqueue_style( 'onboarding-restart-button' );
479    }
480
481    /**
482     * Temporary: Enqueue scripts that hides the build with ai button
483     *
484     * @return void
485     */
486    public static function hide_onboarding_restart_card(): void {
487        \wp_register_style(
488            'hide-onboarding-restart-card',
489            false,
490        );
491
492        \wp_add_inline_style(
493            'hide-onboarding-restart-card',
494            'div[data-testid="restartOnboarding"] {
495                display: none !important;
496            }'
497        );
498
499        \wp_enqueue_style( 'hide-onboarding-restart-card' );
500    }
501
502    /**
503     * Enqueue site editor specific assets when coming from onboarding.
504     *
505     * @return void
506     */
507    public static function enqueue_site_editor_assets() {
508        global $pagenow;
509
510        // Only proceed if we're on site-editor.php and have the right referrer
511        if ( 'site-editor.php' === $pagenow &&
512            isset( $_GET['referrer'] ) &&
513            in_array( $_GET['referrer'], self::$allowed_referrers, true )
514        ) {
515
516            $asset_file = NFD_ONBOARDING_BUILD_DIR . '/onboarding-design-studio.asset.php';
517
518            if ( is_readable( $asset_file ) ) {
519                $asset = include_once $asset_file;
520
521                \wp_register_script(
522                    'nfd-design-studio',
523                    NFD_ONBOARDING_BUILD_URL . '/onboarding-design-studio.js',
524                    $asset['dependencies'] ?? array( 'wp-editor', 'wp-blocks', 'wp-components' ),
525                    $asset['version'] ?? '1.0.0',
526                    true
527                );
528
529                \wp_register_style(
530                    'nfd-design-studio',
531                    NFD_ONBOARDING_BUILD_URL . '/onboarding-design-studio.css.css',
532                    array( 'wp-components' ),
533                    $asset['version'] ?? '1.0.0'
534                );
535
536                \wp_enqueue_script( 'nfd-design-studio' );
537                \wp_enqueue_style( 'nfd-design-studio' );
538
539                // Enqueue fonts for preview
540                self::enqueue_preview_fonts();
541            }
542        }
543    }
544
545    /**
546     * Enqueue all available fonts for preview in design studio
547     *
548     * @return void
549     */
550    private static function enqueue_preview_fonts() {
551
552        $hiive_api_base = defined( 'NFD_DATA_WB_DEV_MODE' ) && NFD_DATA_WB_DEV_MODE
553            ? 'http://patterns-platform.test/api/v1'
554            : 'https://paterns.hiive.cloud/api/v1';
555
556        $font_pairs   = get_option( 'nfd_module_onboarding_editor_fontpair' );
557        $unique_fonts = array();
558
559        if ( $font_pairs && is_array( $font_pairs ) ) {
560            foreach ( $font_pairs as $font_pair ) {
561                if ( isset( $font_pair['font_heading_name'] ) ) {
562                    $unique_fonts[] = $font_pair['font_heading_name'];
563                }
564                if ( isset( $font_pair['font_content_name'] ) ) {
565                    $unique_fonts[] = $font_pair['font_content_name'];
566                }
567            }
568        }
569
570        $unique_fonts = array_unique( $unique_fonts );
571
572        foreach ( $unique_fonts as $font_id ) {
573            if ( empty( $font_id ) ) {
574                continue;
575            }
576
577            // Check if font data is already cached
578            $cache_key = 'nfd_font_data_' . sanitize_key( $font_id );
579            $font_data = get_transient( $cache_key );
580
581            if ( false === $font_data ) {
582                // Fetch font data from Hiive API
583                $response = wp_remote_get(
584                    $hiive_api_base . '/fonts/' . urlencode( sanitize_title( $font_id ) ),
585                    array(
586                        'timeout' => 10,
587                        'headers' => array(
588                            'Accept' => 'application/json',
589                        ),
590                    )
591                );
592
593                if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
594                    $body      = wp_remote_retrieve_body( $response );
595                    $font_data = json_decode( $body, true );
596
597                    set_transient( $cache_key, $font_data, HOUR_IN_SECONDS );
598                }
599            }
600
601            // Enqueue the font if we have valid data
602            if ( $font_data &&
603                isset( $font_data['wp_json']['fontFace'][0]['src'][0] ) &&
604                isset( $font_data['wp_json']['name'] )
605            ) {
606                $font_url    = $font_data['wp_json']['fontFace'][0]['src'][0];
607                $font_name   = $font_data['wp_json']['name'];
608                $font_handle = 'nfd-font-' . sanitize_title( $font_id );
609
610                // Create CSS for the font-face declaration
611                $font_css = '';
612                if ( isset( $font_data['wp_json']['fontFace'] ) && is_array( $font_data['wp_json']['fontFace'] ) ) {
613                    foreach ( $font_data['wp_json']['fontFace'] as $font_face ) {
614                        $font_css .= '@font-face {';
615                        $font_css .= 'font-family: "' . esc_attr( $font_face['fontFamily'] ) . '";';
616
617                        if ( isset( $font_face['fontWeight'] ) ) {
618                            $font_css .= 'font-weight: ' . esc_attr( $font_face['fontWeight'] ) . ';';
619                        }
620
621                        if ( isset( $font_face['fontStyle'] ) ) {
622                            $font_css .= 'font-style: ' . esc_attr( $font_face['fontStyle'] ) . ';';
623                        }
624
625                        if ( isset( $font_face['fontStretch'] ) ) {
626                            $font_css .= 'font-stretch: ' . esc_attr( $font_face['fontStretch'] ) . ';';
627                        }
628
629                        if ( isset( $font_face['src'] ) && is_array( $font_face['src'] ) ) {
630                            $src_declarations = array();
631                            foreach ( $font_face['src'] as $src ) {
632                                $src_declarations[] = 'url("' . esc_url( $src ) . '") format("woff2")';
633                            }
634                            $font_css .= 'src: ' . implode( ', ', $src_declarations ) . ';';
635                        }
636
637                        $font_css .= 'font-display: swap;';
638                        $font_css .= '}';
639                    }
640                }
641
642                // Register and enqueue the font style
643                \wp_register_style( $font_handle, false );
644                \wp_enqueue_style( $font_handle );
645                \wp_add_inline_style( $font_handle, $font_css );
646            }
647        }
648    }
649} // END /NewfoldLabs/WP/Module/Onboarding/Admin()