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