Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteGenService
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 10
1056
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
 get_locale
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 string $locale The locale.
75     * @param bool   $for_onboarding_preview Whether to return the sitekits as array for onboarding preview.
76     * @return array|\WP_Error Array of Sitekit objects, array of sitekits for onboarding preview, or WP_Error if there is an error.
77     */
78    public function get_sitekits( string $site_description, string $site_type, string $locale = 'en_US', bool $for_onboarding_preview = false ) {
79        $prompt              = new ContentGenerationPrompt( $site_description, $site_type, $locale );
80        $site_classification = $this->get_site_classification();
81
82        $sitekits = new SitekitsContentGeneration( $site_type, $locale, $prompt, $site_classification );
83        $sitekits = $sitekits->generate_sitekits();
84
85        // If there is an error, return it.
86        if ( is_wp_error( $sitekits ) ) {
87            return $sitekits;
88        }
89
90        // If we are generating sitekits for onboarding preview, return the sitekits as array.
91        if ( $for_onboarding_preview ) {
92            $previews = array();
93            foreach ( $sitekits as $sitekit ) {
94                $previews[ $sitekit->get_slug() ] = $sitekit->onboarding_preview_data();
95            }
96            $sitekits = $previews;
97        }
98
99        return $sitekits;
100    }
101
102    /**
103     * Publish the selected sitegen homepage.
104     *
105     * @param string $selected_sitegen_homepage The selected sitegen homepage to publish.
106     * @return int|\WP_Error
107     */
108    public function publish_homepage( string $selected_sitegen_homepage ) {
109        // Validate we have the selected homepage.
110        if (
111            ! $this->sitegen_data ||
112            ! is_array( $this->sitegen_data['homepages'] ) ||
113            ! isset( $this->sitegen_data['homepages'][ $selected_sitegen_homepage ] )
114        ) {
115            return new \WP_Error(
116                'sitegen_homepage_publish_validation_error',
117                'Error validating selected homepage.',
118            );
119        }
120
121        $selected_homepage = $this->sitegen_data['homepages'][ $selected_sitegen_homepage ];
122        $content           = $selected_homepage['content'];
123        $title             = __( 'Home', 'wp-module-onboarding' );
124
125        $post_id = SitePagesService::publish_page(
126            $title,
127            $content,
128            true,
129            array(
130                'nf_dc_page' => 'home',
131            )
132        );
133        if ( 0 === $post_id || is_wp_error( $post_id ) ) {
134            return new \WP_Error(
135                'sitegen_homepage_publish_error',
136                'Error publishing homepage.',
137            );
138        }
139
140        // Process images immediately in background (non-blocking)
141        SiteGenImageService::process_homepage_images_immediate_async( $post_id, $content );
142
143        // Change WordPress reading options to show static page as homepage.
144        $wp_reading_homepage_option = get_option( 'show_on_front' );
145        if ( 'page' !== $wp_reading_homepage_option ) {
146            update_option( 'show_on_front', 'page' );
147        }
148        // Set the homepage as the front page.
149        update_option( 'page_on_front', $post_id );
150
151        return $post_id;
152    }
153
154    /**
155     * Get AI generated page title for a given slug (if found in sitemap).
156     *
157     * @param string $slug The slug of the page to get the title for.
158     * @return string|false The page title, or false if not found.
159     */
160    public function get_sitemap_page_title( string $slug ) {
161        $prompt    = $this->get_prompt();
162        $locale    = $this->get_locale();
163        $site_type = $this->get_site_type();
164        if ( ! $prompt || ! $locale || ! $site_type ) {
165            return false;
166        }
167
168        $sitemap = LegacySiteGenService::instantiate_site_meta( $prompt, 'sitemap', $site_type, $locale );
169        if ( ! is_wp_error( $sitemap ) ) {
170            foreach ( $sitemap as $page ) {
171                if ( $slug === $page['slug'] ) {
172                    $title = $page['title'];
173                    return $title;
174                }
175            }
176        }
177
178        return false;
179    }
180
181    /**
182     * Get the site classification.
183     *
184     * @return SiteClassification
185     */
186    public function get_site_classification(): SiteClassification {
187        $primary_type        = 'other';
188        $secondary_type      = 'other';
189        $site_classification = LegacySiteGenService::instantiate_site_meta(
190            $this->get_prompt(),
191            'site_classification',
192            $this->get_site_type(),
193            $this->get_locale()
194        );
195
196        if ( is_array( $site_classification ) ) {
197            $primary_type   = $site_classification['primaryType'] ?? $primary_type;
198            $secondary_type = $site_classification['slug'] ?? $secondary_type;
199        }
200
201        return new SiteClassification( $primary_type, $secondary_type );
202    }
203
204    /**
205     * Get a palette from the color palettes generated during the sitemeta calls.
206     *
207     * @return ColorPalette|null The color palette, or null on error.
208     */
209    public function get_color_palette() {
210        $color_palettes = LegacySiteGenService::instantiate_site_meta(
211            $this->get_prompt(),
212            'color_palette',
213            $this->get_site_type(),
214            $this->get_locale()
215        );
216
217        if ( ! is_array( $color_palettes ) ) {
218            return null;
219        }
220
221        // Select a random color palette from the sitemeta.
222        $selected_color_palette_index = array_rand( $color_palettes );
223        $selected_color_palette       = $color_palettes[ $selected_color_palette_index ];
224
225        $palette_slug   = 'palette_' . ( $selected_color_palette_index + 1 );
226        $palette_colors = array();
227
228        // Create a Color object for each color in the selected color palette.
229        foreach ( $selected_color_palette as $key => $value ) {
230            $slug  = $key;
231            $name  = ucfirst( str_replace( '_', ' ', $slug ) );
232            $color = $value;
233
234            $palette_colors[] = new Color( $name, $slug, $color );
235        }
236
237        return new ColorPalette( $palette_slug, $palette_colors );
238    }
239
240    /**
241     * Get the prompt entered during Onboarding.
242     *
243     * @return string|false
244     */
245    public function get_prompt() {
246        return ! empty( $this->input_data['prompt'] ) ? $this->input_data['prompt'] : false;
247    }
248
249    /**
250     * Get the site type entered during Onboarding.
251     *
252     * @return string
253     */
254    public function get_site_type() {
255        return ! empty( $this->input_data['siteType'] ) ? $this->input_data['siteType'] : 'business';
256    }
257
258    /**
259     * Get the locale entered during Onboarding.
260     *
261     * @return string
262     */
263    public function get_locale() {
264        return ! empty( $this->input_data['locale'] ) ? $this->input_data['locale'] : 'en_US';
265    }
266}