@translationstudio/translationstudio-strapi-extension 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -32
- package/dist/Types.d.ts +146 -0
- package/dist/_chunks/App-Bw7t0TMJ.mjs +277 -0
- package/dist/_chunks/App-CUOeu4IU.js +277 -0
- package/dist/_chunks/en-B4KWt_jN.js +4 -0
- package/dist/_chunks/en-Byx4XI2L.mjs +4 -0
- package/dist/admin/index.js +323 -0
- package/dist/admin/index.mjs +324 -0
- package/dist/admin/src/components/Initializer.d.ts +5 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/components/TranslationMenu.d.ts +2 -0
- package/dist/admin/src/index.d.ts +11 -0
- package/dist/admin/src/pages/App.d.ts +2 -0
- package/dist/admin/src/pages/SettingsPage.d.ts +2 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/server/index.js +1038 -0
- package/dist/server/index.mjs +1039 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +5 -0
- package/dist/server/src/content-types/index.d.ts +2 -0
- package/dist/server/src/controllers/controller.d.ts +19 -0
- package/dist/server/src/controllers/index.d.ts +20 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +85 -0
- package/dist/server/src/middlewares/index.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/index.d.ts +18 -0
- package/dist/server/src/services/functions/exportData/extractRichtextContent.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/getContentType.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/getEntry.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/isFieldLocalizable.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/jsonToHtml.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/parsePayload.d.ts +7 -0
- package/dist/server/src/services/functions/exportData/processComponent.d.ts +3 -0
- package/dist/server/src/services/functions/exportData/processEntryFields.d.ts +2 -0
- package/dist/server/src/services/functions/exportData/transformResponse.d.ts +2 -0
- package/dist/server/src/services/functions/importData/extract.d.ts +6 -0
- package/dist/server/src/services/functions/importData/handleError.d.ts +1 -0
- package/dist/server/src/services/functions/importData/htmlToJson.d.ts +2 -0
- package/dist/server/src/services/functions/importData/organizeFields.d.ts +2 -0
- package/dist/server/src/services/functions/importData/parseImportPayload.d.ts +6 -0
- package/dist/server/src/services/functions/importData/parseInlineElements.d.ts +2 -0
- package/dist/server/src/services/functions/importData/parseNewValuesIntoStructure.d.ts +1 -0
- package/dist/server/src/services/functions/importData/prepareImportData.d.ts +2 -0
- package/dist/server/src/services/functions/importData/processComponentFields.d.ts +2 -0
- package/dist/server/src/services/functions/importData/processDynamicZones.d.ts +2 -0
- package/dist/server/src/services/functions/importData/processRegularFields.d.ts +2 -0
- package/dist/server/src/services/functions/importData/transformFieldsToData.d.ts +2 -0
- package/dist/server/src/services/functions/importData/updateEntry.d.ts +2 -0
- package/dist/server/src/services/index.d.ts +31 -0
- package/dist/server/src/services/service.d.ts +31 -0
- package/package.json +4 -2
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const bootstrap = ({ strapi: strapi2 }) => {
|
|
3
|
+
};
|
|
4
|
+
const destroy = ({ strapi: strapi2 }) => {
|
|
5
|
+
};
|
|
6
|
+
const register = ({ strapi: strapi2 }) => {
|
|
7
|
+
};
|
|
8
|
+
const config = {
|
|
9
|
+
default: {},
|
|
10
|
+
validator() {
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const contentTypes = {};
|
|
14
|
+
const controller = ({ strapi: strapi2 }) => ({
|
|
15
|
+
async validateToken(ctx) {
|
|
16
|
+
const authHeader = ctx.request.header.authorization;
|
|
17
|
+
if (!authHeader) {
|
|
18
|
+
ctx.status = 401;
|
|
19
|
+
ctx.body = { error: "Missing authorization header" };
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const storedToken = await strapi2.plugin("translationstudio").service("service").getToken();
|
|
23
|
+
if (!storedToken?.token || authHeader !== storedToken.token) {
|
|
24
|
+
ctx.status = 401;
|
|
25
|
+
ctx.body = { error: "Invalid token" };
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
async getLicense(ctx) {
|
|
31
|
+
const result = await strapi2.plugin("translationstudio").service("service").getLicense();
|
|
32
|
+
if (result.license) {
|
|
33
|
+
ctx.status = 200;
|
|
34
|
+
} else {
|
|
35
|
+
ctx.status = 404;
|
|
36
|
+
}
|
|
37
|
+
ctx.body = result;
|
|
38
|
+
},
|
|
39
|
+
async setLicense(ctx) {
|
|
40
|
+
const license = ctx.request.body.license;
|
|
41
|
+
const result = await strapi2.plugin("translationstudio").service("service").setLicense(license);
|
|
42
|
+
if (result.success) {
|
|
43
|
+
ctx.status = 200;
|
|
44
|
+
} else {
|
|
45
|
+
ctx.status = 500;
|
|
46
|
+
}
|
|
47
|
+
ctx.body = result;
|
|
48
|
+
},
|
|
49
|
+
async getToken(ctx) {
|
|
50
|
+
const result = await strapi2.plugin("translationstudio").service("service").getToken();
|
|
51
|
+
if (result.token) {
|
|
52
|
+
ctx.status = 200;
|
|
53
|
+
} else {
|
|
54
|
+
ctx.status = 404;
|
|
55
|
+
}
|
|
56
|
+
ctx.body = result;
|
|
57
|
+
},
|
|
58
|
+
async generateToken(ctx) {
|
|
59
|
+
const result = await strapi2.plugin("translationstudio").service("service").generateToken();
|
|
60
|
+
ctx.status = 200;
|
|
61
|
+
ctx.body = result;
|
|
62
|
+
},
|
|
63
|
+
async getLanguageOptions(ctx) {
|
|
64
|
+
const result = await strapi2.plugin("translationstudio").service("service").getLanguageOptions();
|
|
65
|
+
ctx.status = 200;
|
|
66
|
+
ctx.body = result;
|
|
67
|
+
},
|
|
68
|
+
async requestTranslation(ctx) {
|
|
69
|
+
const payload = ctx.request.body;
|
|
70
|
+
const result = await strapi2.plugin("translationstudio").service("service").requestTranslation(payload);
|
|
71
|
+
ctx.body = result;
|
|
72
|
+
},
|
|
73
|
+
async exportData(ctx) {
|
|
74
|
+
if (!await this.validateToken(ctx)) {
|
|
75
|
+
ctx.status = 400;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const payload = JSON.parse(ctx.request.body);
|
|
79
|
+
const result = await strapi2.plugin("translationstudio").service("service").exportData(payload);
|
|
80
|
+
ctx.status = 200;
|
|
81
|
+
ctx.body = [{ fields: result }];
|
|
82
|
+
},
|
|
83
|
+
async importData(ctx) {
|
|
84
|
+
if (!await this.validateToken(ctx)) {
|
|
85
|
+
ctx.status = 400;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const payload = JSON.parse(ctx.request.body);
|
|
89
|
+
const result = await strapi2.plugin("translationstudio").service("service").importData(payload);
|
|
90
|
+
ctx.body = result;
|
|
91
|
+
},
|
|
92
|
+
async ping(ctx) {
|
|
93
|
+
const result = await strapi2.plugin("translationstudio").service("service").ping();
|
|
94
|
+
ctx.status = 204;
|
|
95
|
+
ctx.body = result;
|
|
96
|
+
},
|
|
97
|
+
async getLanguages(ctx) {
|
|
98
|
+
if (!await this.validateToken(ctx)) {
|
|
99
|
+
ctx.status = 400;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const result = await strapi2.plugin("translationstudio").service("service").getLanguages();
|
|
103
|
+
ctx.status = 200;
|
|
104
|
+
ctx.body = result;
|
|
105
|
+
},
|
|
106
|
+
async getEmail(ctx) {
|
|
107
|
+
const result = await strapi2.plugin("translationstudio").service("service").getEmail(ctx);
|
|
108
|
+
ctx.body = result;
|
|
109
|
+
},
|
|
110
|
+
async getEntryData(ctx) {
|
|
111
|
+
const { uid, locale } = ctx.request.body;
|
|
112
|
+
if (!uid) {
|
|
113
|
+
return ctx.badRequest("Missing uid parameter");
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const [contentTypeID, entryID] = uid.split("#");
|
|
117
|
+
const entry = await strapi2.plugin("translationstudio").service("service").getEntryData(contentTypeID, entryID, locale);
|
|
118
|
+
return entry;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return ctx.badRequest("Failed to get entry data", { error: error.message });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
const controllers = {
|
|
125
|
+
controller
|
|
126
|
+
};
|
|
127
|
+
const middlewares = {};
|
|
128
|
+
const policies = {};
|
|
129
|
+
const routes = [
|
|
130
|
+
{
|
|
131
|
+
method: "POST",
|
|
132
|
+
path: "/setLicense",
|
|
133
|
+
handler: "controller.setLicense",
|
|
134
|
+
config: {
|
|
135
|
+
policies: []
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
method: "GET",
|
|
140
|
+
path: "/getLicense",
|
|
141
|
+
handler: "controller.getLicense",
|
|
142
|
+
config: {
|
|
143
|
+
policies: []
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
method: "GET",
|
|
148
|
+
path: "/getToken",
|
|
149
|
+
handler: "controller.getToken",
|
|
150
|
+
config: {
|
|
151
|
+
policies: []
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
method: "POST",
|
|
156
|
+
path: "/generateToken",
|
|
157
|
+
handler: "controller.generateToken",
|
|
158
|
+
config: {
|
|
159
|
+
policies: []
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
method: "GET",
|
|
164
|
+
path: "/mappings",
|
|
165
|
+
handler: "controller.getLanguageOptions",
|
|
166
|
+
config: {
|
|
167
|
+
policies: []
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
method: "POST",
|
|
172
|
+
path: "/translate",
|
|
173
|
+
handler: "controller.requestTranslation",
|
|
174
|
+
config: {
|
|
175
|
+
policies: []
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
method: "POST",
|
|
180
|
+
path: "/import",
|
|
181
|
+
handler: "controller.importData",
|
|
182
|
+
config: {
|
|
183
|
+
auth: false,
|
|
184
|
+
policies: []
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
method: "POST",
|
|
189
|
+
path: "/export",
|
|
190
|
+
handler: "controller.exportData",
|
|
191
|
+
config: {
|
|
192
|
+
auth: false,
|
|
193
|
+
policies: []
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
method: "GET",
|
|
198
|
+
path: "/",
|
|
199
|
+
handler: "controller.ping",
|
|
200
|
+
config: {
|
|
201
|
+
auth: false,
|
|
202
|
+
policies: []
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
method: "GET",
|
|
207
|
+
path: "/languages",
|
|
208
|
+
handler: "controller.getLanguages",
|
|
209
|
+
config: {
|
|
210
|
+
auth: false,
|
|
211
|
+
policies: []
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
method: "GET",
|
|
216
|
+
path: "/email",
|
|
217
|
+
handler: "controller.getEmail",
|
|
218
|
+
config: {
|
|
219
|
+
policies: []
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
method: "POST",
|
|
224
|
+
path: "/entrydata",
|
|
225
|
+
handler: "controller.getEntryData",
|
|
226
|
+
config: {
|
|
227
|
+
policies: []
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
];
|
|
231
|
+
const getContentType = async (contentTypeID) => {
|
|
232
|
+
const contentType = await strapi.contentType(contentTypeID);
|
|
233
|
+
if (!contentType?.attributes) {
|
|
234
|
+
throw new Error(`Content type or schema not found: ${contentTypeID}`);
|
|
235
|
+
}
|
|
236
|
+
return contentType;
|
|
237
|
+
};
|
|
238
|
+
const parsePayload = (payload) => {
|
|
239
|
+
const [contentTypeID, entryID] = payload.element.includes("#") ? payload.element.split("#") : [payload.element, void 0];
|
|
240
|
+
const locale = payload.source.includes("-") ? payload.source.split("-")[0] : payload.source;
|
|
241
|
+
return { contentTypeID, entryID, locale };
|
|
242
|
+
};
|
|
243
|
+
const getComponentSchema = async (componentName) => {
|
|
244
|
+
try {
|
|
245
|
+
return await strapi.components[componentName];
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(`Failed to get component schema for ${componentName}:`, error);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
const buildPopulateConfig = async (schema) => {
|
|
252
|
+
const populate = {};
|
|
253
|
+
for (const [fieldName, attribute] of Object.entries(schema.attributes)) {
|
|
254
|
+
if (attribute.type === "component") {
|
|
255
|
+
const componentSchema = await getComponentSchema(attribute.component);
|
|
256
|
+
if (componentSchema) {
|
|
257
|
+
const nestedPopulate = await buildPopulateConfig(componentSchema);
|
|
258
|
+
populate[fieldName] = {
|
|
259
|
+
populate: Object.keys(nestedPopulate).length ? nestedPopulate : "*"
|
|
260
|
+
};
|
|
261
|
+
} else {
|
|
262
|
+
populate[fieldName] = { populate: "*" };
|
|
263
|
+
}
|
|
264
|
+
} else if (attribute.type === "dynamiczone") {
|
|
265
|
+
populate[fieldName] = { populate: "*" };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return populate;
|
|
269
|
+
};
|
|
270
|
+
const getEntry = async (contentTypeID, entryID, locale) => {
|
|
271
|
+
try {
|
|
272
|
+
const contentType = await strapi.contentTypes[contentTypeID];
|
|
273
|
+
const populateConfig = await buildPopulateConfig(contentType);
|
|
274
|
+
const query = {
|
|
275
|
+
locale,
|
|
276
|
+
populate: populateConfig
|
|
277
|
+
};
|
|
278
|
+
if (entryID) {
|
|
279
|
+
Object.assign(query, { documentId: entryID });
|
|
280
|
+
}
|
|
281
|
+
const entry = await strapi.documents(contentTypeID).findFirst(query);
|
|
282
|
+
return entry;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error("Entry fetch error:", error);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const transformResponse = (data) => data.map(
|
|
289
|
+
(item) => item.realType === "blocks" && Array.isArray(item.translatableValue[0]) ? { ...item, translatableValue: item.translatableValue[0] } : item
|
|
290
|
+
);
|
|
291
|
+
function formatText(child) {
|
|
292
|
+
if (child.type === "link") {
|
|
293
|
+
return `<a href="${child.url}">${child.children.map((sub) => sub.text).join("")}</a>`;
|
|
294
|
+
}
|
|
295
|
+
let text = child.text || "";
|
|
296
|
+
if (child.bold) text = `<strong>${text}</strong>`;
|
|
297
|
+
if (child.italic) text = `<em>${text}</em>`;
|
|
298
|
+
if (child.underline) text = `<u>${text}</u>`;
|
|
299
|
+
if (child.strikethrough) text = `<del>${text}</del>`;
|
|
300
|
+
return text;
|
|
301
|
+
}
|
|
302
|
+
function renderChildren(children) {
|
|
303
|
+
return children.map(formatText).join("");
|
|
304
|
+
}
|
|
305
|
+
function renderHeading(element) {
|
|
306
|
+
return `<h${element.level}>${renderChildren(element.children)}</h${element.level}>`;
|
|
307
|
+
}
|
|
308
|
+
function renderParagraph(element) {
|
|
309
|
+
return `<p>${renderChildren(element.children)}</p>`;
|
|
310
|
+
}
|
|
311
|
+
function renderList(element) {
|
|
312
|
+
const tag = element.format === "unordered" ? "ul" : "ol";
|
|
313
|
+
const items = element.children.map(renderListItem).join("");
|
|
314
|
+
return `<${tag}>${items}</${tag}>`;
|
|
315
|
+
}
|
|
316
|
+
function renderListItem(item) {
|
|
317
|
+
const content = item.children.map(formatText).join("");
|
|
318
|
+
return `<li>${content}</li>`;
|
|
319
|
+
}
|
|
320
|
+
function jsonToHtml(jsonData) {
|
|
321
|
+
return jsonData.map((element) => {
|
|
322
|
+
switch (element.type) {
|
|
323
|
+
case "heading":
|
|
324
|
+
return renderHeading(element);
|
|
325
|
+
case "paragraph":
|
|
326
|
+
return renderParagraph(element);
|
|
327
|
+
case "list":
|
|
328
|
+
return renderList(element);
|
|
329
|
+
default:
|
|
330
|
+
return "";
|
|
331
|
+
}
|
|
332
|
+
}).join("");
|
|
333
|
+
}
|
|
334
|
+
const processComponent = async (fieldName, componentName, value, schemaName, componentId) => {
|
|
335
|
+
const contentFields = [];
|
|
336
|
+
const componentSchema = await strapi.components[componentName];
|
|
337
|
+
if (!componentSchema) {
|
|
338
|
+
throw new Error(`Component schema not found for ${componentName}`);
|
|
339
|
+
}
|
|
340
|
+
const schemaAttributes = componentSchema.attributes || {};
|
|
341
|
+
const dataToProcess = value || {};
|
|
342
|
+
if (Array.isArray(dataToProcess)) {
|
|
343
|
+
for (const item of dataToProcess) {
|
|
344
|
+
const processedFields = await processComponentFields$1(
|
|
345
|
+
item,
|
|
346
|
+
schemaAttributes,
|
|
347
|
+
fieldName,
|
|
348
|
+
componentName,
|
|
349
|
+
schemaName,
|
|
350
|
+
item.id
|
|
351
|
+
);
|
|
352
|
+
contentFields.push(...processedFields);
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
const processedFields = await processComponentFields$1(
|
|
356
|
+
dataToProcess,
|
|
357
|
+
schemaAttributes,
|
|
358
|
+
fieldName,
|
|
359
|
+
componentName,
|
|
360
|
+
schemaName,
|
|
361
|
+
componentId
|
|
362
|
+
);
|
|
363
|
+
contentFields.push(...processedFields);
|
|
364
|
+
}
|
|
365
|
+
return contentFields;
|
|
366
|
+
};
|
|
367
|
+
const shouldSkipField$1 = (key, fieldSchema) => {
|
|
368
|
+
return key === "id" || fieldSchema.private;
|
|
369
|
+
};
|
|
370
|
+
const isTranslatableField$1 = (type) => {
|
|
371
|
+
return ["string", "text", "blocks", "richtext"].includes(type);
|
|
372
|
+
};
|
|
373
|
+
const getTranslatedValue = (type, value) => {
|
|
374
|
+
if (type === "blocks") {
|
|
375
|
+
return value ? jsonToHtml(value) : "";
|
|
376
|
+
}
|
|
377
|
+
return value.toString();
|
|
378
|
+
};
|
|
379
|
+
const buildTranslatable = (key, fieldSchema, value, parentPath, componentId, schemaName) => {
|
|
380
|
+
return {
|
|
381
|
+
field: key,
|
|
382
|
+
type: ["richtext", "blocks"].includes(fieldSchema.type) ? "html" : "text",
|
|
383
|
+
translatableValue: [value],
|
|
384
|
+
realType: fieldSchema.type,
|
|
385
|
+
componentInfo: {
|
|
386
|
+
namePath: parentPath,
|
|
387
|
+
id: componentId,
|
|
388
|
+
schemaName
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
};
|
|
392
|
+
const processComponentFields$1 = async (componentData, schema, parentField, componentName, schemaName, componentId) => {
|
|
393
|
+
const contentFields = [];
|
|
394
|
+
const parentPath = parentField.split(".");
|
|
395
|
+
for (const [key, fieldSchema] of Object.entries(schema)) {
|
|
396
|
+
if (shouldSkipField$1(key, fieldSchema)) continue;
|
|
397
|
+
const value = componentData?.[key];
|
|
398
|
+
const fieldPath = `${parentField}.${key}`;
|
|
399
|
+
if (fieldSchema.type === "component") {
|
|
400
|
+
if (!fieldSchema.component) continue;
|
|
401
|
+
const nestedFields = await processComponent(
|
|
402
|
+
fieldPath,
|
|
403
|
+
fieldSchema.component,
|
|
404
|
+
value || {},
|
|
405
|
+
fieldSchema.component,
|
|
406
|
+
value?.id
|
|
407
|
+
);
|
|
408
|
+
contentFields.push(...nestedFields);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (!isTranslatableField$1(fieldSchema.type)) continue;
|
|
412
|
+
if (value === null || value === void 0 || value === "") continue;
|
|
413
|
+
const translatedValue = getTranslatedValue(fieldSchema.type, value);
|
|
414
|
+
const translatable = buildTranslatable(
|
|
415
|
+
key,
|
|
416
|
+
fieldSchema,
|
|
417
|
+
translatedValue,
|
|
418
|
+
parentPath,
|
|
419
|
+
componentId,
|
|
420
|
+
schemaName
|
|
421
|
+
);
|
|
422
|
+
contentFields.push(translatable);
|
|
423
|
+
}
|
|
424
|
+
return contentFields;
|
|
425
|
+
};
|
|
426
|
+
const isFieldLocalizable = (fieldSchema, parentSchema) => {
|
|
427
|
+
if (fieldSchema.pluginOptions?.i18n?.localized !== void 0) {
|
|
428
|
+
return fieldSchema.pluginOptions.i18n.localized;
|
|
429
|
+
}
|
|
430
|
+
if (parentSchema.pluginOptions?.i18n?.localized === true) {
|
|
431
|
+
const localizableTypes = ["string", "text", "blocks", "richtext"];
|
|
432
|
+
return localizableTypes.includes(fieldSchema.type);
|
|
433
|
+
}
|
|
434
|
+
return false;
|
|
435
|
+
};
|
|
436
|
+
const DEFAULT_FIELDS = /* @__PURE__ */ new Set([
|
|
437
|
+
"id",
|
|
438
|
+
"documentId",
|
|
439
|
+
"createdAt",
|
|
440
|
+
"updatedAt",
|
|
441
|
+
"publishedAt",
|
|
442
|
+
"locale",
|
|
443
|
+
"localizations",
|
|
444
|
+
"updatedBy",
|
|
445
|
+
"createdBy"
|
|
446
|
+
]);
|
|
447
|
+
const isEmpty = (value) => value === null || value === void 0 || value === "";
|
|
448
|
+
const isTranslatableField = (fieldSchema) => ["string", "text", "blocks", "richtext"].includes(fieldSchema.type) && fieldSchema.pluginOptions?.i18n?.localized !== false;
|
|
449
|
+
const processDynamicZone = async (key, value, schema) => {
|
|
450
|
+
const results = [];
|
|
451
|
+
for (const component of value) {
|
|
452
|
+
const componentName = component?.__component;
|
|
453
|
+
if (!componentName) continue;
|
|
454
|
+
const fields = await processComponent(
|
|
455
|
+
key,
|
|
456
|
+
componentName,
|
|
457
|
+
component,
|
|
458
|
+
componentName,
|
|
459
|
+
component.id
|
|
460
|
+
);
|
|
461
|
+
results.push(...fields);
|
|
462
|
+
}
|
|
463
|
+
return results;
|
|
464
|
+
};
|
|
465
|
+
const processComponentField = async (key, value, fieldSchema) => {
|
|
466
|
+
const results = [];
|
|
467
|
+
if (fieldSchema.repeatable && Array.isArray(value)) {
|
|
468
|
+
for (const component of value) {
|
|
469
|
+
const fields = await processComponent(
|
|
470
|
+
key,
|
|
471
|
+
fieldSchema.component,
|
|
472
|
+
component,
|
|
473
|
+
fieldSchema.component,
|
|
474
|
+
component.id
|
|
475
|
+
);
|
|
476
|
+
results.push(...fields);
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
const fields = await processComponent(
|
|
480
|
+
key,
|
|
481
|
+
fieldSchema.component,
|
|
482
|
+
value,
|
|
483
|
+
fieldSchema.component,
|
|
484
|
+
value.id
|
|
485
|
+
);
|
|
486
|
+
results.push(...fields);
|
|
487
|
+
}
|
|
488
|
+
return results;
|
|
489
|
+
};
|
|
490
|
+
const processRegularField = (key, value, fieldSchema) => {
|
|
491
|
+
const translatedValue = fieldSchema.type === "blocks" ? jsonToHtml(value) : value.toString();
|
|
492
|
+
return {
|
|
493
|
+
field: key,
|
|
494
|
+
type: ["richtext", "blocks"].includes(fieldSchema.type) ? "html" : "text",
|
|
495
|
+
translatableValue: [translatedValue],
|
|
496
|
+
realType: fieldSchema.type
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
const processEntryFields = async (entry, schema, locale) => {
|
|
500
|
+
const contentFields = [];
|
|
501
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
502
|
+
if (shouldSkipField(key, value)) continue;
|
|
503
|
+
const fieldSchema = schema[key];
|
|
504
|
+
if (!fieldSchema) continue;
|
|
505
|
+
if (isDynamicZone(fieldSchema, value, schema)) {
|
|
506
|
+
const zoneFields = await processDynamicZone(key, value);
|
|
507
|
+
contentFields.push(...zoneFields);
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (isComponent(fieldSchema, value, schema)) {
|
|
511
|
+
const componentFields = await processComponentField(key, value, fieldSchema);
|
|
512
|
+
contentFields.push(...componentFields);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (isTranslatableField(fieldSchema)) {
|
|
516
|
+
const translatedField = processRegularField(key, value, fieldSchema);
|
|
517
|
+
contentFields.push(translatedField);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return contentFields;
|
|
521
|
+
};
|
|
522
|
+
const shouldSkipField = (key, value) => {
|
|
523
|
+
return DEFAULT_FIELDS.has(key) || isEmpty(value);
|
|
524
|
+
};
|
|
525
|
+
const isDynamicZone = (fieldSchema, value, schema) => {
|
|
526
|
+
return fieldSchema.type === "dynamiczone" && isFieldLocalizable(fieldSchema, schema) && Array.isArray(value);
|
|
527
|
+
};
|
|
528
|
+
const isComponent = (fieldSchema, value, schema) => {
|
|
529
|
+
return fieldSchema.type === "component" && isFieldLocalizable(fieldSchema, schema);
|
|
530
|
+
};
|
|
531
|
+
function parseInlineElements(text) {
|
|
532
|
+
if (!text.includes("<")) {
|
|
533
|
+
return [{ type: "text", text }];
|
|
534
|
+
}
|
|
535
|
+
const linkRegex = /<a\s+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
536
|
+
if (text.includes("<a ")) {
|
|
537
|
+
let linkMatch;
|
|
538
|
+
let lastIndex = 0;
|
|
539
|
+
const elements = [];
|
|
540
|
+
while ((linkMatch = linkRegex.exec(text)) !== null) {
|
|
541
|
+
const [fullMatch, url, linkContent] = linkMatch;
|
|
542
|
+
if (linkMatch.index > lastIndex) {
|
|
543
|
+
const beforeText = text.substring(lastIndex, linkMatch.index);
|
|
544
|
+
if (beforeText) {
|
|
545
|
+
elements.push(...parseInlineElements(beforeText));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
elements.push({
|
|
549
|
+
type: "link",
|
|
550
|
+
url,
|
|
551
|
+
children: parseInlineElements(linkContent)
|
|
552
|
+
});
|
|
553
|
+
lastIndex = linkMatch.index + fullMatch.length;
|
|
554
|
+
}
|
|
555
|
+
if (lastIndex < text.length) {
|
|
556
|
+
const afterText = text.substring(lastIndex);
|
|
557
|
+
if (afterText) {
|
|
558
|
+
elements.push(...parseInlineElements(afterText));
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return elements;
|
|
562
|
+
}
|
|
563
|
+
const formatRegex = /<(strong|em|u|del)>([\s\S]*?)<\/\1>/;
|
|
564
|
+
const match = formatRegex.exec(text);
|
|
565
|
+
if (match) {
|
|
566
|
+
const [fullMatch, tag, content] = match;
|
|
567
|
+
const beforeText = text.substring(0, match.index);
|
|
568
|
+
const afterText = text.substring(match.index + fullMatch.length);
|
|
569
|
+
const elements = [];
|
|
570
|
+
if (beforeText) {
|
|
571
|
+
elements.push(...parseInlineElements(beforeText));
|
|
572
|
+
}
|
|
573
|
+
const nestedElements = parseInlineElements(content);
|
|
574
|
+
nestedElements.forEach((element) => {
|
|
575
|
+
if (tag === "strong") element.bold = true;
|
|
576
|
+
else if (tag === "em") element.italic = true;
|
|
577
|
+
else if (tag === "u") element.underline = true;
|
|
578
|
+
else if (tag === "del") element.strikethrough = true;
|
|
579
|
+
});
|
|
580
|
+
elements.push(...nestedElements);
|
|
581
|
+
if (afterText) {
|
|
582
|
+
elements.push(...parseInlineElements(afterText));
|
|
583
|
+
}
|
|
584
|
+
return elements;
|
|
585
|
+
}
|
|
586
|
+
return [{ type: "text", text }];
|
|
587
|
+
}
|
|
588
|
+
function parseHeading(tag, innerText) {
|
|
589
|
+
const level = parseInt(tag[1]);
|
|
590
|
+
return {
|
|
591
|
+
type: "heading",
|
|
592
|
+
level,
|
|
593
|
+
children: [{ type: "text", text: innerText.trim() }]
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function parseParagraph(innerText) {
|
|
597
|
+
return {
|
|
598
|
+
type: "paragraph",
|
|
599
|
+
children: parseInlineElements(innerText)
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function parseList(tag, innerText) {
|
|
603
|
+
const listType = tag === "ul" ? "unordered" : "ordered";
|
|
604
|
+
const listItems = [];
|
|
605
|
+
const listItemRegex = /<li>(.*?)<\/li>/g;
|
|
606
|
+
let itemMatch;
|
|
607
|
+
while ((itemMatch = listItemRegex.exec(innerText)) !== null) {
|
|
608
|
+
listItems.push({
|
|
609
|
+
type: "list-item",
|
|
610
|
+
children: parseInlineElements(itemMatch[1])
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
type: "list",
|
|
615
|
+
format: listType,
|
|
616
|
+
children: listItems
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function htmlToJson(htmlData) {
|
|
620
|
+
const jsonData = [];
|
|
621
|
+
const blockRegex = /<(h[1-3]|p|ul|ol)(?:[^>]*?)>([\s\S]*?)<\/\1>/g;
|
|
622
|
+
let match;
|
|
623
|
+
while ((match = blockRegex.exec(htmlData)) !== null) {
|
|
624
|
+
const [, tag, content] = match;
|
|
625
|
+
switch (tag) {
|
|
626
|
+
case "h1":
|
|
627
|
+
case "h2":
|
|
628
|
+
case "h3":
|
|
629
|
+
jsonData.push(parseHeading(tag, content));
|
|
630
|
+
break;
|
|
631
|
+
case "p":
|
|
632
|
+
if (content.includes("<a ")) {
|
|
633
|
+
const linkRegex = /<a\s+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
634
|
+
let linkMatch;
|
|
635
|
+
let lastIndex = 0;
|
|
636
|
+
const children = [];
|
|
637
|
+
children.push({ type: "text", text: "" });
|
|
638
|
+
while ((linkMatch = linkRegex.exec(content)) !== null) {
|
|
639
|
+
const [fullMatch, url, linkText] = linkMatch;
|
|
640
|
+
if (linkMatch.index > lastIndex) {
|
|
641
|
+
const beforeLinkText = content.substring(lastIndex, linkMatch.index);
|
|
642
|
+
if (beforeLinkText) {
|
|
643
|
+
children.push({ type: "text", text: beforeLinkText });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
children.push({
|
|
647
|
+
type: "link",
|
|
648
|
+
url,
|
|
649
|
+
children: [{ type: "text", text: linkText }]
|
|
650
|
+
});
|
|
651
|
+
lastIndex = linkMatch.index + fullMatch.length;
|
|
652
|
+
}
|
|
653
|
+
const afterLastLink = content.substring(lastIndex);
|
|
654
|
+
if (afterLastLink) {
|
|
655
|
+
children.push({ type: "text", text: afterLastLink });
|
|
656
|
+
} else {
|
|
657
|
+
children.push({ type: "text", text: "" });
|
|
658
|
+
}
|
|
659
|
+
jsonData.push({
|
|
660
|
+
type: "paragraph",
|
|
661
|
+
children
|
|
662
|
+
});
|
|
663
|
+
} else {
|
|
664
|
+
jsonData.push(parseParagraph(content));
|
|
665
|
+
}
|
|
666
|
+
break;
|
|
667
|
+
case "ul":
|
|
668
|
+
case "ol":
|
|
669
|
+
jsonData.push(parseList(tag, content));
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return jsonData;
|
|
674
|
+
}
|
|
675
|
+
async function updateEntry(contentTypeID, entryID, sourceLocale, targetLocale, data, attributes) {
|
|
676
|
+
if (!entryID) {
|
|
677
|
+
const singleTypeData = await strapi.documents(contentTypeID).findFirst();
|
|
678
|
+
entryID = singleTypeData.documentId;
|
|
679
|
+
}
|
|
680
|
+
const originalEntry = await strapi.documents(contentTypeID).findFirst({
|
|
681
|
+
documentId: entryID,
|
|
682
|
+
locale: sourceLocale
|
|
683
|
+
});
|
|
684
|
+
const processedData = processDataRecursively(data);
|
|
685
|
+
const localizedData = {};
|
|
686
|
+
for (const field in processedData) {
|
|
687
|
+
if (attributes[field] && (!attributes[field].pluginOptions?.i18n || attributes[field].pluginOptions?.i18n?.localized !== false)) {
|
|
688
|
+
localizedData[field] = processedData[field];
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
await strapi.documents(contentTypeID).update({
|
|
692
|
+
documentId: entryID,
|
|
693
|
+
locale: targetLocale,
|
|
694
|
+
data: localizedData
|
|
695
|
+
});
|
|
696
|
+
if (originalEntry.publishedAt !== null) {
|
|
697
|
+
await strapi.documents(contentTypeID).publish({
|
|
698
|
+
documentId: entryID,
|
|
699
|
+
locale: sourceLocale
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function processDataRecursively(data) {
|
|
704
|
+
if (!data || typeof data !== "object") {
|
|
705
|
+
return data;
|
|
706
|
+
}
|
|
707
|
+
if (Array.isArray(data)) {
|
|
708
|
+
return data.map((item) => processDataRecursively(item));
|
|
709
|
+
}
|
|
710
|
+
const result = {};
|
|
711
|
+
for (const key in data) {
|
|
712
|
+
const value = data[key];
|
|
713
|
+
if (typeof value === "string" && (key.includes("Blocks") || key.includes("RTBlocks") || key === "blocks")) {
|
|
714
|
+
result[key] = htmlToJson(value);
|
|
715
|
+
} else if (value && typeof value === "object") {
|
|
716
|
+
result[key] = processDataRecursively(value);
|
|
717
|
+
} else {
|
|
718
|
+
result[key] = value;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return result;
|
|
722
|
+
}
|
|
723
|
+
function organizeFields(fields) {
|
|
724
|
+
const componentFieldsMap = /* @__PURE__ */ new Map();
|
|
725
|
+
const dynamicZoneFields = /* @__PURE__ */ new Map();
|
|
726
|
+
const regularFields = [];
|
|
727
|
+
fields.forEach((field) => {
|
|
728
|
+
if (!field.componentInfo) {
|
|
729
|
+
regularFields.push(field);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const { namePath, id } = field.componentInfo;
|
|
733
|
+
const pathString = namePath.join(".");
|
|
734
|
+
if (namePath[0] === "dynamiczone") {
|
|
735
|
+
if (!dynamicZoneFields.has(id)) {
|
|
736
|
+
dynamicZoneFields.set(id, []);
|
|
737
|
+
}
|
|
738
|
+
dynamicZoneFields.get(id)?.push(field);
|
|
739
|
+
} else {
|
|
740
|
+
if (!componentFieldsMap.has(pathString)) {
|
|
741
|
+
componentFieldsMap.set(pathString, []);
|
|
742
|
+
}
|
|
743
|
+
componentFieldsMap.get(pathString)?.push(field);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
return { regularFields, componentFieldsMap, dynamicZoneFields };
|
|
747
|
+
}
|
|
748
|
+
function processRegularFields(regularFields, acc) {
|
|
749
|
+
regularFields.forEach((field) => {
|
|
750
|
+
acc[field.field] = field.translatableValue[0];
|
|
751
|
+
});
|
|
752
|
+
return acc;
|
|
753
|
+
}
|
|
754
|
+
function processRepeatableComponents(fields, existingEntry, rootPath) {
|
|
755
|
+
const existingComponents = existingEntry?.[rootPath] || [];
|
|
756
|
+
const componentsById = /* @__PURE__ */ new Map();
|
|
757
|
+
fields.forEach((field) => {
|
|
758
|
+
if (!field.componentInfo) {
|
|
759
|
+
console.warn(`Component info missing for field: ${field.field}`);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const componentId = field.componentInfo.id;
|
|
763
|
+
if (!componentsById.has(componentId)) {
|
|
764
|
+
const existingComponent = existingComponents.find(
|
|
765
|
+
(c) => c.id === componentId
|
|
766
|
+
);
|
|
767
|
+
componentsById.set(
|
|
768
|
+
componentId,
|
|
769
|
+
existingComponent ? { ...existingComponent } : {}
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
const component = componentsById.get(componentId);
|
|
773
|
+
component[field.field] = field.translatableValue[0];
|
|
774
|
+
});
|
|
775
|
+
return Array.from(componentsById.values()).map((comp) => {
|
|
776
|
+
if (!existingComponents.find((ec) => ec.id === comp.id)) {
|
|
777
|
+
const { id, ...rest } = comp;
|
|
778
|
+
return rest;
|
|
779
|
+
}
|
|
780
|
+
return comp;
|
|
781
|
+
}).filter((comp) => Object.keys(comp).length > 0);
|
|
782
|
+
}
|
|
783
|
+
function processNestedComponents(fields, pathParts, existingEntry, acc) {
|
|
784
|
+
let current = acc;
|
|
785
|
+
let currentExisting = existingEntry;
|
|
786
|
+
pathParts.forEach((part, index2) => {
|
|
787
|
+
if (!current[part]) {
|
|
788
|
+
current[part] = {};
|
|
789
|
+
if (currentExisting?.[part]?.id) {
|
|
790
|
+
current[part].id = currentExisting[part].id;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (index2 === pathParts.length - 1) {
|
|
794
|
+
fields.forEach((field) => {
|
|
795
|
+
current[part][field.field] = field.translatableValue[0];
|
|
796
|
+
});
|
|
797
|
+
} else {
|
|
798
|
+
current = current[part];
|
|
799
|
+
currentExisting = currentExisting?.[part];
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
function processComponentFields(componentFieldsMap, acc, existingEntry, targetSchema) {
|
|
804
|
+
componentFieldsMap.forEach((fields, namePath) => {
|
|
805
|
+
if (!fields.length) return;
|
|
806
|
+
const pathParts = namePath.split(".");
|
|
807
|
+
const rootPath = pathParts[0];
|
|
808
|
+
const schema = targetSchema.attributes?.[rootPath];
|
|
809
|
+
if (schema?.repeatable) {
|
|
810
|
+
acc[rootPath] = processRepeatableComponents(
|
|
811
|
+
fields,
|
|
812
|
+
existingEntry,
|
|
813
|
+
rootPath
|
|
814
|
+
);
|
|
815
|
+
} else {
|
|
816
|
+
processNestedComponents(fields, pathParts, existingEntry, acc);
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
return acc;
|
|
820
|
+
}
|
|
821
|
+
function transformFieldsToData(fields) {
|
|
822
|
+
return fields.reduce((acc, field) => {
|
|
823
|
+
if (!field.translatableValue) {
|
|
824
|
+
acc[field.field] = "";
|
|
825
|
+
return acc;
|
|
826
|
+
}
|
|
827
|
+
if (field.realType === "blocks") {
|
|
828
|
+
acc[field.field] = htmlToJson(field.translatableValue[0] || "");
|
|
829
|
+
} else if (field.realType === "richtext") {
|
|
830
|
+
acc[field.field] = field.translatableValue[0] || "";
|
|
831
|
+
} else {
|
|
832
|
+
acc[field.field] = Array.isArray(field.translatableValue) && field.translatableValue.length > 1 ? field.translatableValue.join(" ") : field.translatableValue[0] || "";
|
|
833
|
+
}
|
|
834
|
+
return acc;
|
|
835
|
+
}, {});
|
|
836
|
+
}
|
|
837
|
+
function processDynamicZones(dynamicZoneFields, acc, existingEntry) {
|
|
838
|
+
if (dynamicZoneFields.size > 0) {
|
|
839
|
+
const existingDynamicZone = existingEntry?.dynamiczone || [];
|
|
840
|
+
acc.dynamiczone = Array.from(dynamicZoneFields.entries()).sort(([a], [b]) => a - b).map(([_, fields]) => {
|
|
841
|
+
if (!fields[0].componentInfo) {
|
|
842
|
+
console.warn(
|
|
843
|
+
`Component info missing for dynamic zone field: ${fields[0].field}`
|
|
844
|
+
);
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
const { schemaName } = fields[0].componentInfo;
|
|
848
|
+
const componentData = transformFieldsToData(fields);
|
|
849
|
+
const matchingComponent = existingDynamicZone.find(
|
|
850
|
+
(comp) => comp.__component === schemaName
|
|
851
|
+
);
|
|
852
|
+
return {
|
|
853
|
+
__component: schemaName,
|
|
854
|
+
...componentData,
|
|
855
|
+
...matchingComponent?.id ? { id: matchingComponent.id } : {}
|
|
856
|
+
};
|
|
857
|
+
}).filter(Boolean);
|
|
858
|
+
}
|
|
859
|
+
return acc;
|
|
860
|
+
}
|
|
861
|
+
function prepareImportData(translatables, existingEntry, targetSchema) {
|
|
862
|
+
return translatables.reduce((acc, doc) => {
|
|
863
|
+
const { regularFields, componentFieldsMap, dynamicZoneFields } = organizeFields(translatables);
|
|
864
|
+
const withRegularFields = processRegularFields(regularFields, acc);
|
|
865
|
+
const withComponentFields = processComponentFields(
|
|
866
|
+
componentFieldsMap,
|
|
867
|
+
withRegularFields,
|
|
868
|
+
existingEntry,
|
|
869
|
+
targetSchema
|
|
870
|
+
);
|
|
871
|
+
const withDynamicZones = processDynamicZones(
|
|
872
|
+
dynamicZoneFields,
|
|
873
|
+
withComponentFields,
|
|
874
|
+
existingEntry
|
|
875
|
+
);
|
|
876
|
+
return withDynamicZones;
|
|
877
|
+
}, {});
|
|
878
|
+
}
|
|
879
|
+
const jwt = require("jsonwebtoken");
|
|
880
|
+
const crypto = require("crypto");
|
|
881
|
+
const service = ({ strapi: strapi2 }) => {
|
|
882
|
+
const pluginStore = strapi2.store({
|
|
883
|
+
type: "plugin",
|
|
884
|
+
name: "translationstudio"
|
|
885
|
+
});
|
|
886
|
+
return {
|
|
887
|
+
// translationstudio Lizenz
|
|
888
|
+
async getLicense() {
|
|
889
|
+
const result = await pluginStore.get({ key: "license" });
|
|
890
|
+
return { license: result };
|
|
891
|
+
},
|
|
892
|
+
async setLicense(license) {
|
|
893
|
+
try {
|
|
894
|
+
await pluginStore.set({
|
|
895
|
+
key: "license",
|
|
896
|
+
value: license
|
|
897
|
+
});
|
|
898
|
+
return { success: true };
|
|
899
|
+
} catch (error) {
|
|
900
|
+
return { success: false };
|
|
901
|
+
}
|
|
902
|
+
},
|
|
903
|
+
// Access Token
|
|
904
|
+
async getToken() {
|
|
905
|
+
try {
|
|
906
|
+
const result = await pluginStore.get({ key: "token" });
|
|
907
|
+
return { token: result };
|
|
908
|
+
} catch (error) {
|
|
909
|
+
return { token: null };
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
async generateToken() {
|
|
913
|
+
const secretKey = crypto.randomBytes(64).toString("hex");
|
|
914
|
+
const token = jwt.sign(
|
|
915
|
+
{
|
|
916
|
+
app: "translationstudio",
|
|
917
|
+
iat: Math.floor(Date.now() / 1e3)
|
|
918
|
+
},
|
|
919
|
+
secretKey,
|
|
920
|
+
{ expiresIn: "10y" }
|
|
921
|
+
);
|
|
922
|
+
await pluginStore.set({
|
|
923
|
+
key: "token",
|
|
924
|
+
value: token
|
|
925
|
+
});
|
|
926
|
+
return { token };
|
|
927
|
+
},
|
|
928
|
+
async getLanguageOptions() {
|
|
929
|
+
const { license } = await this.getLicense();
|
|
930
|
+
const response = await fetch(
|
|
931
|
+
"https://cms-strapi-service-7866fdd79eab.herokuapp.com/mappings",
|
|
932
|
+
{
|
|
933
|
+
headers: { Authorization: `${license}` }
|
|
934
|
+
}
|
|
935
|
+
);
|
|
936
|
+
const responseData = await response.json();
|
|
937
|
+
return responseData;
|
|
938
|
+
},
|
|
939
|
+
async exportData(payload) {
|
|
940
|
+
const { contentTypeID, entryID, locale } = parsePayload(payload);
|
|
941
|
+
const contentType = await getContentType(contentTypeID);
|
|
942
|
+
const entry = await getEntry(contentTypeID, entryID, locale);
|
|
943
|
+
const contentFields = await processEntryFields(
|
|
944
|
+
entry,
|
|
945
|
+
contentType.attributes
|
|
946
|
+
);
|
|
947
|
+
return transformResponse(contentFields);
|
|
948
|
+
},
|
|
949
|
+
async importData(payload) {
|
|
950
|
+
const { contentTypeID, entryID } = parsePayload(payload);
|
|
951
|
+
const sourceLocale = payload.source;
|
|
952
|
+
const targetLocale = payload.target;
|
|
953
|
+
try {
|
|
954
|
+
const existingEntry = await getEntry(
|
|
955
|
+
contentTypeID,
|
|
956
|
+
entryID,
|
|
957
|
+
targetLocale
|
|
958
|
+
);
|
|
959
|
+
const targetSchema = await getContentType(contentTypeID);
|
|
960
|
+
const data = prepareImportData(
|
|
961
|
+
payload.document[0].fields,
|
|
962
|
+
existingEntry,
|
|
963
|
+
targetSchema
|
|
964
|
+
);
|
|
965
|
+
if (targetSchema.pluginOptions.i18n.localized === true) {
|
|
966
|
+
await updateEntry(
|
|
967
|
+
contentTypeID,
|
|
968
|
+
entryID,
|
|
969
|
+
sourceLocale,
|
|
970
|
+
targetLocale,
|
|
971
|
+
data,
|
|
972
|
+
targetSchema.attributes
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
return { success: true };
|
|
976
|
+
} catch (error) {
|
|
977
|
+
return { success: false };
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
async requestTranslation(payload) {
|
|
981
|
+
const { license } = await this.getLicense();
|
|
982
|
+
const response = await fetch(
|
|
983
|
+
"https://cms-strapi-service-7866fdd79eab.herokuapp.com/translate",
|
|
984
|
+
{
|
|
985
|
+
method: "POST",
|
|
986
|
+
headers: {
|
|
987
|
+
Authorization: `${license}`,
|
|
988
|
+
"Content-Type": "application/json"
|
|
989
|
+
},
|
|
990
|
+
body: JSON.stringify(payload)
|
|
991
|
+
}
|
|
992
|
+
);
|
|
993
|
+
if (response.status === 204) return true;
|
|
994
|
+
},
|
|
995
|
+
async getEmail(ctx) {
|
|
996
|
+
const user = ctx.state.user;
|
|
997
|
+
if (!user) {
|
|
998
|
+
return { email: null };
|
|
999
|
+
}
|
|
1000
|
+
return { email: user.email };
|
|
1001
|
+
},
|
|
1002
|
+
async getLanguages() {
|
|
1003
|
+
try {
|
|
1004
|
+
const locales = await strapi2.plugin("i18n").service("locales").find();
|
|
1005
|
+
const localeMap = {};
|
|
1006
|
+
locales.forEach((locale) => {
|
|
1007
|
+
localeMap[locale.code] = locale.name;
|
|
1008
|
+
});
|
|
1009
|
+
return localeMap;
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
return {};
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
async ping() {
|
|
1015
|
+
return;
|
|
1016
|
+
},
|
|
1017
|
+
async getEntryData(contentTypeID, entryID, locale) {
|
|
1018
|
+
const entry = await getEntry(contentTypeID, entryID, locale);
|
|
1019
|
+
return entry;
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
};
|
|
1023
|
+
const services = {
|
|
1024
|
+
service
|
|
1025
|
+
};
|
|
1026
|
+
const index = {
|
|
1027
|
+
register,
|
|
1028
|
+
bootstrap,
|
|
1029
|
+
destroy,
|
|
1030
|
+
config,
|
|
1031
|
+
controllers,
|
|
1032
|
+
routes,
|
|
1033
|
+
services,
|
|
1034
|
+
contentTypes,
|
|
1035
|
+
policies,
|
|
1036
|
+
middlewares
|
|
1037
|
+
};
|
|
1038
|
+
module.exports = index;
|