PluginProbe
Extendify / 2.2.0
Extendify v2.2.0
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 2.2.0, at src/Launch/lib/linkPages.js

214 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { rawHandler, getBlockContent } from '@wordpress/blocks';
2 import { prependHTTPS } from '@wordpress/url';
3 import { pageNames } from '@shared/lib/pages';
4 import { wasPluginInstalled } from '@shared/lib/utils';
5 import { getLinkSuggestions } from '@launch/api/DataApi';
6 import {
7 getActivePlugins,
8 getOption,
9 getPageById,
10 updatePage,
11 } from '@launch/api/WPApi';
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 let { 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) => ({ ...acc, ...link }), {});
69
70 const linkKeys = Object.keys(suggestedLinks)
71 .filter((k) =>
72 // Remove links sent back that aren't in the availablePages
73 availablePages.includes(`/${suggestedLinks[k].replace(/^\//, '')}`),
74 )
75 .map((v) => `\\"${v}\\"`)
76 .join('|');
77
78 // Replace links and update the pages. Failed pages get ignored.
79 const newPages = (
80 await Promise.allSettled(
81 wpPages.filter(pagesWithButtons).map((p) => {
82 // We want to match \"extendify-cta\" exactly inside the href
83 // So we need to look for the quotes, then replace with the quotes
84 const content = linkKeys
85 ? p.content.raw.replace(new RegExp(linkKeys, 'g'), (match) => {
86 if (!match || suggestedLinks.length === 0) return '';
87
88 const link = suggestedLinks[match.replace(/"/g, '')];
89 // if the link points to the current page or '/'
90 // we should link to the contact page (or default to '/')
91 if ([p.slug, `/${p.slug}`, '/'].includes(link))
92 return `"${homeUrl}/${contactPageSlug ?? ''}"`;
93
94 // The server once sent back slugs without the /
95 // so we need to check
96 return `"${homeUrl}/${link.replace(/^\//, '')}"`;
97 })
98 : p.content.raw.replace(new RegExp(buttonRegex, 'g'), (match) => {
99 return match ? 'href="#"' : '';
100 });
101 return updatePage({ id: p.id, content });
102 }),
103 )
104 )
105 .filter((r) => r.status === 'fulfilled')
106 .map((r) => r.value);
107
108 return (
109 wpPages
110 // Add the new pages into the wpPages array
111 .map((p) => newPages.find(({ id }) => id === p.id) || p)
112 // Also include the originalSlug from wpPages
113 .map((p) => {
114 const { originalSlug } = wpPages.find(({ id }) => id === p.id) || {};
115 return { ...p, originalSlug };
116 })
117 );
118 };
119
120 export const updateSinglePageLinksToSections = async (
121 wpPages,
122 pages,
123 options = {},
124 ) => {
125 let homePageContent = wpPages?.[0]?.content?.raw;
126 if (!homePageContent) return wpPages;
127
128 /**
129 * Special case handling for landing page sites.
130 *
131 * For landing pages, all internal navigation links are either replaced with
132 * a single, provided CTA link, or set to '#' if no CTA link is provided.
133 * This function updates the home page content and returns the updated pages array.
134 */
135 const { linkOverride, siteObjective } = options;
136 if (siteObjective === 'landing-page') {
137 wpPages[0] = updatePage({
138 id: wpPages[0].id,
139 content: homePageContent.replaceAll(
140 /href="(#extendify-[\w|-]+)"/gi,
141 linkOverride ? `href="${prependHTTPS(linkOverride)}"` : 'href="#"',
142 ),
143 });
144
145 return wpPages;
146 }
147
148 // get all the patterns that we have in the home page
149 const patternTypes = pages?.[0]?.patterns
150 ?.map((pattern) => pattern?.patternTypes?.[0])
151 ?.filter((patternType) => patternType !== 'hero-header')
152 ?.map((patternType) => {
153 const { slug } =
154 Object.values(pageNames).find(({ alias }) =>
155 alias.includes(patternType),
156 ) || {};
157 return slug;
158 })
159 ?.filter(Boolean)
160 ?.flat();
161
162 const createdPages =
163 pages
164 ?.filter((page) => page.slug !== 'home')
165 ?.map((page) => page.slug)
166 ?.filter(Boolean) ?? [];
167
168 // get the active plugins
169 const { data: activePlugins } = await getActivePlugins();
170 const pluginPages = [];
171
172 // check if woocommerce is active, if so we add it to the list of pages
173 if (wasPluginInstalled(activePlugins, 'woocommerce')) {
174 const page = await getPageById(
175 await getOption('woocommerce_shop_page_id'),
176 ).catch(() => null);
177
178 page?.slug && pluginPages.push(page.slug);
179 }
180
181 // check if events calendar is active, if so we add it to the list of pages
182 if (wasPluginInstalled(activePlugins, 'the-events-calendar')) {
183 pluginPages.push('events');
184 }
185
186 // get the suggested links from the AI and send both the patterns and the plugin pages.
187 const { suggestedLinks } =
188 (await getLinkSuggestions(
189 homePageContent,
190 patternTypes.concat(pluginPages),
191 )) || {};
192
193 // replace the links
194 homePageContent = Object.keys(suggestedLinks).reduce((content, key) => {
195 const slug = suggestedLinks[key];
196
197 if (!slug) return content;
198
199 const newLink = pluginPages.concat(createdPages).includes(slug)
200 ? `"${homeUrl}/${slug}"`
201 : `"${homeUrl}/#${slug}"`;
202
203 return content.replaceAll(`"${key}"`, newLink);
204 }, homePageContent);
205
206 // Update the first page by replacing the buttons urls with the new slug
207 wpPages[0] = updatePage({
208 id: wpPages[0].id,
209 content: homePageContent,
210 });
211
212 return wpPages;
213 };
214