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
extendify / src / AutoLaunch / functions / pages.js

pages.js in Extendify 3.2.1, at src/AutoLaunch/functions/pages.js

263 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { importImage, updateOption } from '@auto-launch/functions/wp';
2 import { launchStrings } from '@auto-launch/strings';
3 import { PATTERNS_HOST } from '@constants';
4 import { reqDataBasics } from '@shared/lib/data';
5 import { pageNames } from '@shared/lib/pages';
6 import apiFetch from '@wordpress/api-fetch';
7 import { createBlock, parse, serialize } from '@wordpress/blocks';
8 import { __ } from '@wordpress/i18n';
9 import { setStatus } from './helpers';
10
11 // Slugs that plugins own — skip creating design-build pages for these.
12 export const PLUGIN_OWNED_PAGES = [
13 { slug: 'shop', plugin: 'woocommerce' },
14 { slug: 'events', plugin: 'the-events-calendar' },
15 ];
16
17 export const isBlogPage = ({ originalSlug }) => originalSlug === 'blog';
18
19 export const getPagesToCreate = (data) => {
20 const { home, pages, siteProfile } = data;
21 const homepage = {
22 id: 'home',
23 name: pageNames.home.title,
24 slug: 'home',
25 patterns: home.patterns,
26 };
27 const hasBlog = pages.some(({ slug }) => slug === 'blog');
28 const blogPage =
29 siteProfile.blog && !hasBlog
30 ? {
31 name: pageNames.blog.title,
32 id: 'blog',
33 patterns: [],
34 slug: 'blog',
35 }
36 : null;
37
38 // Remove the page title pattern from all pages
39 const patternHasTitle = (pattern) =>
40 !pattern.patternTypes?.includes('page-title');
41 const p = pages.map((page) => ({
42 ...page,
43 patterns: page.patterns.filter(patternHasTitle),
44 }));
45 return [homepage, ...p, blogPage].filter(Boolean);
46 };
47
48 // Replace the page-title pattern in “page-with-title” template with the incoming page-title pattern
49 export const updatePageTitlePattern = async (pageTitlePattern) => {
50 const updatedPattern = transformHeadingToPostTitle(pageTitlePattern);
51
52 const templateContent = `
53 <!-- wp:template-part {"slug":"header","tagName":"header"} /-->
54 <!-- wp:group {"tagName":"main","style":{"spacing":{"margin":{"top":"0px","bottom":"0px"},"blockGap":"0"}}} -->
55 <main class="wp-block-group" style="margin-top:0px;margin-bottom:0px">
56 ${updatedPattern}
57 <!-- wp:post-content {"layout":{"type":"constrained"}} /-->
58 </main>
59 <!-- /wp:group -->
60 <!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
61 `;
62
63 try {
64 await apiFetch({
65 path: '/wp/v2/templates/extendable/page-with-title',
66 method: 'POST',
67 data: {
68 slug: 'page-with-title',
69 theme: 'extendable',
70 type: 'wp_template',
71 status: 'publish',
72 description: __('Added by Launch', 'extendify-local'),
73 content: templateContent,
74 },
75 });
76 } catch {
77 // do nothing
78 }
79 };
80
81 // finds the core/heading in the pattern and replaces it with a core/post-title block
82 const transformHeadingToPostTitle = (rawHTML) => {
83 let done = false;
84
85 const walk = (block) => {
86 if (done) return block;
87
88 if (block.name === 'core/heading') {
89 done = true;
90 const attrs = {
91 level: block.attributes.level,
92 textAlign: block.attributes.textAlign,
93 textColor: block.attributes.textColor,
94 backgroundColor: block.attributes.backgroundColor,
95 isLink: block.attributes.isLink,
96 linkTarget: block.attributes.linkTarget,
97 rel: block.attributes.rel,
98 };
99
100 if (block.attributes.fontSize) {
101 attrs.fontSize = block.attributes.fontSize;
102 }
103
104 const customSize = block.attributes.style?.typography?.fontSize;
105 const linkStyle = block.attributes.style?.elements?.link;
106
107 if (customSize || linkStyle) {
108 attrs.style = {};
109
110 if (customSize) {
111 attrs.style.typography = { fontSize: customSize };
112 }
113 if (linkStyle) {
114 attrs.style.elements = { link: linkStyle };
115 }
116 }
117
118 return createBlock('core/post-title', attrs);
119 }
120
121 if (block.innerBlocks?.length) {
122 block.innerBlocks = block.innerBlocks.map(walk);
123 }
124 return block;
125 };
126
127 return serialize(parse(rawHTML).map(walk));
128 };
129
130 // navSlug: the design menu entry this section was picked for (applyDesignBuildNav).
131 export const sectionSlug = (pattern) =>
132 pattern.navSlug ??
133 Object.values(pageNames).find(({ alias }) =>
134 alias.includes(pattern.patternTypes?.[0]),
135 )?.slug;
136
137 export const createWpPages = async (
138 pagesRaw,
139 { skipSectionIds = false } = {},
140 ) => {
141 const pages = [];
142
143 for (const page of pagesRaw) {
144 const content = [];
145 const seenPatternTypes = new Set();
146
147 setStatus(launchStrings().statusAddingPage(page.name));
148
149 for (const [_, pattern] of page.patterns.entries()) {
150 const code = pattern.code;
151 const slug = sectionSlug(pattern);
152
153 if (skipSectionIds || seenPatternTypes.has(slug) || !slug) {
154 content.push(code);
155 continue;
156 }
157
158 seenPatternTypes.add(slug);
159 content.push(addIdAttributeToBlock(code, slug));
160 }
161
162 const pageData = {
163 title: page.name,
164 status: 'publish',
165 content: content.join(''),
166 template: page.slug === 'home' ? 'no-title' : 'page-with-title',
167 meta: { made_with_extendify_launch: true },
168 };
169
170 let newPage;
171 try {
172 newPage = await createPage(pageData);
173 } catch (_e) {
174 pageData.template = 'no-title';
175 newPage = await createPage(pageData);
176 }
177
178 pages.push({ ...newPage, originalSlug: page.slug });
179 }
180
181 const maybeHome = pages.find(({ originalSlug }) => originalSlug === 'home');
182 if (maybeHome) {
183 await updateOption('show_on_front', 'page');
184 await updateOption('page_on_front', maybeHome.id);
185 }
186
187 const maybeBlog = pages.find(isBlogPage);
188 if (maybeBlog) {
189 await updateOption('page_for_posts', maybeBlog.id);
190 }
191
192 return pages;
193 };
194
195 export const addIdAttributeToBlock = (blockCode, id) =>
196 blockCode.replace(
197 /(<div\s[^>]*class="[^"]*\bwp-block-group\b[^"]*")/,
198 `$1 id="${id}"`,
199 );
200
201 export const createPage = (data) =>
202 apiFetch({ path: 'wp/v2/pages', data, method: 'POST' });
203 export const updatePage = (data) =>
204 apiFetch({ path: `wp/v2/pages/${data.id}`, data, method: 'POST' });
205
206 export const setHelloWorldFeaturedImage = async (imageUrls) => {
207 try {
208 const translatedSlug = window.extLaunchData?.helloWorldPostSlug;
209 let posts = await apiFetch({ path: `wp/v2/posts?slug=${translatedSlug}` });
210 if (!posts.length) {
211 posts = await apiFetch({ path: 'wp/v2/posts?slug=hello-world' });
212 }
213 if (!posts.length) return;
214 const helloPost = posts[0];
215 if (helloPost.featured_media && parseInt(helloPost.featured_media, 10) > 0)
216 return;
217 if (!Array.isArray(imageUrls) || imageUrls.length === 0) {
218 console.error('No image URLs provided.');
219 return;
220 }
221 const lastImageUrl = imageUrls[imageUrls.length - 1];
222 const mediaResponse = await importImage(lastImageUrl, {
223 alt: __('Hello World Featured Image', 'extendify-local'),
224 filename: 'hello-world-featured.jpg',
225 caption: '',
226 });
227 if (!mediaResponse || !mediaResponse.id) {
228 console.error('Image upload failed.');
229 return;
230 }
231 await apiFetch({
232 path: `wp/v2/posts/${helloPost.id}`,
233 method: 'POST',
234 data: { featured_media: mediaResponse.id },
235 });
236 } catch (error) {
237 console.error('Failed to set Hello World featured image:', error);
238 }
239 };
240
241 export const addImprintPage = async ({ siteStyle }) => {
242 try {
243 // Get the imprint page template
244 const imprintPage = await getImprintPageTemplate({ siteStyle });
245 // Create the page in WordPress with the fetched template
246 const [createdImprintPage] = await createWpPages([imprintPage]);
247 return createdImprintPage;
248 } catch (error) {
249 console.error('Failed to add imprint page:', error);
250 return null;
251 }
252 };
253
254 export const getImprintPageTemplate = async ({ siteStyle }) => {
255 const res = await fetch(`${PATTERNS_HOST}/api/page-imprint`, {
256 method: 'POST',
257 headers: { 'Content-Type': 'application/json' },
258 body: JSON.stringify({ ...reqDataBasics, siteStyle }),
259 });
260 const response = await res.json();
261 return { ...response.template };
262 };
263