strapi-relation-names 1.2.1 → 1.3.0
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 +17 -42
- package/dist/{admin/SettingsPage-BebR4ghK.js → _chunks/SettingsPage-BMwx-BuP.js} +122 -48
- package/dist/{admin/SettingsPage-DFZP5CZs.mjs → _chunks/SettingsPage-D8leItKF.mjs} +122 -48
- package/dist/{admin/index-CiS6cuI9.mjs → _chunks/index-BeLPoAiU.mjs} +1 -1
- package/dist/{admin/index-DfSAe961.js → _chunks/index-COuIED98.js} +1 -1
- package/dist/admin/index.js +2 -3
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/index.d.ts +1 -1
- package/dist/admin/src/pages/SettingsPage.d.ts +1 -0
- package/dist/admin/src/utils/template.d.ts +1 -14
- package/dist/server/index.js +279 -130
- package/dist/server/index.mjs +278 -128
- package/dist/server/src/bootstrap.d.ts +1 -1
- package/dist/server/src/config/index.d.ts +2 -0
- package/dist/server/src/controllers/index.d.ts +1 -1
- package/dist/server/src/controllers/settings.d.ts +1 -1
- package/dist/server/src/destroy.d.ts +1 -1
- package/dist/server/src/index.d.ts +20 -11
- package/dist/server/src/register.d.ts +1 -1
- package/dist/server/src/services/index.d.ts +8 -7
- package/dist/server/src/services/relation-labels.d.ts +6 -9
- package/dist/server/src/services/settings.d.ts +2 -2
- package/dist/server/src/services/utils/relation-hydration.d.ts +7 -0
- package/dist/server/src/services/utils/relation-labels.d.ts +4 -22
- package/dist/server/src/services/utils/relation-runtime.d.ts +6 -0
- package/dist/server/src/services/utils/relation-values.d.ts +1 -1
- package/dist/shared/records.d.ts +1 -0
- package/dist/shared/template.d.ts +55 -0
- package/package.json +28 -28
- /package/dist/{admin → _chunks}/de-Bc81LkbY.mjs +0 -0
- /package/dist/{admin → _chunks}/de-CsBvOsWR.js +0 -0
- /package/dist/{admin → _chunks}/en-Bk-HIXNd.mjs +0 -0
- /package/dist/{admin → _chunks}/en-DT4g6X2H.js +0 -0
package/dist/server/index.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
2
|
const bootstrap = ({ strapi }) => {
|
|
4
3
|
};
|
|
5
4
|
const destroy = ({ strapi }) => {
|
|
6
5
|
};
|
|
7
|
-
const PLUGIN_ID
|
|
6
|
+
const PLUGIN_ID = "strapi-relation-names";
|
|
8
7
|
const register = ({ strapi }) => {
|
|
9
|
-
const relationLabels2 = strapi.plugin(PLUGIN_ID
|
|
8
|
+
const relationLabels2 = strapi.plugin(PLUGIN_ID).service("relation-labels");
|
|
10
9
|
const controllers2 = strapi.get("controllers");
|
|
11
10
|
controllers2.extend("plugin::content-manager.relations", (controller) => ({
|
|
12
11
|
...controller,
|
|
@@ -58,6 +57,7 @@ const register = ({ strapi }) => {
|
|
|
58
57
|
await decorateConfiguration(ctx, ctx.params.uid);
|
|
59
58
|
},
|
|
60
59
|
async updateContentTypeConfiguration(ctx, next) {
|
|
60
|
+
ctx.request.body = relationLabels2.sanitizeConfigurationUpdate(ctx.request.body);
|
|
61
61
|
await controller.updateContentTypeConfiguration(ctx, next);
|
|
62
62
|
await decorateConfiguration(ctx, ctx.params.uid);
|
|
63
63
|
}
|
|
@@ -74,12 +74,28 @@ const register = ({ strapi }) => {
|
|
|
74
74
|
}
|
|
75
75
|
}));
|
|
76
76
|
};
|
|
77
|
+
const isOptionalString = (value) => value === void 0 || typeof value === "string";
|
|
77
78
|
const validateConfig = (config2) => {
|
|
78
|
-
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)
|
|
79
|
-
(
|
|
80
|
-
|
|
79
|
+
if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
|
|
80
|
+
throw new Error("The collections config must be an array of strings");
|
|
81
|
+
}
|
|
82
|
+
const typedConfig = config2;
|
|
83
|
+
if (!Array.isArray(typedConfig.collections) || !typedConfig.collections.every((collection) => typeof collection === "string")) {
|
|
81
84
|
throw new Error("The collections config must be an array of strings");
|
|
82
85
|
}
|
|
86
|
+
if (!isOptionalString(typedConfig.locale)) {
|
|
87
|
+
throw new Error("The locale config must be a string");
|
|
88
|
+
}
|
|
89
|
+
if (!isOptionalString(typedConfig.timeZone)) {
|
|
90
|
+
throw new Error("The timeZone config must be a string");
|
|
91
|
+
}
|
|
92
|
+
if (typedConfig.timeZone) {
|
|
93
|
+
try {
|
|
94
|
+
new Intl.DateTimeFormat(void 0, { timeZone: typedConfig.timeZone });
|
|
95
|
+
} catch {
|
|
96
|
+
throw new Error("The timeZone config must be a valid IANA timezone");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
83
99
|
};
|
|
84
100
|
const config = {
|
|
85
101
|
default: {
|
|
@@ -90,7 +106,7 @@ const config = {
|
|
|
90
106
|
const contentTypes = {};
|
|
91
107
|
const settings$1 = ({ strapi }) => ({
|
|
92
108
|
async find(ctx) {
|
|
93
|
-
const data = await strapi.plugin(
|
|
109
|
+
const data = await strapi.plugin(PLUGIN_ID).service("settings").get();
|
|
94
110
|
ctx.body = { data };
|
|
95
111
|
},
|
|
96
112
|
async update(ctx) {
|
|
@@ -98,7 +114,7 @@ const settings$1 = ({ strapi }) => ({
|
|
|
98
114
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
99
115
|
return ctx.badRequest("Settings must be an object");
|
|
100
116
|
}
|
|
101
|
-
const data = await strapi.plugin(
|
|
117
|
+
const data = await strapi.plugin(PLUGIN_ID).service("settings").set(body);
|
|
102
118
|
ctx.body = { data };
|
|
103
119
|
}
|
|
104
120
|
});
|
|
@@ -136,7 +152,8 @@ const routes = {
|
|
|
136
152
|
"content-api": contentAPIRoutes,
|
|
137
153
|
admin: adminAPIRoutes
|
|
138
154
|
};
|
|
139
|
-
const
|
|
155
|
+
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
156
|
+
const FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
140
157
|
"string",
|
|
141
158
|
"text",
|
|
142
159
|
"email",
|
|
@@ -151,30 +168,81 @@ const SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
|
151
168
|
"time",
|
|
152
169
|
"boolean"
|
|
153
170
|
]);
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
171
|
+
const DATE_TYPES = /* @__PURE__ */ new Set(["date", "datetime"]);
|
|
172
|
+
const SYSTEM_SCALAR_TYPES = {
|
|
173
|
+
id: "integer",
|
|
174
|
+
documentId: "string",
|
|
175
|
+
createdAt: "datetime",
|
|
176
|
+
updatedAt: "datetime",
|
|
177
|
+
publishedAt: "datetime",
|
|
178
|
+
locale: "string",
|
|
179
|
+
status: "string"
|
|
180
|
+
};
|
|
181
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
182
|
+
const PATH_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
183
|
+
const getNestedSchemaUid = (attribute) => {
|
|
184
|
+
if (attribute.type === "component") {
|
|
185
|
+
return attribute.component;
|
|
186
|
+
}
|
|
187
|
+
if (attribute.type === "relation") {
|
|
188
|
+
return attribute.targetModel ?? attribute.target;
|
|
189
|
+
}
|
|
190
|
+
return void 0;
|
|
191
|
+
};
|
|
192
|
+
const getDate = (value) => {
|
|
193
|
+
if (value instanceof Date) {
|
|
194
|
+
return Number.isNaN(value.getTime()) ? void 0 : value;
|
|
195
|
+
}
|
|
196
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
197
|
+
return void 0;
|
|
198
|
+
}
|
|
199
|
+
const date = new Date(value);
|
|
200
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
201
|
+
};
|
|
202
|
+
const isDateOnly = (value) => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
203
|
+
const formatDate = (value, options, context, preserveDateOnly) => {
|
|
204
|
+
const date = getDate(value);
|
|
205
|
+
if (!date) {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
const timeZone = preserveDateOnly && isDateOnly(value) ? "UTC" : context.timeZone;
|
|
209
|
+
return new Intl.DateTimeFormat(context.locale, {
|
|
210
|
+
...options,
|
|
211
|
+
...timeZone ? { timeZone } : {}
|
|
212
|
+
}).format(date);
|
|
213
|
+
};
|
|
214
|
+
const TRANSFORMS = {
|
|
215
|
+
toLocalDate: {
|
|
216
|
+
accepts: (fieldType) => DATE_TYPES.has(fieldType ?? ""),
|
|
217
|
+
apply: (value, context) => formatDate(value, { dateStyle: "short" }, context, true)
|
|
218
|
+
},
|
|
219
|
+
toLocalDateTime: {
|
|
220
|
+
accepts: (fieldType) => fieldType === "datetime",
|
|
221
|
+
apply: (value, context) => formatDate(value, { dateStyle: "short", timeStyle: "short" }, context, false)
|
|
222
|
+
}
|
|
223
|
+
};
|
|
165
224
|
const getPathValue = (value, path) => {
|
|
166
225
|
return path.split(".").reduce((current, segment) => {
|
|
167
226
|
if (Array.isArray(current)) {
|
|
168
|
-
return current.map((item) => isRecord
|
|
227
|
+
return current.map((item) => isRecord(item) ? item[segment] : void 0).filter((item) => item !== void 0);
|
|
169
228
|
}
|
|
170
|
-
return isRecord
|
|
229
|
+
return isRecord(current) ? current[segment] : void 0;
|
|
171
230
|
}, value);
|
|
172
231
|
};
|
|
173
|
-
const
|
|
232
|
+
const parseExpression = (value) => {
|
|
233
|
+
const parts = value.split("|").map((part) => part.trim());
|
|
234
|
+
const [path, ...transforms] = parts;
|
|
235
|
+
if (!path || !PATH_PATTERN.test(path) || transforms.some((name) => !IDENTIFIER_PATTERN.test(name))) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return { path, transforms };
|
|
239
|
+
};
|
|
240
|
+
const parseTemplateAst = (template) => {
|
|
174
241
|
if (!template.trim()) {
|
|
175
242
|
return null;
|
|
176
243
|
}
|
|
177
|
-
const
|
|
244
|
+
const tokens = [];
|
|
245
|
+
const expressions = [];
|
|
178
246
|
let cursor = 0;
|
|
179
247
|
while (cursor < template.length) {
|
|
180
248
|
const openingBrace = template.indexOf("{", cursor);
|
|
@@ -183,69 +251,98 @@ const parseTemplate = (template) => {
|
|
|
183
251
|
return null;
|
|
184
252
|
}
|
|
185
253
|
if (openingBrace === -1) {
|
|
254
|
+
tokens.push({ type: "text", value: template.slice(cursor) });
|
|
186
255
|
break;
|
|
187
256
|
}
|
|
257
|
+
if (openingBrace > cursor) {
|
|
258
|
+
tokens.push({ type: "text", value: template.slice(cursor, openingBrace) });
|
|
259
|
+
}
|
|
188
260
|
const end = template.indexOf("}", openingBrace + 1);
|
|
189
261
|
if (end === -1) {
|
|
190
262
|
return null;
|
|
191
263
|
}
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
264
|
+
const expression = parseExpression(template.slice(openingBrace + 1, end));
|
|
265
|
+
if (!expression) {
|
|
194
266
|
return null;
|
|
195
267
|
}
|
|
196
|
-
|
|
268
|
+
tokens.push({ type: "expression", expression });
|
|
269
|
+
expressions.push(expression);
|
|
197
270
|
cursor = end + 1;
|
|
198
271
|
}
|
|
199
|
-
return
|
|
272
|
+
return expressions.length > 0 ? { tokens, expressions } : null;
|
|
273
|
+
};
|
|
274
|
+
const parseTemplate = (template) => {
|
|
275
|
+
const ast = parseTemplateAst(template);
|
|
276
|
+
return ast?.expressions.map(({ path }) => path) ?? null;
|
|
277
|
+
};
|
|
278
|
+
const resolvePathType = (path, targetSchema, resolveSchema) => {
|
|
279
|
+
const segments = path.split(".");
|
|
280
|
+
let schema = targetSchema;
|
|
281
|
+
for (const [index2, segment] of segments.entries()) {
|
|
282
|
+
const isLastSegment = index2 === segments.length - 1;
|
|
283
|
+
if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_TYPES[segment]) {
|
|
284
|
+
return SYSTEM_SCALAR_TYPES[segment];
|
|
285
|
+
}
|
|
286
|
+
const attribute = schema.attributes?.[segment];
|
|
287
|
+
if (!attribute || attribute.private === true) {
|
|
288
|
+
return void 0;
|
|
289
|
+
}
|
|
290
|
+
if (isLastSegment) {
|
|
291
|
+
return FIELD_TYPES.has(attribute.type ?? "") ? attribute.type : void 0;
|
|
292
|
+
}
|
|
293
|
+
const nestedSchemaUid = getNestedSchemaUid(attribute);
|
|
294
|
+
if (!nestedSchemaUid || !resolveSchema) {
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
const nestedSchema = resolveSchema(nestedSchemaUid);
|
|
298
|
+
if (!nestedSchema) {
|
|
299
|
+
return void 0;
|
|
300
|
+
}
|
|
301
|
+
schema = nestedSchema;
|
|
302
|
+
}
|
|
303
|
+
return void 0;
|
|
200
304
|
};
|
|
201
305
|
const compileTemplate = (template, targetSchema, resolveSchema) => {
|
|
202
|
-
const
|
|
203
|
-
if (!
|
|
306
|
+
const ast = parseTemplateAst(template);
|
|
307
|
+
if (!ast || !targetSchema) {
|
|
204
308
|
return null;
|
|
205
309
|
}
|
|
206
|
-
const valid =
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
for (const [index2, segment] of segments.entries()) {
|
|
210
|
-
const attribute = schema.attributes?.[segment];
|
|
211
|
-
const isLastSegment = index2 === segments.length - 1;
|
|
212
|
-
if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_FIELDS.has(segment)) {
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
if (!attribute || attribute.private === true) {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
if (isLastSegment) {
|
|
219
|
-
return SCALAR_TYPES.has(attribute.type ?? "");
|
|
220
|
-
}
|
|
221
|
-
const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
|
|
222
|
-
if (!nestedSchemaUid || !resolveSchema) {
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
schema = resolveSchema(nestedSchemaUid);
|
|
226
|
-
if (!schema) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return false;
|
|
310
|
+
const valid = ast.expressions.every(({ path, transforms }) => {
|
|
311
|
+
const fieldType = resolvePathType(path, targetSchema, resolveSchema);
|
|
312
|
+
return Boolean(fieldType && transforms.every((name) => TRANSFORMS[name]?.accepts(fieldType)));
|
|
231
313
|
});
|
|
232
314
|
if (!valid) {
|
|
233
315
|
return null;
|
|
234
316
|
}
|
|
235
317
|
return {
|
|
318
|
+
...ast,
|
|
236
319
|
template,
|
|
237
|
-
placeholders,
|
|
238
|
-
displayField:
|
|
320
|
+
placeholders: ast.expressions.map(({ path }) => path),
|
|
321
|
+
displayField: ast.expressions[0].path.split(".").slice(-1)[0] ?? ast.expressions[0].path
|
|
239
322
|
};
|
|
240
323
|
};
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
324
|
+
const applyTransform = (value, name, context) => {
|
|
325
|
+
const transform = TRANSFORMS[name];
|
|
326
|
+
if (!transform) {
|
|
327
|
+
return "";
|
|
328
|
+
}
|
|
329
|
+
if (Array.isArray(value)) {
|
|
330
|
+
return value.map((item) => applyTransform(item, name, context));
|
|
331
|
+
}
|
|
332
|
+
return transform.apply(value, context);
|
|
333
|
+
};
|
|
334
|
+
const renderTemplate = (compiled, values, context = compiled.transformContext ?? {}) => {
|
|
335
|
+
const tokens = compiled.tokens ?? parseTemplateAst(compiled.template)?.tokens ?? [];
|
|
336
|
+
const rendered = tokens.map((token) => {
|
|
337
|
+
if (token.type === "text") {
|
|
338
|
+
return token.value;
|
|
339
|
+
}
|
|
340
|
+
const expressionValue = token.expression.transforms.reduce(
|
|
341
|
+
(value, name) => applyTransform(value, name, context),
|
|
342
|
+
getPathValue(values, token.expression.path)
|
|
343
|
+
);
|
|
344
|
+
return expressionValue === null || expressionValue === void 0 ? "" : String(expressionValue);
|
|
345
|
+
}).join("");
|
|
249
346
|
return rendered.trim();
|
|
250
347
|
};
|
|
251
348
|
const getIdentity = (value, modelType) => modelType === "component" ? value.id : value.documentId ?? value.id;
|
|
@@ -267,6 +364,11 @@ const matchesRelation = (source, candidate, targetModelType) => {
|
|
|
267
364
|
const applyLabel = (value, hydrated, runtime) => {
|
|
268
365
|
const label = renderTemplate(runtime, hydrated);
|
|
269
366
|
if (label) {
|
|
367
|
+
const isNestedTemplate = runtime.placeholders.some((placeholder) => placeholder.includes("."));
|
|
368
|
+
const isIdentityField = runtime.displayField === "id" || runtime.displayField === "documentId";
|
|
369
|
+
if (isNestedTemplate && isIdentityField) {
|
|
370
|
+
return { ...value, label };
|
|
371
|
+
}
|
|
270
372
|
return { ...value, [runtime.displayField]: label };
|
|
271
373
|
}
|
|
272
374
|
if (runtime.displayField === runtime.originalMainField) {
|
|
@@ -278,60 +380,15 @@ const applyLabel = (value, hydrated, runtime) => {
|
|
|
278
380
|
};
|
|
279
381
|
const relationValues = (value) => {
|
|
280
382
|
if (Array.isArray(value)) {
|
|
281
|
-
return value.filter(isRecord
|
|
383
|
+
return value.filter(isRecord);
|
|
282
384
|
}
|
|
283
|
-
return isRecord
|
|
385
|
+
return isRecord(value) ? [value] : [];
|
|
284
386
|
};
|
|
285
387
|
const replaceRelationValue = (original, values, hydrated, runtime) => {
|
|
286
388
|
const decorated = values.map((value) => applyLabel(value, hydrated.get(value) ?? value, runtime));
|
|
287
389
|
return Array.isArray(original) ? decorated : decorated[0] ?? original;
|
|
288
390
|
};
|
|
289
391
|
const isCollectionEnabled = (collections, uid) => collections.length === 0 || collections.includes(uid);
|
|
290
|
-
const getOriginalMainField = async (strapi, sourceSchema, targetSchema, targetField, userAbility) => {
|
|
291
|
-
const serviceName = sourceSchema.modelType === "component" ? "components" : "content-types";
|
|
292
|
-
const configuration = await strapi.plugin("content-manager").service(serviceName).findConfiguration(sourceSchema);
|
|
293
|
-
const configuredMainField = configuration?.metadatas?.[targetField]?.edit?.mainField;
|
|
294
|
-
if (typeof configuredMainField !== "string" || configuredMainField === "documentId" || configuredMainField === "id") {
|
|
295
|
-
return configuredMainField === "documentId" ? "documentId" : "id";
|
|
296
|
-
}
|
|
297
|
-
const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility, model: targetSchema.uid });
|
|
298
|
-
const attribute = targetSchema.attributes?.[configuredMainField];
|
|
299
|
-
if (!attribute || attribute.private === true || permissionChecker.cannot.read(null, configuredMainField)) {
|
|
300
|
-
return "documentId";
|
|
301
|
-
}
|
|
302
|
-
return configuredMainField;
|
|
303
|
-
};
|
|
304
|
-
const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility) => {
|
|
305
|
-
const sourceSchema = strapi.getModel(sourceUid);
|
|
306
|
-
const attribute = sourceSchema?.attributes?.[targetField];
|
|
307
|
-
if (!sourceSchema || !attribute || attribute.type !== "relation" || !attribute.target) {
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
const template = settings2.relations[sourceUid]?.[targetField];
|
|
311
|
-
const targetSchema = strapi.getModel(attribute.target);
|
|
312
|
-
const compiled = typeof template === "string" ? compileTemplate(
|
|
313
|
-
template,
|
|
314
|
-
targetSchema,
|
|
315
|
-
(uid) => strapi.getModel(uid)
|
|
316
|
-
) : null;
|
|
317
|
-
if (!compiled || !targetSchema) {
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
const originalMainField = await getOriginalMainField(
|
|
321
|
-
strapi,
|
|
322
|
-
sourceSchema,
|
|
323
|
-
targetSchema,
|
|
324
|
-
targetField,
|
|
325
|
-
userAbility
|
|
326
|
-
);
|
|
327
|
-
return {
|
|
328
|
-
...compiled,
|
|
329
|
-
displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
|
|
330
|
-
sourceUid,
|
|
331
|
-
targetUid: targetSchema.uid,
|
|
332
|
-
originalMainField
|
|
333
|
-
};
|
|
334
|
-
};
|
|
335
392
|
const buildPopulate = (placeholders) => {
|
|
336
393
|
const populate = {};
|
|
337
394
|
for (const placeholder of placeholders) {
|
|
@@ -404,8 +461,61 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
404
461
|
return new Map(values.map((value) => [value, value]));
|
|
405
462
|
}
|
|
406
463
|
};
|
|
464
|
+
const getOriginalMainField = async (strapi, sourceSchema, targetSchema, targetField, userAbility) => {
|
|
465
|
+
const serviceName = sourceSchema.modelType === "component" ? "components" : "content-types";
|
|
466
|
+
const configuration = await strapi.plugin("content-manager").service(serviceName).findConfiguration(sourceSchema);
|
|
467
|
+
const configuredMainField = configuration?.metadatas?.[targetField]?.edit?.mainField;
|
|
468
|
+
if (typeof configuredMainField !== "string" || configuredMainField === "documentId" || configuredMainField === "id") {
|
|
469
|
+
return configuredMainField === "documentId" ? "documentId" : "id";
|
|
470
|
+
}
|
|
471
|
+
const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility, model: targetSchema.uid });
|
|
472
|
+
const attribute = targetSchema.attributes?.[configuredMainField];
|
|
473
|
+
if (!attribute || attribute.private === true || permissionChecker.cannot.read(null, configuredMainField)) {
|
|
474
|
+
return "documentId";
|
|
475
|
+
}
|
|
476
|
+
return configuredMainField;
|
|
477
|
+
};
|
|
478
|
+
const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility, transformContext) => {
|
|
479
|
+
const sourceSchema = strapi.getModel(sourceUid);
|
|
480
|
+
const attribute = sourceSchema?.attributes?.[targetField];
|
|
481
|
+
if (!sourceSchema || !attribute || attribute.type !== "relation" || !attribute.target) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
const template = settings2.relations[sourceUid]?.[targetField];
|
|
485
|
+
const targetSchema = strapi.getModel(attribute.target);
|
|
486
|
+
const compiled = typeof template === "string" ? compileTemplate(
|
|
487
|
+
template,
|
|
488
|
+
targetSchema,
|
|
489
|
+
(uid) => strapi.getModel(uid)
|
|
490
|
+
) : null;
|
|
491
|
+
if (!compiled || !targetSchema) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const originalMainField = await getOriginalMainField(
|
|
495
|
+
strapi,
|
|
496
|
+
sourceSchema,
|
|
497
|
+
targetSchema,
|
|
498
|
+
targetField,
|
|
499
|
+
userAbility
|
|
500
|
+
);
|
|
501
|
+
return {
|
|
502
|
+
...compiled,
|
|
503
|
+
transformContext,
|
|
504
|
+
displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
|
|
505
|
+
sourceUid,
|
|
506
|
+
targetUid: targetSchema.uid,
|
|
507
|
+
originalMainField
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
const getTransformContext = (strapi) => {
|
|
511
|
+
const config2 = strapi.config.get("plugin::strapi-relation-names");
|
|
512
|
+
return {
|
|
513
|
+
locale: typeof config2?.locale === "string" ? config2.locale : void 0,
|
|
514
|
+
timeZone: typeof config2?.timeZone === "string" ? config2.timeZone : void 0
|
|
515
|
+
};
|
|
516
|
+
};
|
|
407
517
|
const relationLabels = ({ strapi }) => {
|
|
408
|
-
const getSettings = () => strapi.plugin(
|
|
518
|
+
const getSettings = () => strapi.plugin(PLUGIN_ID).service("settings").get();
|
|
409
519
|
const decorateRelationResults = async (ctx, sourceUid, targetField, results) => {
|
|
410
520
|
const settings2 = await getSettings();
|
|
411
521
|
if (!isCollectionEnabled(settings2.collections, sourceUid)) {
|
|
@@ -416,26 +526,27 @@ const relationLabels = ({ strapi }) => {
|
|
|
416
526
|
settings2,
|
|
417
527
|
sourceUid,
|
|
418
528
|
targetField,
|
|
419
|
-
ctx.state.userAbility
|
|
529
|
+
ctx.state.userAbility,
|
|
530
|
+
getTransformContext(strapi)
|
|
420
531
|
);
|
|
421
532
|
if (!runtime) {
|
|
422
533
|
return results;
|
|
423
534
|
}
|
|
424
|
-
const values = results.filter(isRecord
|
|
535
|
+
const values = results.filter(isRecord);
|
|
425
536
|
const hydrated = await hydrateValues(strapi, ctx, runtime, values);
|
|
426
537
|
return results.map(
|
|
427
|
-
(result) => isRecord
|
|
538
|
+
(result) => isRecord(result) ? applyLabel(result, hydrated.get(result) ?? result, runtime) : result
|
|
428
539
|
);
|
|
429
540
|
};
|
|
430
541
|
const decorateConfiguration = async (configuration, sourceUid) => {
|
|
431
|
-
if (!configuration || !isRecord
|
|
542
|
+
if (!configuration || !isRecord(configuration.metadatas)) {
|
|
432
543
|
return configuration;
|
|
433
544
|
}
|
|
434
545
|
const settings2 = await getSettings();
|
|
435
546
|
const metadatas = { ...configuration.metadatas };
|
|
436
547
|
const relationNames = {};
|
|
548
|
+
const sourceSchema = strapi.getModel(sourceUid);
|
|
437
549
|
for (const [fieldName, metadata] of Object.entries(metadatas)) {
|
|
438
|
-
const sourceSchema = strapi.getModel(sourceUid);
|
|
439
550
|
const attribute = sourceSchema?.attributes?.[fieldName];
|
|
440
551
|
if (!attribute || attribute.type !== "relation" || !attribute.target) {
|
|
441
552
|
continue;
|
|
@@ -446,12 +557,16 @@ const relationLabels = ({ strapi }) => {
|
|
|
446
557
|
targetSchema,
|
|
447
558
|
(uid) => strapi.getModel(uid)
|
|
448
559
|
);
|
|
449
|
-
if (!compiled || !isRecord
|
|
560
|
+
if (!compiled || !isRecord(metadata)) {
|
|
450
561
|
continue;
|
|
451
562
|
}
|
|
452
|
-
const editMetadata = isRecord
|
|
563
|
+
const editMetadata = isRecord(metadata.edit) ? metadata.edit : {};
|
|
453
564
|
const configuredMainField = typeof editMetadata.mainField === "string" ? editMetadata.mainField : "id";
|
|
454
|
-
const
|
|
565
|
+
const isNestedTemplate = compiled.placeholders.some(
|
|
566
|
+
(placeholder) => placeholder.includes(".")
|
|
567
|
+
);
|
|
568
|
+
const isIdentityField = configuredMainField === "id" || configuredMainField === "documentId";
|
|
569
|
+
const displayField = isNestedTemplate && isIdentityField ? "label" : isNestedTemplate ? configuredMainField : compiled.displayField;
|
|
455
570
|
metadatas[fieldName] = {
|
|
456
571
|
...metadata,
|
|
457
572
|
edit: {
|
|
@@ -459,7 +574,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
459
574
|
mainField: displayField
|
|
460
575
|
},
|
|
461
576
|
list: {
|
|
462
|
-
...isRecord
|
|
577
|
+
...isRecord(metadata.list) ? metadata.list : {},
|
|
463
578
|
mainField: displayField
|
|
464
579
|
}
|
|
465
580
|
};
|
|
@@ -469,13 +584,13 @@ const relationLabels = ({ strapi }) => {
|
|
|
469
584
|
...configuration,
|
|
470
585
|
metadatas,
|
|
471
586
|
settings: {
|
|
472
|
-
...isRecord
|
|
587
|
+
...isRecord(configuration.settings) ? configuration.settings : {},
|
|
473
588
|
relationNames
|
|
474
589
|
}
|
|
475
590
|
};
|
|
476
591
|
};
|
|
477
592
|
const decorateContentManagerConfiguration = async (data, sourceUid) => {
|
|
478
|
-
if (!isRecord
|
|
593
|
+
if (!isRecord(data)) {
|
|
479
594
|
return data;
|
|
480
595
|
}
|
|
481
596
|
const settings2 = await getSettings();
|
|
@@ -489,7 +604,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
489
604
|
if (next.component) {
|
|
490
605
|
next.component = await decorateConfiguration(next.component, sourceUid);
|
|
491
606
|
}
|
|
492
|
-
if (isRecord
|
|
607
|
+
if (isRecord(next.components)) {
|
|
493
608
|
const components = { ...next.components };
|
|
494
609
|
for (const [uid, configuration] of Object.entries(components)) {
|
|
495
610
|
components[uid] = await decorateConfiguration(configuration, uid);
|
|
@@ -498,6 +613,33 @@ const relationLabels = ({ strapi }) => {
|
|
|
498
613
|
}
|
|
499
614
|
return next;
|
|
500
615
|
};
|
|
616
|
+
const sanitizeConfigurationUpdate = (configuration) => {
|
|
617
|
+
if (!isRecord(configuration) || !isRecord(configuration.metadatas)) {
|
|
618
|
+
return configuration;
|
|
619
|
+
}
|
|
620
|
+
const configurationSettings = isRecord(configuration.settings) ? configuration.settings : {};
|
|
621
|
+
const relationNames = isRecord(configurationSettings.relationNames) ? configurationSettings.relationNames : {};
|
|
622
|
+
const { relationNames: _relationNames, ...sanitizedSettings } = configurationSettings;
|
|
623
|
+
const metadatas = { ...configuration.metadatas };
|
|
624
|
+
for (const [fieldName, metadata] of Object.entries(metadatas)) {
|
|
625
|
+
const relationName = relationNames[fieldName];
|
|
626
|
+
if (!isRecord(relationName) || relationName.mainField !== "label" || !isRecord(metadata) || !isRecord(metadata.edit) || metadata.edit.mainField !== "label") {
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
metadatas[fieldName] = {
|
|
630
|
+
...metadata,
|
|
631
|
+
edit: {
|
|
632
|
+
...metadata.edit,
|
|
633
|
+
mainField: "id"
|
|
634
|
+
},
|
|
635
|
+
list: {
|
|
636
|
+
...isRecord(metadata.list) ? metadata.list : {},
|
|
637
|
+
mainField: "id"
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return { ...configuration, settings: sanitizedSettings, metadatas };
|
|
642
|
+
};
|
|
501
643
|
const decorateCollectionResults = async (ctx, sourceUid, results) => {
|
|
502
644
|
const settings2 = await getSettings();
|
|
503
645
|
if (!isCollectionEnabled(settings2.collections, sourceUid)) {
|
|
@@ -505,6 +647,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
505
647
|
}
|
|
506
648
|
const runtimeCache = /* @__PURE__ */ new Map();
|
|
507
649
|
const sourceSchemas = /* @__PURE__ */ new Map();
|
|
650
|
+
const transformContext = getTransformContext(strapi);
|
|
508
651
|
const getSchema = (uid) => {
|
|
509
652
|
if (!sourceSchemas.has(uid)) {
|
|
510
653
|
sourceSchemas.set(uid, strapi.getModel(uid));
|
|
@@ -518,7 +661,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
518
661
|
}
|
|
519
662
|
return;
|
|
520
663
|
}
|
|
521
|
-
if (!isRecord
|
|
664
|
+
if (!isRecord(value)) {
|
|
522
665
|
return;
|
|
523
666
|
}
|
|
524
667
|
const schema = getSchema(uid);
|
|
@@ -528,7 +671,14 @@ const relationLabels = ({ strapi }) => {
|
|
|
528
671
|
if (!runtimeCache.has(cacheKey)) {
|
|
529
672
|
runtimeCache.set(
|
|
530
673
|
cacheKey,
|
|
531
|
-
await getRuntime(
|
|
674
|
+
await getRuntime(
|
|
675
|
+
strapi,
|
|
676
|
+
settings2,
|
|
677
|
+
uid,
|
|
678
|
+
fieldName,
|
|
679
|
+
ctx.state.userAbility,
|
|
680
|
+
transformContext
|
|
681
|
+
)
|
|
532
682
|
);
|
|
533
683
|
}
|
|
534
684
|
const runtime = runtimeCache.get(cacheKey);
|
|
@@ -564,6 +714,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
564
714
|
return {
|
|
565
715
|
decorateCollectionResults,
|
|
566
716
|
decorateConfiguration: decorateContentManagerConfiguration,
|
|
717
|
+
sanitizeConfigurationUpdate,
|
|
567
718
|
decorateRelationResults,
|
|
568
719
|
compileTemplate,
|
|
569
720
|
parseTemplate,
|
|
@@ -574,9 +725,7 @@ const EMPTY_SETTINGS = {
|
|
|
574
725
|
collections: [],
|
|
575
726
|
relations: {}
|
|
576
727
|
};
|
|
577
|
-
const PLUGIN_ID = "strapi-relation-names";
|
|
578
728
|
const STORE_KEY = "settings";
|
|
579
|
-
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
580
729
|
const normalizeSettings = (value, collections = []) => {
|
|
581
730
|
if (!isRecord(value) || !isRecord(value.relations)) {
|
|
582
731
|
return { collections, relations: {} };
|
|
@@ -638,4 +787,4 @@ const index = {
|
|
|
638
787
|
policies,
|
|
639
788
|
middlewares
|
|
640
789
|
};
|
|
641
|
-
exports
|
|
790
|
+
module.exports = index;
|