Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteGenService
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 9
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_sitekits
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 publish_homepage
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
56
 get_sitemap_page_title
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
56
 get_site_classification
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 get_color_palette
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 get_prompt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 get_site_type
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace NewfoldLabs\WP\Module\Onboarding\Services;
4
5use NewfoldLabs\WP\Module\Onboarding\Data\Services\SitePagesService;
6use NewfoldLabs\WP\Module\Onboarding\Data\Services\SiteGenService as LegacySiteGenService;
7use NewfoldLabs\WP\Module\Onboarding\Services\Ai\ContentGeneration\ContentGenerationPrompt;
8use NewfoldLabs\WP\Module\Onboarding\Services\Ai\ContentGeneration\SitekitsContentGeneration;
9use NewfoldLabs\WP\Module\Onboarding\Types\Color;
10use NewfoldLabs\WP\Module\Onboarding\Types\ColorPalette;
11use NewfoldLabs\WP\Module\Onboarding\Types\SiteClassification;
12use NewfoldLabs\WP\Module\Onboarding\Services\SiteGenImageService;
13use NewfoldLabs\WP\Module\Onboarding\Services\ReduxStateService;
14
15/**
16 * Class SiteGenService
17 *
18 * Handles the onboarding SiteGen flow for generating, publishing, and managing AI-generated homepages and related content.
19 *
20 * This service is designed to work with the onboarding module's Redux state and integrates with other onboarding services.
21 *
22 * @package NewfoldLabs\WP\Module\Onboarding\Services
23 */
24class SiteGenService {
25
26    /**
27     * The singleton instance.
28     *
29     * @var SiteGenService|null
30     */
31    private static $instance = null;
32
33    /**
34     * The Redux input object.
35     *
36     * @var array|null
37     */
38    private $input_data = null;
39
40    /**
41     * The Redux sitegen object.
42     *
43     * @var array|null
44     */
45    private $sitegen_data = null;
46
47    /**
48     * SiteGenService constructor.
49     *
50     * Initializes the service by loading the Redux input and sitegen data from the ReduxStateService.
51     */
52    public function __construct() {
53        $this->input_data   = ReduxStateService::get( 'input' );
54        $this->sitegen_data = ReduxStateService::get( 'sitegen' );
55    }
56
57    /**
58     * Get the singleton instance.
59     *
60     * @return SiteGenService
61     */
62    public static function get_instance(): SiteGenService {
63        if ( null === self::$instance ) {
64            self::$instance = new self();
65        }
66        return self::$instance;
67    }
68
69    /**
70     * Generate the sitekits.
71     *
72     * @param string $site_description The site description.
73     * @param string $site_type The site type.
74     * @param bool   $for_onboarding_preview Whether to return the sitekits as array for onboarding preview.
75     * @return array|\WP_Error Array of Sitekit objects, array of sitekits for onboarding preview, or WP_Error if there is an error.
76     */
77    public function get_sitekits( string $site_description, string $site_type, bool $for_onboarding_preview = false ) {
78        $prompt              = new ContentGenerationPrompt( $site_description, $site_type );
79        $site_classification = $this->get_site_classification();
80
81        $sitekits = new SitekitsContentGeneration( $site_type, $prompt, $site_classification );
82        $sitekits = $sitekits->generate_sitekits();
83
84        // If there is an error, return it.
85        if ( is_wp_error( $sitekits ) ) {
86            return $sitekits;
87        }
88
89        // If we are generating sitekits for onboarding preview, return the sitekits as array.
90        if ( $for_onboarding_preview ) {
91            $previews = array();
92            foreach ( $sitekits as $sitekit ) {
93                $previews[ $sitekit->get_slug() ] = $sitekit->onboarding_preview_data();
94            }
95            $sitekits = $previews;
96        }
97
98        return $sitekits;
99    }
100
101    /**
102     * Publish the selected sitegen homepage.
103     *
104     * @param string $selected_sitegen_homepage The selected sitegen homepage to publish.
105     * @return int|\WP_Error
106     */
107    public function publish_homepage( string $selected_sitegen_homepage ) {
108        // Validate we have the selected homepage.
109        if (
110            ! $this->sitegen_data ||
111            ! is_array( $this->sitegen_data['homepages'] ) ||
112            ! isset( $this->sitegen_data['homepages'][ $selected_sitegen_homepage ] )
113        ) {
114            return new \WP_Error(
115                'sitegen_homepage_publish_validation_error',
116                'Error validating selected homepage.',
117            );
118        }
119
120        $selected_homepage = $this->sitegen_data['homepages'][ $selected_sitegen_homepage ];
121        $content           = $selected_homepage['content'];
122        $title             = __( 'Home', 'wp-module-onboarding' );
123
124        $post_id = SitePagesService::publish_page(
125            $title,
126            $content,
127            true,
128            array(
129                'nf_dc_page' => 'home',
130            )
131        );
132        if ( 0 === $post_id || is_wp_error( $post_id ) ) {
133            return new \WP_Error(
134                'sitegen_homepage_publish_error',
135                'Error publishing homepage.',
136            );
137        }
138
139        // Process images immediately in background (non-blocking)
140        SiteGenImageService::process_homepage_images_immediate_async( $post_id, $content );
141
142        // Change WordPress reading options to show static page as homepage.
143        $wp_reading_homepage_option = get_option( 'show_on_front' );
144        if ( 'page' !== $wp_reading_homepage_option ) {
145            update_option( 'show_on_front', 'page' );
146        }
147        // Set the homepage as the front page.
148        update_option( 'page_on_front', $post_id );
149
150        return $post_id;
151    }
152
153    /**
154     * Get AI generated page title for a given slug (if found in sitemap).
155     *
156     * @param string $slug The slug of the page to get the title for.
157     * @return string|false The page title, or false if not found.
158     */
159    public function get_sitemap_page_title( string $slug ) {
160        $prompt    = $this->get_prompt();
161        $locale    = LanguageService::get_site_locale();
162        $site_type = $this->get_site_type();
163        if ( ! $prompt || ! $locale || ! $site_type ) {
164            return false;
165        }
166
167        $sitemap = LegacySiteGenService::instantiate_site_meta( $prompt, 'sitemap', $site_type, $locale );
168        if ( ! is_wp_error( $sitemap ) ) {
169            foreach ( $sitemap as $page ) {
170                if ( $slug === $page['slug'] ) {
171                    $title = $page['title'];
172                    return $title;
173                }
174            }
175        }
176
177        return false;
178    }
179
180    /**
181     * Get the site classification.
182     *
183     * @return SiteClassification
184     */
185    public function get_site_classification(): SiteClassification {
186        $primary_type        = 'other';
187        $secondary_type      = 'other';
188        $site_classification = LegacySiteGenService::instantiate_site_meta(
189            $this->get_prompt(),
190            'site_classification',
191            $this->get_site_type(),
192            LanguageService::get_site_locale()
193        );
194
195        if ( is_array( $site_classification ) ) {
196            $primary_type   = $site_classification['primaryType'] ?? $primary_type;
197            $secondary_type = $site_classification['slug'] ?? $secondary_type;
198        }
199
200        return new SiteClassification( $primary_type, $secondary_type );
201    }
202
203    /**
204     * Get a palette from the color palettes generated during the sitemeta calls.
205     *
206     * @return ColorPalette|null The color palette, or null on error.
207     */
208    public function get_color_palette() {
209        $color_palettes = LegacySiteGenService::instantiate_site_meta(
210            $this->get_prompt(),
211            'color_palette',
212            $this->get_site_type(),
213            LanguageService::get_site_locale()
214        );
215
216        if ( ! is_array( $color_palettes ) ) {
217            return null;
218        }
219
220        // Select a random color palette from the sitemeta.
221        $selected_color_palette_index = array_rand( $color_palettes );
222        $selected_color_palette       = $color_palettes[ $selected_color_palette_index ];
223
224        $palette_slug   = 'palette_' . ( $selected_color_palette_index + 1 );
225        $palette_colors = array();
226
227        // Create a Color object for each color in the selected color palette.
228        foreach ( $selected_color_palette as $key => $value ) {
229            $slug  = $key;
230            $name  = ucfirst( str_replace( '_', ' ', $slug ) );
231            $color = $value;
232
233            $palette_colors[] = new Color( $name, $slug, $color );
234        }
235
236        return new ColorPalette( $palette_slug, $palette_colors );
237    }
238
239    /**
240     * Get the prompt entered during Onboarding.
241     *
242     * @return string|false
243     */
244    public function get_prompt() {
245        return ! empty( $this->input_data['prompt'] ) ? $this->input_data['prompt'] : false;
246    }
247
248    /**
249     * Get the site type entered during Onboarding.
250     *
251     * @return string
252     */
253    public function get_site_type() {
254        return ! empty( $this->input_data['siteType'] ) ? $this->input_data['siteType'] : 'business';
255    }
256}