PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
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 0.7.0 All 126 releases
extendify / src / AutoLaunch / functions / pages.js

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

262 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 { 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 // navSlug: the design menu entry this section was picked for (applyDesignBuildNav).
130 export const sectionSlug = (pattern) =>
131 pattern.navSlug ??
132 Object.values(pageNames).find(({ alias }) =>
133 alias.includes(pattern.patternTypes?.[0]),
134 )?.slug;
135
136 export const createWpPages = async (
137 pagesRaw,
138 { skipSectionIds = false } = {},
139 ) => {
140 const pages = [];
141
142 for (const page of pagesRaw) {
143 const content = [];
144 const seenPatternTypes = new Set();
145
146 setStatus(sprintf(__('Adding page: %s', 'extendify-local'), page.name));
147
148 for (const [_, pattern] of page.patterns.entries()) {
149 const code = pattern.code;
150 const slug = sectionSlug(pattern);
151
152 if (skipSectionIds || seenPatternTypes.has(slug) || !slug) {
153 content.push(code);
154 continue;
155 }
156
157 seenPatternTypes.add(slug);
158 content.push(addIdAttributeToBlock(code, slug));
159 }
160
161 const pageData = {
162 title: page.name,
163 status: 'publish',
164 content: content.join(''),
165 template: page.slug === 'home' ? 'no-title' : 'page-with-title',
166 meta: { made_with_extendify_launch: true },
167 };
168
169 let newPage;
170 try {
171 newPage = await createPage(pageData);
172 } catch (_e) {
173 pageData.template = 'no-title';
174 newPage = await createPage(pageData);
175 }
176
177 pages.push({ ...newPage, originalSlug: page.slug });
178 }
179
180 const maybeHome = pages.find(({ originalSlug }) => originalSlug === 'home');
181 if (maybeHome) {
182 await updateOption('show_on_front', 'page');
183 await updateOption('page_on_front', maybeHome.id);
184 }
185
186 const maybeBlog = pages.find(isBlogPage);
187 if (maybeBlog) {
188 await updateOption('page_for_posts', maybeBlog.id);
189 }
190
191 return pages;
192 };
193
194 export const addIdAttributeToBlock = (blockCode, id) =>
195 blockCode.replace(
196 /(<div\s[^>]*class="[^"]*\bwp-block-group\b[^"]*")/,
197 `$1 id="${id}"`,
198 );
199
200 export const createPage = (data) =>
201 apiFetch({ path: 'wp/v2/pages', data, method: 'POST' });
202 export const updatePage = (data) =>
203 apiFetch({ path: `wp/v2/pages/${data.id}`, data, method: 'POST' });
204
205 export const setHelloWorldFeaturedImage = async (imageUrls) => {
206 try {
207 const translatedSlug = window.extLaunchData?.helloWorldPostSlug;
208 let posts = await apiFetch({ path: `wp/v2/posts?slug=${translatedSlug}` });
209 if (!posts.length) {
210 posts = await apiFetch({ path: 'wp/v2/posts?slug=hello-world' });
211 }
212 if (!posts.length) return;
213 const helloPost = posts[0];
214 if (helloPost.featured_media && parseInt(helloPost.featured_media, 10) > 0)
215 return;
216 if (!Array.isArray(imageUrls) || imageUrls.length === 0) {
217 console.error('No image URLs provided.');
218 return;
219 }
220 const lastImageUrl = imageUrls[imageUrls.length - 1];
221 const mediaResponse = await importImage(lastImageUrl, {
222 alt: __('Hello World Featured Image', 'extendify-local'),
223 filename: 'hello-world-featured.jpg',
224 caption: '',
225 });
226 if (!mediaResponse || !mediaResponse.id) {
227 console.error('Image upload failed.');
228 return;
229 }
230 await apiFetch({
231 path: `wp/v2/posts/${helloPost.id}`,
232 method: 'POST',
233 data: { featured_media: mediaResponse.id },
234 });
235 } catch (error) {
236 console.error('Failed to set Hello World featured image:', error);
237 }
238 };
239
240 export const addImprintPage = async ({ siteStyle }) => {
241 try {
242 // Get the imprint page template
243 const imprintPage = await getImprintPageTemplate({ siteStyle });
244 // Create the page in WordPress with the fetched template
245 const [createdImprintPage] = await createWpPages([imprintPage]);
246 return createdImprintPage;
247 } catch (error) {
248 console.error('Failed to add imprint page:', error);
249 return null;
250 }
251 };
252
253 export const getImprintPageTemplate = async ({ siteStyle }) => {
254 const res = await fetch(`${PATTERNS_HOST}/api/page-imprint`, {
255 method: 'POST',
256 headers: { 'Content-Type': 'application/json' },
257 body: JSON.stringify({ ...reqDataBasics, siteStyle }),
258 });
259 const response = await res.json();
260 return { ...response.template };
261 };
262