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 +111 -76 3.1.33.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';
@@ -38,20 +41,22 @@
38 41 alreadyActive,
39 42 ensurePluginsActive,
40 43 getActivePlugins,
41 44 replacePlaceholderPatterns,
42 - verifyPluginsActive,
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,13 +151,12 @@
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 157 ensurePluginsActive(
151 158 data.sitePlugins.map(({ wordpressSlug }) => wordpressSlug),
152 - { installedSlugs: installedPluginsSlugs },
153 159 );
154 160 }, [data.sitePlugins]);
155 161
156 162 // needs: siteProfile
@@ -157,9 +163,8 @@
157 163 // provides: style: {}
158 164 useRunStep(
159 165 'siteStyle',
160 166 () => {
161 - // We just need the site profile, which has this
162 167 if (!data.siteProfile?.title) return null;
163 168 return data;
164 169 },
165 170 async (params) => {
@@ -172,9 +177,8 @@
172 177 // provides: aiHeaders: [], aiBlogTitles: []
173 178 useRunStep(
174 179 'siteStrings',
175 180 () => {
176 - // We just need the site profile, which has this
177 181 if (!data.siteProfile?.title) return null;
178 182 return data;
179 183 },
180 184 async (params) => {
@@ -187,9 +191,8 @@
187 191 // provides: siteImages: []
188 192 useRunStep(
189 193 'siteImages',
190 194 () => {
191 - // We just need the site profile, which has this
192 195 if (!data.siteProfile?.title) return null;
193 196 return data;
194 197 },
195 198 async (params) => {
@@ -216,9 +219,8 @@
216 219 // provides: home: { id, slug, patterns, siteStyle }
217 220 useRunStep(
218 221 'home',
219 222 () => {
220 - // Checking various data from calls above
221 223 const ok = [
222 224 data.siteProfile,
223 225 data.siteStyle,
224 226 data.siteImages,
@@ -232,14 +234,13 @@
232 234 return await handleHome(params);
233 235 },
234 236 );
235 237
236 - // siteProfile, sitePlugins, siteStyle, siteImages
238 + // needs: siteProfile, sitePlugins, siteStyle, siteImages
237 239 // provides: pages: [{ id, slug, name, patterns, siteStyle }]
238 240 useRunStep(
239 241 'pages',
240 242 () => {
241 - // Checking various data from calls above
242 243 const ok = [
243 244 data.siteProfile,
244 245 data.siteStyle,
245 246 data.siteImages,
@@ -266,14 +267,11 @@
266 267 checkIn({ stage: 'set_general', siteProfile, sitePlugins, siteStyle });
267 268
268 269 const { title } = siteProfile;
269 270 // translators: this is for a action log UI. Keep it short
270 - addStatusMessage(__('Adding admin configurations', 'extendify-local'));
271 - // update permalinks
271 + addStatusMessage(launchStrings().statusAdmin);
272 272 await updateOption('permalink_structure', '/%postname%/');
273 - // make sure consent is set
274 273 setUserGaveConsent(true);
275 - // Update title
276 274 if (title) await updateOption('blogname', title);
277 275 },
278 276 );
279 277
@@ -319,16 +317,23 @@
319 317 if (homeStretch.current) return;
320 318 homeStretch.current = true;
321 319 (async () => {
322 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 + );
323 332
324 333 // Guarantee plugins are active before the pattern imports below rely on them.
325 - await verifyPluginsActive(
326 - (sitePlugins ?? []).map(({ wordpressSlug }) => wordpressSlug),
327 - { installedSlugs: installedPluginsSlugs },
328 - );
334 + await ensurePluginsActive(intendedPlugins);
329 335
330 - // Do they need an imprint page?
331 336 const needsImprint = Array.isArray(showImprint)
332 337 ? showImprint.includes(wpLanguage ?? '') && category === 'Business'
333 338 : false;
334 339
@@ -337,9 +342,9 @@
337 342 let variation = siteStyle?.variation;
338 343 if (customFonts?.length) {
339 344 checkIn({ stage: 'install_fonts' });
340 345 // translators: this is for a action log UI. Keep it short
341 - addStatusMessage(__('Installing fonts locally', 'extendify-local'));
346 + addStatusMessage(launchStrings().statusFonts);
342 347 const installed = await installFontFamilies(customFonts).catch(
343 348 () => [],
344 349 );
345 350 variation = mergeFontsIntoVariation(siteStyle.variation, installed);
@@ -346,19 +351,15 @@
346 351 }
347 352
348 353 if (siteStyle?.vibe && siteStyle.vibe !== 'natural-1') {
349 354 // translators: vibe in this context is a noun - the feeling of their site design.
350 - addStatusMessage(__('Setting the website style', 'extendify-local'));
355 + addStatusMessage(launchStrings().statusSiteStyle);
351 356 checkIn({ stage: 'compute_vibe' });
352 - const vibeBlocks = await computeVibeAdjustedBlocks(
357 + const vibe = await computeVibeAdjustments(
353 358 siteStyle.vibe,
359 + variation,
354 360 ).catch(() => null);
355 - if (vibeBlocks) {
356 - variation = {
357 - ...variation,
358 - styles: { ...variation.styles, blocks: vibeBlocks },
359 - };
360 - }
361 + if (vibe) variation = { ...variation, ...vibe };
361 362 }
362 363
363 364 checkIn({ stage: 'set_vibe' });
364 365 await updateVariation(variation);
@@ -363,9 +364,9 @@
363 364 checkIn({ stage: 'set_vibe' });
364 365 await updateVariation(variation);
365 366
366 367 // navigation menu
367 - addStatusMessage(__('Working on the navigation', 'extendify-local'));
368 + addStatusMessage(launchStrings().statusNavigation);
368 369 const { id: headerNavId } = await createNavigation({
369 370 title: __('Header Navigation', 'extendify-local'),
370 371 slug: 'site-navigation',
371 372 });
@@ -375,9 +376,9 @@
375 376 headerCode = injectNavExtras(headerCode, launchDecisions);
376 377 // remove the header navigation from the landing page
377 378 if (objective === 'landing-page') {
378 379 // translators: this is for a action log UI. Keep it short
379 - addStatusMessage(__('Perfecting a landing page', 'extendify-local'));
380 + addStatusMessage(launchStrings().statusLanding);
380 381 const social =
381 382 /<!--\s*wp:social-links\b[^>]*>.*?<!--\s*\/wp:social-links\s*-->/gis;
382 383 headerCode = headerCode
383 384 .replace(/<!--\s*wp:navigation\b[^>]*.*\/-->/gis, '')
@@ -394,8 +395,15 @@
394 395
395 396 // footer
396 397 let footerNavId = null;
397 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 + }
398 406 if (needsImprint) {
399 407 const nav = await createNavigation({
400 408 title: __('Footer Navigation', 'extendify-local'),
401 409 slug: 'footer-navigation',
@@ -402,14 +410,15 @@
402 410 });
403 411 footerNavId = nav.id;
404 412 footerCode = updateNavAttributes(footerCode, { ref: footerNavId });
405 413 }
414 + footerCode = applySocialProfiles(footerCode, siteProfile.socialProfiles);
406 415 checkIn({ stage: 'set_footer' });
407 416 await updateTemplatePart('extendable/footer', footerCode);
408 417
409 418 // pages
410 419 // translators: this is for a action log UI. Keep it short
411 - addStatusMessage(__('Creating pages', 'extendify-local'));
420 + addStatusMessage(launchStrings().statusCreatingPages);
412 421 const pagesToCreate = getPagesToCreate(data);
413 422 const titlePattern = pages?.[0]?.patterns?.find((p) =>
414 423 p.patternTypes?.includes('page-title'),
415 424 );
@@ -418,9 +427,9 @@
418 427 await updatePageTitlePattern(titlePattern.code);
419 428 }
420 429
421 430 const activePlugins = await getActivePlugins();
422 - // 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.
423 432 const reservedSlugs = new Set(
424 433 PLUGIN_OWNED_PAGES.filter(
425 434 ({ plugin }) =>
426 435 sitePlugins.some((p) => p.wordpressSlug === plugin) ||
@@ -451,25 +460,36 @@
451 460 p.patternTypes?.includes('hero-header'),
452 461 );
453 462 const pMatch = heroPattern?.code?.match(/<p[^>]*>([\s\S]*?)<\/p>/);
454 463 const heroDesc = pMatch?.[1]?.replace(/<[^>]+>/g, '').trim();
455 - 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 + }
456 471
457 - const createdPagesWP = await createWpPages(customPages);
472 + const createdPagesWP = await createWpPages(customPages, {
473 + skipSectionIds: isSinglePageDesign,
474 + });
458 475 // Aux pages
459 - const hasBlogPattern = home?.patterns?.some((pattern) =>
476 + const blogPattern = home?.patterns?.find((pattern) =>
460 477 pattern.patternTypes.includes('blog-section'),
461 478 );
462 - if (objective === 'blog' || hasBlogPattern) {
479 + if (siteProfile.blog || blogPattern) {
463 480 checkIn({ stage: 'create_blog_sample_data' });
464 481 // translators: this is for a action log UI. Keep it short
465 - addStatusMessage(__('Creating blog sample data', 'extendify-local'));
466 - await createBlogSampleData({ aiBlogTitles }, siteImages);
482 + addStatusMessage(launchStrings().statusBlog);
483 + await createBlogSampleData(
484 + { aiBlogTitles },
485 + imageUrls,
486 + blogPattern?.blogImages,
487 + );
467 488 }
468 - // If we have site images then set up the hello world image
469 - if (siteImages?.length) {
489 + if (imageUrls.length) {
470 490 checkIn({ stage: 'set_hello_world_image' });
471 - await setHelloWorldFeaturedImage(siteImages);
491 + await setHelloWorldFeaturedImage(imageUrls);
472 492 }
473 493
474 494 let imprint = {};
475 495 if (needsImprint) {
@@ -481,9 +501,9 @@
481 501 if (alreadyActive(activePlugins, 'woocommerce')) {
482 502 checkIn({ stage: 'import_woocommerce_products' });
483 503 addStatusMessage(
484 504 // translators: this is for a action log UI. Keep it short
485 - __('Setting up your online store', 'extendify-local'),
505 + launchStrings().statusStore,
486 506 );
487 507 await apiFetchWithTimeout({
488 508 path: '/extendify/v1/auto-launch/import-woocommerce',
489 509 }).catch(() => null);
@@ -498,25 +518,35 @@
498 518 link: `${homeUrl}/events`,
499 519 });
500 520 }
501 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 +
502 526 // Adding pages to the nav
503 527 checkIn({ stage: 'set_page_links' });
504 - const linksResult =
505 - structure === 'single-page'
506 - ? await updateSinglePageLinksToSections(
507 - createdPagesWP,
508 - customPages,
509 - {
510 - objective,
511 - activePlugins,
512 - landingPageCTALink: siteProfile.landingPageCTALink,
513 - },
514 - headerCode,
515 - )
516 - : 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 + }
517 544 const pagesWithLinksUpdated = linksResult.wpPages;
518 545 headerCode = linksResult.headerCode;
546 + if (alreadyActive(activePlugins, 'woocommerce')) {
547 + headerCode = injectWooCommerceIcons(headerCode);
548 + }
519 549 await updateTemplatePart('extendable/header', headerCode);
520 550 const footerNavPages = [];
521 551 if (footerNavId && imprint?.title) {
522 552 const { originalSlug, title } = imprint;
@@ -530,9 +560,20 @@
530 560
531 561 checkIn({ stage: 'set_navigation_links' });
532 562 if (objective !== 'landing-page') {
533 563 const orderedSlugs = designBuild?.pages?.map((p) => p.slug) ?? [];
534 - 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') {
535 576 await addSectionLinksToNav(
536 577 headerNavId,
537 578 home?.patterns,
538 579 pluginPages,
@@ -564,14 +605,18 @@
564 605 await prefetchAssistData();
565 606 checkIn({ stage: 'final_steps' });
566 607 await setThemeRenderingMode('template-locked');
567 608 await postLaunchFunctions();
568 - if (siteImages?.length) {
609 + if (imageUrls.length) {
569 610 await storeSiteImages(siteImages).catch(() => null);
570 611 }
571 612 // translators: this is for a action log UI. Keep it short
572 - addStatusMessage(__('All done!', 'extendify-local'));
573 - 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 + ]);
574 619 setWarnOnReload(false);
575 620 setDone(true);
576 621 })().catch((error) => {
577 622 console.error(error);
@@ -581,14 +626,9 @@
581 626 });
582 627 // if we error here we can try again by resetting the home stretch and stalling again to refetch data
583 628 homeStretch.current = false;
584 629 needToStall(true);
585 - setErrorMessage(
586 - __(
587 - 'Something went wrong during the final steps. We will try again but you may need to refresh the page.',
588 - 'extendify-local',
589 - ),
590 - );
630 + setErrorMessage(launchStrings().errorFinalSteps);
591 631 });
592 632 }, [data, needToStall, setUserGaveConsent]);
593 633
594 634 return { done };
@@ -612,12 +652,7 @@
612 652 useEffect(() => {
613 653 if (!error || needToStall()) return;
614 654 console.error(error);
615 655 digest({ error, details: { source: 'auto-launch', caller: 'run-step' } });
616 - setErrorMessage(
617 - __(
618 - 'Having some trouble with this step. Trying again...',
619 - 'extendify-local',
620 - ),
621 - );
656 + setErrorMessage(launchStrings().errorStep);
622 657 }, [error, setErrorMessage, needToStall]);
623 658 };