Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SitekitsContentGeneration
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 9
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 generate_sitekits
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
42
 get_sitekit_object
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 process_sitekit_item
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 get_page_content_from_patterns
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 install_pre_requisites_in_background
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 publish_content
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 site_type_supported
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 check_custom_logo
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace NewfoldLabs\WP\Module\Onboarding\Services\Ai\ContentGeneration;
4
5use NewfoldLabs\WP\Module\Onboarding\RestApi\ParallelRequestsController;
6use NewfoldLabs\WP\Module\Onboarding\Services\ParallelRequestsService;
7use NewfoldLabs\WP\Module\Onboarding\Services\SiteGenService;
8use NewfoldLabs\WP\Module\Onboarding\Services\SiteTypes\EcommerceSiteTypeService;
9use NewfoldLabs\WP\Module\Onboarding\Services\SiteTypes\CommonSiteTypeService;
10use NewfoldLabs\WP\Module\Onboarding\Types\Page;
11use NewfoldLabs\WP\Module\Onboarding\Types\Pages;
12use NewfoldLabs\WP\Module\Onboarding\Types\ParallelRequest;
13use NewfoldLabs\WP\Module\Onboarding\Types\SiteClassification;
14use NewfoldLabs\WP\Module\Onboarding\Types\Sitekit;
15use WpOrg\Requests\Requests;
16
17class SitekitsContentGeneration {
18
19    private static $site_types_supported = [
20        'ecommerce', 'personal', 'business', 'linkinbio'
21    ];
22
23    /**
24     * The site type.
25     *
26     * @var string
27     */
28    private $site_type;
29
30    /**
31     * The locale.
32     *
33     * @var string
34     */
35    private $locale;
36
37    /**
38     * The prompt.
39     *
40     * @var ContentGenerationPrompt
41     */
42    private $prompt;
43
44    /**
45     * The site classification.
46     *
47     * @var SiteClassification
48     */
49    private $site_classification;
50
51    /**
52     * Constructor.
53     *
54     * @param string $site_type The site type.
55     * @param string $locale The locale.
56     * @param ContentGenerationPrompt $prompt The prompt.
57     * @param SiteClassification $site_classification The site classification.
58     */
59    public function __construct( string $site_type, string $locale, ContentGenerationPrompt $prompt, SiteClassification $site_classification ) {
60        $this->site_type           = $site_type;
61        $this->locale              = $locale;
62        $this->prompt              = $prompt;
63        $this->site_classification = $site_classification;
64    }
65
66    /**
67     * Generates the sitekits.
68     *
69     * @param int $count The number of sitekits to generate.
70     * @return array|\WP_Error The sitekits - array of Sitekit objects or WP_Error on failure.
71     */
72    public function generate_sitekits( int $count = 3 ) {
73        $prompt = $this->prompt->get_prompt();
74        $request_body = array(
75            'siteType'      => $this->site_type,
76            'count'         => $count,
77            'prompt'        => $prompt,
78            'locale'        => $this->locale,
79            'primaryType'   => $this->site_classification->get_primary_type(),
80            'secondaryType' => $this->site_classification->get_secondary_type(),
81        );
82
83        // Install sitekits pre-requisites plugins in background.
84        $this->install_pre_requisites_in_background();
85
86        // Generate sitekits.
87        $request = new ContentGenerationServiceRequest( 'sitekits/generate', $request_body );
88        $request->send();
89        // Success.
90        if (
91            $request->is_successful() &&
92            isset( $request->get_response_body()['sitekits'] )
93        ) {
94            $response = array();
95
96            // Process the sitekits.
97            $sitekits = $request->get_response_body()['sitekits'];
98            foreach ( $sitekits as $sitekit ) {
99                $sitekit_object = $this->get_sitekit_object( $sitekit );
100                $response[]     = $sitekit_object;
101            }
102
103            // Publish site content.
104            $posts = $request->get_response_body()['posts'] ?? array();
105            $this->publish_content( $posts );
106
107            return $response;
108        }
109
110        // Error.
111        $response_code = $request->get_response_code();
112        $response_body = $request->get_error_response_body();
113        $error_message = ( $response_body && isset( $response_body['message'] ) ) ? $response_body['message'] : __( 'An unknown error occurred', 'wp-module-onboarding' );
114        $response = new \WP_Error(
115            'sitekits_generation_failed',
116            $error_message,
117            array( 'status' => $response_code )
118        );
119        return $response;
120    }
121
122    /**
123     * Gets the sitekit object.
124     *
125     * @param array $sitekit The sitekit.
126     * @return Sitekit The sitekit object.
127     */
128    private function get_sitekit_object( array $sitekit ): Sitekit {
129        $processed_sitekit = $this->process_sitekit_item( $sitekit );
130
131        $sitekit = new Sitekit(
132            $processed_sitekit['slug'],
133            $processed_sitekit['title'],
134            $processed_sitekit['header'],
135            $processed_sitekit['footer'],
136            $processed_sitekit['pages'],
137            $processed_sitekit['color_palette']
138        );
139
140        return $sitekit;
141    }
142
143    /**
144     * Processes and prepares a sitekit item from the response.
145     *
146     * @param array $sitekit_item The sitekit item.
147     * @return array The processed sitekit item — ready to be converted to a Sitekit object.
148     */
149    private function process_sitekit_item( array $sitekit_item ): array {
150        $result           = array();
151        $result['slug']   = $sitekit_item['slug'];
152        $result['title']  = $sitekit_item['title'];
153        $result['header'] = $this->check_custom_logo( $sitekit_item['header']['patternContent'] );
154        $result['footer'] = $this->check_custom_logo( $sitekit_item['footer']['patternContent'] );
155
156        $pages = array();
157        foreach ( $sitekit_item['pages'] as $page_slug => $page_patterns ) {
158            $page_title    = ucfirst( str_replace( '-', ' ', $page_slug ) );
159            $page_content  = $this->get_page_content_from_patterns( $page_patterns );
160            $is_front_page = $page_slug === 'home';
161            $pages[]       = new Page( $page_title, $page_slug, $page_content, $is_front_page );
162        }
163        $result['pages'] = new Pages( $pages );
164
165        // Attach a color palette to the sitekit.
166        $result['color_palette'] = SiteGenService::get_instance()->get_color_palette();
167
168        return $result;
169    }
170
171    /**
172     * Gets the page content from the response patterns array.
173     *
174     * @param array $page_patterns The page patterns.
175     * @return string The page content.
176     */
177    private function get_page_content_from_patterns( array $page_patterns ): string {
178        $page_content = '';
179        foreach ( $page_patterns as $pattern ) {
180            $page_content .= $pattern['patternContent'];
181        }
182        return $page_content;
183    }
184
185    /**
186     * Installs the pre-requisites in background.
187     *
188     * @return void
189     */
190    private function install_pre_requisites_in_background(): void {
191        if ( $this->site_type === 'ecommerce' ) {
192            EcommerceSiteTypeService::install_ecommerce_plugins();
193        }
194    }
195
196    /**
197     * Publishes the demo content.
198     *
199     * @param array $posts The posts.
200     * @return void
201     */
202    private function publish_content( array $posts = array() ): void {
203        // Publish WooCommerce products.
204        $products = $posts['products'] ?? array();
205        if ( ! empty( $products ) ) {
206            foreach ( $products as $index => $product ) {
207                EcommerceSiteTypeService::publish_woo_product(
208                    $product['name'] ?? 'Product ' . $index + 1,
209                    $product['description'] ?? 'Description for Product ' . $index + 1,
210                    $product['price'] ?? '24.99',
211                    $product['image'] ?? '',
212                    $product['categories'] ?? array()
213                );
214            }
215        }
216        $articles = $posts['articles']['posts'] ?? array();
217
218        if ( ! empty( $articles ) ) {
219            foreach ( $articles as $index => $article ) {
220                CommonSiteTypeService::publish_article(
221                    $article['title'] ?? 'Article ' . $index + 1,
222                    $article['excerpt'] ?? 'Excerpt for Article ' . $index + 1,
223                    $article['content'] ?? 'Content for Article ' . $index + 1,
224                    $article['image'] ?? '',
225                    $article['categories'] ?? array()
226                );
227            }
228        }
229    }
230
231    /**
232     * Checks if the site type supports sitekits`.
233     *
234     * @param string $site_type The site type.
235     * @return bool
236     */
237    public static function site_type_supported( string $site_type ): bool {
238        return in_array( $site_type, self::$site_types_supported );
239    }
240
241    /**
242     * Check if a custom logo exists; otherwise, replace the site logo block with the site title block.
243     *
244     * @var string $content Content to check.
245     * @return string
246     */
247    private function check_custom_logo( string $content ): string {
248        if ( function_exists('has_custom_logo') && ! has_custom_logo() ) {
249            $content = preg_replace(
250                '/<!--\s*wp:site-logo\s*\/-->/',
251                '<!-- wp:site-logo /--><!-- wp:site-title /-->',
252                $content
253            );
254        }
255
256        return $content;
257    }
258}