diff --git a/includes/Services/FontService.php b/includes/Services/FontService.php new file mode 100644 index 0000000..813644c --- /dev/null +++ b/includes/Services/FontService.php @@ -0,0 +1,78 @@ + $font ) { + if ( isset( $font['slug'] ) && in_array( $font['slug'], $active_slugs, true ) ) { + array_push( $final_selected_fonts, $font ); + } + } + // Update the theme settings with the filtered list of fonts + $theme_json_settings['typography']['fontFamilies'] = $final_selected_fonts; + + return $theme_json_settings; + } + + /** + * Deselects inactive DIY flow fonts from the theme JSON settings. + * + * This function retrieves the currently selected font style from the flow + * data, checks which fonts are used, and then calls another function to + * remove unused fonts from the theme JSON settings. + * + * @param array $theme_json_settings The theme JSON settings array. + * + * @return array The modified theme JSON settings with inactive DIY fonts removed. + */ + public static function deselect_inactive_diy_fonts( $theme_json_settings ) { + // Get the selected font style from the flow data + $selected_font_style = FlowService::get_selected_font_style(); + if ( empty( $selected_font_style ) ) { + return $theme_json_settings; + } + + // Retrieve the fonts available in the flow + $theme_fonts = Fonts::get_fonts_from_theme(); + if ( ! isset( $theme_fonts[ $selected_font_style ] ) ) { + return $theme_json_settings; + } + + // Get the data for the selected font style + $selected_font_data = $theme_fonts[ $selected_font_style ]; + $selected_slugs = $selected_font_data['slugs']; + + // Deselect fonts that are not part of the selected slugs + return self::deselect_fonts_from_theme_json_settings( $theme_json_settings, $selected_slugs ); + } +} diff --git a/includes/Services/GlobalStylesService.php b/includes/Services/GlobalStylesService.php new file mode 100644 index 0000000..d75534d --- /dev/null +++ b/includes/Services/GlobalStylesService.php @@ -0,0 +1,164 @@ + 404 ) + ); + } + + // If styles are empty, use styles from user-selected theme settings. + if ( empty( $styles ) ) { + if ( empty( $user_selected_theme_settings['styles'] ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Styles does not exist under the selected theme settings.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + + $styles = $user_selected_theme_settings['styles']; + } + + // If settings are empty, use settings from user-selected theme settings. + if ( empty( $settings ) ) { + if ( empty( $user_selected_theme_settings['settings'] ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Settings does not exist under the selected theme settings.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + + // Remove specific keys from the settings before using them as these are large and unnecessary. + unset( $user_selected_theme_settings['settings']['styles'] ); + unset( $user_selected_theme_settings['settings']['__unstableResolvedAssets'] ); + unset( $user_selected_theme_settings['settings']['__experimentalFeatures'] ); + $settings = $user_selected_theme_settings['settings']; + } + + // Remove inactive DIY flow fonts from the theme JSON settings, retaining only the fonts that are currently in use or selected + $settings = FontService::deselect_inactive_diy_fonts( $settings ); + + return self::update_global_style_variation( + $id, + $styles, + $settings + ); + } + + /** + * Updates the global style variation given the id. + * + * This private function sends a POST request to update the global style using the provided + * styles and settings. + * + * @param string $id The ID of the global style variation to update. + * @param array $styles The styles to apply. + * @param array $settings The settings to apply. + * @return true|\WP_Error Returns true on success, or a WP_Error on failure. + */ + private static function update_global_style_variation( $id, $styles, $settings ) { + // Create a REST request to update global styles. + $update_active_global_style_request = new \WP_REST_Request( + 'POST', + "/wp/v2/global-styles/$id" + ); + $update_active_global_style_request->set_header( 'Content-Type', 'application/json' ); + // Generate custom theme.json data. + $custom_theme_json = self::create_custom_theme_json( $styles, $settings ); + if ( ! isset( $custom_theme_json['styles'] ) || ! isset( $custom_theme_json['settings'] ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'There is an error with your styles or settings.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + // Set the request body parameters. + $update_active_global_style_request->set_body_params( + array( + 'id' => $id, + 'styles' => $custom_theme_json['styles'], + 'settings' => $custom_theme_json['settings'], + ) + ); + + // Execute the REST request. + $update_active_global_style_response = rest_do_request( $update_active_global_style_request ); + if ( $update_active_global_style_response->is_error() ) { + return $update_active_global_style_response->as_error(); + } + + return true; + } + + /** + * Retrieves the post ID of the active/parent custom global styles. + * + * @return int The post ID of the active custom global styles. + */ + public static function get_active_custom_global_styles_post_id() { + return \WP_Theme_JSON_Resolver::get_user_global_styles_post_id(); + } + + /** + * Creates a custom theme.json array. + * + * This function generates a theme.json structure based on the provided styles and settings. + * + * @param array $styles The styles to include in the theme.json. + * @param array $settings The settings to include in the theme.json. + * @param int $version The version of the theme.json schema. Defaults to the latest schema. + * @return array The raw theme.json data. + */ + public static function create_custom_theme_json( $styles, $settings, $version = \WP_Theme_JSON::LATEST_SCHEMA ) { + $theme_json = new \WP_Theme_JSON( + array( + 'version' => $version, + 'styles' => $styles, + 'settings' => $settings, + ) + ); + + return $theme_json->get_raw_data(); + } +} diff --git a/includes/Services/SiteGenService.php b/includes/Services/SiteGenService.php index b01842f..1182382 100644 --- a/includes/Services/SiteGenService.php +++ b/includes/Services/SiteGenService.php @@ -12,6 +12,7 @@ use NewfoldLabs\WP\Module\Patterns\SiteClassification as PatternsSiteClassification; use NewfoldLabs\WP\Module\Data\SiteClassification\PrimaryType; use NewfoldLabs\WP\Module\Data\SiteClassification\SecondaryType; +use NewfoldLabs\WP\Module\Onboarding\Data\Services\GlobalStylesService; use NewfoldLabs\WP\Module\Onboarding\Data\Events; use function NewfoldLabs\WP\ModuleLoader\container; @@ -227,14 +228,12 @@ public static function complete( $active_homepage, $homepage_data ) { \update_option( Options::get_option_name( 'page_on_front', false ), $post_id ); - // Update name and slug before generating child theme - $active_homepage = self::update_info_for_child_theme( $site_config, $active_homepage ); - self::generate_child_theme( $active_homepage ); + // Use global styles and global theme to add the styles + self::update_styles_for_sitegen( $site_config ); foreach ( $homepage_data as $index => $data ) { if ( $data['isFavorite'] && $data['slug'] !== $active_homepage['slug'] ) { - $data = self::update_info_for_child_theme( $site_config, $data, $index ); - self::generate_child_theme( $data ); + self::update_styles_for_sitegen( $data ); } if ( $data['slug'] === $active_homepage['slug'] ) { $homepage_data[ $active_homepage['slug'] ] = $active_homepage; @@ -242,8 +241,6 @@ public static function complete( $active_homepage, $homepage_data ) { } self::sync_flow_data( $homepage_data ); - ThemeGeneratorService::activate_theme( $active_homepage['slug'] ); - self::trash_sample_page(); container()->get( 'cachePurger' )->purgeAll(); @@ -284,6 +281,36 @@ public static function populate_fonts_in_theme_styles( $theme_styles, $homepage_ return $theme_styles; } + /** + * Updates the global theme for the sitegen flow. + * + * @param array $data Data on homepage and it's corresponding styles. + * @return true|\WP_Error + */ + public static function update_styles_for_sitegen( $data ) { + global $wp_filesystem; + + // Get the currently activated theme and the corresponding theme json + $active_theme_dir = \get_template_directory(); + $active_theme_json_file = $active_theme_dir . '/theme.json'; + + // If the theme json doesn't exist, give up + if ( ! $wp_filesystem->exists( $active_theme_json_file ) ) { + return false; + } + + $active_theme_json = $wp_filesystem->get_content( $active_theme_json_file ); + $active_theme_json_data = json_decode( $active_theme_json_file, true ); + + // Apply the required changes to theme json + $active_theme_json_data['settings']['color']['palette'] = $data['color']['palette']; + $active_theme_json_data['styles'] = self::populate_fonts_in_theme_styles( $active_theme_json_data['styles'], $data['styles'] ); + + // Add the styles to global variation + $active_global_styles_post_id = GlobalStylesService::get_active_custom_global_styles_post_id(); + GlobalStylesService::update_global_style_variation( $active_global_styles_post_id, $active_theme_json_data['styles'], $active_theme_json_data['settings'] ); + } + /** * Generates a child theme for the sitegen flow. * diff --git a/includes/Services/TemplatePartsService.php b/includes/Services/TemplatePartsService.php new file mode 100644 index 0000000..475da3c --- /dev/null +++ b/includes/Services/TemplatePartsService.php @@ -0,0 +1,102 @@ +is_error() ) { + return $get_template_part_response->as_error(); + } + + return $get_template_part_response->get_data(); + } + + /** + * Updates a template part with new content using the WordPress REST API. + * + * @param string $id The ID of the template part to update. + * @param string $content The new content for the template part. + * @return bool|WP_Error True on success, or a WP_Error object on failure. + */ + public static function update_template_part( $id, $content ) { + // Create a POST request for the specified template part ID. + $update_template_part_request = new \WP_REST_Request( + 'POST', + "/wp/v2/template-parts/$id" + ); + // Set the body parameters for the request with the new content. + $update_template_part_request->set_body_params( + array( + 'content' => $content, + ) + ); + + // Execute the request and get the response. + $update_template_part_response = rest_do_request( $update_template_part_request ); + if ( $update_template_part_response->is_error() ) { + return $update_template_part_response->as_error(); + } + + return true; + } + + /** + * Updates the default template parts with the user-selected header template part for the DIY flow. + * + * @return bool|WP_Error True on success, or a WP_Error object on failure. + */ + public static function update_diy_selected_template_parts() { + // Retrieve the selected header template part ID from the flow data. + $selected_template_part_id = FlowService::get_selected_header_template_part(); + if ( ! $selected_template_part_id ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Selected header template part not stored.', 'wp-module-onboarding' ), + array( 'status' => 404 ) + ); + } + + // Retrieve the template part content from Wonder Theme or Wonder Blocks. + $selected_template_part = Patterns::get_pattern_from_slug( $selected_template_part_id ); + if ( ! $selected_template_part ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Selected header template part not found.', 'wp-module-onboarding' ), + array( 'status' => 404 ) + ); + } + $selected_template_part_content = $selected_template_part['content']; + // Get the active theme and construct the default header ID. + $active_theme = Themes::get_active_theme(); + $default_header_id = "$active_theme/header"; + // Update the default header template part with the selected content. + $update_status = self::update_template_part( $default_header_id, $selected_template_part_content ); + if ( is_wp_error( $update_status ) ) { + return $update_status; + } + + return true; + } +}