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 / links.js

links.js in Extendify 3.0.6, at src/AutoLaunch/functions/links.js

238 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { updatePage } from '@auto-launch/functions/pages';
2 import { alreadyActive } from '@auto-launch/functions/plugins';
3 import { getOption, getPageById } from '@auto-launch/functions/wp';
4 import { AI_HOST } from '@constants';
5 import { reqDataBasics } from '@shared/lib/data';
6 import { pageNames } from '@shared/lib/pages';
7 import { getBlockContent, parse } from '@wordpress/blocks';
8
9 const { homeUrl } = window.extSharedData;
10 const buttonRegex = /href="(#extendify-[\w-]+)"/gi;
11 const pagesWithButtons = (p) => p?.content?.raw?.match(buttonRegex);
12
13 export const updateButtonLinks = async (wpPages, pluginPages) => {
14 // Fetch active plugins after installing plugins
15 const contactPageSlug = wpPages.find(({ originalSlug }) =>
16 originalSlug?.startsWith('contact'),
17 )?.slug;
18
19 const patternsToProcess = wpPages
20 // Look for pages with links
21 .filter(pagesWithButtons)
22 .map(({ content }) => {
23 // 1. Convert to individual blocks
24 return (
25 parse(content.raw || '')
26 // 2. Convert back to HTML
27 .map((b) => getBlockContent(b))
28 // 3. Filter only blocks with links
29 .filter((b) => b.match(buttonRegex))
30 .join('')
31 // TODO: Filter out patterns from pages that have identical buttons?
32 );
33 });
34
35 // Collect the page slugs to share with the server
36 const availablePages = wpPages
37 .concat(pluginPages)
38 .filter(({ slug }) => !slug.startsWith('home'))
39 .map(({ slug }) => `/${slug}`);
40
41 // Fetch the links from the server. If a request fails, ignore it.
42 const suggestedLinks = (
43 await Promise.allSettled(
44 patternsToProcess.map(
45 (pageContent) =>
46 getLinkSuggestions({ pageContent, availablePages }) || {},
47 ),
48 )
49 )
50 .filter((r) => r.status === 'fulfilled')
51 .map((r) => r.value?.suggestedLinks || [])
52 // Combine all suggested links
53 .reduce((acc, link) => {
54 for (const key in link) {
55 acc[key] = link[key];
56 }
57 return acc;
58 }, {});
59
60 const linkKeys = Object.keys(suggestedLinks)
61 .filter((k) =>
62 // Remove links sent back that aren't in the availablePages
63 availablePages.includes(`/${suggestedLinks[k].replace(/^\//, '')}`),
64 )
65 .map((v) => `\\"${v}\\"`)
66 .join('|');
67
68 // Replace links and update the pages. Failed pages get ignored.
69 const newPages = (
70 await Promise.allSettled(
71 wpPages.filter(pagesWithButtons).map((p) => {
72 // We want to match \"extendify-cta\" exactly inside the href
73 // So we need to look for the quotes, then replace with the quotes
74 const content = linkKeys
75 ? p.content.raw.replace(new RegExp(linkKeys, 'g'), (match) => {
76 if (!match || suggestedLinks.length === 0) return '';
77
78 const link = suggestedLinks[match.replace(/"/g, '')];
79 // if the link points to the current page or '/'
80 // we should link to the contact page (or default to '/')
81 if ([p.slug, `/${p.slug}`, '/'].includes(link))
82 return `"${homeUrl}/${contactPageSlug ?? ''}"`;
83
84 // The server once sent back slugs without the /
85 // so we need to check
86 return `"${homeUrl}/${link.replace(/^\//, '')}"`;
87 })
88 : p.content.raw.replace(new RegExp(buttonRegex, 'g'), (match) => {
89 return match ? 'href="#"' : '';
90 });
91 return updatePage({ id: p.id, content });
92 }),
93 )
94 )
95 .filter((r) => r.status === 'fulfilled')
96 .map((r) => r.value);
97
98 return (
99 wpPages
100 // Add the new pages into the wpPages array
101 .map((p) => newPages.find(({ id }) => id === p.id) || p)
102 // Also include the originalSlug from wpPages
103 .map((p) => {
104 const { originalSlug } = wpPages.find(({ id }) => id === p.id) || {};
105 return { ...p, originalSlug };
106 })
107 );
108 };
109
110 export const updateSinglePageLinksToSections = async (
111 wpPages,
112 pages,
113 options,
114 ) => {
115 let homePageContent = wpPages?.[0]?.content?.raw;
116 if (!homePageContent) return wpPages;
117 const { objective, activePlugins, landingPageCTALink } = options;
118
119 /**
120 * Special case handling for landing page sites.
121 *
122 * For landing pages, all internal navigation links are either replaced with
123 * a single, provided CTA link, or set to '#' if no CTA link is provided.
124 * This function updates the home page content and returns the updated pages array.
125 */
126 if (objective === 'landing-page') {
127 const ctaHref =
128 landingPageCTALink && typeof landingPageCTALink === 'string'
129 ? landingPageCTALink
130 : '#';
131 wpPages[0] = updatePage({
132 id: wpPages[0].id,
133 content: homePageContent.replaceAll(
134 /href="(#extendify-[\w|-]+)"/gi,
135 `href="${ctaHref}"`,
136 ),
137 });
138
139 return wpPages;
140 }
141
142 // get all the patterns that we have in the home page
143 const patternTypes = pages?.[0]?.patterns
144 ?.map((pattern) => pattern?.patternTypes?.[0])
145 ?.filter((patternType) => patternType !== 'hero-header')
146 ?.map((patternType) => {
147 const { slug } =
148 Object.values(pageNames).find(({ alias }) =>
149 alias.includes(patternType),
150 ) || {};
151 return slug;
152 })
153 ?.filter(Boolean)
154 ?.flat();
155
156 const createdPages =
157 pages
158 ?.filter((page) => page.slug !== 'home')
159 ?.map((page) => page.slug)
160 ?.filter(Boolean) ?? [];
161
162 // get the active plugins
163 const pluginPages = [];
164
165 // check if woocommerce is active, if so we add it to the list of pages
166 if (alreadyActive(activePlugins, 'woocommerce')) {
167 const page = await getPageById(
168 await getOption('woocommerce_shop_page_id'),
169 ).catch(() => null);
170
171 page?.slug && pluginPages.push(page.slug);
172 }
173
174 // check if events calendar is active, if so we add it to the list of pages
175 if (alreadyActive(activePlugins, 'the-events-calendar')) {
176 pluginPages.push('events');
177 }
178
179 const allAvailablePages = (patternTypes ?? []).concat(pluginPages);
180 if (!allAvailablePages.length) {
181 wpPages[0] = updatePage({
182 id: wpPages[0].id,
183 content: homePageContent.replaceAll(
184 /href="(#extendify-[\w|-]+)"/gi,
185 'href="#"',
186 ),
187 });
188 return wpPages;
189 }
190
191 // get the suggested links from the AI and send both the patterns and the plugin pages.
192 const { suggestedLinks } =
193 (await getLinkSuggestions({
194 pageContent: homePageContent,
195 availablePages: allAvailablePages,
196 })) || {};
197
198 // replace the links
199 homePageContent = Object.keys(suggestedLinks ?? {}).reduce((content, key) => {
200 const slug = suggestedLinks[key];
201
202 if (!slug) return content;
203
204 const newLink = pluginPages.concat(createdPages).includes(slug)
205 ? `"${homeUrl}/${slug}"`
206 : `"${homeUrl}/#${slug}"`;
207
208 return content.replaceAll(`"${key}"`, newLink);
209 }, homePageContent);
210
211 // Update the first page by replacing the buttons urls with the new slug
212 wpPages[0] = updatePage({
213 id: wpPages[0].id,
214 content: homePageContent,
215 });
216
217 return wpPages;
218 };
219
220 const getLinkSuggestions = async ({ pageContent, availablePages }) => {
221 try {
222 const response = await fetch(`${AI_HOST}/api/link-pages`, {
223 method: 'POST',
224 headers: { 'Content-Type': 'application/json' },
225 body: JSON.stringify({
226 ...reqDataBasics,
227 pageContent,
228 availablePages,
229 }),
230 });
231 if (!response.ok) throw new Error('Bad response from server');
232 return await response.json();
233 } catch (_error) {
234 // fail gracefully
235 return {};
236 }
237 };
238