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

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