PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
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.1.0, at src/AutoLaunch/functions/pages.js

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