diff --git a/build/3.2.11/onboarding.asset.php b/build/3.2.11/onboarding.asset.php index f5b62a9fa..c2da2f8d7 100644 --- a/build/3.2.11/onboarding.asset.php +++ b/build/3.2.11/onboarding.asset.php @@ -1 +1 @@ - array('lodash', 'react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-core-data', 'wp-data', 'wp-deprecated', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-preferences', 'wp-url'), 'version' => 'a33e7ab480d416b05814'); + array('lodash', 'react', 'react-jsx-runtime', 'wp-api-fetch', 'wp-core-data', 'wp-data', 'wp-deprecated', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-preferences', 'wp-url'), 'version' => 'f661930075f68d48ffeb'); diff --git a/includes/RestApi/SiteGenController.php b/includes/RestApi/SiteGenController.php index cbc3a4930..8fc729461 100644 --- a/includes/RestApi/SiteGenController.php +++ b/includes/RestApi/SiteGenController.php @@ -55,7 +55,7 @@ public function register_routes() { ); \register_rest_route( $this->namespace, - $this->rest_base . '/homepages', + $this->rest_base . '/homepages', array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( $this, 'get_homepages' ), @@ -225,6 +225,7 @@ public function get_enabled_identifiers() { * @return array|WP_Error */ public function generate_sitegen_meta( \WP_REST_Request $request ) { + if ( ! LegacySiteGenService::is_enabled() ) { return new \WP_Error( 'nfd_onboarding_error', @@ -234,7 +235,7 @@ public function generate_sitegen_meta( \WP_REST_Request $request ) { } $site_info = $request->get_param( 'site_info' ); - $identifier = $request->get_param( 'identifier' ); + $identifier = $request->get_param( 'identifier' ); $site_type = $request->get_param( 'site_type' ); $locale = $request->get_param( 'locale' ); $skip_cache = $request->get_param( 'skip_cache' ); @@ -274,6 +275,7 @@ public function get_homepages( \WP_REST_Request $request ) { $locale, true ); + } else { $target_audience = LegacySiteGenService::instantiate_site_meta( $site_info, 'target_audience', $site_type, $locale ); if ( is_wp_error( $target_audience ) ) { diff --git a/includes/Services/Ai/ContentGeneration/ContentGenerationPrompt.php b/includes/Services/Ai/ContentGeneration/ContentGenerationPrompt.php index 269e5c870..9727e30ea 100644 --- a/includes/Services/Ai/ContentGeneration/ContentGenerationPrompt.php +++ b/includes/Services/Ai/ContentGeneration/ContentGenerationPrompt.php @@ -30,7 +30,7 @@ class ContentGenerationPrompt { private $site_description; /** - * The type of site (e.g., 'business', 'blog', 'ecommerce'). + * The type of site (e.g., 'business', 'blog', 'ecommerce', 'linkinbio'). * * @var string */ @@ -58,7 +58,7 @@ class ContentGenerationPrompt { * Constructor for ContentGenerationPrompt. * * @param string $site_description The user-provided site description. - * @param string $site_type The type of site (e.g., 'business', 'personal', 'ecommerce'). + * @param string $site_type The type of site (e.g., 'business', 'personal', 'ecommerce', 'linkinbio'). * @param string $locale The locale/language code for the site. */ public function __construct( $site_description, $site_type, $locale ) { diff --git a/includes/Services/Ai/ContentGeneration/SitekitsContentGeneration.php b/includes/Services/Ai/ContentGeneration/SitekitsContentGeneration.php index bd7cc74e0..c2e65be41 100644 --- a/includes/Services/Ai/ContentGeneration/SitekitsContentGeneration.php +++ b/includes/Services/Ai/ContentGeneration/SitekitsContentGeneration.php @@ -6,6 +6,7 @@ use NewfoldLabs\WP\Module\Onboarding\Services\ParallelRequestsService; use NewfoldLabs\WP\Module\Onboarding\Services\SiteGenService; use NewfoldLabs\WP\Module\Onboarding\Services\SiteTypes\EcommerceSiteTypeService; +use NewfoldLabs\WP\Module\Onboarding\Services\SiteTypes\CommonSiteTypeService; use NewfoldLabs\WP\Module\Onboarding\Types\Page; use NewfoldLabs\WP\Module\Onboarding\Types\Pages; use NewfoldLabs\WP\Module\Onboarding\Types\ParallelRequest; @@ -16,7 +17,7 @@ class SitekitsContentGeneration { private static $site_types_supported = [ - 'ecommerce', + 'ecommerce', 'personal', 'business', 'linkinbio' ]; /** @@ -115,7 +116,6 @@ public function generate_sitekits( int $count = 3 ) { $error_message, array( 'status' => $response_code ) ); - return $response; } @@ -150,9 +150,9 @@ private function process_sitekit_item( array $sitekit_item ): array { $result = array(); $result['slug'] = $sitekit_item['slug']; $result['title'] = $sitekit_item['title']; - $result['header'] = $sitekit_item['header']['patternContent']; - $result['footer'] = $sitekit_item['footer']['patternContent']; - + $result['header'] = $this->check_custom_logo( $sitekit_item['header']['patternContent'] ); + $result['footer'] = $this->check_custom_logo( $sitekit_item['footer']['patternContent'] ); + $pages = array(); foreach ( $sitekit_item['pages'] as $page_slug => $page_patterns ) { $page_title = ucfirst( str_replace( '-', ' ', $page_slug ) ); @@ -213,6 +213,19 @@ private function publish_content( array $posts = array() ): void { ); } } + $articles = $posts['articles']['posts'] ?? array(); + + if ( ! empty( $articles ) ) { + foreach ( $articles as $index => $article ) { + CommonSiteTypeService::publish_article( + $article['title'] ?? 'Article ' . $index + 1, + $article['excerpt'] ?? 'Excerpt for Article ' . $index + 1, + $article['content'] ?? 'Content for Article ' . $index + 1, + $article['image'] ?? '', + $article['categories'] ?? array() + ); + } + } } /** @@ -224,4 +237,22 @@ private function publish_content( array $posts = array() ): void { public static function site_type_supported( string $site_type ): bool { return in_array( $site_type, self::$site_types_supported ); } + + /** + * Check if a custom logo exists; otherwise, replace the site logo block with the site title block. + * + * @var string $content Content to check. + * @return string + */ + private function check_custom_logo( string $content ): string { + if ( function_exists('has_custom_logo') && ! has_custom_logo() ) { + $content = preg_replace( + '//', + '', + $content + ); + } + + return $content; + } } \ No newline at end of file diff --git a/includes/Services/Blueprints/BlueprintImportService.php b/includes/Services/Blueprints/BlueprintImportService.php index c27a7dabc..7d247b822 100644 --- a/includes/Services/Blueprints/BlueprintImportService.php +++ b/includes/Services/Blueprints/BlueprintImportService.php @@ -220,6 +220,8 @@ private function search_replace( string $sql_content ) { // Replace URLs in content $sql_content = str_replace( $source_site_url, $target_site_url, $sql_content ); + // Replace uploads URLs also for multisite. + $sql_content = preg_replace( "/uploads\/sites\/[0-9]+/", 'uploads/', $sql_content ); // Also handle URLs with trailing slashes $sql_content = str_replace( $source_site_url . '/', $target_site_url . '/', $sql_content ); diff --git a/includes/Services/SiteTypes/CommonSitetypeService.php b/includes/Services/SiteTypes/CommonSitetypeService.php new file mode 100644 index 000000000..48c8b7f99 --- /dev/null +++ b/includes/Services/SiteTypes/CommonSitetypeService.php @@ -0,0 +1,112 @@ + $title, + 'post_content' => $content, + 'post_excerpt' => $excerpt, + 'post_status' => 'publish', + 'post_type' => 'post', + 'post_author' => get_current_user_id() ?: 1, + ); + // Insert post. + $post_id = wp_insert_post( $post_data ); + // Validate post was created successfully. + if ( is_wp_error( $post_id ) || !$post_id ) { + return new \WP_Error( 'error_publishing_blog_post', 'Failed to create post' ); + } + // Post categories. + if ( ! empty( $categories ) ) { + $category_ids = array(); + foreach ( $categories as $category ) { + $category_ids[] = self::create_blog_category( $category ); + } + wp_set_post_terms( $post_id, $category_ids, 'category' ); + } + // Featured image. + if ( ! empty( $image ) ) { + self::set_featured_image_from_url( $image, $post_id ); + } + + return $post_id; + } + + /** + * Sets the featured image for a blog post. + * + * @param string $image_url The URL of the image. + * @param int $post_id The ID of the post. + * @return void + */ + private static function set_featured_image_from_url( string $image_url, int $post_id ): void { + $image_id = self::import_image_from_url( $image_url, $post_id ); + if ( $image_id ) { + update_post_meta( $post_id, '_thumbnail_id', $image_id ); + } + } + + /** + * Imports an image from a URL. + * + * @param string $image_url The URL of the image. + * @param int $post_id The ID of the post. + * @return int The ID of the attachment. + */ + private static function import_image_from_url( string $image_url, int $post_id ): int { + if ( ! function_exists( 'media_handle_sideload' ) ) { + require_once( ABSPATH . 'wp-admin/includes/media.php' ); + require_once( ABSPATH . 'wp-admin/includes/file.php' ); + require_once( ABSPATH . 'wp-admin/includes/image.php' ); + } + + // Add an arbitrary extension to the image URL to trick media_sideload_image to download the image. + $image_url = $image_url . '?ext=.jpeg'; + $attachment_id = media_sideload_image( $image_url, $post_id, null, 'id' ); + if ( is_wp_error( $attachment_id ) ) { + return 0; + } + + return $attachment_id; + } + + /** + * Creates or gets a blog category. + * + * @param string $name The name of the category. + * @return int The ID of the category. + */ + public static function create_blog_category( string $name ): int { + $category_slug = sanitize_title( $name ); + $category = get_term_by( 'slug', $category_slug, 'category' ); + if ( $category ) { + return $category->term_id; + } + $category = wp_insert_term( $name, 'category' ); + if ( is_wp_error( $category ) ) { + return 0; + } + return $category['term_id']; + } +} diff --git a/src/app/steps/Blueprints/BlueprintsStep.js b/src/app/steps/Blueprints/BlueprintsStep.js index 9f1a6b7f7..8dac03a76 100644 --- a/src/app/steps/Blueprints/BlueprintsStep.js +++ b/src/app/steps/Blueprints/BlueprintsStep.js @@ -27,6 +27,10 @@ const Tabs = ( { activeTab } ) => { label: __( 'Blog', 'wp-module-onboarding' ), value: 'personal', }, + { + label: __( 'Link in bio', 'wp-module-onboarding' ), + value: 'linkinbio', + }, ]; return ( diff --git a/src/app/steps/Intake/SiteTypeSelector.js b/src/app/steps/Intake/SiteTypeSelector.js index 437421272..a90c0a5e5 100644 --- a/src/app/steps/Intake/SiteTypeSelector.js +++ b/src/app/steps/Intake/SiteTypeSelector.js @@ -15,6 +15,10 @@ const SiteTypeSelector = ( { value: selectedSiteType, onChange } ) => { value: 'ecommerce', label: __( 'Online Store', 'wp-module-onboarding' ), }, + { + value: 'linkinbio', + label: __( 'Link in bio', 'wp-module-onboarding' ), + }, ]; const getSelectedLabel = ( value ) => { @@ -42,9 +46,7 @@ const SiteTypeSelector = ( { value: selectedSiteType, onChange } ) => { onChange={ handleChange } value={ selectedSiteType || '' } selectedLabel={ getSelectedLabel( selectedSiteType ) } - className={ classNames( - ! selectedSiteType && '[&_button>span]:nfd-text-gray-500', - ) } + className={ classNames( ! selectedSiteType && '[&_button>span]:nfd-text-gray-500' ) } /> );