strapi-relation-names 1.2.2 → 1.3.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/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 +267 -129
- package/dist/server/index.mjs +266 -127
- 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;
|
|
@@ -283,60 +380,15 @@ const applyLabel = (value, hydrated, runtime) => {
|
|
|
283
380
|
};
|
|
284
381
|
const relationValues = (value) => {
|
|
285
382
|
if (Array.isArray(value)) {
|
|
286
|
-
return value.filter(isRecord
|
|
383
|
+
return value.filter(isRecord);
|
|
287
384
|
}
|
|
288
|
-
return isRecord
|
|
385
|
+
return isRecord(value) ? [value] : [];
|
|
289
386
|
};
|
|
290
387
|
const replaceRelationValue = (original, values, hydrated, runtime) => {
|
|
291
388
|
const decorated = values.map((value) => applyLabel(value, hydrated.get(value) ?? value, runtime));
|
|
292
389
|
return Array.isArray(original) ? decorated : decorated[0] ?? original;
|
|
293
390
|
};
|
|
294
391
|
const isCollectionEnabled = (collections, uid) => collections.length === 0 || collections.includes(uid);
|
|
295
|
-
const getOriginalMainField = async (strapi, sourceSchema, targetSchema, targetField, userAbility) => {
|
|
296
|
-
const serviceName = sourceSchema.modelType === "component" ? "components" : "content-types";
|
|
297
|
-
const configuration = await strapi.plugin("content-manager").service(serviceName).findConfiguration(sourceSchema);
|
|
298
|
-
const configuredMainField = configuration?.metadatas?.[targetField]?.edit?.mainField;
|
|
299
|
-
if (typeof configuredMainField !== "string" || configuredMainField === "documentId" || configuredMainField === "id") {
|
|
300
|
-
return configuredMainField === "documentId" ? "documentId" : "id";
|
|
301
|
-
}
|
|
302
|
-
const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility, model: targetSchema.uid });
|
|
303
|
-
const attribute = targetSchema.attributes?.[configuredMainField];
|
|
304
|
-
if (!attribute || attribute.private === true || permissionChecker.cannot.read(null, configuredMainField)) {
|
|
305
|
-
return "documentId";
|
|
306
|
-
}
|
|
307
|
-
return configuredMainField;
|
|
308
|
-
};
|
|
309
|
-
const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility) => {
|
|
310
|
-
const sourceSchema = strapi.getModel(sourceUid);
|
|
311
|
-
const attribute = sourceSchema?.attributes?.[targetField];
|
|
312
|
-
if (!sourceSchema || !attribute || attribute.type !== "relation" || !attribute.target) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
const template = settings2.relations[sourceUid]?.[targetField];
|
|
316
|
-
const targetSchema = strapi.getModel(attribute.target);
|
|
317
|
-
const compiled = typeof template === "string" ? compileTemplate(
|
|
318
|
-
template,
|
|
319
|
-
targetSchema,
|
|
320
|
-
(uid) => strapi.getModel(uid)
|
|
321
|
-
) : null;
|
|
322
|
-
if (!compiled || !targetSchema) {
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
const originalMainField = await getOriginalMainField(
|
|
326
|
-
strapi,
|
|
327
|
-
sourceSchema,
|
|
328
|
-
targetSchema,
|
|
329
|
-
targetField,
|
|
330
|
-
userAbility
|
|
331
|
-
);
|
|
332
|
-
return {
|
|
333
|
-
...compiled,
|
|
334
|
-
displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
|
|
335
|
-
sourceUid,
|
|
336
|
-
targetUid: targetSchema.uid,
|
|
337
|
-
originalMainField
|
|
338
|
-
};
|
|
339
|
-
};
|
|
340
392
|
const buildPopulate = (placeholders) => {
|
|
341
393
|
const populate = {};
|
|
342
394
|
for (const placeholder of placeholders) {
|
|
@@ -409,8 +461,61 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
409
461
|
return new Map(values.map((value) => [value, value]));
|
|
410
462
|
}
|
|
411
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
|
+
};
|
|
412
517
|
const relationLabels = ({ strapi }) => {
|
|
413
|
-
const getSettings = () => strapi.plugin(
|
|
518
|
+
const getSettings = () => strapi.plugin(PLUGIN_ID).service("settings").get();
|
|
414
519
|
const decorateRelationResults = async (ctx, sourceUid, targetField, results) => {
|
|
415
520
|
const settings2 = await getSettings();
|
|
416
521
|
if (!isCollectionEnabled(settings2.collections, sourceUid)) {
|
|
@@ -421,26 +526,27 @@ const relationLabels = ({ strapi }) => {
|
|
|
421
526
|
settings2,
|
|
422
527
|
sourceUid,
|
|
423
528
|
targetField,
|
|
424
|
-
ctx.state.userAbility
|
|
529
|
+
ctx.state.userAbility,
|
|
530
|
+
getTransformContext(strapi)
|
|
425
531
|
);
|
|
426
532
|
if (!runtime) {
|
|
427
533
|
return results;
|
|
428
534
|
}
|
|
429
|
-
const values = results.filter(isRecord
|
|
535
|
+
const values = results.filter(isRecord);
|
|
430
536
|
const hydrated = await hydrateValues(strapi, ctx, runtime, values);
|
|
431
537
|
return results.map(
|
|
432
|
-
(result) => isRecord
|
|
538
|
+
(result) => isRecord(result) ? applyLabel(result, hydrated.get(result) ?? result, runtime) : result
|
|
433
539
|
);
|
|
434
540
|
};
|
|
435
541
|
const decorateConfiguration = async (configuration, sourceUid) => {
|
|
436
|
-
if (!configuration || !isRecord
|
|
542
|
+
if (!configuration || !isRecord(configuration.metadatas)) {
|
|
437
543
|
return configuration;
|
|
438
544
|
}
|
|
439
545
|
const settings2 = await getSettings();
|
|
440
546
|
const metadatas = { ...configuration.metadatas };
|
|
441
547
|
const relationNames = {};
|
|
548
|
+
const sourceSchema = strapi.getModel(sourceUid);
|
|
442
549
|
for (const [fieldName, metadata] of Object.entries(metadatas)) {
|
|
443
|
-
const sourceSchema = strapi.getModel(sourceUid);
|
|
444
550
|
const attribute = sourceSchema?.attributes?.[fieldName];
|
|
445
551
|
if (!attribute || attribute.type !== "relation" || !attribute.target) {
|
|
446
552
|
continue;
|
|
@@ -451,10 +557,10 @@ const relationLabels = ({ strapi }) => {
|
|
|
451
557
|
targetSchema,
|
|
452
558
|
(uid) => strapi.getModel(uid)
|
|
453
559
|
);
|
|
454
|
-
if (!compiled || !isRecord
|
|
560
|
+
if (!compiled || !isRecord(metadata)) {
|
|
455
561
|
continue;
|
|
456
562
|
}
|
|
457
|
-
const editMetadata = isRecord
|
|
563
|
+
const editMetadata = isRecord(metadata.edit) ? metadata.edit : {};
|
|
458
564
|
const configuredMainField = typeof editMetadata.mainField === "string" ? editMetadata.mainField : "id";
|
|
459
565
|
const isNestedTemplate = compiled.placeholders.some(
|
|
460
566
|
(placeholder) => placeholder.includes(".")
|
|
@@ -468,7 +574,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
468
574
|
mainField: displayField
|
|
469
575
|
},
|
|
470
576
|
list: {
|
|
471
|
-
...isRecord
|
|
577
|
+
...isRecord(metadata.list) ? metadata.list : {},
|
|
472
578
|
mainField: displayField
|
|
473
579
|
}
|
|
474
580
|
};
|
|
@@ -478,13 +584,13 @@ const relationLabels = ({ strapi }) => {
|
|
|
478
584
|
...configuration,
|
|
479
585
|
metadatas,
|
|
480
586
|
settings: {
|
|
481
|
-
...isRecord
|
|
587
|
+
...isRecord(configuration.settings) ? configuration.settings : {},
|
|
482
588
|
relationNames
|
|
483
589
|
}
|
|
484
590
|
};
|
|
485
591
|
};
|
|
486
592
|
const decorateContentManagerConfiguration = async (data, sourceUid) => {
|
|
487
|
-
if (!isRecord
|
|
593
|
+
if (!isRecord(data)) {
|
|
488
594
|
return data;
|
|
489
595
|
}
|
|
490
596
|
const settings2 = await getSettings();
|
|
@@ -498,7 +604,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
498
604
|
if (next.component) {
|
|
499
605
|
next.component = await decorateConfiguration(next.component, sourceUid);
|
|
500
606
|
}
|
|
501
|
-
if (isRecord
|
|
607
|
+
if (isRecord(next.components)) {
|
|
502
608
|
const components = { ...next.components };
|
|
503
609
|
for (const [uid, configuration] of Object.entries(components)) {
|
|
504
610
|
components[uid] = await decorateConfiguration(configuration, uid);
|
|
@@ -507,6 +613,31 @@ const relationLabels = ({ strapi }) => {
|
|
|
507
613
|
}
|
|
508
614
|
return next;
|
|
509
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) || !isRecord(metadata)) {
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
const sanitizedMetadata = { ...metadata };
|
|
630
|
+
if (isRecord(metadata.list)) {
|
|
631
|
+
const { mainField: _mainField, ...sanitizedList } = metadata.list;
|
|
632
|
+
sanitizedMetadata.list = sanitizedList;
|
|
633
|
+
}
|
|
634
|
+
if (relationName.mainField === "label" && isRecord(metadata.edit) && metadata.edit.mainField === "label") {
|
|
635
|
+
sanitizedMetadata.edit = { ...metadata.edit, mainField: "id" };
|
|
636
|
+
}
|
|
637
|
+
metadatas[fieldName] = sanitizedMetadata;
|
|
638
|
+
}
|
|
639
|
+
return { ...configuration, settings: sanitizedSettings, metadatas };
|
|
640
|
+
};
|
|
510
641
|
const decorateCollectionResults = async (ctx, sourceUid, results) => {
|
|
511
642
|
const settings2 = await getSettings();
|
|
512
643
|
if (!isCollectionEnabled(settings2.collections, sourceUid)) {
|
|
@@ -514,6 +645,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
514
645
|
}
|
|
515
646
|
const runtimeCache = /* @__PURE__ */ new Map();
|
|
516
647
|
const sourceSchemas = /* @__PURE__ */ new Map();
|
|
648
|
+
const transformContext = getTransformContext(strapi);
|
|
517
649
|
const getSchema = (uid) => {
|
|
518
650
|
if (!sourceSchemas.has(uid)) {
|
|
519
651
|
sourceSchemas.set(uid, strapi.getModel(uid));
|
|
@@ -527,7 +659,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
527
659
|
}
|
|
528
660
|
return;
|
|
529
661
|
}
|
|
530
|
-
if (!isRecord
|
|
662
|
+
if (!isRecord(value)) {
|
|
531
663
|
return;
|
|
532
664
|
}
|
|
533
665
|
const schema = getSchema(uid);
|
|
@@ -537,7 +669,14 @@ const relationLabels = ({ strapi }) => {
|
|
|
537
669
|
if (!runtimeCache.has(cacheKey)) {
|
|
538
670
|
runtimeCache.set(
|
|
539
671
|
cacheKey,
|
|
540
|
-
await getRuntime(
|
|
672
|
+
await getRuntime(
|
|
673
|
+
strapi,
|
|
674
|
+
settings2,
|
|
675
|
+
uid,
|
|
676
|
+
fieldName,
|
|
677
|
+
ctx.state.userAbility,
|
|
678
|
+
transformContext
|
|
679
|
+
)
|
|
541
680
|
);
|
|
542
681
|
}
|
|
543
682
|
const runtime = runtimeCache.get(cacheKey);
|
|
@@ -573,6 +712,7 @@ const relationLabels = ({ strapi }) => {
|
|
|
573
712
|
return {
|
|
574
713
|
decorateCollectionResults,
|
|
575
714
|
decorateConfiguration: decorateContentManagerConfiguration,
|
|
715
|
+
sanitizeConfigurationUpdate,
|
|
576
716
|
decorateRelationResults,
|
|
577
717
|
compileTemplate,
|
|
578
718
|
parseTemplate,
|
|
@@ -583,9 +723,7 @@ const EMPTY_SETTINGS = {
|
|
|
583
723
|
collections: [],
|
|
584
724
|
relations: {}
|
|
585
725
|
};
|
|
586
|
-
const PLUGIN_ID = "strapi-relation-names";
|
|
587
726
|
const STORE_KEY = "settings";
|
|
588
|
-
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
589
727
|
const normalizeSettings = (value, collections = []) => {
|
|
590
728
|
if (!isRecord(value) || !isRecord(value.relations)) {
|
|
591
729
|
return { collections, relations: {} };
|
|
@@ -647,4 +785,4 @@ const index = {
|
|
|
647
785
|
policies,
|
|
648
786
|
middlewares
|
|
649
787
|
};
|
|
650
|
-
exports
|
|
788
|
+
module.exports = index;
|