@depup/payload 3.81.0-depup.1 → 3.82.1-depup.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 +5 -5
- package/changes.json +4 -4
- package/dist/bin/generateTypes.d.ts.map +1 -1
- package/dist/bin/generateTypes.js +8 -0
- package/dist/bin/generateTypes.js.map +1 -1
- package/dist/collections/config/sanitize.d.ts +2 -1
- package/dist/collections/config/sanitize.d.ts.map +1 -1
- package/dist/collections/config/sanitize.js +4 -1
- package/dist/collections/config/sanitize.js.map +1 -1
- package/dist/collections/operations/utilities/update.d.ts.map +1 -1
- package/dist/collections/operations/utilities/update.js +2 -1
- package/dist/collections/operations/utilities/update.js.map +1 -1
- package/dist/config/orderable/index.d.ts +2 -10
- package/dist/config/orderable/index.d.ts.map +1 -1
- package/dist/config/orderable/index.js +20 -67
- package/dist/config/orderable/index.js.map +1 -1
- package/dist/config/sanitize.d.ts.map +1 -1
- package/dist/config/sanitize.js +45 -5
- package/dist/config/sanitize.js.map +1 -1
- package/dist/config/types.d.ts +20 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/fields/config/sanitize.d.ts +54 -2
- package/dist/fields/config/sanitize.d.ts.map +1 -1
- package/dist/fields/config/sanitize.js +333 -285
- package/dist/fields/config/sanitize.js.map +1 -1
- package/dist/fields/config/sanitize.spec.js +82 -0
- package/dist/fields/config/sanitize.spec.js.map +1 -1
- package/dist/fields/config/sanitizeJoinField.d.ts +15 -1
- package/dist/fields/config/sanitizeJoinField.d.ts.map +1 -1
- package/dist/fields/config/sanitizeJoinField.js +17 -1
- package/dist/fields/config/sanitizeJoinField.js.map +1 -1
- package/dist/globals/operations/findOne.d.ts +1 -0
- package/dist/globals/operations/findOne.d.ts.map +1 -1
- package/dist/globals/operations/findOne.js +10 -3
- package/dist/globals/operations/findOne.js.map +1 -1
- package/dist/globals/operations/local/findOne.d.ts +8 -0
- package/dist/globals/operations/local/findOne.d.ts.map +1 -1
- package/dist/globals/operations/local/findOne.js +2 -1
- package/dist/globals/operations/local/findOne.js.map +1 -1
- package/dist/globals/operations/update.d.ts.map +1 -1
- package/dist/globals/operations/update.js +2 -1
- package/dist/globals/operations/update.js.map +1 -1
- package/dist/index.bundled.d.ts +112 -18
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/uploads/checkFileAccess.d.ts +2 -1
- package/dist/uploads/checkFileAccess.d.ts.map +1 -1
- package/dist/uploads/checkFileAccess.js +25 -14
- package/dist/uploads/checkFileAccess.js.map +1 -1
- package/dist/uploads/checkFileAccess.spec.js +109 -0
- package/dist/uploads/checkFileAccess.spec.js.map +1 -0
- package/dist/uploads/endpoints/getFile.d.ts.map +1 -1
- package/dist/uploads/endpoints/getFile.js +4 -1
- package/dist/uploads/endpoints/getFile.js.map +1 -1
- package/dist/uploads/types.d.ts +2 -1
- package/dist/uploads/types.d.ts.map +1 -1
- package/dist/uploads/types.js.map +1 -1
- package/dist/utilities/slugify.d.ts.map +1 -1
- package/dist/utilities/slugify.js +1 -1
- package/dist/utilities/slugify.js.map +1 -1
- package/dist/utilities/validateTimezones.d.ts.map +1 -1
- package/dist/utilities/validateTimezones.js +7 -0
- package/dist/utilities/validateTimezones.js.map +1 -1
- package/dist/versions/types.d.ts +1 -1
- package/dist/versions/types.js.map +1 -1
- package/package.json +10 -10
|
@@ -14,132 +14,146 @@ import { validations } from '../validations.js';
|
|
|
14
14
|
import { reservedAPIKeyFieldNames, reservedBaseAuthFieldNames, reservedBaseUploadFieldNames, reservedVerifyFieldNames } from './reservedFieldNames.js';
|
|
15
15
|
import { sanitizeJoinField } from './sanitizeJoinField.js';
|
|
16
16
|
import { fieldAffectsData as _fieldAffectsData, fieldIsLocalized, tabHasName } from './types.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize a single field. Handles all per-field logic including:
|
|
19
|
+
* - Validation setup
|
|
20
|
+
* - Hooks/access/admin defaults
|
|
21
|
+
* - Type-specific handling
|
|
22
|
+
* - Recursive sanitization of nested fields
|
|
23
|
+
*
|
|
24
|
+
* @returns Result containing any fields to insert after this one
|
|
25
|
+
*/ export const sanitizeField = async ({ collectionConfig, config, existingFieldNames, field, globalConfig, index, isTopLevelField, joinPath, joins, orderableJoins, parentIndexPath, parentIsLocalized, parentSchemaPath, polymorphicJoins, requireFieldLevelRichTextEditor, richTextSanitizationPromises, validRelationships })=>{
|
|
26
|
+
const result = {};
|
|
27
|
+
if ('_sanitized' in field && field._sanitized === true) {
|
|
28
|
+
return result;
|
|
20
29
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if ('_sanitized' in field) {
|
|
31
|
+
field._sanitized = true;
|
|
32
|
+
}
|
|
33
|
+
if (!field.type) {
|
|
34
|
+
throw new MissingFieldType(field);
|
|
35
|
+
}
|
|
36
|
+
const fieldAffectsData = _fieldAffectsData(field);
|
|
37
|
+
const { indexPath, schemaPath } = getFieldPaths({
|
|
38
|
+
field,
|
|
39
|
+
index,
|
|
40
|
+
parentIndexPath,
|
|
41
|
+
parentSchemaPath
|
|
42
|
+
});
|
|
43
|
+
// Reserved field name checks
|
|
44
|
+
if (isTopLevelField && fieldAffectsData && field.name) {
|
|
45
|
+
if (collectionConfig && collectionConfig.upload) {
|
|
46
|
+
if (reservedBaseUploadFieldNames.includes(field.name)) {
|
|
47
|
+
throw new ReservedFieldName(field, field.name);
|
|
48
|
+
}
|
|
31
49
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
index: i,
|
|
36
|
-
parentIndexPath,
|
|
37
|
-
parentSchemaPath
|
|
38
|
-
});
|
|
39
|
-
if (isTopLevelField && fieldAffectsData && field.name) {
|
|
40
|
-
if (collectionConfig && collectionConfig.upload) {
|
|
41
|
-
if (reservedBaseUploadFieldNames.includes(field.name)) {
|
|
42
|
-
throw new ReservedFieldName(field, field.name);
|
|
43
|
-
}
|
|
50
|
+
if (collectionConfig && collectionConfig.auth && typeof collectionConfig.auth === 'object' && !collectionConfig.auth.disableLocalStrategy) {
|
|
51
|
+
if (reservedBaseAuthFieldNames.includes(field.name)) {
|
|
52
|
+
throw new ReservedFieldName(field, field.name);
|
|
44
53
|
}
|
|
45
|
-
if (collectionConfig
|
|
46
|
-
|
|
54
|
+
if (collectionConfig.auth.verify) {
|
|
55
|
+
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
|
56
|
+
if (reservedAPIKeyFieldNames.includes(field.name)) {
|
|
47
57
|
throw new ReservedFieldName(field, field.name);
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new ReservedFieldName(field, field.name);
|
|
53
|
-
}
|
|
54
|
-
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
|
55
|
-
if (reservedVerifyFieldNames.includes(field.name)) {
|
|
56
|
-
throw new ReservedFieldName(field, field.name);
|
|
57
|
-
}
|
|
59
|
+
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
|
|
60
|
+
if (reservedVerifyFieldNames.includes(field.name)) {
|
|
61
|
+
throw new ReservedFieldName(field, field.name);
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
}
|
|
66
|
+
// Invalid field name check
|
|
67
|
+
if (fieldAffectsData && field.name.includes('.')) {
|
|
68
|
+
throw new InvalidFieldName(field, field.name);
|
|
69
|
+
}
|
|
70
|
+
// Auto-label
|
|
71
|
+
if ('name' in field && field.name && typeof field.label !== 'object' && typeof field.label !== 'string' && typeof field.label !== 'function' && field.label !== false) {
|
|
72
|
+
field.label = toWords(field.name);
|
|
73
|
+
}
|
|
74
|
+
// Checkbox default
|
|
75
|
+
if (field.type === 'checkbox' && typeof field.defaultValue === 'undefined' && field.required === true) {
|
|
76
|
+
field.defaultValue = false;
|
|
77
|
+
}
|
|
78
|
+
// Join field sanitization
|
|
79
|
+
if (field.type === 'join') {
|
|
80
|
+
sanitizeJoinField({
|
|
81
|
+
config,
|
|
82
|
+
field,
|
|
83
|
+
joinPath,
|
|
84
|
+
joins,
|
|
85
|
+
orderableJoins,
|
|
86
|
+
parentIsLocalized,
|
|
87
|
+
polymorphicJoins
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Relationship/upload validation
|
|
91
|
+
if (field.type === 'relationship' || field.type === 'upload') {
|
|
92
|
+
if (Array.isArray(field.relationTo) && field.relationTo.length === 0) {
|
|
93
|
+
throw new Error(`Field "${field.name}" of type "${field.type}" has an empty relationTo array. At least one collection must be specified.`);
|
|
71
94
|
}
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
if (validRelationships) {
|
|
96
|
+
const relationships = Array.isArray(field.relationTo) ? field.relationTo : [
|
|
97
|
+
field.relationTo
|
|
98
|
+
];
|
|
99
|
+
relationships.forEach((relationship)=>{
|
|
100
|
+
if (!validRelationships.includes(relationship)) {
|
|
101
|
+
throw new InvalidFieldRelationship(field, relationship);
|
|
102
|
+
}
|
|
80
103
|
});
|
|
81
104
|
}
|
|
82
|
-
if (field.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw new Error(`Field "${field.name}" of type "${field.type}" has an empty relationTo array. At least one collection must be specified.`);
|
|
86
|
-
}
|
|
87
|
-
if (validRelationships) {
|
|
88
|
-
const relationships = Array.isArray(field.relationTo) ? field.relationTo : [
|
|
89
|
-
field.relationTo
|
|
90
|
-
];
|
|
91
|
-
relationships.forEach((relationship)=>{
|
|
92
|
-
if (!validRelationships.includes(relationship)) {
|
|
93
|
-
throw new InvalidFieldRelationship(field, relationship);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (field.min && !field.minRows) {
|
|
98
|
-
console.warn(`(payload): The "min" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "minRows" instead.`);
|
|
99
|
-
field.minRows = field.min;
|
|
100
|
-
}
|
|
101
|
-
if (field.max && !field.maxRows) {
|
|
102
|
-
console.warn(`(payload): The "max" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "maxRows" instead.`);
|
|
103
|
-
field.maxRows = field.max;
|
|
104
|
-
}
|
|
105
|
+
if (field.min && !field.minRows) {
|
|
106
|
+
console.warn(`(payload): The "min" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "minRows" instead.`);
|
|
107
|
+
field.minRows = field.min;
|
|
105
108
|
}
|
|
106
|
-
if (field.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
isSortable: true,
|
|
110
|
-
...field.admin
|
|
111
|
-
};
|
|
112
|
-
}
|
|
109
|
+
if (field.max && !field.maxRows) {
|
|
110
|
+
console.warn(`(payload): The "max" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "maxRows" instead.`);
|
|
111
|
+
field.maxRows = field.max;
|
|
113
112
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
}
|
|
114
|
+
// Upload isSortable default
|
|
115
|
+
if (field.type === 'upload') {
|
|
116
|
+
if (!field.admin || !('isSortable' in field.admin)) {
|
|
117
|
+
field.admin = {
|
|
118
|
+
isSortable: true,
|
|
119
|
+
...field.admin
|
|
120
|
+
};
|
|
119
121
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
}
|
|
123
|
+
// Array ID field
|
|
124
|
+
if (field.type === 'array' && field.fields) {
|
|
125
|
+
const hasCustomID = field.fields.some((f)=>'name' in f && f.name === 'id');
|
|
126
|
+
if (!hasCustomID) {
|
|
127
|
+
field.fields.push(baseIDField);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Blocks/array labels
|
|
131
|
+
if ((field.type === 'blocks' || field.type === 'array') && field.label) {
|
|
132
|
+
field.labels = field.labels || formatLabels(field.name);
|
|
133
|
+
}
|
|
134
|
+
if (fieldAffectsData) {
|
|
135
|
+
if (existingFieldNames.has(field.name)) {
|
|
136
|
+
throw new DuplicateFieldName(field.name);
|
|
137
|
+
} else if (![
|
|
138
|
+
'blockName',
|
|
139
|
+
'id'
|
|
140
|
+
].includes(field.name)) {
|
|
141
|
+
existingFieldNames.add(field.name);
|
|
122
142
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
'id'
|
|
129
|
-
].includes(field.name)) {
|
|
130
|
-
existingFieldNames.add(field.name);
|
|
143
|
+
if (typeof field.localized !== 'undefined') {
|
|
144
|
+
let shouldDisableLocalized = !config.localization;
|
|
145
|
+
if (process.env.NEXT_PUBLIC_PAYLOAD_COMPATIBILITY_allowLocalizedWithinLocalized !== 'true' && parentIsLocalized && // @todo PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY=true will be the default in 4.0
|
|
146
|
+
process.env.PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY !== 'true') {
|
|
147
|
+
shouldDisableLocalized = true;
|
|
131
148
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
if (process.env.NEXT_PUBLIC_PAYLOAD_COMPATIBILITY_allowLocalizedWithinLocalized !== 'true' && parentIsLocalized && // @todo PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY=true will be the default in 4.0
|
|
135
|
-
process.env.PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY !== 'true') {
|
|
136
|
-
shouldDisableLocalized = true;
|
|
137
|
-
}
|
|
138
|
-
if (shouldDisableLocalized) {
|
|
139
|
-
delete field.localized;
|
|
140
|
-
}
|
|
149
|
+
if (shouldDisableLocalized) {
|
|
150
|
+
delete field.localized;
|
|
141
151
|
}
|
|
142
|
-
|
|
152
|
+
}
|
|
153
|
+
if (typeof field.validate === 'undefined') {
|
|
154
|
+
if ('virtual' in field && field.virtual) {
|
|
155
|
+
field.validate = ()=>true;
|
|
156
|
+
} else {
|
|
143
157
|
const defaultValidate = validations[field.type];
|
|
144
158
|
if (defaultValidate) {
|
|
145
159
|
field.validate = (val, options)=>defaultValidate(val, {
|
|
@@ -150,207 +164,241 @@ export const sanitizeFields = async ({ collectionConfig, config, existingFieldNa
|
|
|
150
164
|
field.validate = ()=>true;
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
|
-
if (!field.hooks) {
|
|
154
|
-
field.hooks = {};
|
|
155
|
-
}
|
|
156
|
-
if (!field.access) {
|
|
157
|
-
field.access = {};
|
|
158
|
-
}
|
|
159
|
-
setDefaultBeforeDuplicate(field, parentIsLocalized);
|
|
160
167
|
}
|
|
161
|
-
if (!field.
|
|
162
|
-
field.
|
|
168
|
+
if (!field.hooks) {
|
|
169
|
+
field.hooks = {};
|
|
163
170
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const sanitizeRichText = async (_config)=>{
|
|
167
|
-
if (!field.editor) {
|
|
168
|
-
if (_config.editor && !requireFieldLevelRichTextEditor) {
|
|
169
|
-
// config.editor should be sanitized at this point
|
|
170
|
-
field.editor = _config.editor;
|
|
171
|
-
} else {
|
|
172
|
-
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
|
173
|
-
;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (typeof field.editor === 'function') {
|
|
177
|
-
field.editor = await field.editor({
|
|
178
|
-
config: _config,
|
|
179
|
-
isRoot: requireFieldLevelRichTextEditor,
|
|
180
|
-
parentIsLocalized: parentIsLocalized || field.localized
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (field.editor.i18n && Object.keys(field.editor.i18n).length >= 0) {
|
|
184
|
-
config.i18n.translations = deepMergeSimple(config.i18n.translations, field.editor.i18n);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
if (richTextSanitizationPromises) {
|
|
188
|
-
richTextSanitizationPromises.push(sanitizeRichText);
|
|
189
|
-
} else {
|
|
190
|
-
await sanitizeRichText(config);
|
|
191
|
-
}
|
|
171
|
+
if (!field.access) {
|
|
172
|
+
field.access = {};
|
|
192
173
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
continue;
|
|
174
|
+
setDefaultBeforeDuplicate(field, parentIsLocalized);
|
|
175
|
+
}
|
|
176
|
+
if (!field.admin) {
|
|
177
|
+
field.admin = {};
|
|
178
|
+
}
|
|
179
|
+
// Make sure that the richText field has an editor
|
|
180
|
+
if (field.type === 'richText') {
|
|
181
|
+
const sanitizeRichText = async (_config)=>{
|
|
182
|
+
if (!field.editor) {
|
|
183
|
+
if (_config.editor && !requireFieldLevelRichTextEditor) {
|
|
184
|
+
// config.editor should be sanitized at this point
|
|
185
|
+
field.editor = _config.editor;
|
|
186
|
+
} else {
|
|
187
|
+
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
|
|
188
|
+
;
|
|
209
189
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
existingFieldNames: new Set(),
|
|
217
|
-
fields: block.fields,
|
|
218
|
-
isTopLevelField: false,
|
|
219
|
-
parentIndexPath: '',
|
|
220
|
-
parentIsLocalized: parentIsLocalized || field.localized,
|
|
221
|
-
parentSchemaPath: schemaPath + '.' + block.slug,
|
|
222
|
-
requireFieldLevelRichTextEditor,
|
|
223
|
-
richTextSanitizationPromises,
|
|
224
|
-
validRelationships
|
|
190
|
+
}
|
|
191
|
+
if (typeof field.editor === 'function') {
|
|
192
|
+
field.editor = await field.editor({
|
|
193
|
+
config: _config,
|
|
194
|
+
isRoot: requireFieldLevelRichTextEditor,
|
|
195
|
+
parentIsLocalized: parentIsLocalized || field.localized
|
|
225
196
|
});
|
|
226
197
|
}
|
|
198
|
+
if (field.editor.i18n && Object.keys(field.editor.i18n).length >= 0) {
|
|
199
|
+
config.i18n.translations = deepMergeSimple(config.i18n.translations, field.editor.i18n);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
if (richTextSanitizationPromises) {
|
|
203
|
+
richTextSanitizationPromises.push(sanitizeRichText);
|
|
204
|
+
} else {
|
|
205
|
+
await sanitizeRichText(config);
|
|
227
206
|
}
|
|
228
|
-
|
|
229
|
-
|
|
207
|
+
}
|
|
208
|
+
if (field.type === 'blocks' && field.blocks) {
|
|
209
|
+
if (field.blockReferences && field.blocks?.length) {
|
|
210
|
+
throw new Error('You cannot have both blockReferences and blocks in the same blocks field');
|
|
211
|
+
}
|
|
212
|
+
const blockSlugs = [];
|
|
213
|
+
for (const block of field.blockReferences ?? field.blocks){
|
|
214
|
+
const blockSlug = typeof block === 'string' ? block : block.slug;
|
|
215
|
+
if (blockSlugs.includes(blockSlug)) {
|
|
216
|
+
throw new DuplicateFieldName(blockSlug);
|
|
217
|
+
}
|
|
218
|
+
blockSlugs.push(blockSlug);
|
|
219
|
+
if (typeof block === 'string') {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (block._sanitized === true) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
block._sanitized = true;
|
|
226
|
+
block.fields = block.fields.concat(baseBlockFields);
|
|
227
|
+
block.labels = !block.labels ? formatLabels(block.slug) : block.labels;
|
|
228
|
+
block.fields = await sanitizeFields({
|
|
230
229
|
collectionConfig,
|
|
231
230
|
config,
|
|
232
|
-
existingFieldNames:
|
|
233
|
-
fields:
|
|
234
|
-
isTopLevelField:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
parentIsLocalized: parentIsLocalized || fieldIsLocalized(field),
|
|
239
|
-
parentSchemaPath: schemaPath,
|
|
240
|
-
polymorphicJoins,
|
|
231
|
+
existingFieldNames: new Set(),
|
|
232
|
+
fields: block.fields,
|
|
233
|
+
isTopLevelField: false,
|
|
234
|
+
parentIndexPath: '',
|
|
235
|
+
parentIsLocalized: parentIsLocalized || field.localized,
|
|
236
|
+
parentSchemaPath: schemaPath + '.' + block.slug,
|
|
241
237
|
requireFieldLevelRichTextEditor,
|
|
242
238
|
richTextSanitizationPromises,
|
|
243
239
|
validRelationships
|
|
244
240
|
});
|
|
245
241
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
parentSchemaPath: tabSchemaPath,
|
|
273
|
-
polymorphicJoins,
|
|
274
|
-
requireFieldLevelRichTextEditor,
|
|
275
|
-
richTextSanitizationPromises,
|
|
276
|
-
validRelationships
|
|
277
|
-
});
|
|
278
|
-
field.tabs[j] = tab;
|
|
242
|
+
}
|
|
243
|
+
if ('fields' in field && field.fields) {
|
|
244
|
+
field.fields = await sanitizeFields({
|
|
245
|
+
collectionConfig,
|
|
246
|
+
config,
|
|
247
|
+
existingFieldNames: fieldAffectsData ? new Set() : existingFieldNames,
|
|
248
|
+
fields: field.fields,
|
|
249
|
+
isTopLevelField: isTopLevelField && !fieldAffectsData,
|
|
250
|
+
joinPath: fieldAffectsData ? `${joinPath ? joinPath + '.' : ''}${field.name}` : joinPath,
|
|
251
|
+
joins,
|
|
252
|
+
orderableJoins,
|
|
253
|
+
parentIndexPath: fieldAffectsData ? '' : indexPath,
|
|
254
|
+
parentIsLocalized: parentIsLocalized || fieldIsLocalized(field),
|
|
255
|
+
parentSchemaPath: schemaPath,
|
|
256
|
+
polymorphicJoins,
|
|
257
|
+
requireFieldLevelRichTextEditor,
|
|
258
|
+
richTextSanitizationPromises,
|
|
259
|
+
validRelationships
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (field.type === 'tabs') {
|
|
263
|
+
for(let j = 0; j < field.tabs.length; j++){
|
|
264
|
+
const tab = field.tabs[j];
|
|
265
|
+
const isNamedTab = tabHasName(tab);
|
|
266
|
+
if (isNamedTab && typeof tab.label === 'undefined') {
|
|
267
|
+
tab.label = toWords(tab.name);
|
|
279
268
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Insert our field after assignment
|
|
286
|
-
if (field.type === 'date' && field.timezone) {
|
|
287
|
-
const name = field.name + '_tz';
|
|
288
|
-
let defaultTimezone = field.timezone && typeof field.timezone === 'object' ? field.timezone.defaultTimezone : config.admin?.timezones?.defaultTimezone;
|
|
289
|
-
const required = field.required || field.timezone && typeof field.timezone === 'object' && field.timezone.required;
|
|
290
|
-
const supportedTimezones = field.timezone && typeof field.timezone === 'object' && field.timezone.supportedTimezones ? field.timezone.supportedTimezones : config.admin?.timezones?.supportedTimezones;
|
|
291
|
-
const options = typeof supportedTimezones === 'function' ? supportedTimezones({
|
|
292
|
-
defaultTimezones
|
|
293
|
-
}) : supportedTimezones;
|
|
294
|
-
validateTimezones({
|
|
295
|
-
source: `field "${field.name}" timezone.supportedTimezones`,
|
|
296
|
-
timezones: options
|
|
269
|
+
const { indexPath: tabIndexPath, schemaPath: tabSchemaPath } = getFieldPaths({
|
|
270
|
+
field: tab,
|
|
271
|
+
index: j,
|
|
272
|
+
parentIndexPath: indexPath,
|
|
273
|
+
parentSchemaPath: schemaPath
|
|
297
274
|
});
|
|
298
|
-
if (
|
|
299
|
-
|
|
275
|
+
if ('admin' in tab && tab.admin?.condition && typeof tab.admin.condition === 'function' && !tab.id) {
|
|
276
|
+
tab.id = tabSchemaPath;
|
|
300
277
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
name,
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
278
|
+
tab.fields = await sanitizeFields({
|
|
279
|
+
collectionConfig,
|
|
280
|
+
config,
|
|
281
|
+
existingFieldNames: isNamedTab ? new Set() : existingFieldNames,
|
|
282
|
+
fields: tab.fields,
|
|
283
|
+
isTopLevelField: isTopLevelField && !isNamedTab,
|
|
284
|
+
joinPath: isNamedTab ? `${joinPath ? joinPath + '.' : ''}${tab.name}` : joinPath,
|
|
285
|
+
joins,
|
|
286
|
+
orderableJoins,
|
|
287
|
+
parentIndexPath: isNamedTab ? '' : tabIndexPath,
|
|
288
|
+
parentIsLocalized: parentIsLocalized || isNamedTab && tab.localized,
|
|
289
|
+
parentSchemaPath: tabSchemaPath,
|
|
290
|
+
polymorphicJoins,
|
|
291
|
+
requireFieldLevelRichTextEditor,
|
|
292
|
+
richTextSanitizationPromises,
|
|
293
|
+
validRelationships
|
|
312
294
|
});
|
|
313
|
-
|
|
314
|
-
const timezoneField = typeof field.timezone === 'object' && typeof field.timezone.override === 'function' ? field.timezone.override({
|
|
315
|
-
baseField
|
|
316
|
-
}) : baseField;
|
|
317
|
-
fields.splice(++i, 0, timezoneField);
|
|
295
|
+
field.tabs[j] = tab;
|
|
318
296
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
297
|
+
}
|
|
298
|
+
if (field.type === 'ui' && typeof field.admin.disableBulkEdit === 'undefined') {
|
|
299
|
+
field.admin.disableBulkEdit = true;
|
|
300
|
+
}
|
|
301
|
+
// Timezone field insertion
|
|
302
|
+
if (field.type === 'date' && field.timezone) {
|
|
303
|
+
const name = field.name + '_tz';
|
|
304
|
+
let defaultTimezone = field.timezone && typeof field.timezone === 'object' ? field.timezone.defaultTimezone : config.admin?.timezones?.defaultTimezone;
|
|
305
|
+
const required = field.required || field.timezone && typeof field.timezone === 'object' && field.timezone.required;
|
|
306
|
+
const supportedTimezones = field.timezone && typeof field.timezone === 'object' && field.timezone.supportedTimezones ? field.timezone.supportedTimezones : config.admin?.timezones?.supportedTimezones;
|
|
307
|
+
const options = typeof supportedTimezones === 'function' ? supportedTimezones({
|
|
308
|
+
defaultTimezones
|
|
309
|
+
}) : supportedTimezones;
|
|
310
|
+
validateTimezones({
|
|
311
|
+
source: `field "${field.name}" timezone.supportedTimezones`,
|
|
312
|
+
timezones: options
|
|
313
|
+
});
|
|
314
|
+
if (options && options.length === 1 && options[0]?.value) {
|
|
315
|
+
defaultTimezone = options[0].value;
|
|
316
|
+
}
|
|
317
|
+
// Generate label for timezone field
|
|
318
|
+
const timezoneLabel = typeof field.label === 'string' ? `${field.label} Tz` : toWords(name);
|
|
319
|
+
const baseField = baseTimezoneField({
|
|
320
|
+
name,
|
|
321
|
+
defaultValue: defaultTimezone,
|
|
322
|
+
label: timezoneLabel,
|
|
323
|
+
options,
|
|
324
|
+
required
|
|
325
|
+
});
|
|
326
|
+
// Apply override if provided
|
|
327
|
+
const timezoneField = typeof field.timezone === 'object' && typeof field.timezone.override === 'function' ? field.timezone.override({
|
|
328
|
+
baseField
|
|
329
|
+
}) : baseField;
|
|
330
|
+
result.fieldsToInsert = [
|
|
331
|
+
timezoneField
|
|
332
|
+
];
|
|
333
|
+
}
|
|
334
|
+
// Virtual field handling
|
|
335
|
+
if ('virtual' in field && typeof field.virtual === 'string') {
|
|
336
|
+
const virtualField = field;
|
|
337
|
+
const configFields = (collectionConfig || globalConfig)?.fields;
|
|
338
|
+
if (configFields) {
|
|
339
|
+
let flattenFields = flattenAllFields({
|
|
340
|
+
fields: configFields
|
|
341
|
+
});
|
|
342
|
+
const paths = field.virtual.split('.');
|
|
343
|
+
let isHasMany = false;
|
|
344
|
+
for (const [idx, segment] of paths.entries()){
|
|
345
|
+
const foundField = flattenFields.find((e)=>e.name === segment);
|
|
346
|
+
if (!foundField) {
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
if (foundField.type === 'group' || foundField.type === 'tab' || foundField.type === 'array') {
|
|
350
|
+
flattenFields = foundField.flattenedFields;
|
|
351
|
+
} else if ((foundField.type === 'relationship' || foundField.type === 'upload') && idx !== paths.length - 1 && typeof foundField.relationTo === 'string') {
|
|
352
|
+
if (foundField.hasMany && (virtualField.type === 'text' || virtualField.type === 'number' || virtualField.type === 'select')) {
|
|
353
|
+
if (isHasMany) {
|
|
354
|
+
throw new InvalidConfiguration(`Virtual field ${virtualField.name} in ${globalConfig ? `global ${globalConfig.slug}` : `collection ${collectionConfig?.slug}`} references 2 or more hasMany relationships on the path ${virtualField.virtual} which is not allowed.`);
|
|
348
355
|
}
|
|
356
|
+
isHasMany = true;
|
|
357
|
+
virtualField.hasMany = true;
|
|
358
|
+
}
|
|
359
|
+
const relatedCollection = config.collections?.find((e)=>e.slug === foundField.relationTo);
|
|
360
|
+
if (relatedCollection) {
|
|
361
|
+
flattenFields = flattenAllFields({
|
|
362
|
+
fields: relatedCollection.fields
|
|
363
|
+
});
|
|
349
364
|
}
|
|
350
365
|
}
|
|
351
366
|
}
|
|
352
367
|
}
|
|
353
368
|
}
|
|
369
|
+
return result;
|
|
370
|
+
};
|
|
371
|
+
export const sanitizeFields = async ({ collectionConfig, config, existingFieldNames = new Set(), fields, globalConfig, isTopLevelField = true, joinPath = '', joins, orderableJoins, parentIndexPath = '', parentIsLocalized, parentSchemaPath = '', polymorphicJoins, requireFieldLevelRichTextEditor = false, richTextSanitizationPromises, validRelationships })=>{
|
|
372
|
+
if (!fields) {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
for(let i = 0; i < fields.length; i++){
|
|
376
|
+
const field = fields[i];
|
|
377
|
+
const result = await sanitizeField({
|
|
378
|
+
collectionConfig,
|
|
379
|
+
config,
|
|
380
|
+
existingFieldNames,
|
|
381
|
+
field,
|
|
382
|
+
globalConfig,
|
|
383
|
+
index: i,
|
|
384
|
+
isTopLevelField,
|
|
385
|
+
joinPath,
|
|
386
|
+
joins,
|
|
387
|
+
orderableJoins,
|
|
388
|
+
parentIndexPath,
|
|
389
|
+
parentIsLocalized,
|
|
390
|
+
parentSchemaPath,
|
|
391
|
+
polymorphicJoins,
|
|
392
|
+
requireFieldLevelRichTextEditor,
|
|
393
|
+
richTextSanitizationPromises,
|
|
394
|
+
validRelationships
|
|
395
|
+
});
|
|
396
|
+
fields[i] = field;
|
|
397
|
+
if (result.fieldsToInsert?.length) {
|
|
398
|
+
fields.splice(i + 1, 0, ...result.fieldsToInsert);
|
|
399
|
+
i += result.fieldsToInsert.length;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
354
402
|
return fields;
|
|
355
403
|
};
|
|
356
404
|
|