From 284f345328e0cddeb0ed0abcbac4e944d8fb88a3 Mon Sep 17 00:00:00 2001 From: arunshenoy99 Date: Mon, 26 Aug 2024 15:59:51 +0530 Subject: [PATCH 1/4] Add support for theme json customization --- .../Themes/ThemeVariationsController.php | 76 ++++++++- includes/Services/GlobalStylesService.php | 158 ++++++++++++++++++ 2 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 includes/Services/GlobalStylesService.php diff --git a/includes/RestApi/Themes/ThemeVariationsController.php b/includes/RestApi/Themes/ThemeVariationsController.php index bd8cf8260..3b18c1423 100644 --- a/includes/RestApi/Themes/ThemeVariationsController.php +++ b/includes/RestApi/Themes/ThemeVariationsController.php @@ -1,9 +1,10 @@ namespace, $this->rest_base . $this->rest_extended_base, array( @@ -54,6 +55,19 @@ public function register_routes() { ), ) ); + + register_rest_route( + $this->namespace, + $this->rest_base . $this->rest_extended_base . '/update', + array( + array( + 'methods' => \WP_REST_Server::EDITABLE, + 'args' => $this->get_update_diy_global_style_variation_args(), + 'callback' => array( $this, 'update_diy_global_style_variation' ), + 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ), + ), + ) + ); } /** @@ -89,6 +103,31 @@ public function set_pattern_args() { ); } + /** + * Gets the arguments for updating DIY global style variations. + * + * @return array An array of argument definitions for the REST API request. + */ + public function get_update_diy_global_style_variation_args() { + return array( + 'id' => array( + 'description' => __( 'The id of a template', 'wp-module-onboarding' ), + 'type' => 'integer', + 'default' => GlobalStylesService::get_active_custom_global_styles_post_id(), + ), + 'styles' => array( + 'description' => __( 'The custom styles', 'wp-module-onboarding' ), + 'default' => array(), + 'required' => false, + ), + 'settings' => array( + 'description' => __( 'The custom settings', 'wp-module-onboarding' ), + 'default' => array(), + 'required' => false, + ), + ); + } + /** * Translate decoded json file. * @@ -140,9 +179,9 @@ public function get_theme_variations( \WP_REST_Request $request ) { $default = $request->get_param( 'variations' ); // If there exists an old Custom Theme then return that - if ( false === $default && false !== \get_option( Options::get_option_name( 'theme_settings' ) ) ) { + if ( false === $default && false !== Themes::get_selected_diy_theme_settings() ) { return array( - \get_option( Options::get_option_name( 'theme_settings' ) ), + Themes::get_selected_diy_theme_settings(), ); } @@ -174,7 +213,7 @@ public function set_theme_variation( \WP_REST_Request $request ) { if ( $theme_data ) { // Save the new Theme style into the db - \update_option( Options::get_option_name( 'theme_settings' ), $theme_data ); + Themes::set_diy_theme_settings( $theme_data ); return new \WP_REST_Response( $theme_data, @@ -188,4 +227,31 @@ public function set_theme_variation( \WP_REST_Request $request ) { 'Settings parameter is found to be missing' ); } + + /** + * Handles the update DIY global style variation request. + * + * @param \WP_REST_Request $request The REST API request object containing the parameters. + * + * @return \WP_REST_Response|\WP_Error Returns a WP_REST_Response on success, or a WP_Error on failure. + */ + public function update_diy_global_style_variation( \WP_REST_Request $request ) { + $request_data = $request->get_params(); + $id = $request_data['id']; + $styles = $request_data['styles']; + $settings = $request_data['settings']; + + $status = GlobalStylesService::update_diy_global_style_variation( $id, $styles, $settings ); + + if ( is_wp_error( $status ) ) { + return $status; + } + + return new \WP_REST_Response( + array( + 'message' => __( 'Global style customization created.', 'wp-module-onboarding' ), + ), + 201 + ); + } } diff --git a/includes/Services/GlobalStylesService.php b/includes/Services/GlobalStylesService.php new file mode 100644 index 000000000..2fd36072b --- /dev/null +++ b/includes/Services/GlobalStylesService.php @@ -0,0 +1,158 @@ + 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']; + } + + // Update the global style variation with the prepared styles and 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(); + } +} From aa2bd94378c5d7073952b64a87d8a0cd9a4f77ca Mon Sep 17 00:00:00 2001 From: arunshenoy99 Date: Tue, 27 Aug 2024 14:14:53 +0530 Subject: [PATCH 2/4] Add support to customize template parts --- includes/RestApi/FlowController.php | 53 +- includes/RestApi/RestApi.php | 3 +- .../Themes/ThemeGeneratorController.php | 638 ------------------ .../Themes/ThemeVariationsController.php | 1 - includes/Services/GlobalStylesService.php | 2 +- includes/Services/TemplatePartsService.php | 102 +++ 6 files changed, 141 insertions(+), 658 deletions(-) delete mode 100644 includes/RestApi/Themes/ThemeGeneratorController.php create mode 100644 includes/Services/TemplatePartsService.php diff --git a/includes/RestApi/FlowController.php b/includes/RestApi/FlowController.php index 47cf206a3..2acaa0c89 100644 --- a/includes/RestApi/FlowController.php +++ b/includes/RestApi/FlowController.php @@ -6,7 +6,9 @@ use NewfoldLabs\WP\Module\Onboarding\Data\Services\FlowService; use NewfoldLabs\WP\Module\Onboarding\Services\PluginService; use NewfoldLabs\WP\Module\Onboarding\Data\Services\SiteGenService; +use NewfoldLabs\WP\Module\Onboarding\Services\GlobalStylesService; use NewfoldLabs\WP\Module\Onboarding\Services\StatusService; +use NewfoldLabs\WP\Module\Onboarding\Services\TemplatePartsService; /** * Class FlowController @@ -128,51 +130,70 @@ public function save_onboarding_flow_data( \WP_REST_Request $request ) { } /** - * Flow completion API for child theme generation, verify child theme and publish site pages. + * Handles the completion of different onboarding flows. * - * @param \WP_REST_Request $request The incoming request. + * This function processes the completion of various onboarding flows, + * such as site generation and site page publishing, and updates global + * styles and template parts if necessary. + * + * @param \WP_REST_Request $request The request object containing the flow parameter. * - * @return \WP_REST_Response|\WP_Error + * @return \WP_REST_Response|\WP_Error A WP_REST_Response object on success, or a WP_Error object on failure. */ public function complete( $request ) { + // Retrieve the 'flow' parameter from the request $flow = $request->get_param( 'flow' ); + if ( 'sitegen' === $flow ) { - $flow_data_option = \get_option( Options::get_option_name( 'flow' ), false ); + // Read flow data from the WordPress options table + $flow_data_option = FlowService::read_data_from_wp_option( false ); + // Check if flow data does not exist or 'data' key is not set if ( false === $flow_data_option || ! isset( $flow_data_option['data'] ) ) { return new \WP_Error( 'nfd_onboarding_error', - 'Flow data does not exist to generate a child theme.', + __( 'Flow data does not exist to generate a child theme.', 'wp-module-onboarding' ), array( 'status' => 500 ) ); } + // Retrieve homepage data and the active homepage from the flow data $homepage_data = $flow_data_option['sitegen']['homepages']['data']; $active_homepage = $flow_data_option['sitegen']['homepages']['active']; + // Complete the Sitegen flow using the retrieved data SiteGenService::complete( $active_homepage, $homepage_data ); return new \WP_REST_Response( array(), 201 ); } - - $site_pages_publish_request = new \WP_REST_Request( + // If the flow is not 'sitegen', proceed with publishing site pages + $site_pages_publish_request = new \WP_REST_Request( 'POST', '/newfold-onboarding/v1/site-pages/publish' ); - $site_pages_publish_response = \rest_do_request( $site_pages_publish_request ); + // Execute the request to publish site pages + $site_pages_publish_response = rest_do_request( $site_pages_publish_request ); if ( $site_pages_publish_response->is_error() ) { return $site_pages_publish_response->as_error(); } - $child_theme_generation_request = new \WP_REST_Request( - 'POST', - '/newfold-onboarding/v1/themes/child/generate' - ); - $child_theme_generation_response = \rest_do_request( $child_theme_generation_request ); - if ( $child_theme_generation_response->is_error() ) { - return $child_theme_generation_response->as_error(); + + // Get the post ID of the active custom global styles and update it + $active_custom_global_styles_post_id = GlobalStylesService::get_active_custom_global_styles_post_id(); + $status = GlobalStylesService::update_diy_global_style_variation( $active_custom_global_styles_post_id, ); + if ( is_wp_error( $status ) ) { + return $status; + } + + // Update the selected template parts + $status = TemplatePartsService::update_diy_selected_template_parts(); + if ( is_wp_error( $status ) ) { + return $status; } + // Return a successful response after completing all processes return new \WP_REST_Response( - array(), + array( + 'message' => __( 'Success.', 'wp-module-onboarding' ), + ), 201 ); } diff --git a/includes/RestApi/RestApi.php b/includes/RestApi/RestApi.php index c0b8caebf..67c08b7c8 100644 --- a/includes/RestApi/RestApi.php +++ b/includes/RestApi/RestApi.php @@ -16,7 +16,6 @@ final class RestApi { */ protected $controllers = array( 'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\SiteImagesController', - 'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\Themes\\ThemeGeneratorController', 'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\PluginsController', 'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\Themes\\ThemeVariationsController', 'NewfoldLabs\WP\\Module\\Onboarding\\RestApi\\Themes\\ApprovedThemesController', @@ -56,4 +55,4 @@ public function register_routes() { $instance->register_routes(); } } -} // END /NewfoldLabs/WP/Module/Onboarding/RestApi() +} diff --git a/includes/RestApi/Themes/ThemeGeneratorController.php b/includes/RestApi/Themes/ThemeGeneratorController.php deleted file mode 100644 index 503c89a74..000000000 --- a/includes/RestApi/Themes/ThemeGeneratorController.php +++ /dev/null @@ -1,638 +0,0 @@ -namespace, - $this->rest_base . $this->rest_extended_base, - array( - array( - 'methods' => \WP_Rest_Server::CREATABLE, - 'callback' => array( $this, 'generate_child_theme' ), - 'permission_callback' => array( Permissions::class, 'rest_can_manage_themes' ), - ), - ) - ); - } - - /** - * Generates the child theme from an active parent theme. - * - * @return \WP_REST_Response|\WP_Error - */ - public function generate_child_theme() { - // Ensure that we have sufficient data to generate a child theme. - $flow_data_option = \get_option( Options::get_option_name( 'flow' ), false ); - if ( false === $flow_data_option || ! isset( $flow_data_option['data'] ) ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Flow data does not exist to generate a child theme.', - array( 'status' => 500 ) - ); - } - - $flow_data = $this->validate_and_sanitize_flow_data( $flow_data_option['data'] ); - if ( false === $flow_data ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Flow data is incomplete to generate a child theme.', - array( 'status' => 500 ) - ); - } - - // Connect to the WordPress file system. - if ( ! $this->connect_to_filesystem() ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Could not connect to the filesystem.', - array( 'status' => 500 ) - ); - } - - // Check if the parent theme exists. - $parent_theme_slug = $flow_data['theme']['template']; - $parent_theme_exists = ( \wp_get_theme( $parent_theme_slug ) )->exists(); - if ( ! $parent_theme_exists ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Parent theme is missing to generate the child theme.', - array( 'status' => 500 ) - ); - } - - /* - Activate the parent theme if it is not active. - This is necessary to register the parent theme's block patterns. - */ - $active_theme = Themes::get_active_theme(); - if ( $active_theme !== $parent_theme_slug ) { - $this->activate_theme( $parent_theme_slug ); - } - - // Generate the necessary slugs and paths. - $themes_dir = dirname( \get_stylesheet_directory() ); - $parent_theme_dir = $themes_dir . '/' . $parent_theme_slug; - $child_theme_slug = $this->get_child_theme_slug( $flow_data ); - $child_theme_dir = $themes_dir . '/' . $child_theme_slug; - - // Generate theme.json data for the child theme. - $theme_json_data = $this->generate_child_theme_json( $flow_data, $parent_theme_dir ); - if ( ! $theme_json_data ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Could not generate theme.json', - array( 'status' => 500 ) - ); - } - - // Generate the pattern for a given header part to place it into the relevant HTML file. - $part_patterns = array(); - if ( $flow_data['partHeader'] ) { - $part_patterns['header'] = $this->generate_theme_part_pattern( $flow_data['partHeader'] ); - if ( \is_wp_error( $part_patterns['header'] ) ) { - return $part_patterns['header']; - } - } - - if ( $flow_data['partFooter'] ) { - $part_patterns['footer'] = $this->generate_theme_part_pattern( $flow_data['partFooter'] ); - if ( \is_wp_error( $part_patterns['footer'] ) ) { - return $part_patterns['footer']; - } - } - - $child_theme_stylesheet_comment = $this->generate_child_theme_stylesheet_comment( $parent_theme_slug, $child_theme_slug ); - - // Write the child theme to the filesystem under themes. - $child_theme_data = array( - 'parent_theme_slug' => $parent_theme_slug, - 'child_theme_slug' => $child_theme_slug, - 'parent_theme_dir' => $parent_theme_dir, - 'child_theme_dir' => $child_theme_dir, - 'child_theme_json' => \wp_json_encode( $theme_json_data ), - 'child_theme_stylesheet_comment' => $child_theme_stylesheet_comment, - 'part_patterns' => $part_patterns, - ); - - $child_theme_written = $this->write_child_theme( $child_theme_data ); - if ( true !== $child_theme_written ) { - return new \WP_Error( - 'nfd_onboarding_error', - $child_theme_written, - array( 'status' => 500 ) - ); - } - - // Activate the child theme. - $this->activate_theme( $child_theme_slug ); - - $child_theme_verified = $this->verify_child_theme( $child_theme_data ); - - if ( ! $child_theme_verified ) { - return new \WP_Error( - 'nfd_onboarding_error', - 'Error generating child theme.', - array( 'status' => 500 ) - ); - } - - return new \WP_REST_Response( - array(), - 201 - ); - } - - /** - * Retrieve Site Url Hash Value - * - * @param integer $length hash length - * - * @return string - */ - private function get_site_url_hash( $length = 8 ) { - return substr( hash( 'sha256', site_url() ), 0, $length ); - } - - /** - * Retrieve Default Dashed Site Title - * - * @return array - */ - private function get_dashed_site_title_defaults() { - return array( 'welcome', 'wordpress-site' ); - } - - /** - * Get the child theme stylesheet from flow data. - * - * @param array $flow_data Flow Data - * - * @return string - */ - protected function get_child_theme_slug( $flow_data ) { - if ( ! $flow_data['theme']['stylesheet'] || - ( $flow_data['theme']['template'] === $flow_data['theme']['stylesheet'] ) ) { - $current_brand = Data::current_brand()['brand']; - $default_site_titles_dashed = $this->get_dashed_site_title_defaults(); - $site_title_dashed = \sanitize_title_with_dashes( \get_bloginfo( 'name' ) ); - if ( empty( $site_title_dashed ) || in_array( $site_title_dashed, $default_site_titles_dashed, true ) ) { - $site_title_dashed = $this->get_site_url_hash(); - } - return $current_brand . '-' . $site_title_dashed; - } - return $flow_data['theme']['stylesheet']; - } - - /** - * Activates a given theme. - * - * @param string $theme_slug WordPress slug for theme - * - * @return void - */ - protected function activate_theme( $theme_slug ) { - \switch_theme( $theme_slug ); - } - - /** - * Generates the child theme.json from the relevant parent theme.json - * - * @param array $flow_data Flow Data - * @param string $parent_theme_dir Parent Theme Directory - * - * @return boolean|array - */ - private function generate_child_theme_json( $flow_data, $parent_theme_dir ) { - global $wp_filesystem; - - $theme_data = \get_option( Options::get_option_name( 'theme_settings' ), false ); - - if ( false !== $theme_data ) { - unset( $theme_data['settings']['styles'] ); - unset( $theme_data['settings']['__unstableResolvedAssets'] ); - unset( $theme_data['settings']['__experimentalFeatures'] ); - $theme_json_data = $theme_data; - } else { - $theme_json_file = $parent_theme_dir . '/theme.json'; - if ( ! $wp_filesystem->exists( $theme_json_file ) ) { - return false; - } - $theme_json = $wp_filesystem->get_contents( $theme_json_file ); - $theme_json_data = json_decode( $theme_json, true ); - } - - return $theme_json_data; - } - - /** - * Generates the child theme.json style.css comment from the relevant parent theme slug - * - * @param array $parent_theme_slug Parent Theme Slug - * @param string $child_theme_slug Child Theme Slug - * - * @return string - */ - private function generate_child_theme_stylesheet_comment( $parent_theme_slug, $child_theme_slug ) { - $current_brand = Data::current_brand(); - $customer = \wp_get_current_user(); - - $default_site_titles_dashed = $this->get_dashed_site_title_defaults(); - $site_title = \get_bloginfo( 'name' ); - $site_title_dashed = \sanitize_title_with_dashes( $site_title ); - if ( empty( $site_title ) || in_array( $site_title_dashed, $default_site_titles_dashed, true ) ) { - $site_title = $current_brand['brand'] . '-' . $this->get_site_url_hash(); - } - - $theme_style_data = array( - 'current_brand' => Data::current_brand(), - 'brand' => $current_brand['brand'], - 'brand_name' => $current_brand['name'] ? $current_brand['name'] : 'Newfold Digital', - 'site_title' => $site_title, - 'site_url' => \site_url(), - 'author' => $customer->user_firstname, - 'parent_theme_slug' => $parent_theme_slug, - 'child_theme_slug' => $child_theme_slug, - ); - - $mustache = new Mustache(); - return $mustache->render_template( 'themeStylesheet', $theme_style_data ); - } - - /** - * Get the pattern for the theme part. - * - * @param string $pattern_slug Patten Slug - * - * @return string|\WP_Error the pattern for the part. - */ - private function generate_theme_part_pattern( $pattern_slug ) { - $pattern = Patterns::get_pattern_from_slug( $pattern_slug ); - if ( ! $pattern ) { - return new \WP_Error( - 'nfd_onboarding_pattern_not_registered', - "{$pattern_slug} has not been registered. Please retry.", - array( 'status' => 500 ) - ); - } - - return $pattern['content']; - } - - /** - * Write the child theme to the themes directory. - * - * @param array $child_theme_data Child Theme Data - * @var string parent_theme_slug - * @var string child_theme_slug - * @var string parent_theme_dir - * @var string child_theme_dir - * @var string child_theme_json - * @var string part_patterns - * - * @return string|boolean - */ - private function write_child_theme( $child_theme_data ) { - $child_directory_created = $this->create_directory( $child_theme_data['child_theme_dir'] ); - if ( ! $child_directory_created ) { - return 'Error creating child directory.'; - } - - $theme_json_written = $this->write_theme_json( - $child_theme_data['child_theme_dir'], - $child_theme_data['child_theme_json'] - ); - if ( ! $theme_json_written ) { - return 'Error writing theme.json.'; - } - - if ( ! empty( $child_theme_data['part_patterns'] ) ) { - $template_parts_written = $this->write_template_parts( - $child_theme_data['child_theme_dir'], - $child_theme_data['part_patterns'] - ); - if ( ! $template_parts_written ) { - return 'Error writing template part.'; - } - } - - $child_stylesheet_written = $this->write_child_stylesheet( - $child_theme_data['child_theme_stylesheet_comment'], - $child_theme_data['child_theme_dir'] - ); - if ( ! $child_stylesheet_written ) { - return 'Error writing stylesheet.'; - } - - $generate_screenshot = $this->generate_screenshot( - $child_theme_data['parent_theme_dir'], - $child_theme_data['child_theme_dir'] - ); - if ( ! $generate_screenshot ) { - return 'Error generating screenshot'; - } - - return true; - } - - /** - * Creates a directory if necessary. - * - * @param string $dir Directory - * - * @return boolean - */ - private function create_directory( $dir ) { - global $wp_filesystem; - - if ( ! $wp_filesystem->exists( $dir ) ) { - return $wp_filesystem->mkdir( $dir ); - } - - return true; - } - - /** - * Writes $theme_json to a theme's theme.json file. - * - * @param string $theme_dir Theme Directory - * @param string $theme_json Theme json content - * - * @return boolean - */ - private function write_theme_json( $theme_dir, $theme_json ) { - return $this->write_to_filesystem( $theme_dir . '/theme.json', $theme_json ); - } - - /** - * Writes HTML template parts to the theme's parts directory. - * - * @param string $theme_dir Theme Directory - * @param array $part_patterns HTML Template Part - * - * @return boolean - */ - private function write_template_parts( $theme_dir, $part_patterns ) { - global $wp_filesystem; - - if ( ! $wp_filesystem->exists( $theme_dir . '/parts' ) ) { - $parts_directory_created = mkdir( $theme_dir . '/parts' ); - if ( ! $parts_directory_created ) { - return false; - } - } - foreach ( $part_patterns as $part => $pattern ) { - $status = $this->write_to_filesystem( $theme_dir . "/parts/{$part}.html", $pattern ); - if ( ! $status ) { - return false; - } - } - - return true; - } - - /** - * Writes style.css for the child theme. - * - * @param string $child_theme_stylesheet_comment Stylesheet comment of Child Theme - * @param string $child_theme_dir Child Theme Directory - * - * @return boolean - */ - private function write_child_stylesheet( $child_theme_stylesheet_comment, $child_theme_dir ) { - return $this->write_to_filesystem( $child_theme_dir . '/style.css', $child_theme_stylesheet_comment ); - } - - /** - * Checks if $flow_data has all the necessary data to generate a child theme. - * - * @param array $flow_data Flow Data - * - * @return boolean - */ - private function validate_and_sanitize_flow_data( $flow_data ) { - $current_flow = Data::current_flow(); - if ( ! $flow_data['theme']['template'] ) { - $current_flow = Data::current_flow(); - - $flow_data['theme']['template'] = Themes::get_flow_default_theme_slug( $current_flow ); - } - - return $flow_data; - } - - /** - * Copy parent's screenshot.png to the child theme directory. - * - * [TODO] Generate the actual child theme screenshot. - * - * @param string $parent_theme_dir Parent Theme Directory - * @param string $child_theme_dir Child Theme Directory - * - * @return boolean - */ - private function generate_screenshot( $parent_theme_dir, $child_theme_dir ) { - global $wp_filesystem; - - $screenshot_files = array( '/screenshot.png', '/screenshot.jpg' ); - foreach ( $screenshot_files as $key => $screenshot_file ) { - $child_theme_screenshot_file = $child_theme_dir . $screenshot_file; - $parent_theme_screenshot_file = $parent_theme_dir . $screenshot_file; - if ( $wp_filesystem->exists( $parent_theme_screenshot_file ) ) { - break; - } - } - - if ( $wp_filesystem->exists( $child_theme_screenshot_file ) ) { - $wp_filesystem->delete( $child_theme_screenshot_file ); - } - - return $wp_filesystem->copy( - $parent_theme_screenshot_file, - $child_theme_screenshot_file - ); - } - - /** - * Writes content to the specified file. - * - * @param string $file Specific File where $content is to be written - * @param string $content Content to write to the $file - * - * @return boolean - */ - private function write_to_filesystem( $file, $content ) { - global $wp_filesystem; - - return $wp_filesystem->put_contents( - $file, - $content, - FS_CHMOD_FILE // predefined mode settings for WP files - ); - } - - /** - * Connect to the WordPress filesystem. - * - * @return boolean - */ - protected function connect_to_filesystem() { - require_once ABSPATH . 'wp-admin/includes/file.php'; - - // We want to ensure that the user has direct access to the filesystem. - $access_type = \get_filesystem_method(); - if ( 'direct' !== $access_type ) { - return false; - } - - $creds = \request_filesystem_credentials( site_url() . '/wp-admin', '', false, false, array() ); - - if ( ! \WP_Filesystem( $creds ) ) { - return false; - } - - return true; - } - - /** - * To verify the valid child theme. - * - * @param array $child_theme_data Child Theme Data - * - * @return boolean - */ - public function verify_child_theme( $child_theme_data ) { - $child_theme_directory_exists = $this->verify_child_theme_directory( $child_theme_data['child_theme_dir'] ); - if ( ! $child_theme_directory_exists ) { - return false; - } - $theme_json_valid = $this->verify_theme_json( $child_theme_data['child_theme_json'], $child_theme_data['child_theme_dir'] ); - if ( ! $theme_json_valid ) { - return false; - } - - $stylesheet_valid = $this->verify_stylesheet( $child_theme_data['child_theme_stylesheet_comment'], $child_theme_data['child_theme_dir'] ); - if ( ! $stylesheet_valid ) { - return false; - } - - $child_theme_active = $this->verify_child_theme_is_active( $child_theme_data['child_theme_slug'] ); - if ( ! $child_theme_active ) { - return false; - } - - return true; - } - - /** - * To verify the valid child theme directory. - * - * @param string $child_theme_dir Child Theme Directory - * - * @return boolean - */ - private function verify_child_theme_directory( $child_theme_dir ) { - global $wp_filesystem; - - return $wp_filesystem->exists( $child_theme_dir ); - } - - /** - * To verify the valid child theme.json. - * - * @param string $child_theme_json Child Theme json - * @param string $child_theme_dir Child Theme Directory - * - * @return boolean - */ - private function verify_theme_json( $child_theme_json, $child_theme_dir ) { - global $wp_filesystem; - - $theme_json_path = $child_theme_dir . '/theme.json'; - if ( ! $wp_filesystem->exists( $theme_json_path ) ) { - return false; - } - - $theme_json_written = $wp_filesystem->get_contents( $theme_json_path ); - if ( $child_theme_json !== $theme_json_written ) { - return false; - } - - return true; - } - - /** - * To verify the valid style.css of the respective child theme - * - * @param string $child_theme_stylesheet_comment Child Theme stylesheet comment - * @param string $child_theme_dir Child Theme Directory - * - * @return boolean - */ - private function verify_stylesheet( $child_theme_stylesheet_comment, $child_theme_dir ) { - global $wp_filesystem; - - $stylesheet_path = $child_theme_dir . '/style.css'; - if ( ! $wp_filesystem->exists( $stylesheet_path ) ) { - return false; - } - - $written_stylesheet = $wp_filesystem->get_contents( $stylesheet_path ); - if ( $child_theme_stylesheet_comment !== $written_stylesheet ) { - return false; - } - - return true; - } - - /** - * To verify the valid child theme is active. - * - * @param string $child_theme_slug Child Theme Slug - * - * @return boolean - */ - private function verify_child_theme_is_active( $child_theme_slug ) { - $active_theme = Themes::get_active_theme(); - if ( $active_theme !== $child_theme_slug ) { - return false; - } - return true; - } -} diff --git a/includes/RestApi/Themes/ThemeVariationsController.php b/includes/RestApi/Themes/ThemeVariationsController.php index 3b18c1423..e74bc0537 100644 --- a/includes/RestApi/Themes/ThemeVariationsController.php +++ b/includes/RestApi/Themes/ThemeVariationsController.php @@ -2,7 +2,6 @@ namespace NewfoldLabs\WP\Module\Onboarding\RestApi\Themes; use NewfoldLabs\WP\Module\Onboarding\Permissions; -use NewfoldLabs\WP\Module\Onboarding\Data\Options; use NewfoldLabs\WP\Module\Onboarding\Data\Themes; use NewfoldLabs\WP\Module\Onboarding\Services\GlobalStylesService; diff --git a/includes/Services/GlobalStylesService.php b/includes/Services/GlobalStylesService.php index 2fd36072b..6d78cc611 100644 --- a/includes/Services/GlobalStylesService.php +++ b/includes/Services/GlobalStylesService.php @@ -22,7 +22,7 @@ class GlobalStylesService { * @param array $settings The settings to apply. Defaults to empty. * @return true|\WP_Error Returns true on success, or a WP_Error on failure. */ - public static function update_diy_global_style_variation( $id, $styles, $settings ) { + public static function update_diy_global_style_variation( $id, $styles = array(), $settings = array() ) { // If both styles and settings are not empty, update directly. if ( ! ( empty( $styles ) && empty( $settings ) ) ) { return self::update_global_style_variation( diff --git a/includes/Services/TemplatePartsService.php b/includes/Services/TemplatePartsService.php new file mode 100644 index 000000000..6e2765acb --- /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; + } +} From 3b7043dd904395ca4150195d61ffdfe00e1e0727 Mon Sep 17 00:00:00 2001 From: arunshenoy99 Date: Tue, 27 Aug 2024 16:08:46 +0530 Subject: [PATCH 3/4] Add support for font deselection --- includes/Services/FontService.php | 78 +++++++++++++++++++++++ includes/Services/GlobalStylesService.php | 8 ++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 includes/Services/FontService.php diff --git a/includes/Services/FontService.php b/includes/Services/FontService.php new file mode 100644 index 000000000..656770ce8 --- /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 index 6d78cc611..8200f35a0 100644 --- a/includes/Services/GlobalStylesService.php +++ b/includes/Services/GlobalStylesService.php @@ -25,6 +25,10 @@ class GlobalStylesService { public static function update_diy_global_style_variation( $id, $styles = array(), $settings = array() ) { // If both styles and settings are not empty, update directly. if ( ! ( empty( $styles ) && empty( $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, @@ -72,7 +76,9 @@ public static function update_diy_global_style_variation( $id, $styles = array() $settings = $user_selected_theme_settings['settings']; } - // Update the global style variation with the prepared styles and 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, From 9a089a4f982869ea349925696f5b427bc37de0a9 Mon Sep 17 00:00:00 2001 From: arunshenoy99 Date: Wed, 11 Sep 2024 11:33:41 +0530 Subject: [PATCH 4/4] Update Onboarding Data --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 0a9a8e542..b5709897a 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "require": { "mustache/mustache": "^2.14", "wp-cli/wp-config-transformer": "^1.3", - "newfold-labs/wp-module-onboarding-data": "^1.1", + "newfold-labs/wp-module-onboarding-data": "^1.0", "newfold-labs/wp-module-patterns": "^2.1", "newfold-labs/wp-module-facebook": "^1.0", "newfold-labs/wp-module-migration": "^1.0", diff --git a/composer.lock b/composer.lock index 4e4cf9e81..5552d3b07 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d854476e41bd250a75b83837c30dda6f", + "content-hash": "3fa73b59f1e1bc767882f4ac521ab442", "packages": [ { "name": "doctrine/inflector", @@ -771,16 +771,16 @@ }, { "name": "newfold-labs/wp-module-onboarding-data", - "version": "1.1.16", + "version": "1.1.17", "source": { "type": "git", "url": "https://github.com/newfold-labs/wp-module-onboarding-data.git", - "reference": "fe195d1bddc75b812e32d20e758544e5358e59de" + "reference": "1c60007a352cb18511536b8b58c3b9dc65821f73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/newfold-labs/wp-module-onboarding-data/zipball/fe195d1bddc75b812e32d20e758544e5358e59de", - "reference": "fe195d1bddc75b812e32d20e758544e5358e59de", + "url": "https://api.github.com/repos/newfold-labs/wp-module-onboarding-data/zipball/1c60007a352cb18511536b8b58c3b9dc65821f73", + "reference": "1c60007a352cb18511536b8b58c3b9dc65821f73", "shasum": "" }, "require": { @@ -816,10 +816,10 @@ ], "description": "A non-toggleable module containing a standardized interface for interacting with Onboarding data.", "support": { - "source": "https://github.com/newfold-labs/wp-module-onboarding-data/tree/1.1.16", + "source": "https://github.com/newfold-labs/wp-module-onboarding-data/tree/1.1.17", "issues": "https://github.com/newfold-labs/wp-module-onboarding-data/issues" }, - "time": "2024-07-31T11:51:46+00:00" + "time": "2024-09-11T05:58:44+00:00" }, { "name": "newfold-labs/wp-module-patterns",