multi-content-type-relation 0.1.0 → 2.0.1
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/dist/_chunks/en-Bk9okOMP.js +32 -0
- package/dist/_chunks/en-Cj4T04Z2.mjs +32 -0
- package/dist/_chunks/fr-KHPiQOFP.mjs +32 -0
- package/dist/_chunks/fr-ZS3aTnjj.js +32 -0
- package/dist/_chunks/index-5liGVtQX.js +3566 -0
- package/dist/_chunks/index-CQ_pNHWj.js +335 -0
- package/dist/_chunks/index-Cyd6H1uV.mjs +336 -0
- package/dist/_chunks/index-D3M5NaOA.mjs +3564 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/Input/InputContentSuggestions.d.ts +13 -0
- package/dist/admin/src/components/Input/MainInput.d.ts +16 -0
- package/dist/admin/src/components/Input/PublicationState.d.ts +6 -0
- package/dist/admin/src/components/Input/TableItem.d.ts +12 -0
- package/dist/admin/src/components/Input/index.d.ts +2 -0
- package/dist/admin/src/components/PluginIcon/index.d.ts +2 -0
- package/dist/admin/src/components/SidePanel/SidePanel.d.ts +7 -0
- package/dist/admin/src/helpers/content.d.ts +5 -0
- package/dist/admin/src/helpers/storage.d.ts +15 -0
- package/dist/admin/src/hooks/useSearchedEntries.d.ts +6 -0
- package/dist/admin/src/hooks/useTranslate.d.ts +4 -0
- package/dist/admin/src/index.d.ts +9 -0
- package/dist/admin/src/interface.d.ts +33 -0
- package/dist/admin/src/pluginId.d.ts +2 -0
- package/dist/server/index.js +506 -25
- package/dist/server/index.mjs +509 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +13 -0
- package/dist/server/src/content-types/index.d.ts +35 -0
- package/dist/server/src/content-types/mctr-relation/index.d.ts +33 -0
- package/dist/server/src/content-types/mctr-relation/schema.d.ts +31 -0
- package/dist/server/src/controllers/controller.d.ts +27 -0
- package/dist/server/src/controllers/index.d.ts +28 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/helpers/index.d.ts +2 -0
- package/dist/server/src/index.d.ts +113 -0
- package/dist/server/src/interface.d.ts +42 -0
- package/dist/server/src/middlewares/index.d.ts +4 -0
- package/dist/server/src/middlewares/middleware.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/index.d.ts +8 -0
- package/dist/server/src/services/service.d.ts +7 -0
- package/dist/server/src/utils.d.ts +3 -0
- package/package.json +55 -27
- package/TODO.md +0 -4
- package/admin/src/components/Input/InputContentSuggestions.tsx +0 -162
- package/admin/src/components/Input/MainInput.tsx +0 -135
- package/admin/src/components/Input/PublicationState.tsx +0 -28
- package/admin/src/components/Input/TableItem.tsx +0 -109
- package/admin/src/components/Input/index.tsx +0 -27
- package/admin/src/components/PluginIcon/index.tsx +0 -12
- package/admin/src/helpers/content.ts +0 -60
- package/admin/src/helpers/storage.ts +0 -32
- package/admin/src/hooks/useSearchedEntries.ts +0 -41
- package/admin/src/index.tsx +0 -140
- package/admin/src/interface.ts +0 -37
- package/admin/src/pluginId.ts +0 -5
- package/admin/src/translations/en.json +0 -1
- package/admin/src/translations/fr.json +0 -1
- package/admin/src/utils/getTrad.ts +0 -5
- package/dist/server/bootstrap.js +0 -5
- package/dist/server/config/index.js +0 -27
- package/dist/server/content-types/index.js +0 -3
- package/dist/server/controllers/controller.js +0 -92
- package/dist/server/controllers/index.js +0 -9
- package/dist/server/destroy.js +0 -5
- package/dist/server/interface.js +0 -2
- package/dist/server/middlewares/index.js +0 -9
- package/dist/server/middlewares/middleware.js +0 -163
- package/dist/server/policies/index.js +0 -3
- package/dist/server/register.js +0 -15
- package/dist/server/routes/index.js +0 -29
- package/dist/server/services/index.js +0 -9
- package/dist/server/services/service.js +0 -8
- package/dist/server/utils.js +0 -15
- package/dist/tsconfig.server.tsbuildinfo +0 -1
- package/server/bootstrap.ts +0 -5
- package/server/config/index.ts +0 -28
- package/server/content-types/index.ts +0 -1
- package/server/controllers/controller.ts +0 -107
- package/server/controllers/index.ts +0 -5
- package/server/destroy.ts +0 -5
- package/server/index.ts +0 -23
- package/server/interface.ts +0 -50
- package/server/middlewares/index.ts +0 -5
- package/server/middlewares/middleware.ts +0 -197
- package/server/policies/index.ts +0 -1
- package/server/register.ts +0 -14
- package/server/routes/index.ts +0 -27
- package/server/services/index.ts +0 -5
- package/server/services/service.ts +0 -11
- package/server/utils.ts +0 -14
- package/strapi-admin.js +0 -3
- package/strapi-server.js +0 -3
- package/tsconfig.json +0 -20
- package/tsconfig.server.json +0 -25
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
const bootstrap = ({ strapi: strapi2 }) => {
|
|
2
|
+
};
|
|
3
|
+
const destroy = ({ strapi: strapi2 }) => {
|
|
4
|
+
};
|
|
5
|
+
const getPluginConfiguration = () => {
|
|
6
|
+
const pluginConfiguration = strapi.config.get(
|
|
7
|
+
"plugin::multi-content-type-relation"
|
|
8
|
+
);
|
|
9
|
+
return pluginConfiguration;
|
|
10
|
+
};
|
|
11
|
+
const log = (message) => {
|
|
12
|
+
const { debug } = getPluginConfiguration();
|
|
13
|
+
if (debug) {
|
|
14
|
+
console.log(`[MCTR DEBUG] ${message}`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const flattenObj = (obj, parent, res = {}) => {
|
|
18
|
+
for (let key in obj) {
|
|
19
|
+
let propName = parent ? parent + "." + key : key;
|
|
20
|
+
if (typeof obj[key] == "object") {
|
|
21
|
+
flattenObj(obj[key], propName, res);
|
|
22
|
+
} else {
|
|
23
|
+
res[propName] = obj[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return res;
|
|
27
|
+
};
|
|
28
|
+
const unflatten = (data) => {
|
|
29
|
+
var result = {};
|
|
30
|
+
for (var i in data) {
|
|
31
|
+
var keys = i.split(".");
|
|
32
|
+
keys.reduce(function(r, e, j) {
|
|
33
|
+
return r[e] || (r[e] = isNaN(Number(keys[j + 1])) ? keys.length - 1 == j ? data[i] : {} : []);
|
|
34
|
+
}, result);
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
const middleware = async (ctx, next) => {
|
|
39
|
+
await next();
|
|
40
|
+
if (!ctx.body) return;
|
|
41
|
+
if (!ctx.body.data) return;
|
|
42
|
+
if ([
|
|
43
|
+
"collection-types.create",
|
|
44
|
+
"collection-types.update",
|
|
45
|
+
"single-types.createOrUpdate"
|
|
46
|
+
].includes(ctx?.state?.route?.handler)) {
|
|
47
|
+
const [, _, __, rest] = ctx?.request.url.split("/");
|
|
48
|
+
const contentType = rest.split("?")[0];
|
|
49
|
+
const isDraftAndPublish = strapi.contentTypes[contentType].options?.draftAndPublish;
|
|
50
|
+
if (isDraftAndPublish) {
|
|
51
|
+
log(`[MIDDLEWARE] ${contentType}is draft and publish`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const documentId = ctx.body.data.documentId;
|
|
55
|
+
syncMctrRelation(documentId, contentType);
|
|
56
|
+
}
|
|
57
|
+
if (["collection-types.publish", "single-types.publish"].includes(
|
|
58
|
+
ctx?.state?.route?.handler
|
|
59
|
+
)) {
|
|
60
|
+
const [, _, __, rest] = ctx?.request.url.split("/");
|
|
61
|
+
const contentType = rest.split("?")[0];
|
|
62
|
+
const documentId = ctx.body.data.documentId;
|
|
63
|
+
syncMctrRelation(documentId, contentType);
|
|
64
|
+
}
|
|
65
|
+
if (!ctx?.request?.url?.startsWith("/api")) return;
|
|
66
|
+
if (ctx.request.method !== "GET") return;
|
|
67
|
+
if (!ctx.body) return;
|
|
68
|
+
const configuration = getPluginConfiguration();
|
|
69
|
+
const handler = ctx.state.route.handler;
|
|
70
|
+
const contentTypes2 = Object.keys(strapi.contentTypes);
|
|
71
|
+
log(`[MIDDLEWARE] URL: ${ctx.request.url} (${ctx.request.method})`);
|
|
72
|
+
log(`[MIDDLEWARE] Strapi Route: ${JSON.stringify(ctx.state.route, null, 2)}`);
|
|
73
|
+
if (typeof handler !== "string") return;
|
|
74
|
+
const validHandler = contentTypes2.filter((contentType) => contentType.startsWith("api::")).some(
|
|
75
|
+
(contentType) => handler.includes(`${contentType}.findOne`) || handler.includes(`${contentType}.findMany`) || handler.includes(`${contentType}.find`)
|
|
76
|
+
);
|
|
77
|
+
log(`[MIDDLEWARE] Is valid handler: ${validHandler}`);
|
|
78
|
+
if (!validHandler) return;
|
|
79
|
+
const context = {
|
|
80
|
+
configuration,
|
|
81
|
+
publicationState: ctx.request.query?.["publicationState"] ?? "live"
|
|
82
|
+
};
|
|
83
|
+
log(`[MIDDLEWARE] Context Body: ${JSON.stringify(ctx.body, null, 2)}`);
|
|
84
|
+
if (ctx.body.error || !ctx.body?.data) return;
|
|
85
|
+
const hydratedData = await augmentMRCT(ctx.body, 1, context);
|
|
86
|
+
ctx.body.data = hydratedData;
|
|
87
|
+
};
|
|
88
|
+
const augmentMRCT = async (strapiResponse, currentDepth, context) => {
|
|
89
|
+
if (Array.isArray(strapiResponse.data)) {
|
|
90
|
+
const promises = strapiResponse.data.map(
|
|
91
|
+
(item) => hydrateMRCT(item, currentDepth, context)
|
|
92
|
+
);
|
|
93
|
+
return await Promise.all(promises);
|
|
94
|
+
} else {
|
|
95
|
+
return await hydrateMRCT(strapiResponse.data, currentDepth, context);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const hydrateMRCT = async (content, currentDepth, context) => {
|
|
99
|
+
const eligibleProperties = /* @__PURE__ */ new Set();
|
|
100
|
+
const contentsToFetch = /* @__PURE__ */ new Set();
|
|
101
|
+
const { configuration } = context;
|
|
102
|
+
const flattenedProperties = flattenObj(content, null);
|
|
103
|
+
for (const [key, value] of Object.entries(flattenedProperties)) {
|
|
104
|
+
if (typeof value !== "string" || !value.includes("MRCT")) continue;
|
|
105
|
+
try {
|
|
106
|
+
const field = JSON.parse(value);
|
|
107
|
+
if (!Array.isArray(field)) continue;
|
|
108
|
+
for (const item of field) {
|
|
109
|
+
if (Object.keys(item).length !== 3 || !item.uid && typeof item.uid !== "string" || !item.documentId)
|
|
110
|
+
continue;
|
|
111
|
+
const compositeID = `${item.uid}####${item.documentId}`;
|
|
112
|
+
eligibleProperties.add(key);
|
|
113
|
+
if (contentsToFetch.has(compositeID)) continue;
|
|
114
|
+
else contentsToFetch.add(compositeID);
|
|
115
|
+
}
|
|
116
|
+
} catch (e) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!contentsToFetch.size) return content;
|
|
121
|
+
log(
|
|
122
|
+
`[MCTR HYDRATOR] Depth: ${currentDepth}, Hydrating MCTR for ID ${content.id}`
|
|
123
|
+
);
|
|
124
|
+
const promises = [];
|
|
125
|
+
for (const item of Array.from(contentsToFetch)) {
|
|
126
|
+
const [uid, documentId] = item.split("####");
|
|
127
|
+
const promise = strapi.documents(uid).findOne({ documentId, populate: "*", status: "published" }).then(async (response) => {
|
|
128
|
+
if (!response) return { uid, response };
|
|
129
|
+
if (configuration.recursive.enabled && currentDepth < configuration.recursive.maxDepth) {
|
|
130
|
+
const hydratedResponse = await hydrateMRCT(
|
|
131
|
+
response,
|
|
132
|
+
//TODO: fix me
|
|
133
|
+
currentDepth + 1,
|
|
134
|
+
context
|
|
135
|
+
);
|
|
136
|
+
return {
|
|
137
|
+
uid,
|
|
138
|
+
response: {
|
|
139
|
+
documentId: response.documentId,
|
|
140
|
+
...hydratedResponse
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
return {
|
|
145
|
+
uid,
|
|
146
|
+
response: {
|
|
147
|
+
documentId: response.documentId,
|
|
148
|
+
...response
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
promises.push(promise);
|
|
154
|
+
}
|
|
155
|
+
const linkedEntries = await Promise.all(promises);
|
|
156
|
+
const filteredLinkedEntries = linkedEntries.filter((linkedEntry) => Boolean(linkedEntry.response)).filter((linkedEntry) => {
|
|
157
|
+
const contentTypeConfiguration = strapi.contentTypes[linkedEntry.uid];
|
|
158
|
+
if (!contentTypeConfiguration) return true;
|
|
159
|
+
if (!contentTypeConfiguration.options?.draftAndPublish) return true;
|
|
160
|
+
if (context.publicationState === "preview") return true;
|
|
161
|
+
return typeof linkedEntry.response?.publishedAt === "string";
|
|
162
|
+
});
|
|
163
|
+
for (const key of Array.from(eligibleProperties)) {
|
|
164
|
+
const hydratedArray = [];
|
|
165
|
+
const unhydratedField = JSON.parse(flattenedProperties[key]);
|
|
166
|
+
for (const item of unhydratedField) {
|
|
167
|
+
const matchingContent = filteredLinkedEntries.find(
|
|
168
|
+
(linkedEntry) => item.uid === linkedEntry.uid && item.documentId === linkedEntry.response.documentId
|
|
169
|
+
);
|
|
170
|
+
if (matchingContent) {
|
|
171
|
+
hydratedArray.push(matchingContent.response);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
flattenedProperties[key] = hydratedArray;
|
|
175
|
+
}
|
|
176
|
+
const newContent = unflatten(flattenedProperties);
|
|
177
|
+
return {
|
|
178
|
+
...content,
|
|
179
|
+
...newContent
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
const syncMctrRelation = async (documentId, uid) => {
|
|
183
|
+
const mctrDocuments = await strapi.documents("plugin::multi-content-type-relation.mctr-relation").findMany({
|
|
184
|
+
filters: {
|
|
185
|
+
sourceDocId: documentId
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
if (mctrDocuments.length !== 0) {
|
|
189
|
+
log(`[SYNC] Delete MCTR relations for ${documentId}`);
|
|
190
|
+
await Promise.all(
|
|
191
|
+
mctrDocuments.map(async ({ documentId: documentId2 }) => {
|
|
192
|
+
await strapi.documents("plugin::multi-content-type-relation.mctr-relation").delete({
|
|
193
|
+
documentId: documentId2
|
|
194
|
+
});
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
log(`[SYNC] Find document ${documentId}`);
|
|
199
|
+
const document = await strapi.documents(uid).findOne({
|
|
200
|
+
documentId
|
|
201
|
+
});
|
|
202
|
+
const contentTypeKey = Object.keys(strapi.contentTypes).find(
|
|
203
|
+
(ct) => strapi.contentTypes[ct].uid === uid
|
|
204
|
+
);
|
|
205
|
+
if (!contentTypeKey) return;
|
|
206
|
+
const contentType = strapi.contentTypes[contentTypeKey];
|
|
207
|
+
const mctrFields = Object.keys(contentType.attributes).filter(
|
|
208
|
+
(field) => contentType.attributes[field].customField === "plugin::multi-content-type-relation.multi-content-type-relation"
|
|
209
|
+
);
|
|
210
|
+
if (mctrFields.length === 0) return;
|
|
211
|
+
const targetJSON = [];
|
|
212
|
+
mctrFields.forEach(async (field) => {
|
|
213
|
+
const fieldValue = document[field];
|
|
214
|
+
if (!fieldValue) return;
|
|
215
|
+
try {
|
|
216
|
+
const mctrField = JSON.parse(fieldValue);
|
|
217
|
+
mctrField.forEach((item) => {
|
|
218
|
+
targetJSON.push(`${field}##${item.uid}##${item.documentId}`);
|
|
219
|
+
});
|
|
220
|
+
} catch (e) {
|
|
221
|
+
log(`[SYNC] Error parsing field ${field} ${fieldValue}`);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
log(`[SYNC] Target JSON ${targetJSON}`);
|
|
225
|
+
if (targetJSON.length === 0) return;
|
|
226
|
+
await strapi.documents("plugin::multi-content-type-relation.mctr-relation").create({
|
|
227
|
+
data: {
|
|
228
|
+
sourceUID: uid,
|
|
229
|
+
sourceDocId: documentId,
|
|
230
|
+
target: targetJSON
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
log(`[SYNC] MCTR relation created for ${documentId}`);
|
|
234
|
+
};
|
|
235
|
+
const middlewares = {
|
|
236
|
+
middleware
|
|
237
|
+
};
|
|
238
|
+
const register = ({ strapi: strapi2 }) => {
|
|
239
|
+
strapi2.customFields.register({
|
|
240
|
+
name: "multi-content-type-relation",
|
|
241
|
+
plugin: "multi-content-type-relation",
|
|
242
|
+
type: "richtext"
|
|
243
|
+
});
|
|
244
|
+
strapi2.server.use(middlewares.middleware);
|
|
245
|
+
};
|
|
246
|
+
const config = {
|
|
247
|
+
default: ({ env }) => {
|
|
248
|
+
return {
|
|
249
|
+
recursive: {
|
|
250
|
+
enabled: false,
|
|
251
|
+
maxDepth: 1
|
|
252
|
+
},
|
|
253
|
+
debug: false
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
validator(config2) {
|
|
257
|
+
if (typeof config2.recursive !== "object") {
|
|
258
|
+
throw new Error("recursive must be an object");
|
|
259
|
+
}
|
|
260
|
+
if (typeof config2.recursive.enabled !== "boolean") {
|
|
261
|
+
throw new Error("recursive.enabled must be a boolean");
|
|
262
|
+
}
|
|
263
|
+
if (typeof config2.recursive.maxDepth !== "number") {
|
|
264
|
+
throw new Error("recursive.maxDepth must be a number");
|
|
265
|
+
}
|
|
266
|
+
if (typeof config2.debug !== "boolean") {
|
|
267
|
+
throw new Error("Debug must be a boolean");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const schema = {
|
|
272
|
+
collectionName: "mctr-relation",
|
|
273
|
+
info: {
|
|
274
|
+
singularName: "mctr-relation",
|
|
275
|
+
pluralName: "mctr-relations",
|
|
276
|
+
displayName: "MCTR Relation",
|
|
277
|
+
name: "mctr-relation"
|
|
278
|
+
},
|
|
279
|
+
pluginOptions: {
|
|
280
|
+
"content-manager": {
|
|
281
|
+
visible: false
|
|
282
|
+
},
|
|
283
|
+
"content-type-builder": {
|
|
284
|
+
visible: false
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
attributes: {
|
|
288
|
+
sourceUID: {
|
|
289
|
+
type: "string",
|
|
290
|
+
required: true
|
|
291
|
+
},
|
|
292
|
+
sourceDocId: {
|
|
293
|
+
type: "string",
|
|
294
|
+
required: true
|
|
295
|
+
},
|
|
296
|
+
target: {
|
|
297
|
+
type: "json"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const mctrRelation = {
|
|
302
|
+
schema
|
|
303
|
+
};
|
|
304
|
+
const contentTypes = {
|
|
305
|
+
"mctr-relation": mctrRelation
|
|
306
|
+
};
|
|
307
|
+
const controller = ({ strapi: strapi2 }) => ({
|
|
308
|
+
getMatchingContent(ctx) {
|
|
309
|
+
const contentTypes2 = strapi2.contentTypes;
|
|
310
|
+
const body = ctx.request.body;
|
|
311
|
+
const requestedContentTypes = body.contentTypes;
|
|
312
|
+
const keyword = body.keyword;
|
|
313
|
+
const locale = body.locale;
|
|
314
|
+
const mapping = requestedContentTypes.reduce(
|
|
315
|
+
(accumulator, contentType) => {
|
|
316
|
+
Object.keys(contentTypes2).forEach((model) => {
|
|
317
|
+
const strapiContentType = contentTypes2[model];
|
|
318
|
+
if (strapiContentType.info.singularName === contentType || strapiContentType.info.pluralName === contentType) {
|
|
319
|
+
accumulator[contentType] = {
|
|
320
|
+
uid: model,
|
|
321
|
+
displayName: contentTypes2[model].info.displayName,
|
|
322
|
+
searchableField: strapi2.plugin("multi-content-type-relation").service("service").getFirstStringFieldInContentType(contentTypes2[model])
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return accumulator;
|
|
327
|
+
},
|
|
328
|
+
{}
|
|
329
|
+
);
|
|
330
|
+
const promises = Object.keys(mapping).map((contentType) => {
|
|
331
|
+
const uid = mapping[contentType].uid;
|
|
332
|
+
return strapi2.documents(uid).findMany({
|
|
333
|
+
filters: {
|
|
334
|
+
[mapping[contentType].searchableField]: {
|
|
335
|
+
$containsi: keyword
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
locale,
|
|
339
|
+
status: "published"
|
|
340
|
+
}).then((results) => {
|
|
341
|
+
let contents = Array.isArray(results) ? results : typeof results === "object" && results ? [results] : [];
|
|
342
|
+
const contentTypeDefinition = strapi2.contentType(uid);
|
|
343
|
+
if (contentTypeDefinition?.options?.draftAndPublish) {
|
|
344
|
+
contents = contents.filter(
|
|
345
|
+
(content) => content.publishedAt !== null
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
uid,
|
|
350
|
+
displayName: mapping[contentType].displayName,
|
|
351
|
+
searchableField: mapping[contentType].searchableField,
|
|
352
|
+
results: contents
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
return Promise.all(promises);
|
|
357
|
+
},
|
|
358
|
+
validateRelations: async function(ctx) {
|
|
359
|
+
const contentTypes2 = strapi2.contentTypes;
|
|
360
|
+
const body = ctx.request.body;
|
|
361
|
+
const entries = body.entries;
|
|
362
|
+
const promises = entries.map((entry) => {
|
|
363
|
+
return strapi2.documents(entry.uid).findOne({
|
|
364
|
+
documentId: entry.documentId,
|
|
365
|
+
populate: "*",
|
|
366
|
+
status: "published"
|
|
367
|
+
}).then((result) => {
|
|
368
|
+
return {
|
|
369
|
+
uid: entry.uid,
|
|
370
|
+
result
|
|
371
|
+
};
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
const responses = await Promise.all(promises);
|
|
375
|
+
return responses.map((response) => {
|
|
376
|
+
return {
|
|
377
|
+
displayName: contentTypes2[response.uid].info.displayName,
|
|
378
|
+
uid: response.uid,
|
|
379
|
+
searchableField: strapi2.plugin("multi-content-type-relation").service("service").getFirstStringFieldInContentType(contentTypes2[response.uid]),
|
|
380
|
+
item: response.result
|
|
381
|
+
};
|
|
382
|
+
}).filter((entry) => entry.item);
|
|
383
|
+
},
|
|
384
|
+
listContentTypes: async function() {
|
|
385
|
+
const contentTypes2 = [];
|
|
386
|
+
for (const contentType of Object.values(strapi2.contentTypes)) {
|
|
387
|
+
if ((contentType.kind === "collectionType" || contentType.kind === "singleType") && !contentType.plugin) {
|
|
388
|
+
contentTypes2.push(contentType);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return contentTypes2;
|
|
392
|
+
},
|
|
393
|
+
fetchRevertRelations: async function(ctx) {
|
|
394
|
+
const body = ctx.request.body;
|
|
395
|
+
let documentId = body.documentId;
|
|
396
|
+
const uid = body.uid;
|
|
397
|
+
const isSingleType = body.isSingleType;
|
|
398
|
+
if (isSingleType) {
|
|
399
|
+
const document = await strapi2.documents(uid).findFirst({
|
|
400
|
+
populate: "*",
|
|
401
|
+
status: "published"
|
|
402
|
+
});
|
|
403
|
+
if (!document) return [];
|
|
404
|
+
documentId = document.documentId;
|
|
405
|
+
}
|
|
406
|
+
const mctrRelations = await strapi2.documents("plugin::multi-content-type-relation.mctr-relation").findMany({
|
|
407
|
+
filters: {
|
|
408
|
+
target: {
|
|
409
|
+
$containsi: `${uid}##${documentId}`
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
const relations = await Promise.all(
|
|
414
|
+
mctrRelations.map(async (relation) => {
|
|
415
|
+
const sourceDocumentId = relation.sourceDocId;
|
|
416
|
+
const sourceUid = relation.sourceUID;
|
|
417
|
+
const target = relation.target.find(
|
|
418
|
+
(target2) => target2.includes(`${uid}##${documentId}`)
|
|
419
|
+
);
|
|
420
|
+
const [field] = target.split("##");
|
|
421
|
+
const contentTypeName = Object.keys(strapi2.contentTypes).find(
|
|
422
|
+
(key) => strapi2.contentTypes[key].uid === sourceUid
|
|
423
|
+
);
|
|
424
|
+
if (!contentTypeName) return null;
|
|
425
|
+
const contentType = strapi2.contentTypes[contentTypeName];
|
|
426
|
+
const document = await strapi2.documents(sourceUid).findOne({
|
|
427
|
+
documentId: sourceDocumentId,
|
|
428
|
+
populate: "*",
|
|
429
|
+
status: "published"
|
|
430
|
+
});
|
|
431
|
+
const searchableField = strapi2.plugin("multi-content-type-relation").service("service").getFirstStringFieldInContentType(contentType);
|
|
432
|
+
return {
|
|
433
|
+
title: document[searchableField],
|
|
434
|
+
uid: sourceUid,
|
|
435
|
+
isSingleType: contentType.kind === "singleType",
|
|
436
|
+
field,
|
|
437
|
+
type: contentType.info.displayName,
|
|
438
|
+
documentId: sourceDocumentId
|
|
439
|
+
};
|
|
440
|
+
})
|
|
441
|
+
);
|
|
442
|
+
return relations;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
const controllers = {
|
|
446
|
+
controller
|
|
447
|
+
};
|
|
448
|
+
const policies = {};
|
|
449
|
+
const routes = [
|
|
450
|
+
{
|
|
451
|
+
method: "GET",
|
|
452
|
+
path: "/list-content-types",
|
|
453
|
+
handler: "controller.listContentTypes",
|
|
454
|
+
config: {
|
|
455
|
+
policies: [],
|
|
456
|
+
auth: false
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
method: "POST",
|
|
461
|
+
path: "/get-content",
|
|
462
|
+
handler: "controller.getMatchingContent",
|
|
463
|
+
config: {
|
|
464
|
+
policies: []
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
method: "POST",
|
|
469
|
+
path: "/validate-relations",
|
|
470
|
+
handler: "controller.validateRelations",
|
|
471
|
+
config: {
|
|
472
|
+
policies: []
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
method: "POST",
|
|
477
|
+
path: "/fetch-revert-relations",
|
|
478
|
+
handler: "controller.fetchRevertRelations",
|
|
479
|
+
config: {
|
|
480
|
+
policies: []
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
];
|
|
484
|
+
const service = ({ strapi: strapi2 }) => ({
|
|
485
|
+
getFirstStringFieldInContentType(contentType) {
|
|
486
|
+
const result = Object.keys(contentType.attributes).find(
|
|
487
|
+
(attribute) => contentType.attributes[attribute].type === "string"
|
|
488
|
+
);
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
const services = {
|
|
493
|
+
service
|
|
494
|
+
};
|
|
495
|
+
const index = {
|
|
496
|
+
register,
|
|
497
|
+
bootstrap,
|
|
498
|
+
destroy,
|
|
499
|
+
config,
|
|
500
|
+
controllers,
|
|
501
|
+
routes,
|
|
502
|
+
services,
|
|
503
|
+
contentTypes,
|
|
504
|
+
policies,
|
|
505
|
+
middlewares
|
|
506
|
+
};
|
|
507
|
+
export {
|
|
508
|
+
index as default
|
|
509
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
'mctr-relation': {
|
|
3
|
+
schema: {
|
|
4
|
+
collectionName: string;
|
|
5
|
+
info: {
|
|
6
|
+
singularName: string;
|
|
7
|
+
pluralName: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
pluginOptions: {
|
|
12
|
+
'content-manager': {
|
|
13
|
+
visible: boolean;
|
|
14
|
+
};
|
|
15
|
+
'content-type-builder': {
|
|
16
|
+
visible: boolean;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
attributes: {
|
|
20
|
+
sourceUID: {
|
|
21
|
+
type: string;
|
|
22
|
+
required: boolean;
|
|
23
|
+
};
|
|
24
|
+
sourceDocId: {
|
|
25
|
+
type: string;
|
|
26
|
+
required: boolean;
|
|
27
|
+
};
|
|
28
|
+
target: {
|
|
29
|
+
type: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export default _default;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
schema: {
|
|
3
|
+
collectionName: string;
|
|
4
|
+
info: {
|
|
5
|
+
singularName: string;
|
|
6
|
+
pluralName: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
pluginOptions: {
|
|
11
|
+
'content-manager': {
|
|
12
|
+
visible: boolean;
|
|
13
|
+
};
|
|
14
|
+
'content-type-builder': {
|
|
15
|
+
visible: boolean;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
attributes: {
|
|
19
|
+
sourceUID: {
|
|
20
|
+
type: string;
|
|
21
|
+
required: boolean;
|
|
22
|
+
};
|
|
23
|
+
sourceDocId: {
|
|
24
|
+
type: string;
|
|
25
|
+
required: boolean;
|
|
26
|
+
};
|
|
27
|
+
target: {
|
|
28
|
+
type: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
collectionName: string;
|
|
3
|
+
info: {
|
|
4
|
+
singularName: string;
|
|
5
|
+
pluralName: string;
|
|
6
|
+
displayName: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
pluginOptions: {
|
|
10
|
+
'content-manager': {
|
|
11
|
+
visible: boolean;
|
|
12
|
+
};
|
|
13
|
+
'content-type-builder': {
|
|
14
|
+
visible: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
attributes: {
|
|
18
|
+
sourceUID: {
|
|
19
|
+
type: string;
|
|
20
|
+
required: boolean;
|
|
21
|
+
};
|
|
22
|
+
sourceDocId: {
|
|
23
|
+
type: string;
|
|
24
|
+
required: boolean;
|
|
25
|
+
};
|
|
26
|
+
target: {
|
|
27
|
+
type: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export default _default;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Core, UID } from '@strapi/strapi';
|
|
2
|
+
declare const _default: ({ strapi }: {
|
|
3
|
+
strapi: Core.Strapi;
|
|
4
|
+
}) => {
|
|
5
|
+
getMatchingContent(ctx: any): Promise<{
|
|
6
|
+
uid: UID.ContentType;
|
|
7
|
+
displayName: string;
|
|
8
|
+
searchableField: string;
|
|
9
|
+
results: import("@strapi/types/dist/modules/documents").AnyDocument[];
|
|
10
|
+
}[]>;
|
|
11
|
+
validateRelations: (ctx: any) => Promise<{
|
|
12
|
+
displayName: any;
|
|
13
|
+
uid: string;
|
|
14
|
+
searchableField: any;
|
|
15
|
+
item: import("@strapi/types/dist/modules/documents").AnyDocument;
|
|
16
|
+
}[]>;
|
|
17
|
+
listContentTypes: () => Promise<Record<string, unknown>[]>;
|
|
18
|
+
fetchRevertRelations: (ctx: any) => Promise<{
|
|
19
|
+
title: any;
|
|
20
|
+
uid: any;
|
|
21
|
+
isSingleType: boolean;
|
|
22
|
+
field: any;
|
|
23
|
+
type: any;
|
|
24
|
+
documentId: any;
|
|
25
|
+
}[]>;
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
controller: ({ strapi }: {
|
|
3
|
+
strapi: import("@strapi/types/dist/core").Strapi;
|
|
4
|
+
}) => {
|
|
5
|
+
getMatchingContent(ctx: any): Promise<{
|
|
6
|
+
uid: import("@strapi/types/dist/uid").ContentType;
|
|
7
|
+
displayName: string;
|
|
8
|
+
searchableField: string;
|
|
9
|
+
results: import("@strapi/types/dist/modules/documents").AnyDocument[];
|
|
10
|
+
}[]>;
|
|
11
|
+
validateRelations: (ctx: any) => Promise<{
|
|
12
|
+
displayName: any;
|
|
13
|
+
uid: string;
|
|
14
|
+
searchableField: any;
|
|
15
|
+
item: import("@strapi/types/dist/modules/documents").AnyDocument;
|
|
16
|
+
}[]>;
|
|
17
|
+
listContentTypes: () => Promise<Record<string, unknown>[]>;
|
|
18
|
+
fetchRevertRelations: (ctx: any) => Promise<{
|
|
19
|
+
title: any;
|
|
20
|
+
uid: any;
|
|
21
|
+
isSingleType: boolean;
|
|
22
|
+
field: any;
|
|
23
|
+
type: any;
|
|
24
|
+
documentId: any;
|
|
25
|
+
}[]>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export default _default;
|