@aduptive/strapi-blockscene 2.0.0-alpha.14 → 2.0.0-alpha.15
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 +82 -1
- package/dist/admin.cjs +8590 -7740
- package/dist/admin.mjs +8765 -7911
- package/dist/server.cjs +336 -34
- package/package.json +7 -2
- package/text-template.js +201 -0
package/dist/server.cjs
CHANGED
|
@@ -4,6 +4,223 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
4
4
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// server/text-template.js
|
|
8
|
+
var require_text_template = __commonJS({
|
|
9
|
+
"server/text-template.js"(exports2, module2) {
|
|
10
|
+
"use strict";
|
|
11
|
+
var CUSTOM_FIELD = "plugin::blockscene.text-template";
|
|
12
|
+
var TAGS = ["h1", "h2", "h3", "h4", "h5", "h6", "p"];
|
|
13
|
+
var ROLES = ["eyebrow", "title", "description", "body"];
|
|
14
|
+
var MODIFIERS = ["uppercase", "lowercase", "underline"];
|
|
15
|
+
var ID = /^[a-z][a-z0-9_-]{0,47}$/;
|
|
16
|
+
var TOKEN = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
|
|
17
|
+
var MAX_ENTRIES = 24;
|
|
18
|
+
var MAX_TEXT = 5e4;
|
|
19
|
+
var BINDING = /^[a-z][a-z0-9_-]*(?:::[a-z][a-z0-9_-]*)?(?:\.[A-Za-z_][A-Za-z0-9_-]*)+$/;
|
|
20
|
+
var plain = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
21
|
+
var codePoints = (value) => [...String(value || "")].length;
|
|
22
|
+
var cleanLineBreaks = (value) => String(value || "").replace(/\r\n?/g, "\n");
|
|
23
|
+
function templateError(message) {
|
|
24
|
+
const error = new Error(message);
|
|
25
|
+
error.name = "ValidationError";
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
function validateTemplate(value) {
|
|
29
|
+
if (!Array.isArray(value) || value.length > MAX_ENTRIES) templateError(`Text template must contain at most ${MAX_ENTRIES} entries`);
|
|
30
|
+
const ids = /* @__PURE__ */ new Set();
|
|
31
|
+
return value.map((raw, index) => {
|
|
32
|
+
if (!plain(raw)) templateError(`Text template entry ${index + 1} must be an object`);
|
|
33
|
+
const allowed = ["id", "label", "role", "tag", "token", "modifiers", "required", "allowBreaks", "softLines", "maxChars", "demo"];
|
|
34
|
+
for (const key of Object.keys(raw)) if (!allowed.includes(key)) templateError(`Unknown text template option "${key}"`);
|
|
35
|
+
if (typeof raw.id !== "string" || !ID.test(raw.id) || ids.has(raw.id)) templateError(`Invalid or duplicate text template id "${raw.id}"`);
|
|
36
|
+
ids.add(raw.id);
|
|
37
|
+
if (typeof raw.label !== "string" || !raw.label.trim() || raw.label.length > 80) templateError(`Invalid label for text template entry "${raw.id}"`);
|
|
38
|
+
if (!ROLES.includes(raw.role)) templateError(`Invalid role for text template entry "${raw.id}"`);
|
|
39
|
+
if (!TAGS.includes(raw.tag)) templateError(`Invalid Tag HTML for text template entry "${raw.id}"`);
|
|
40
|
+
if (raw.token !== void 0 && (typeof raw.token !== "string" || raw.token && !TOKEN.test(raw.token))) templateError(`Invalid style token for text template entry "${raw.id}"`);
|
|
41
|
+
if (!Array.isArray(raw.modifiers) || raw.modifiers.some((item) => !MODIFIERS.includes(item)) || new Set(raw.modifiers).size !== raw.modifiers.length || raw.modifiers.includes("uppercase") && raw.modifiers.includes("lowercase")) templateError(`Invalid modifiers for text template entry "${raw.id}"`);
|
|
42
|
+
if (typeof raw.required !== "boolean" || typeof raw.allowBreaks !== "boolean") templateError(`Invalid rules for text template entry "${raw.id}"`);
|
|
43
|
+
if (raw.softLines !== void 0 && (!Number.isInteger(raw.softLines) || raw.softLines < 1 || raw.softLines > 20)) templateError(`Invalid line guidance for text template entry "${raw.id}"`);
|
|
44
|
+
if (raw.maxChars !== void 0 && (!Number.isInteger(raw.maxChars) || raw.maxChars < 1 || raw.maxChars > MAX_TEXT)) templateError(`Invalid character limit for text template entry "${raw.id}"`);
|
|
45
|
+
if (typeof raw.demo !== "string" || codePoints(raw.demo) > MAX_TEXT) templateError(`Invalid demo text for text template entry "${raw.id}"`);
|
|
46
|
+
if (!raw.allowBreaks && /[\r\n]/.test(raw.demo)) templateError(`Demo text for "${raw.id}" cannot contain line breaks`);
|
|
47
|
+
if (raw.maxChars && codePoints(raw.demo) > raw.maxChars) templateError(`Demo text for "${raw.id}" exceeds its character limit`);
|
|
48
|
+
return {
|
|
49
|
+
id: raw.id,
|
|
50
|
+
label: raw.label.trim(),
|
|
51
|
+
role: raw.role,
|
|
52
|
+
tag: raw.tag,
|
|
53
|
+
token: raw.token || "",
|
|
54
|
+
modifiers: [...raw.modifiers],
|
|
55
|
+
required: raw.required,
|
|
56
|
+
allowBreaks: raw.allowBreaks,
|
|
57
|
+
...raw.softLines && { softLines: raw.softLines },
|
|
58
|
+
...raw.maxChars && { maxChars: raw.maxChars },
|
|
59
|
+
demo: cleanLineBreaks(raw.demo)
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function normalizeValue(value) {
|
|
64
|
+
if (value == null) return { version: 1, values: {} };
|
|
65
|
+
if (!plain(value) || value.version !== 1 || !plain(value.values)) templateError("Text template value must be { version: 1, values: { ... } }");
|
|
66
|
+
if (Object.keys(value.values).length > 100) templateError("Text template value has too many entries");
|
|
67
|
+
const values = {};
|
|
68
|
+
for (const [id, text] of Object.entries(value.values)) {
|
|
69
|
+
if (!ID.test(id) || typeof text !== "string" || codePoints(text) > MAX_TEXT) templateError(`Invalid text template value for "${id}"`);
|
|
70
|
+
values[id] = cleanLineBreaks(text);
|
|
71
|
+
}
|
|
72
|
+
return { version: 1, values };
|
|
73
|
+
}
|
|
74
|
+
function validateValue(template, value) {
|
|
75
|
+
const entries = validateTemplate(template);
|
|
76
|
+
const normalized = normalizeValue(value);
|
|
77
|
+
const errors = [];
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
const text = normalized.values[entry.id] || "";
|
|
80
|
+
if (entry.required && !text.trim()) errors.push({ id: entry.id, code: "required", message: `${entry.label} is required` });
|
|
81
|
+
if (!entry.allowBreaks && /\n/.test(text)) errors.push({ id: entry.id, code: "lineBreak", message: `${entry.label} does not allow line breaks` });
|
|
82
|
+
if (entry.maxChars && codePoints(text) > entry.maxChars) errors.push({ id: entry.id, code: "maxChars", message: `${entry.label} exceeds ${entry.maxChars} characters` });
|
|
83
|
+
}
|
|
84
|
+
return { value: normalized, errors };
|
|
85
|
+
}
|
|
86
|
+
function demoValue(template) {
|
|
87
|
+
return { version: 1, values: Object.fromEntries(validateTemplate(template).map((entry) => [entry.id, entry.demo])) };
|
|
88
|
+
}
|
|
89
|
+
function renderTextTemplate(template, value) {
|
|
90
|
+
const entries = validateTemplate(template);
|
|
91
|
+
const normalized = normalizeValue(value);
|
|
92
|
+
return entries.flatMap((entry) => {
|
|
93
|
+
const text = normalized.values[entry.id] || "";
|
|
94
|
+
if (!text.trim()) return [];
|
|
95
|
+
return [{ id: entry.id, tag: entry.tag, text, lines: text.split("\n"), className: textTemplateClassName(entry) }];
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function textTemplateClassName(entry) {
|
|
99
|
+
if (!plain(entry) || entry.token && !TOKEN.test(entry.token) || !Array.isArray(entry.modifiers) || entry.modifiers.some((item) => !MODIFIERS.includes(item))) return "";
|
|
100
|
+
return [entry.token, ...entry.modifiers].filter(Boolean).join(" ");
|
|
101
|
+
}
|
|
102
|
+
function renderTextTemplateElement(part, createElement) {
|
|
103
|
+
if (!plain(part) || !TAGS.includes(part.tag) || typeof part.text !== "string" || typeof createElement !== "function") templateError("Invalid projected text template part");
|
|
104
|
+
const children = part.text.split("\n").flatMap((line, index) => index ? [createElement("br", { key: `${part.id}-break-${index}` }), line] : [line]);
|
|
105
|
+
return createElement(part.tag, { key: part.id, className: part.className || void 0 }, ...children);
|
|
106
|
+
}
|
|
107
|
+
function binding(uid, name) {
|
|
108
|
+
return `${uid}.${name}`;
|
|
109
|
+
}
|
|
110
|
+
function isTextTemplateBinding(value) {
|
|
111
|
+
return typeof value === "string" && BINDING.test(value);
|
|
112
|
+
}
|
|
113
|
+
function resolveBinding(attribute, contentTypeUID, name) {
|
|
114
|
+
const explicit = attribute?.options?.blockscene?.textTemplate || attribute?.pluginOptions?.blockscene?.textTemplate;
|
|
115
|
+
if (isTextTemplateBinding(explicit)) return explicit;
|
|
116
|
+
return typeof contentTypeUID === "string" && contentTypeUID ? binding(contentTypeUID, String(name || "").split(".").pop()) : "";
|
|
117
|
+
}
|
|
118
|
+
function createEntryId(entries, values, nonce) {
|
|
119
|
+
const used = /* @__PURE__ */ new Set([...entries.map((entry) => entry.id), ...Object.keys(values || {})]);
|
|
120
|
+
const safe = String(nonce || "").toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 32);
|
|
121
|
+
const base = `text-${safe || Date.now().toString(36)}`;
|
|
122
|
+
let id = base, suffix = 2;
|
|
123
|
+
while (used.has(id)) id = `${base}-${suffix++}`;
|
|
124
|
+
return id;
|
|
125
|
+
}
|
|
126
|
+
function applyTextChange(entry, current, next, { composing = false, compositionStart = current } = {}) {
|
|
127
|
+
const value = entry.allowBreaks ? cleanLineBreaks(next) : String(next || "").replace(/[\r\n]+/g, " ");
|
|
128
|
+
const previous = String(compositionStart ?? current ?? "");
|
|
129
|
+
const exceeds = entry.maxChars && codePoints(value) > entry.maxChars;
|
|
130
|
+
const recoverable = codePoints(previous) > entry.maxChars && codePoints(value) <= codePoints(previous);
|
|
131
|
+
if (exceeds && !composing && !recoverable) return { accepted: false, value: previous, code: "maxChars" };
|
|
132
|
+
return { accepted: true, value };
|
|
133
|
+
}
|
|
134
|
+
function mergePartialDocument2(stored, patch) {
|
|
135
|
+
return { ...plain(stored) ? stored : {}, ...plain(patch) ? patch : {} };
|
|
136
|
+
}
|
|
137
|
+
function draftReadPlan2(localized, { documentId, locale } = {}, populate = {}, defaultLocale) {
|
|
138
|
+
const common = { status: "draft", ...Object.keys(populate).length && { populate } };
|
|
139
|
+
if (localized && locale === "*") return { many: true, query: { filters: { documentId }, locale: "*", ...common } };
|
|
140
|
+
return { many: false, query: { documentId, ...localized && { locale: locale || defaultLocale }, ...common } };
|
|
141
|
+
}
|
|
142
|
+
async function validateBulkUpdates2(query, where, populate, patch, validate) {
|
|
143
|
+
const entries = await query.findMany({ where: where || {}, ...Object.keys(populate || {}).length && { populate } });
|
|
144
|
+
for (const entry of entries) validate(mergePartialDocument2(entry, patch));
|
|
145
|
+
return entries.length;
|
|
146
|
+
}
|
|
147
|
+
function isTextTemplate(attr) {
|
|
148
|
+
return attr?.customField === CUSTOM_FIELD;
|
|
149
|
+
}
|
|
150
|
+
function textTemplateBindings(schemas2) {
|
|
151
|
+
const bindings = /* @__PURE__ */ new Set();
|
|
152
|
+
for (const [uid, schema] of Object.entries(schemas2 || {})) {
|
|
153
|
+
for (const [name, attribute] of Object.entries(schema?.attributes || {})) {
|
|
154
|
+
if (!isTextTemplate(attribute)) continue;
|
|
155
|
+
const key = resolveBinding(attribute, uid, name);
|
|
156
|
+
if (key) bindings.add(key);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return bindings;
|
|
160
|
+
}
|
|
161
|
+
function validateDocument2(schemas2, uid, data, templates, path = []) {
|
|
162
|
+
if (!plain(data)) return [];
|
|
163
|
+
const schema = schemas2?.[uid];
|
|
164
|
+
if (!schema) return [];
|
|
165
|
+
const errors = [];
|
|
166
|
+
for (const [name, attr] of Object.entries(schema.attributes || {})) {
|
|
167
|
+
const here = [...path, name];
|
|
168
|
+
if (isTextTemplate(attr)) {
|
|
169
|
+
const template = templates?.[resolveBinding(attr, uid, name)];
|
|
170
|
+
if (!template) continue;
|
|
171
|
+
let result;
|
|
172
|
+
try {
|
|
173
|
+
result = validateValue(template, data[name]);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
errors.push({ path: here, code: "invalid", message: error.message });
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
errors.push(...result.errors.map((error) => ({ ...error, path: [...here, error.id] })));
|
|
179
|
+
} else if (!(name in data)) continue;
|
|
180
|
+
else if (attr?.type === "component") {
|
|
181
|
+
const list = attr.repeatable ? data[name] : [data[name]];
|
|
182
|
+
if (Array.isArray(list)) list.forEach((item, index) => errors.push(...validateDocument2(schemas2, attr.component, item, templates, attr.repeatable ? [...here, index] : here)));
|
|
183
|
+
} else if (attr?.type === "dynamiczone" && Array.isArray(data[name])) {
|
|
184
|
+
data[name].forEach((item, index) => errors.push(...validateDocument2(schemas2, item?.__component, item, templates, [...here, index])));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return errors;
|
|
188
|
+
}
|
|
189
|
+
function textTemplatePopulate2(schemas2, uid, templates) {
|
|
190
|
+
const active = new Set(Object.keys(templates || {}));
|
|
191
|
+
const walk = (schemaUid, seen = /* @__PURE__ */ new Set()) => {
|
|
192
|
+
if (seen.has(schemaUid)) return { configured: false, populate: {} };
|
|
193
|
+
const next = new Set(seen).add(schemaUid);
|
|
194
|
+
let configured = false;
|
|
195
|
+
const populate = {};
|
|
196
|
+
for (const [name, attr] of Object.entries(schemas2?.[schemaUid]?.attributes || {})) {
|
|
197
|
+
if (isTextTemplate(attr) && active.has(resolveBinding(attr, schemaUid, name))) configured = true;
|
|
198
|
+
else if (attr?.type === "component") {
|
|
199
|
+
const child = walk(attr.component, next);
|
|
200
|
+
if (child.configured) {
|
|
201
|
+
populate[name] = Object.keys(child.populate).length ? { populate: child.populate } : true;
|
|
202
|
+
configured = true;
|
|
203
|
+
}
|
|
204
|
+
} else if (attr?.type === "dynamiczone") {
|
|
205
|
+
const children = (attr.components || []).map((component) => [component, walk(component, next)]).filter(([, child]) => child.configured);
|
|
206
|
+
if (children.length) {
|
|
207
|
+
populate[name] = { on: Object.fromEntries(children.map(([component, child]) => [
|
|
208
|
+
component,
|
|
209
|
+
Object.keys(child.populate).length ? { populate: child.populate } : {}
|
|
210
|
+
])) };
|
|
211
|
+
configured = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { configured, populate };
|
|
216
|
+
};
|
|
217
|
+
const result = walk(uid);
|
|
218
|
+
return result.configured ? result.populate : null;
|
|
219
|
+
}
|
|
220
|
+
module2.exports = { CUSTOM_FIELD, TAGS, ROLES, MODIFIERS, MAX_ENTRIES, codePoints, validateTemplate, normalizeValue, validateValue, demoValue, renderTextTemplate, renderTextTemplateElement, textTemplateClassName, binding, isTextTemplateBinding, resolveBinding, createEntryId, applyTextChange, mergePartialDocument: mergePartialDocument2, draftReadPlan: draftReadPlan2, validateBulkUpdates: validateBulkUpdates2, isTextTemplate, textTemplateBindings, validateDocument: validateDocument2, textTemplatePopulate: textTemplatePopulate2 };
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
7
224
|
// server/groups.js
|
|
8
225
|
var require_groups = __commonJS({
|
|
9
226
|
"server/groups.js"(exports2, module2) {
|
|
@@ -163,6 +380,7 @@ var require_groups = __commonJS({
|
|
|
163
380
|
var require_settings = __commonJS({
|
|
164
381
|
"server/settings.js"(exports2, module2) {
|
|
165
382
|
"use strict";
|
|
383
|
+
var { validateTemplate, isTextTemplateBinding, textTemplateBindings } = require_text_template();
|
|
166
384
|
var PLUGIN2 = "blockscene";
|
|
167
385
|
var TEMPLATES2 = ["generic", "banner", "cards", "imageText", "faq", "none"];
|
|
168
386
|
var INITIAL_STATES = ["closed", "open", "remember"];
|
|
@@ -203,6 +421,8 @@ var require_settings = __commonJS({
|
|
|
203
421
|
// Per content type (only the ones with a Dynamic Zone): { enabled: false } turns the plugin off there;
|
|
204
422
|
// previewMode overrides editor.previewMode as the mode the edit view opens in. Absent means the global behaviour.
|
|
205
423
|
contentTypes: {},
|
|
424
|
+
// Developer-owned templates for plugin::blockscene.text-template fields, keyed by "schema.uid.attribute".
|
|
425
|
+
textTemplates: {},
|
|
206
426
|
// Version history (Strapi 5, see README "Version history"): off until turned on. contentTypes: 'all' (every api:: type)
|
|
207
427
|
// or a list of uids. Snapshots are kept retentionDays and at most maxSnapshots per document; events eventDays; deleted
|
|
208
428
|
// documents stay in the trash trashDays (fixed per document when it is deleted).
|
|
@@ -270,7 +490,7 @@ var require_settings = __commonJS({
|
|
|
270
490
|
const name = `${uid.split(".").pop()} ${displayName}`.toLowerCase();
|
|
271
491
|
return GUESSES.find(([, pattern]) => pattern.test(name))?.[0] || "text";
|
|
272
492
|
}
|
|
273
|
-
function facetsOf(uid, schema,
|
|
493
|
+
function facetsOf(uid, schema, schemas2 = {}) {
|
|
274
494
|
const facets = /* @__PURE__ */ new Set();
|
|
275
495
|
const read = (attributes, nested) => {
|
|
276
496
|
for (const attr of Object.values(attributes || {})) {
|
|
@@ -283,7 +503,7 @@ var require_settings = __commonJS({
|
|
|
283
503
|
else if (attr?.type === "relation") facets.add("dynamic");
|
|
284
504
|
else if (attr?.type === "component") {
|
|
285
505
|
if (attr.repeatable) facets.add("list");
|
|
286
|
-
if (!nested) read(
|
|
506
|
+
if (!nested) read(schemas2[attr.component]?.attributes, true);
|
|
287
507
|
}
|
|
288
508
|
}
|
|
289
509
|
};
|
|
@@ -340,7 +560,7 @@ var require_settings = __commonJS({
|
|
|
340
560
|
groups: safeGroups2(config.groups, config.componentUids)
|
|
341
561
|
};
|
|
342
562
|
}
|
|
343
|
-
function validateSettings2(input, componentUids2, contentTypeUids2 = [], historyTypes = null,
|
|
563
|
+
function validateSettings2(input, componentUids2, contentTypeUids2 = [], historyTypes = null, schemas2 = null) {
|
|
344
564
|
const types = typeMap(contentTypeUids2);
|
|
345
565
|
if (!input || typeof input !== "object" || Array.isArray(input)) fail2("Settings must be an object");
|
|
346
566
|
if (JSON.stringify(input).length > 64 * 1024) fail2("Settings payload too large");
|
|
@@ -380,7 +600,7 @@ var require_settings = __commonJS({
|
|
|
380
600
|
if (typeof entry.showRowThumbnail !== "boolean") fail2(`Invalid row thumbnail setting for "${uid}"`);
|
|
381
601
|
clean.showRowThumbnail = entry.showRowThumbnail;
|
|
382
602
|
} else if (key === "layout") {
|
|
383
|
-
if (!validLayout(entry.layout,
|
|
603
|
+
if (!validLayout(entry.layout, schemas2 && (schemas2[uid]?.attributes || {}))) fail2(`Invalid layout for "${uid}": columnsField (and mobileColumnsField) must name a number, string or numeric enumeration attribute of the component; maxColumns 1 to 12`);
|
|
384
604
|
clean.layout = structuredClone(entry.layout);
|
|
385
605
|
} else fail2(`Unknown component setting "${key}"`);
|
|
386
606
|
}
|
|
@@ -397,6 +617,17 @@ var require_settings = __commonJS({
|
|
|
397
617
|
}
|
|
398
618
|
if (Object.keys(entry).length) out.contentTypes[uid] = { ...structuredClone(entry), ...entry.previewDevices && { previewDevices: cleanDevices(entry.previewDevices) } };
|
|
399
619
|
}
|
|
620
|
+
const textTemplates = input.textTemplates || {};
|
|
621
|
+
if (!plain(textTemplates) || Object.keys(textTemplates).length > 200) fail2("textTemplates must be an object with at most 200 field bindings");
|
|
622
|
+
const bindings = schemas2 ? textTemplateBindings(schemas2) : null;
|
|
623
|
+
for (const [key, entries] of Object.entries(textTemplates)) {
|
|
624
|
+
if (!isTextTemplateBinding(key) || bindings && !bindings.has(key)) fail2(`Unknown text template field "${key}"`);
|
|
625
|
+
try {
|
|
626
|
+
out.textTemplates[key] = validateTemplate(entries);
|
|
627
|
+
} catch (error) {
|
|
628
|
+
fail2(`${key}: ${error.message}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
400
631
|
const history = input.history || {};
|
|
401
632
|
if (typeof history !== "object" || Array.isArray(history)) fail2("history must be an object");
|
|
402
633
|
for (const [key, value] of Object.entries(history)) {
|
|
@@ -407,8 +638,9 @@ var require_settings = __commonJS({
|
|
|
407
638
|
if (out.history.eventDays < out.history.retentionDays) fail2("history.eventDays must be at least history.retentionDays");
|
|
408
639
|
return out;
|
|
409
640
|
}
|
|
410
|
-
function mergeSaved2(saved, componentUids2, contentTypeUids2 = [], historyTypes = null,
|
|
641
|
+
function mergeSaved2(saved, componentUids2, contentTypeUids2 = [], historyTypes = null, schemas2 = null) {
|
|
411
642
|
const types = typeMap(contentTypeUids2);
|
|
643
|
+
const templateBindings = schemas2 ? textTemplateBindings(schemas2) : null;
|
|
412
644
|
const out = structuredClone(DEFAULTS2);
|
|
413
645
|
if (!saved || typeof saved !== "object") return out;
|
|
414
646
|
for (const key of Object.keys(DEFAULTS2.palette)) if (COLOR.test(String(saved.palette?.[key]))) out.palette[key] = saved.palette[key];
|
|
@@ -438,7 +670,7 @@ var require_settings = __commonJS({
|
|
|
438
670
|
if (!clean.tags.length) delete clean.tags;
|
|
439
671
|
}
|
|
440
672
|
if ("showRowThumbnail" in clean && typeof clean.showRowThumbnail !== "boolean") delete clean.showRowThumbnail;
|
|
441
|
-
if ("layout" in clean && !validLayout(clean.layout,
|
|
673
|
+
if ("layout" in clean && !validLayout(clean.layout, schemas2 && (schemas2[uid]?.attributes || {}))) delete clean.layout;
|
|
442
674
|
if (Object.keys(clean).length) out.components[uid] = clean;
|
|
443
675
|
}
|
|
444
676
|
for (const [uid, entry] of Object.entries(saved.contentTypes || {})) {
|
|
@@ -447,6 +679,14 @@ var require_settings = __commonJS({
|
|
|
447
679
|
const clean = Object.fromEntries(Object.entries(entry).map(([key, value]) => [key, fix(key, value)]).filter(([key, value]) => TYPE_KEYS[key]?.(value, types[uid])));
|
|
448
680
|
if (Object.keys(clean).length) out.contentTypes[uid] = clean;
|
|
449
681
|
}
|
|
682
|
+
for (const [key, entries] of Object.entries(saved.textTemplates || {})) {
|
|
683
|
+
if (!isTextTemplateBinding(key) || templateBindings && !templateBindings.has(key)) continue;
|
|
684
|
+
try {
|
|
685
|
+
out.textTemplates[key] = validateTemplate(entries);
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (error?.name !== "ValidationError") throw error;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
450
690
|
for (const key of Object.keys(HISTORY_KEYS)) {
|
|
451
691
|
let value = saved.history?.[key];
|
|
452
692
|
if (key === "contentTypes" && Array.isArray(value)) value = value.filter((uid) => HISTORY_KEYS.contentTypes([uid], historyTypes));
|
|
@@ -455,7 +695,7 @@ var require_settings = __commonJS({
|
|
|
455
695
|
out.history.eventDays = Math.max(out.history.eventDays, out.history.retentionDays);
|
|
456
696
|
return out;
|
|
457
697
|
}
|
|
458
|
-
var ENTRY_KEYS = ["components", "contentTypes"];
|
|
698
|
+
var ENTRY_KEYS = ["components", "contentTypes", "textTemplates"];
|
|
459
699
|
var UNSET_WHEN_EMPTY = ["previewUrl", "blockPreviewUrl"];
|
|
460
700
|
var withoutEmpty = (editor) => Object.fromEntries(Object.entries(editor).filter(([key, value]) => !(UNSET_WHEN_EMPTY.includes(key) && value === "")));
|
|
461
701
|
function layer2(base, saved) {
|
|
@@ -478,7 +718,7 @@ var require_settings = __commonJS({
|
|
|
478
718
|
var LOCALE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
479
719
|
var plain = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
480
720
|
var fieldText = (value, max) => typeof value === "string" ? value.trim() !== "" && value.length <= max : plain(value) && Object.keys(value).length > 0 && Object.keys(value).length <= 30 && Object.entries(value).every(([locale, text2]) => LOCALE.test(locale) && fieldText(text2, max));
|
|
481
|
-
function validateFields2(input,
|
|
721
|
+
function validateFields2(input, schemas2 = {}, stale = []) {
|
|
482
722
|
if (input === null || input === void 0) return {};
|
|
483
723
|
if (!plain(input)) fail2("fields must be an object");
|
|
484
724
|
if (JSON.stringify(input).length > 256 * 1024) fail2("fields config too large");
|
|
@@ -491,7 +731,7 @@ var require_settings = __commonJS({
|
|
|
491
731
|
if (!(key in FIELD_TEXT)) fail2(`Unknown field setting "${key}" of "${uid}.${name}"`);
|
|
492
732
|
if (!fieldText(value, FIELD_TEXT[key])) fail2(`Invalid ${key} of "${uid}.${name}": a string of 1 to ${FIELD_TEXT[key]} characters, or { "<locale>": string }`);
|
|
493
733
|
}
|
|
494
|
-
if (!
|
|
734
|
+
if (!schemas2[uid]?.includes(name)) {
|
|
495
735
|
stale.push(`${uid}.${name}`);
|
|
496
736
|
continue;
|
|
497
737
|
}
|
|
@@ -506,7 +746,7 @@ var require_settings = __commonJS({
|
|
|
506
746
|
var localize = (holder, key, source, locale, text2) => {
|
|
507
747
|
holder[key] = { ...plain(holder[key]) ? holder[key] : { en: holder[key] ?? source }, [locale]: text2 };
|
|
508
748
|
};
|
|
509
|
-
function schemaMetadata2(
|
|
749
|
+
function schemaMetadata2(schemas2 = {}, translations = null, notes = []) {
|
|
510
750
|
const blocks = {}, fields = {};
|
|
511
751
|
const own = (holder, where) => {
|
|
512
752
|
const value = holder?.pluginOptions?.blockscene;
|
|
@@ -514,7 +754,7 @@ var require_settings = __commonJS({
|
|
|
514
754
|
notes.push(`${where}: pluginOptions.blockscene must be an object`);
|
|
515
755
|
return {};
|
|
516
756
|
};
|
|
517
|
-
for (const [uid, schema] of Object.entries(
|
|
757
|
+
for (const [uid, schema] of Object.entries(schemas2)) {
|
|
518
758
|
if (!uid.includes("::")) {
|
|
519
759
|
const block = {};
|
|
520
760
|
for (const [key, value] of Object.entries(own(schema, uid))) {
|
|
@@ -546,10 +786,10 @@ var require_settings = __commonJS({
|
|
|
546
786
|
continue;
|
|
547
787
|
}
|
|
548
788
|
for (const [key, text2] of Object.entries(entries)) {
|
|
549
|
-
const uid = Object.keys(
|
|
789
|
+
const uid = Object.keys(schemas2).find((uid2) => key === uid2 || key.startsWith(`${uid2}.`));
|
|
550
790
|
const [name, part = "label", extra] = uid ? key.slice(uid.length + 1).split(".") : [];
|
|
551
791
|
const block = uid && !uid.includes("::");
|
|
552
|
-
const target = block && key === uid ? [blocks[uid] ||= {}, "label",
|
|
792
|
+
const target = block && key === uid ? [blocks[uid] ||= {}, "label", schemas2[uid]?.info?.displayName || uid, 80] : block && key === `${uid}.description` ? [blocks[uid] ||= {}, "description", null, 300] : uid && Object.hasOwn(schemas2[uid]?.attributes || {}, name) && Object.hasOwn(FIELD_TEXT, part) && extra === void 0 ? [null, part, null, FIELD_TEXT[part]] : null;
|
|
553
793
|
if (!target) {
|
|
554
794
|
stale.push(`${locale} "${key}"`);
|
|
555
795
|
continue;
|
|
@@ -588,7 +828,7 @@ var require_settings = __commonJS({
|
|
|
588
828
|
blocks: Array.isArray,
|
|
589
829
|
enumeration: (value, attr) => (attr.enum || []).includes(value)
|
|
590
830
|
};
|
|
591
|
-
function variantValues(values, schema,
|
|
831
|
+
function variantValues(values, schema, schemas2, path, skipped) {
|
|
592
832
|
if (!plain(values)) throw new Error(`${path} must be an object`);
|
|
593
833
|
const out = {};
|
|
594
834
|
for (const [name, value] of Object.entries(values)) {
|
|
@@ -600,22 +840,22 @@ var require_settings = __commonJS({
|
|
|
600
840
|
continue;
|
|
601
841
|
}
|
|
602
842
|
if (attr.type === "component") {
|
|
603
|
-
const nested =
|
|
843
|
+
const nested = schemas2[attr.component], max = Math.min(attr.max ?? Infinity, VARIANT.items);
|
|
604
844
|
if (!nested) throw new Error(`${at}: unknown component "${attr.component}"`);
|
|
605
|
-
if (!attr.repeatable) out[name] = value === null ? null : variantValues(value, nested,
|
|
845
|
+
if (!attr.repeatable) out[name] = value === null ? null : variantValues(value, nested, schemas2, at, skipped);
|
|
606
846
|
else if (!Array.isArray(value) || value.length > max) throw new Error(`${at} must be a list of at most ${max} items`);
|
|
607
|
-
else out[name] = value.map((item, index) => variantValues(item, nested,
|
|
847
|
+
else out[name] = value.map((item, index) => variantValues(item, nested, schemas2, `${at}[${index}]`, skipped));
|
|
608
848
|
} else if (value === null || SCALARS[attr.type]?.(value, attr)) out[name] = structuredClone(value);
|
|
609
849
|
else throw new Error(`${at}: ${SCALARS[attr.type] ? `not a valid ${attr.type}` : `${attr.type} values are not supported`}`);
|
|
610
850
|
}
|
|
611
851
|
return out;
|
|
612
852
|
}
|
|
613
|
-
function validateVariants2(config,
|
|
853
|
+
function validateVariants2(config, schemas2 = {}, notes = []) {
|
|
614
854
|
const out = {};
|
|
615
855
|
let total = 0;
|
|
616
856
|
for (const [uid, entry] of Object.entries(plain(config) ? config : {})) {
|
|
617
857
|
if (!plain(entry) || entry.variants === void 0) continue;
|
|
618
|
-
if (!
|
|
858
|
+
if (!schemas2[uid]) {
|
|
619
859
|
notes.push(`${uid}: unknown component`);
|
|
620
860
|
continue;
|
|
621
861
|
}
|
|
@@ -631,7 +871,7 @@ var require_settings = __commonJS({
|
|
|
631
871
|
if (!text(variant.id) || !VARIANT.id.test(variant.id) || ids.has(variant.id)) throw new Error('id must be 1 to 40 lowercase letters, digits, "-" or "_", unique per component');
|
|
632
872
|
if (!fieldText(variant.label, 60)) throw new Error('label must be a string of 1 to 60 characters, or { "<locale>": string }');
|
|
633
873
|
const skipped = [];
|
|
634
|
-
const values = variantValues(variant.values ?? {},
|
|
874
|
+
const values = variantValues(variant.values ?? {}, schemas2[uid], schemas2, "values", skipped);
|
|
635
875
|
const size = JSON.stringify(values).length;
|
|
636
876
|
if (size > VARIANT.bytes) throw new Error(`values over ${VARIANT.bytes / 1024} KB`);
|
|
637
877
|
if (total + size > VARIANT.total) throw new Error(`all variants together over ${VARIANT.total / 1024} KB`);
|
|
@@ -646,7 +886,7 @@ var require_settings = __commonJS({
|
|
|
646
886
|
}
|
|
647
887
|
return out;
|
|
648
888
|
}
|
|
649
|
-
function requiredProblems(schema, values,
|
|
889
|
+
function requiredProblems(schema, values, schemas2, path, media, problems) {
|
|
650
890
|
for (const [field, attr] of Object.entries(schema?.attributes || {})) {
|
|
651
891
|
const at = `${path}.${field}`;
|
|
652
892
|
const supplied = Object.hasOwn(values, field);
|
|
@@ -656,7 +896,7 @@ var require_settings = __commonJS({
|
|
|
656
896
|
continue;
|
|
657
897
|
}
|
|
658
898
|
if (attr.type === "component") {
|
|
659
|
-
const nested =
|
|
899
|
+
const nested = schemas2[attr.component];
|
|
660
900
|
if (!nested) {
|
|
661
901
|
problems.push(`${at}: unknown component "${attr.component}"`);
|
|
662
902
|
continue;
|
|
@@ -665,16 +905,16 @@ var require_settings = __commonJS({
|
|
|
665
905
|
const minimum = Math.max(attr.min ?? 0, attr.required ? 1 : 0);
|
|
666
906
|
const items = supplied ? value : Array.from({ length: minimum }, () => ({}));
|
|
667
907
|
if (!Array.isArray(items) || items.length < minimum) problems.push(`${at} requires at least ${minimum} item${minimum === 1 ? "" : "s"}`);
|
|
668
|
-
else items.forEach((item, index) => requiredProblems(nested, item,
|
|
908
|
+
else items.forEach((item, index) => requiredProblems(nested, item, schemas2, `${at}[${index}]`, media, problems));
|
|
669
909
|
} else if (supplied && value === null) {
|
|
670
910
|
if (attr.required) problems.push(`${at} is required`);
|
|
671
|
-
} else if (supplied || attr.required) requiredProblems(nested, supplied ? value : {},
|
|
911
|
+
} else if (supplied || attr.required) requiredProblems(nested, supplied ? value : {}, schemas2, at, media, problems);
|
|
672
912
|
continue;
|
|
673
913
|
}
|
|
674
914
|
if (attr.required && (value === void 0 || value === null || value === "")) problems.push(`${at} is required`);
|
|
675
915
|
}
|
|
676
916
|
}
|
|
677
|
-
function validateKits2(input, contentTypes = {},
|
|
917
|
+
function validateKits2(input, contentTypes = {}, schemas2 = {}, groups = null, notes = []) {
|
|
678
918
|
if (input === null || input === void 0) return {};
|
|
679
919
|
if (!plain(input) || JSON.stringify(input).length > 256 * 1024) {
|
|
680
920
|
notes.push("expected an object up to 256 KB");
|
|
@@ -710,18 +950,18 @@ var require_settings = __commonJS({
|
|
|
710
950
|
const row = rows[index];
|
|
711
951
|
if (!plain(row) || !text(row.__component)) throw new Error(`${zoneName}[${index}] must have __component`);
|
|
712
952
|
const component = row.__component;
|
|
713
|
-
if (!zone.components?.includes(component) || !
|
|
953
|
+
if (!zone.components?.includes(component) || !schemas2[component]) throw new Error(`${zoneName}[${index}]: component "${component}" is not allowed`);
|
|
714
954
|
const skipped = [];
|
|
715
|
-
const values = variantValues(row,
|
|
955
|
+
const values = variantValues(row, schemas2[component], schemas2, `${zoneName}[${index}]`, skipped);
|
|
716
956
|
if (skipped.length) notes.push(`${name}: media and relation values are not inserted (${skipped.slice(0, 5).join(", ")}${skipped.length > 5 ? ", ..." : ""})`);
|
|
717
957
|
const media = [], required = [];
|
|
718
|
-
requiredProblems(
|
|
958
|
+
requiredProblems(schemas2[component], values, schemas2, `${zoneName}[${index}]`, media, required);
|
|
719
959
|
if (required.length) throw new Error(required.slice(0, 5).join("; "));
|
|
720
960
|
if (media.length) notes.push(`${name}: required media or relations must be completed before publishing (${media.slice(0, 5).join(", ")}${media.length > 5 ? ", ..." : ""})`);
|
|
721
961
|
clean.push({ __component: component, values });
|
|
722
962
|
const close = groups?.[component];
|
|
723
963
|
if (close) {
|
|
724
|
-
if (!zone.components?.includes(close) || !
|
|
964
|
+
if (!zone.components?.includes(close) || !schemas2[close]) throw new Error(`${zoneName}[${index}]: close component "${close}" is not allowed`);
|
|
725
965
|
clean.push({ __component: close, values: {} });
|
|
726
966
|
}
|
|
727
967
|
}
|
|
@@ -1838,6 +2078,7 @@ var { safeGroups, validateGroups, invalidComponents, layoutFields } = require_gr
|
|
|
1838
2078
|
var { hiddenName, injectHidden, hideFromBuilder, registerHiddenStrip } = require_hidden();
|
|
1839
2079
|
var { CRON, covers, eventContentType, historyService, historyController, registerHistory, registerPurge } = require_history();
|
|
1840
2080
|
var { trashContentType, trashService, trashController } = require_trash();
|
|
2081
|
+
var { validateDocument, textTemplatePopulate, mergePartialDocument, draftReadPlan, validateBulkUpdates } = require_text_template();
|
|
1841
2082
|
var STRAPI5 = true;
|
|
1842
2083
|
var withHidden = [];
|
|
1843
2084
|
var store = (strapi) => strapi.store({ type: "plugin", name: PLUGIN });
|
|
@@ -1853,6 +2094,7 @@ async function readSettings(strapi) {
|
|
|
1853
2094
|
return legacy;
|
|
1854
2095
|
}
|
|
1855
2096
|
var componentUids = (strapi) => Object.keys(strapi.components || {});
|
|
2097
|
+
var schemas = (strapi) => ({ ...strapi.contentTypes || {}, ...strapi.components || {} });
|
|
1856
2098
|
var findMedia = (strapi, id) => strapi.db.query("plugin::upload.file").findOne({ where: { id }, select: ["id", "url", "mime", "name"] });
|
|
1857
2099
|
async function resolveMedia(strapi, settings) {
|
|
1858
2100
|
const resolved = {};
|
|
@@ -1968,6 +2210,63 @@ function registerZoneGuards(strapi) {
|
|
|
1968
2210
|
}
|
|
1969
2211
|
});
|
|
1970
2212
|
}
|
|
2213
|
+
function registerTextTemplateGuard(strapi) {
|
|
2214
|
+
const check = (uid, data, templates) => {
|
|
2215
|
+
const errors = validateDocument(schemas(strapi), uid, data, templates);
|
|
2216
|
+
if (errors.length) fail("Text template content is invalid.", { plugin: PLUGIN, errors: errors.map((error) => ({ ...error, name: "ValidationError" })) });
|
|
2217
|
+
};
|
|
2218
|
+
if (strapi.documents?.use) {
|
|
2219
|
+
const defaultLocale = async () => {
|
|
2220
|
+
try {
|
|
2221
|
+
return await strapi.plugin("i18n")?.service("locales")?.getDefaultLocale?.();
|
|
2222
|
+
} catch {
|
|
2223
|
+
return void 0;
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
strapi.documents.use(async (ctx, next) => {
|
|
2227
|
+
const { action, params = {} } = ctx;
|
|
2228
|
+
if (!ctx.uid || !["create", "update", "publish"].includes(action)) return next();
|
|
2229
|
+
const templates = (await strapi.plugin(PLUGIN).service("settings").get()).textTemplates;
|
|
2230
|
+
const populate = textTemplatePopulate(schemas(strapi), ctx.uid, templates);
|
|
2231
|
+
if (!populate) return next();
|
|
2232
|
+
if (action === "create") {
|
|
2233
|
+
check(ctx.uid, params.data || {}, templates);
|
|
2234
|
+
return next();
|
|
2235
|
+
}
|
|
2236
|
+
const localized = Boolean(strapi.getModel(ctx.uid)?.pluginOptions?.i18n?.localized);
|
|
2237
|
+
const plan = draftReadPlan(localized, params, populate, await defaultLocale());
|
|
2238
|
+
const stored = plan.many ? await strapi.documents(ctx.uid).findMany(plan.query) : [await strapi.documents(ctx.uid).findOne(plan.query)];
|
|
2239
|
+
for (const entry of stored) check(ctx.uid, action === "update" ? mergePartialDocument(entry, params.data) : entry || {}, templates);
|
|
2240
|
+
return next();
|
|
2241
|
+
});
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
strapi.db.lifecycles.subscribe({
|
|
2245
|
+
beforeCreate: async (event) => {
|
|
2246
|
+
const templates = (await strapi.plugin(PLUGIN).service("settings").get()).textTemplates;
|
|
2247
|
+
check(event.model.uid, event.params?.data || {}, templates);
|
|
2248
|
+
},
|
|
2249
|
+
beforeUpdate: async (event) => {
|
|
2250
|
+
const templates = (await strapi.plugin(PLUGIN).service("settings").get()).textTemplates;
|
|
2251
|
+
const populate = textTemplatePopulate(schemas(strapi), event.model.uid, templates);
|
|
2252
|
+
if (!populate) return;
|
|
2253
|
+
const stored = await strapi.db.query(event.model.uid).findOne({ where: event.params?.where || {}, ...Object.keys(populate).length && { populate } });
|
|
2254
|
+
check(event.model.uid, mergePartialDocument(stored, event.params?.data), templates);
|
|
2255
|
+
},
|
|
2256
|
+
beforeUpdateMany: async (event) => {
|
|
2257
|
+
const templates = (await strapi.plugin(PLUGIN).service("settings").get()).textTemplates;
|
|
2258
|
+
const populate = textTemplatePopulate(schemas(strapi), event.model.uid, templates);
|
|
2259
|
+
if (!populate) return;
|
|
2260
|
+
await validateBulkUpdates(
|
|
2261
|
+
strapi.db.query(event.model.uid),
|
|
2262
|
+
event.params?.where,
|
|
2263
|
+
populate,
|
|
2264
|
+
event.params?.data,
|
|
2265
|
+
(entry) => check(event.model.uid, entry, templates)
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
1971
2270
|
module.exports = {
|
|
1972
2271
|
config: {
|
|
1973
2272
|
default: {
|
|
@@ -2000,6 +2299,7 @@ module.exports = {
|
|
|
2000
2299
|
},
|
|
2001
2300
|
// Before the database schema sync, so the column exists. A name the host already uses with another type is skipped.
|
|
2002
2301
|
register({ strapi }) {
|
|
2302
|
+
strapi.customFields.register({ name: "text-template", plugin: PLUGIN, type: "json", inputSize: { default: 12, isResizable: true } });
|
|
2003
2303
|
const plugin = strapi.plugin(PLUGIN);
|
|
2004
2304
|
const name = hiddenName(plugin.config("hiddenAttribute"));
|
|
2005
2305
|
withHidden = [];
|
|
@@ -2027,6 +2327,7 @@ module.exports = {
|
|
|
2027
2327
|
strapi.plugin(PLUGIN).service("settings").variants();
|
|
2028
2328
|
strapi.plugin(PLUGIN).service("settings").kits();
|
|
2029
2329
|
registerZoneGuards(strapi);
|
|
2330
|
+
registerTextTemplateGuard(strapi);
|
|
2030
2331
|
registerHiddenStrip(strapi, hiddenName(strapi.plugin(PLUGIN).config("hiddenAttribute")), strapi.plugin(PLUGIN));
|
|
2031
2332
|
if (withHidden.length) hideFromBuilder(typeof strapi.get === "function" ? strapi.get("services") : strapi.container.get("services"), withHidden, hiddenName(strapi.plugin(PLUGIN).config("hiddenAttribute")));
|
|
2032
2333
|
if (STRAPI5) {
|
|
@@ -2046,7 +2347,7 @@ module.exports = {
|
|
|
2046
2347
|
const code = strapi.plugin(PLUGIN).config("settings");
|
|
2047
2348
|
project = null;
|
|
2048
2349
|
if (code) try {
|
|
2049
|
-
project = validateSettings(code, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi
|
|
2350
|
+
project = validateSettings(code, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), schemas(strapi));
|
|
2050
2351
|
} catch (error) {
|
|
2051
2352
|
strapi.log?.warn(`[${PLUGIN}] "settings" config ignored: ${error.message}. Using the built-in defaults under the saved settings.`);
|
|
2052
2353
|
}
|
|
@@ -2063,11 +2364,11 @@ module.exports = {
|
|
|
2063
2364
|
let fields;
|
|
2064
2365
|
const fieldTexts = () => {
|
|
2065
2366
|
if (fields !== void 0) return fields;
|
|
2066
|
-
const
|
|
2367
|
+
const schemas2 = Object.fromEntries(Object.entries({ ...strapi.contentTypes, ...strapi.components }).map(([uid, schema]) => [uid, Object.keys(schema?.attributes || {})]));
|
|
2067
2368
|
const stale = [];
|
|
2068
2369
|
let code = {};
|
|
2069
2370
|
try {
|
|
2070
|
-
code = validateFields(strapi.plugin(PLUGIN).config("fields"),
|
|
2371
|
+
code = validateFields(strapi.plugin(PLUGIN).config("fields"), schemas2, stale);
|
|
2071
2372
|
} catch (error) {
|
|
2072
2373
|
strapi.log?.warn(`[${PLUGIN}] "fields" config ignored: ${error.message}. Field labels fall back to the schema metadata and the Content Manager's own.`);
|
|
2073
2374
|
}
|
|
@@ -2092,7 +2393,7 @@ module.exports = {
|
|
|
2092
2393
|
for (const note of notes) strapi.log?.warn(`[${PLUGIN}] starter kits: ${note}.`);
|
|
2093
2394
|
return kits;
|
|
2094
2395
|
};
|
|
2095
|
-
const get = async () => mergeSaved(layer(projectDefaults(), await readSettings(strapi)), componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi
|
|
2396
|
+
const get = async () => mergeSaved(layer(projectDefaults(), await readSettings(strapi)), componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), schemas(strapi));
|
|
2096
2397
|
return {
|
|
2097
2398
|
projectDefaults,
|
|
2098
2399
|
get,
|
|
@@ -2101,7 +2402,7 @@ module.exports = {
|
|
|
2101
2402
|
variants: insertVariants,
|
|
2102
2403
|
kits: starterKits,
|
|
2103
2404
|
async set(value) {
|
|
2104
|
-
const next = validateSettings(value, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi
|
|
2405
|
+
const next = validateSettings(value, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), schemas(strapi));
|
|
2105
2406
|
for (const [uid, entry] of Object.entries(next.components)) {
|
|
2106
2407
|
if (entry.mediaId && !await findMedia(strapi, entry.mediaId).catch(() => null)) fail(`Media for "${uid}" does not exist`);
|
|
2107
2408
|
}
|
|
@@ -2154,6 +2455,7 @@ module.exports = {
|
|
|
2154
2455
|
kits: plugin.service("settings").kits(),
|
|
2155
2456
|
palette: settings.palette,
|
|
2156
2457
|
contentTypes: settings.contentTypes,
|
|
2458
|
+
textTemplates: settings.textTemplates,
|
|
2157
2459
|
hiddenAttribute: hidden,
|
|
2158
2460
|
fields: plugin.service("settings").fields(),
|
|
2159
2461
|
types,
|