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

261 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 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, { stickyNav }) => {
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: stickyNav
159 ? 'no-title-sticky-header'
160 : page.slug === 'home'
161 ? 'no-title'
162 : 'page-with-title',
163 meta: { made_with_extendify_launch: true },
164 };
165
166 let newPage;
167 try {
168 newPage = await createPage(pageData);
169 } catch (_e) {
170 pageData.template = 'no-title';
171 newPage = await createPage(pageData);
172 }
173
174 pages.push({ ...newPage, originalSlug: page.slug });
175 }
176
177 const maybeHome = pages.find(({ originalSlug }) => originalSlug === 'home');
178 if (maybeHome) {
179 await updateOption('show_on_front', 'page');
180 await updateOption('page_on_front', maybeHome.id);
181 }
182
183 const maybeBlog = pages.find(({ originalSlug }) => originalSlug === 'blog');
184 if (maybeBlog) {
185 await updateOption('page_for_posts', maybeBlog.id);
186 }
187
188 return pages;
189 };
190
191 export const addIdAttributeToBlock = (blockCode, id) =>
192 blockCode.replace(
193 /(<div\s[^>]*class="[^"]*\bwp-block-group\b[^"]*")/,
194 `$1 id="${id}"`,
195 );
196
197 export const createPage = (data) =>
198 apiFetch({ path: 'wp/v2/pages', data, method: 'POST' });
199 export const updatePage = (data) =>
200 apiFetch({ path: `wp/v2/pages/${data.id}`, data, method: 'POST' });
201
202 export const setHelloWorldFeaturedImage = async (imageUrls) => {
203 try {
204 const translatedSlug = window.extLaunchData?.helloWorldPostSlug;
205 let posts = await apiFetch({ path: `wp/v2/posts?slug=${translatedSlug}` });
206 if (!posts.length) {
207 posts = await apiFetch({ path: 'wp/v2/posts?slug=hello-world' });
208 }
209 if (!posts.length) return;
210 const helloPost = posts[0];
211 if (helloPost.featured_media && parseInt(helloPost.featured_media, 10) > 0)
212 return;
213 if (!Array.isArray(imageUrls) || imageUrls.length === 0) {
214 console.error('No image URLs provided.');
215 return;
216 }
217 const lastImageUrl = imageUrls[imageUrls.length - 1];
218 const mediaResponse = await importImage(lastImageUrl, {
219 alt: __('Hello World Featured Image', 'extendify-local'),
220 filename: 'hello-world-featured.jpg',
221 caption: '',
222 });
223 if (!mediaResponse || !mediaResponse.id) {
224 console.error('Image upload failed.');
225 return;
226 }
227 await apiFetch({
228 path: `wp/v2/posts/${helloPost.id}`,
229 method: 'POST',
230 data: { featured_media: mediaResponse.id },
231 });
232 } catch (error) {
233 console.error('Failed to set Hello World featured image:', error);
234 }
235 };
236
237 export const addImprintPage = async ({ siteStyle }) => {
238 try {
239 // Get the imprint page template
240 const imprintPage = await getImprintPageTemplate({ siteStyle });
241 // Create the page in WordPress with the fetched template
242 const [createdImprintPage] = await createWpPages([imprintPage], {
243 stickyNav: false,
244 });
245 return createdImprintPage;
246 } catch (error) {
247 console.error('Failed to add imprint page:', error);
248 return null;
249 }
250 };
251
252 export const getImprintPageTemplate = async ({ siteStyle }) => {
253 const res = await fetch(`${PATTERNS_HOST}/api/page-imprint`, {
254 method: 'POST',
255 headers: { 'Content-Type': 'application/json' },
256 body: JSON.stringify({ ...reqDataBasics, siteStyle }),
257 });
258 const response = await res.json();
259 return { ...response.template };
260 };
261