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 / Shared / api / wp.js

wp.js in Extendify 3.1.5, at src/Shared/api/wp.js

190 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { AI_HOST } from '@constants';
2 import { renderAiLabelPill, stampAiLabel } from '@shared/lib/ai-label';
3 import apiFetch from '@wordpress/api-fetch';
4 import { sprintf } from '@wordpress/i18n';
5 import { addQueryArgs } from '@wordpress/url';
6
7 export const getPlugin = async (slug) => {
8 const response = await apiFetch({
9 path: addQueryArgs('/wp/v2/plugins', { search: slug }),
10 });
11
12 const plugin = response?.[0];
13
14 if (!plugin) throw new Error('Plugin not found');
15
16 return plugin;
17 };
18
19 export const getAllPlugins = async () => {
20 const response = await apiFetch({
21 path: '/wp/v2/plugins',
22 });
23
24 if (!response) {
25 throw new Error('Failed to fetch installed plugins');
26 }
27
28 return response;
29 };
30
31 export const enableAutoUpdate = async (plugin) => {
32 if (!plugin) return;
33 try {
34 await apiFetch({
35 path: '/extendify/v1/shared/enable-auto-update',
36 method: 'POST',
37 data: { plugin },
38 });
39 } catch (_e) {
40 // Best-effort: the install already succeeded.
41 }
42 };
43
44 export const installPlugin = async (slug) => {
45 const plugin = await apiFetch({
46 path: '/wp/v2/plugins',
47 method: 'POST',
48 data: {
49 slug,
50 },
51 });
52
53 await enableAutoUpdate(plugin?.plugin);
54
55 return plugin;
56 };
57
58 export const activatePlugin = async (slug) => {
59 const plugin = await getPlugin(slug);
60
61 return await apiFetch({
62 path: `/wp/v2/plugins/${plugin.plugin}`,
63 method: 'POST',
64 data: {
65 status: 'active',
66 },
67 });
68 };
69
70 export const loadImage = (img) => {
71 return new Promise((resolve, reject) => {
72 img.onload = () => resolve(img);
73 img.onerror = (e) => reject(e);
74 });
75 };
76
77 const shouldDisclose = (metadata) =>
78 Boolean(metadata.aiGenerated) && Boolean(metadata.disclose);
79
80 const altText = (metadata) => {
81 if (!shouldDisclose(metadata)) return metadata.alt ?? '';
82 const pattern = window.extSharedData?.aiImageAltPattern ?? 'AI Generated: %s';
83 return sprintf(pattern, metadata.alt ?? '').trim();
84 };
85
86 export const importImage = async (imageUrl, metadata = {}) => {
87 const image = new Image();
88 image.src = imageUrl;
89 image.crossOrigin = 'anonymous';
90 await loadImage(image);
91
92 const canvas = document.createElement('canvas');
93 canvas.width = image.width;
94 canvas.height = image.height;
95
96 const ctx = canvas.getContext('2d');
97 if (!ctx) return;
98 ctx.drawImage(image, 0, 0);
99 if (shouldDisclose(metadata)) {
100 stampAiLabel(ctx, canvas.width, canvas.height);
101 }
102
103 const blob = await new Promise((resolve) => {
104 canvas.toBlob((blob) => {
105 blob && resolve(blob);
106 }, 'image/jpeg');
107 });
108
109 const formData = new FormData();
110 formData.append('file', new File([blob], metadata.filename));
111 formData.append('alt_text', altText(metadata));
112 formData.append('caption', metadata.caption ?? '');
113 formData.append('status', 'publish');
114 if (metadata.aiGenerated) {
115 formData.append('meta[extendify_ai_generated]', '1');
116 }
117
118 return await apiFetch({
119 path: 'wp/v2/media',
120 method: 'POST',
121 body: formData,
122 });
123 };
124
125 export const importImageServer = async (src, metadata = {}) => {
126 const formData = new FormData();
127 formData.append('source', src);
128 // Fallback doesn't support custom file_name
129 formData.append('alt_text', altText(metadata));
130 formData.append('caption', metadata.caption ?? '');
131 if (metadata.aiGenerated) {
132 formData.append('ai_generated', '1');
133 if (shouldDisclose(metadata)) {
134 const pill = await renderAiLabelPill();
135 if (pill) {
136 formData.append(
137 'disclosure_label',
138 new File([pill], 'disclosure-label.png', { type: 'image/png' }),
139 );
140 }
141 }
142 }
143
144 return await apiFetch({
145 path: '/extendify/v1/draft/upload-image',
146 method: 'POST',
147 body: formData,
148 });
149 };
150
151 export const downloadImage = async (
152 id,
153 src,
154 source,
155 unsplashId,
156 metadata = { alt: '', caption: '' },
157 ) => {
158 let image;
159 if (unsplashId) {
160 await downloadPing(id, source, { unsplashId });
161 }
162 const aiGenerated = source === 'ai-generated';
163 try {
164 image = await importImage(src, {
165 alt: metadata.alt,
166 filename: 'image.jpg',
167 caption: metadata.caption,
168 aiGenerated,
169 disclose: metadata.disclose,
170 });
171 } catch (_e) {
172 image = await importImageServer(src, {
173 alt: metadata.alt,
174 filename: 'image.jpg',
175 caption: metadata.caption,
176 aiGenerated,
177 disclose: metadata.disclose,
178 });
179 }
180
181 return image;
182 };
183
184 export const downloadPing = (id, source, details = {}) =>
185 fetch(`${AI_HOST}/api/draft/image/download`, {
186 method: 'POST',
187 headers: { 'Content-Type': 'application/json' },
188 body: JSON.stringify({ id, source, ...details }),
189 });
190