PluginProbe
Extendify / 3.1.4
Extendify v3.1.4
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 / Launch / lib / linkPages.js

linkPages.js in Extendify 3.1.4, at src/Launch/lib/linkPages.js

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