PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/AutoLaunch/hooks/useCreateSite.js +117 -81 3.1.13.2.1 View file →
@@ -19,11 +19,13 @@
19 19 updateSinglePageLinksToSections,
20 20 } from '@auto-launch/functions/links';
21 21 import {
22 22 addPageLinksToNav,
23 + addSectionLinksFromDesign,
23 24 addSectionLinksToNav,
24 25 createNavigation,
25 26 injectNavExtras,
27 + injectWooCommerceIcons,
26 28 updateNavAttributes,
27 29 } from '@auto-launch/functions/nav';
28 30 import {
29 31 addImprintPage,
@@ -28,8 +30,9 @@
28 30 import {
29 31 addImprintPage,
30 32 createWpPages,
31 33 getPagesToCreate,
34 + isBlogPage,
32 35 PLUGIN_OWNED_PAGES,
33 36 setHelloWorldFeaturedImage,
34 37 updatePageTitlePattern,
35 38 } from '@auto-launch/functions/pages';
@@ -34,24 +37,26 @@
34 37 updatePageTitlePattern,
35 38 } from '@auto-launch/functions/pages';
36 39 import { generatePageContent } from '@auto-launch/functions/patterns';
37 40 import {
38 - activatePlugin,
39 41 alreadyActive,
42 + ensurePluginsActive,
40 43 getActivePlugins,
41 - installPlugin,
42 44 replacePlaceholderPatterns,
45 + reportFailedDependencies,
46 + reportInactivePlugins,
43 47 } from '@auto-launch/functions/plugins';
44 48 import {
45 49 postLaunchFunctions,
46 50 prefetchAssistData,
47 51 } from '@auto-launch/functions/setup';
52 +import { applySocialProfiles } from '@auto-launch/functions/social-links';
48 53 import {
49 54 setThemeRenderingMode,
50 55 updateTemplatePart,
51 56 updateVariation,
52 57 } from '@auto-launch/functions/theme';
53 -import { computeVibeAdjustedBlocks } from '@auto-launch/functions/vibes';
58 +import { computeVibeAdjustments } from '@auto-launch/functions/vibes';
54 59 import {
55 60 createBlogSampleData,
56 61 getOption,
57 62 getPageById,
@@ -59,21 +64,21 @@
59 64 updateOption,
60 65 } from '@auto-launch/functions/wp';
61 66 import { useWarnOnLeave } from '@auto-launch/hooks/useWarnOnLeave';
62 67 import { useLaunchDataStore } from '@auto-launch/state/launch-data';
68 +import { launchStrings } from '@auto-launch/strings';
63 69 import { digest } from '@shared/api/digest';
70 +import { siteImageUrls } from '@shared/lib/site-images';
64 71 import { useAIConsentStore } from '@shared/state/ai-consent';
65 72 import { useEffect, useRef, useState } from '@wordpress/element';
66 73 import { __ } from '@wordpress/i18n';
67 74 import useSWRImmutable from 'swr/immutable';
68 75
69 -const { homeUrl, showImprint, wpLanguage, installedPluginsSlugs } =
70 - window.extSharedData;
76 +const { homeUrl, showImprint, wpLanguage } = window.extSharedData;
71 77
72 78 // TODO: I think a good strategy is "if something fails, try to refetch some state"
73 79
74 80 export const useCreateSite = () => {
75 - // All the data we need to finish
76 81 const { setErrorMessage, addStatusMessage, needToStall, setData, ...data } =
77 82 useLaunchDataStore();
78 83 const { setUserGaveConsent } = useAIConsentStore();
79 84 const homeStretch = useRef(false);
@@ -115,8 +120,11 @@
115 120 useRunStep(
116 121 'siteLogo',
117 122 () => {
118 123 if (!data.siteProfile?.title) return null;
124 + // Logo was already uploaded by handleDesignBuild;
125 + // generating an AI logo here is not necessary.
126 + if (data.designBuild?.logoUrl) return null;
119 127 return data;
120 128 },
121 129 async (params) => {
122 130 checkIn({ stage: 'get_logo' });
@@ -124,13 +132,12 @@
124 132 },
125 133 );
126 134
127 135 // needs: siteProfile
128 - // provides:sitePlugins: [{name, wordpressSlug}]
136 + // provides: sitePlugins: [{name, wordpressSlug}]
129 137 useRunStep(
130 138 'sitePlugins',
131 139 () => {
132 - // We just need the site profile, which has this
133 140 if (!data.siteProfile?.title) return null;
134 141 return data;
135 142 },
136 143 async (params) => {
@@ -144,19 +151,13 @@
144 151 if (!data.sitePlugins?.length) return;
145 152
146 153 setStatus(
147 154 // translators: this is for a action log UI. Keep it short
148 - __('Setting up functionality for your website', 'extendify-local'),
155 + launchStrings().statusFunctionality,
149 156 );
150 - (async function install() {
151 - for (const { wordpressSlug: slug } of data.sitePlugins) {
152 - let plugin;
153 - if (!installedPluginsSlugs?.includes(slug)) {
154 - plugin = await installPlugin(slug);
155 - }
156 - await activatePlugin(plugin?.plugin ?? slug);
157 - }
158 - })();
157 + ensurePluginsActive(
158 + data.sitePlugins.map(({ wordpressSlug }) => wordpressSlug),
159 + );
159 160 }, [data.sitePlugins]);
160 161
161 162 // needs: siteProfile
162 163 // provides: style: {}
@@ -162,9 +163,8 @@
162 163 // provides: style: {}
163 164 useRunStep(
164 165 'siteStyle',
165 166 () => {
166 - // We just need the site profile, which has this
167 167 if (!data.siteProfile?.title) return null;
168 168 return data;
169 169 },
170 170 async (params) => {
@@ -177,9 +177,8 @@
177 177 // provides: aiHeaders: [], aiBlogTitles: []
178 178 useRunStep(
179 179 'siteStrings',
180 180 () => {
181 - // We just need the site profile, which has this
182 181 if (!data.siteProfile?.title) return null;
183 182 return data;
184 183 },
185 184 async (params) => {
@@ -192,9 +191,8 @@
192 191 // provides: siteImages: []
193 192 useRunStep(
194 193 'siteImages',
195 194 () => {
196 - // We just need the site profile, which has this
197 195 if (!data.siteProfile?.title) return null;
198 196 return data;
199 197 },
200 198 async (params) => {
@@ -221,9 +219,8 @@
221 219 // provides: home: { id, slug, patterns, siteStyle }
222 220 useRunStep(
223 221 'home',
224 222 () => {
225 - // Checking various data from calls above
226 223 const ok = [
227 224 data.siteProfile,
228 225 data.siteStyle,
229 226 data.siteImages,
@@ -237,14 +234,13 @@
237 234 return await handleHome(params);
238 235 },
239 236 );
240 237
241 - // siteProfile, sitePlugins, siteStyle, siteImages
238 + // needs: siteProfile, sitePlugins, siteStyle, siteImages
242 239 // provides: pages: [{ id, slug, name, patterns, siteStyle }]
243 240 useRunStep(
244 241 'pages',
245 242 () => {
246 - // Checking various data from calls above
247 243 const ok = [
248 244 data.siteProfile,
249 245 data.siteStyle,
250 246 data.siteImages,
@@ -271,14 +267,11 @@
271 267 checkIn({ stage: 'set_general', siteProfile, sitePlugins, siteStyle });
272 268
273 269 const { title } = siteProfile;
274 270 // translators: this is for a action log UI. Keep it short
275 - addStatusMessage(__('Adding admin configurations', 'extendify-local'));
276 - // update permalinks
271 + addStatusMessage(launchStrings().statusAdmin);
277 272 await updateOption('permalink_structure', '/%postname%/');
278 - // make sure consent is set
279 273 setUserGaveConsent(true);
280 - // Update title
281 274 if (title) await updateOption('blogname', title);
282 275 },
283 276 );
284 277
@@ -324,10 +317,23 @@
324 317 if (homeStretch.current) return;
325 318 homeStretch.current = true;
326 319 (async () => {
327 320 const { objective, structure, category } = siteProfile;
321 + const builtHome = designBuild?.builtPages?.find((p) => p.slug === 'home');
322 + // Must match get-home.js's full-page test; if they drift, the home is
323 + // built one way and planted another.
324 + const isSinglePageDesign =
325 + structure === 'single-page' &&
326 + Boolean(builtHome?.fullPage && builtHome.patterns?.length) &&
327 + Boolean(designBuild?.pages?.length);
328 + const imageUrls = siteImageUrls(siteImages);
329 + const intendedPlugins = (sitePlugins ?? []).map(
330 + ({ wordpressSlug }) => wordpressSlug,
331 + );
328 332
329 - // Do they need an imprint page?
333 + // Guarantee plugins are active before the pattern imports below rely on them.
334 + await ensurePluginsActive(intendedPlugins);
335 +
330 336 const needsImprint = Array.isArray(showImprint)
331 337 ? showImprint.includes(wpLanguage ?? '') && category === 'Business'
332 338 : false;
333 339
@@ -336,9 +342,9 @@
336 342 let variation = siteStyle?.variation;
337 343 if (customFonts?.length) {
338 344 checkIn({ stage: 'install_fonts' });
339 345 // translators: this is for a action log UI. Keep it short
340 - addStatusMessage(__('Installing fonts locally', 'extendify-local'));
346 + addStatusMessage(launchStrings().statusFonts);
341 347 const installed = await installFontFamilies(customFonts).catch(
342 348 () => [],
343 349 );
344 350 variation = mergeFontsIntoVariation(siteStyle.variation, installed);
@@ -345,19 +351,15 @@
345 351 }
346 352
347 353 if (siteStyle?.vibe && siteStyle.vibe !== 'natural-1') {
348 354 // translators: vibe in this context is a noun - the feeling of their site design.
349 - addStatusMessage(__('Setting the website style', 'extendify-local'));
355 + addStatusMessage(launchStrings().statusSiteStyle);
350 356 checkIn({ stage: 'compute_vibe' });
351 - const vibeBlocks = await computeVibeAdjustedBlocks(
357 + const vibe = await computeVibeAdjustments(
352 358 siteStyle.vibe,
359 + variation,
353 360 ).catch(() => null);
354 - if (vibeBlocks) {
355 - variation = {
356 - ...variation,
357 - styles: { ...variation.styles, blocks: vibeBlocks },
358 - };
359 - }
361 + if (vibe) variation = { ...variation, ...vibe };
360 362 }
361 363
362 364 checkIn({ stage: 'set_vibe' });
363 365 await updateVariation(variation);
@@ -362,9 +364,9 @@
362 364 checkIn({ stage: 'set_vibe' });
363 365 await updateVariation(variation);
364 366
365 367 // navigation menu
366 - addStatusMessage(__('Working on the navigation', 'extendify-local'));
368 + addStatusMessage(launchStrings().statusNavigation);
367 369 const { id: headerNavId } = await createNavigation({
368 370 title: __('Header Navigation', 'extendify-local'),
369 371 slug: 'site-navigation',
370 372 });
@@ -374,9 +376,9 @@
374 376 headerCode = injectNavExtras(headerCode, launchDecisions);
375 377 // remove the header navigation from the landing page
376 378 if (objective === 'landing-page') {
377 379 // translators: this is for a action log UI. Keep it short
378 - addStatusMessage(__('Perfecting a landing page', 'extendify-local'));
380 + addStatusMessage(launchStrings().statusLanding);
379 381 const social =
380 382 /<!--\s*wp:social-links\b[^>]*>.*?<!--\s*\/wp:social-links\s*-->/gis;
381 383 headerCode = headerCode
382 384 .replace(/<!--\s*wp:navigation\b[^>]*.*\/-->/gis, '')
@@ -393,8 +395,15 @@
393 395
394 396 // footer
395 397 let footerNavId = null;
396 398 let footerCode = home.footerCode || '';
399 + // The logo already carries the brand, so the site title would be redundant.
400 + if (designBuild?.hasExternalLogo && footerCode.includes('wp:site-logo')) {
401 + footerCode = footerCode.replace(
402 + /\s*<!--\s*wp:site-title[\s\S]*?\/-->/g,
403 + '',
404 + );
405 + }
397 406 if (needsImprint) {
398 407 const nav = await createNavigation({
399 408 title: __('Footer Navigation', 'extendify-local'),
400 409 slug: 'footer-navigation',
@@ -401,14 +410,15 @@
401 410 });
402 411 footerNavId = nav.id;
403 412 footerCode = updateNavAttributes(footerCode, { ref: footerNavId });
404 413 }
414 + footerCode = applySocialProfiles(footerCode, siteProfile.socialProfiles);
405 415 checkIn({ stage: 'set_footer' });
406 416 await updateTemplatePart('extendable/footer', footerCode);
407 417
408 418 // pages
409 419 // translators: this is for a action log UI. Keep it short
410 - addStatusMessage(__('Creating pages', 'extendify-local'));
420 + addStatusMessage(launchStrings().statusCreatingPages);
411 421 const pagesToCreate = getPagesToCreate(data);
412 422 const titlePattern = pages?.[0]?.patterns?.find((p) =>
413 423 p.patternTypes?.includes('page-title'),
414 424 );
@@ -417,9 +427,9 @@
417 427 await updatePageTitlePattern(titlePattern.code);
418 428 }
419 429
420 430 const activePlugins = await getActivePlugins();
421 - // This lets us keep plugin pages in th enav but skip making the page
431 + // Keep plugin pages in the nav but skip creating the page itself.
422 432 const reservedSlugs = new Set(
423 433 PLUGIN_OWNED_PAGES.filter(
424 434 ({ plugin }) =>
425 435 sitePlugins.some((p) => p.wordpressSlug === plugin) ||
@@ -450,25 +460,36 @@
450 460 p.patternTypes?.includes('hero-header'),
451 461 );
452 462 const pMatch = heroPattern?.code?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
453 463 const heroDesc = pMatch?.[1]?.replace(/<[^>]+>/g, '').trim();
454 - setData('heroDescription', heroDesc || data.heroDescription);
464 + const heroDescription = heroDesc || data.heroDescription;
465 + setData('heroDescription', heroDescription);
466 + if (heroDescription) {
467 + await updateOption('extendify_hero_description', heroDescription).catch(
468 + () => null,
469 + );
470 + }
455 471
456 - const createdPagesWP = await createWpPages(customPages);
472 + const createdPagesWP = await createWpPages(customPages, {
473 + skipSectionIds: isSinglePageDesign,
474 + });
457 475 // Aux pages
458 - const hasBlogPattern = home?.patterns?.some((pattern) =>
476 + const blogPattern = home?.patterns?.find((pattern) =>
459 477 pattern.patternTypes.includes('blog-section'),
460 478 );
461 - if (objective === 'blog' || hasBlogPattern) {
479 + if (siteProfile.blog || blogPattern) {
462 480 checkIn({ stage: 'create_blog_sample_data' });
463 481 // translators: this is for a action log UI. Keep it short
464 - addStatusMessage(__('Creating blog sample data', 'extendify-local'));
465 - await createBlogSampleData({ aiBlogTitles }, siteImages);
482 + addStatusMessage(launchStrings().statusBlog);
483 + await createBlogSampleData(
484 + { aiBlogTitles },
485 + imageUrls,
486 + blogPattern?.blogImages,
487 + );
466 488 }
467 - // If we have site images then set up the hello world image
468 - if (siteImages?.length) {
489 + if (imageUrls.length) {
469 490 checkIn({ stage: 'set_hello_world_image' });
470 - await setHelloWorldFeaturedImage(siteImages);
491 + await setHelloWorldFeaturedImage(imageUrls);
471 492 }
472 493
473 494 let imprint = {};
474 495 if (needsImprint) {
@@ -480,9 +501,9 @@
480 501 if (alreadyActive(activePlugins, 'woocommerce')) {
481 502 checkIn({ stage: 'import_woocommerce_products' });
482 503 addStatusMessage(
483 504 // translators: this is for a action log UI. Keep it short
484 - __('Setting up your online store', 'extendify-local'),
505 + launchStrings().statusStore,
485 506 );
486 507 await apiFetchWithTimeout({
487 508 path: '/extendify/v1/auto-launch/import-woocommerce',
488 509 }).catch(() => null);
@@ -497,25 +518,35 @@
497 518 link: `${homeUrl}/events`,
498 519 });
499 520 }
500 521
522 + // The design's page list omits the posts page like it omits shop, so the
523 + // nav needs it here — but it's ours to create, not a PLUGIN_OWNED_PAGES.
524 + pluginPages.push(...createdPagesWP.filter(isBlogPage));
525 +
501 526 // Adding pages to the nav
502 527 checkIn({ stage: 'set_page_links' });
503 - const linksResult =
504 - structure === 'single-page'
505 - ? await updateSinglePageLinksToSections(
506 - createdPagesWP,
507 - customPages,
508 - {
509 - objective,
510 - activePlugins,
511 - landingPageCTALink: siteProfile.landingPageCTALink,
512 - },
513 - headerCode,
514 - )
515 - : await updateButtonLinks(createdPagesWP, pluginPages, headerCode);
528 + let linksResult = { wpPages: createdPagesWP, headerCode };
529 + if (!isSinglePageDesign) {
530 + linksResult =
531 + structure === 'single-page'
532 + ? await updateSinglePageLinksToSections(
533 + createdPagesWP,
534 + customPages,
535 + {
536 + objective,
537 + activePlugins,
538 + landingPageCTALink: siteProfile.landingPageCTALink,
539 + },
540 + headerCode,
541 + )
542 + : await updateButtonLinks(createdPagesWP, pluginPages, headerCode);
543 + }
516 544 const pagesWithLinksUpdated = linksResult.wpPages;
517 545 headerCode = linksResult.headerCode;
546 + if (alreadyActive(activePlugins, 'woocommerce')) {
547 + headerCode = injectWooCommerceIcons(headerCode);
548 + }
518 549 await updateTemplatePart('extendable/header', headerCode);
519 550 const footerNavPages = [];
520 551 if (footerNavId && imprint?.title) {
521 552 const { originalSlug, title } = imprint;
@@ -529,9 +560,20 @@
529 560
530 561 checkIn({ stage: 'set_navigation_links' });
531 562 if (objective !== 'landing-page') {
532 563 const orderedSlugs = designBuild?.pages?.map((p) => p.slug) ?? [];
533 - if (structure === 'single-page') {
564 + // The tag only exists where the sections came back matched 1:1 to our list;
565 + // read it off what was planted, so menu and section ids can't disagree.
566 + const designOwnsNav =
567 + isSinglePageDesign ||
568 + Boolean(homePage?.patterns?.some(({ navSlug }) => navSlug));
569 + if (designOwnsNav) {
570 + await addSectionLinksFromDesign(
571 + headerNavId,
572 + designBuild.pages,
573 + pluginPages,
574 + );
575 + } else if (structure === 'single-page') {
534 576 await addSectionLinksToNav(
535 577 headerNavId,
536 578 home?.patterns,
537 579 pluginPages,
@@ -563,14 +605,18 @@
563 605 await prefetchAssistData();
564 606 checkIn({ stage: 'final_steps' });
565 607 await setThemeRenderingMode('template-locked');
566 608 await postLaunchFunctions();
567 - if (siteImages?.length) {
609 + if (imageUrls.length) {
568 610 await storeSiteImages(siteImages).catch(() => null);
569 611 }
570 612 // translators: this is for a action log UI. Keep it short
571 - addStatusMessage(__('All done!', 'extendify-local'));
572 - await checkIn({ stage: 'finished', siteProfile, sitePlugins, siteStyle });
613 + addStatusMessage(launchStrings().statusDone);
614 + await Promise.all([
615 + reportInactivePlugins(intendedPlugins).catch(() => null),
616 + reportFailedDependencies(pagesReplaced),
617 + checkIn({ stage: 'finished', siteProfile, sitePlugins, siteStyle }),
618 + ]);
573 619 setWarnOnReload(false);
574 620 setDone(true);
575 621 })().catch((error) => {
576 622 console.error(error);
@@ -580,14 +626,9 @@
580 626 });
581 627 // if we error here we can try again by resetting the home stretch and stalling again to refetch data
582 628 homeStretch.current = false;
583 629 needToStall(true);
584 - setErrorMessage(
585 - __(
586 - 'Something went wrong during the final steps. We will try again but you may need to refresh the page.',
587 - 'extendify-local',
588 - ),
589 - );
630 + setErrorMessage(launchStrings().errorFinalSteps);
590 631 });
591 632 }, [data, needToStall, setUserGaveConsent]);
592 633
593 634 return { done };
@@ -611,12 +652,7 @@
611 652 useEffect(() => {
612 653 if (!error || needToStall()) return;
613 654 console.error(error);
614 655 digest({ error, details: { source: 'auto-launch', caller: 'run-step' } });
615 - setErrorMessage(
616 - __(
617 - 'Having some trouble with this step. Trying again...',
618 - 'extendify-local',
619 - ),
620 - );
656 + setErrorMessage(launchStrings().errorStep);
621 657 }, [error, setErrorMessage, needToStall]);
622 658 };