mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Use CSV for special, add on create / on update for fields
This commit is contained in:
@@ -1459,6 +1459,11 @@ rows:
|
||||
hidden: true
|
||||
locked: true
|
||||
special: boolean
|
||||
- collection: directus_fields
|
||||
field: special
|
||||
hidden: true
|
||||
locked: true
|
||||
special: csv
|
||||
- collection: directus_fields
|
||||
field: translation
|
||||
hidden: true
|
||||
|
||||
@@ -56,6 +56,7 @@ const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
|
||||
{
|
||||
message: err.message,
|
||||
extensions: {
|
||||
...err.extensions,
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -47,8 +47,6 @@ export default class FieldsService {
|
||||
fields = (await nonAuthorizedItemsService.readByQuery({ limit: -1 })) as FieldMeta[];
|
||||
}
|
||||
|
||||
fields = (await this.payloadService.processValues('read', fields)) as FieldMeta[];
|
||||
|
||||
let columns = await schemaInspector.columnInfo(collection);
|
||||
|
||||
columns = columns.map((column) => {
|
||||
|
||||
@@ -125,6 +125,7 @@ export default class PayloadService {
|
||||
},
|
||||
async csv(action, value) {
|
||||
if (!value) return;
|
||||
// if (Array.isArray(value) && action === 'read') return value;
|
||||
if (action === 'read') return value.split(',');
|
||||
|
||||
if (Array.isArray(value)) return value.join(',');
|
||||
@@ -146,7 +147,7 @@ export default class PayloadService {
|
||||
|
||||
const specialFieldsQuery = this.knex
|
||||
.select('field', 'special')
|
||||
.from<FieldMeta>('directus_fields')
|
||||
.from('directus_fields')
|
||||
.where({ collection: this.collection })
|
||||
.whereNotNull('special');
|
||||
|
||||
@@ -190,13 +191,12 @@ export default class PayloadService {
|
||||
}
|
||||
|
||||
async processField(
|
||||
field: Pick<FieldMeta, 'field' | 'special'>,
|
||||
field: { field: string; special: string },
|
||||
payload: Partial<Item>,
|
||||
action: Action,
|
||||
accountability: Accountability | null
|
||||
) {
|
||||
if (!field.special) return payload[field.field];
|
||||
|
||||
const fieldSpecials = field.special.split(',').map((s) => s.trim());
|
||||
|
||||
let value = clone(payload[field.field]);
|
||||
|
||||
@@ -22,7 +22,7 @@ export type FieldMeta = {
|
||||
id: number;
|
||||
collection: string;
|
||||
field: string;
|
||||
special: string | null;
|
||||
special: string[] | null;
|
||||
interface: string | null;
|
||||
options: Record<string, any> | null;
|
||||
locked: boolean;
|
||||
|
||||
@@ -80,18 +80,13 @@ const localTypeMap: Record<string, { type: typeof types[number]; useTimezone?: b
|
||||
|
||||
export default function getLocalType(
|
||||
databaseType: string,
|
||||
special?: string | null
|
||||
special?: string[] | null
|
||||
): typeof types[number] | 'unknown' {
|
||||
const type = localTypeMap[databaseType.toLowerCase().split('(')[0]];
|
||||
|
||||
switch (special) {
|
||||
case 'json':
|
||||
return 'json';
|
||||
case 'csv':
|
||||
return 'csv';
|
||||
case 'uuid':
|
||||
return 'uuid';
|
||||
}
|
||||
if (special?.includes('json')) return 'json';
|
||||
if (special?.includes('csv')) return 'csv';
|
||||
if (special?.includes('uuid')) return 'uuid';
|
||||
|
||||
if (type) {
|
||||
return type.type;
|
||||
|
||||
@@ -25,7 +25,7 @@ export function useCollection(collectionKey: string | Ref<string>) {
|
||||
});
|
||||
|
||||
const userCreatedField = computed(() => {
|
||||
return fields.value?.find((field) => field.meta?.special === 'user_created') || null;
|
||||
return fields.value?.find((field) => field.meta?.special?.includes('user_created')) || null;
|
||||
});
|
||||
|
||||
const sortField = computed(() => {
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function useFieldTree(collection: Ref<string>) {
|
||||
const tree = computed<FieldTree[]>(() => {
|
||||
return fieldsStore
|
||||
.getFieldsForCollection(collection.value)
|
||||
.filter((field: Field) => field.meta?.hidden === false && field.meta?.special?.toLowerCase() !== 'alias')
|
||||
.filter((field: Field) => field.meta?.hidden === false && field.meta?.special?.includes('alias') === false)
|
||||
.map((field: Field) => parseField(field, []));
|
||||
|
||||
function parseField(field: Field, parents: Field[]) {
|
||||
@@ -39,7 +39,7 @@ export default function useFieldTree(collection: Ref<string>) {
|
||||
.getFieldsForCollection(relatedCollection)
|
||||
.filter(
|
||||
(field: Field) =>
|
||||
field.meta?.hidden === false && field.meta?.special?.toLowerCase() !== 'alias'
|
||||
field.meta?.hidden === false && field.meta?.special?.includes('alias') === false
|
||||
);
|
||||
})
|
||||
.flat()
|
||||
|
||||
@@ -1027,6 +1027,12 @@
|
||||
}
|
||||
},
|
||||
|
||||
"do_nothing": "Do Nothing",
|
||||
"generate_and_save_uuid": "Generate and Save UUID",
|
||||
"save_current_user_id": "Save Current User ID",
|
||||
"save_current_user_role": "Save Current User Role",
|
||||
"save_current_datetime": "Save Current Date/Time",
|
||||
|
||||
"modules": {},
|
||||
|
||||
"coming_soon": "Coming Soon",
|
||||
|
||||
@@ -227,7 +227,7 @@ export default defineComponent({
|
||||
if (!newField.meta) newField.meta = {};
|
||||
if (enabled === true) {
|
||||
newField.meta.interface = 'many-to-one';
|
||||
newField.meta.special = 'many-to-one';
|
||||
newField.meta.special = ['m2o'];
|
||||
} else {
|
||||
newField.meta.interface = null;
|
||||
newField.meta.special = null;
|
||||
|
||||
@@ -82,6 +82,18 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-if="['uuid', 'date', 'time', 'datetime', 'timestamp'].includes(fieldData.type)">
|
||||
<div class="field">
|
||||
<div class="label type-label">{{ $t('on_create') }}</div>
|
||||
<v-select :items="onCreateOptions" v-model="onCreateValue" />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="label type-label">{{ $t('on_update') }}</div>
|
||||
<v-select :items="onUpdateOptions" v-model="onUpdateValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="field" v-if="fieldData.schema">
|
||||
<div class="label type-label">{{ $t('length') }}</div>
|
||||
<v-input
|
||||
@@ -247,13 +259,148 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
const { onCreateOptions, onCreateValue } = useOnCreate();
|
||||
const { onUpdateOptions, onUpdateValue } = useOnUpdate();
|
||||
|
||||
return {
|
||||
fieldData: state.fieldData,
|
||||
typesWithLabels,
|
||||
typeDisabled,
|
||||
typePlaceholder,
|
||||
defaultValue,
|
||||
onCreateOptions,
|
||||
onCreateValue,
|
||||
onUpdateOptions,
|
||||
onUpdateValue
|
||||
};
|
||||
|
||||
function useOnCreate() {
|
||||
const onCreateSpecials = ['uuid', 'user-created', 'role-created', 'date-created'];
|
||||
|
||||
const onCreateOptions = computed(() => {
|
||||
if (state.fieldData.type === 'uuid') {
|
||||
return [
|
||||
{
|
||||
text: i18n.t('do_nothing'),
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
text: i18n.t('generate_and_save_uuid'),
|
||||
value: 'uuid',
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_user_id'),
|
||||
value: 'user-created'
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_user_role'),
|
||||
value: 'role-created'
|
||||
},
|
||||
]
|
||||
} else if (['date', 'time', 'datetime', 'timestamp'].includes(state.fieldData.type)) {
|
||||
return [
|
||||
{
|
||||
text: i18n.t('do_nothing'),
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_datetime'),
|
||||
value: 'date-created'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
const onCreateValue = computed({
|
||||
get() {
|
||||
const specials = state.fieldData.meta.special || [];
|
||||
|
||||
for (const special of onCreateSpecials) {
|
||||
if (specials.includes(special)) {
|
||||
return special;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
set(newOption: string | null) {
|
||||
state.fieldData.meta.special = (state.fieldData.meta.special || []).filter((special: string) => onCreateSpecials.includes(special) === false);
|
||||
|
||||
if (newOption) {
|
||||
state.fieldData.meta.special = [
|
||||
...(state.fieldData.meta.special || []),
|
||||
newOption
|
||||
];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return { onCreateSpecials, onCreateOptions, onCreateValue };
|
||||
}
|
||||
|
||||
function useOnUpdate() {
|
||||
const onUpdateSpecials = ['user-updated', 'role-updated', 'date-updated'];
|
||||
|
||||
const onUpdateOptions = computed(() => {
|
||||
if (state.fieldData.type === 'uuid') {
|
||||
return [
|
||||
{
|
||||
text: i18n.t('do_nothing'),
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_user_id'),
|
||||
value: 'user-updated'
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_user_role'),
|
||||
value: 'role-updated'
|
||||
},
|
||||
]
|
||||
} else if (['date', 'time', 'datetime', 'timestamp'].includes(state.fieldData.type)) {
|
||||
return [
|
||||
{
|
||||
text: i18n.t('do_nothing'),
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
text: i18n.t('save_current_datetime'),
|
||||
value: 'date-updated'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
const onUpdateValue = computed({
|
||||
get() {
|
||||
const specials = state.fieldData.meta.special || [];
|
||||
|
||||
for (const special of onUpdateSpecials) {
|
||||
if (specials.includes(special)) {
|
||||
return special;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
set(newOption: string | null) {
|
||||
state.fieldData.meta.special = (state.fieldData.meta.special || []).filter((special: string) => onUpdateSpecials.includes(special) === false);
|
||||
|
||||
if (newOption) {
|
||||
state.fieldData.meta.special = [
|
||||
...(state.fieldData.meta.special || []),
|
||||
newOption
|
||||
];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return { onUpdateSpecials, onUpdateOptions, onUpdateValue };
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -282,7 +282,7 @@ function initLocalStore(
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.meta.special = 'o2m';
|
||||
state.fieldData.meta.special = ['o2m'];
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
@@ -402,7 +402,7 @@ function initLocalStore(
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.meta.special = 'm2m';
|
||||
state.fieldData.meta.special = ['m2m'];
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
@@ -539,7 +539,7 @@ function initLocalStore(
|
||||
delete state.fieldData.schema;
|
||||
delete state.fieldData.type;
|
||||
|
||||
state.fieldData.meta.special = 'alias';
|
||||
state.fieldData.meta.special = ['alias'];
|
||||
}
|
||||
|
||||
if (type === 'standard') {
|
||||
@@ -555,16 +555,16 @@ function initLocalStore(
|
||||
|
||||
switch (state.fieldData.type) {
|
||||
case 'uuid':
|
||||
state.fieldData.meta.special = 'uuid';
|
||||
state.fieldData.meta.special = ['uuid'];
|
||||
break;
|
||||
case 'json':
|
||||
state.fieldData.meta.special = 'json';
|
||||
state.fieldData.meta.special = ['json'];
|
||||
break;
|
||||
case 'csv':
|
||||
state.fieldData.meta.special = 'csv';
|
||||
state.fieldData.meta.special = ['csv'];
|
||||
break;
|
||||
case 'boolean':
|
||||
state.fieldData.meta.special = 'boolean';
|
||||
state.fieldData.meta.special = ['boolean'];
|
||||
state.fieldData.schema.is_nullable = false;
|
||||
state.fieldData.schema.default_value = false;
|
||||
break;
|
||||
|
||||
@@ -235,7 +235,7 @@ export default defineComponent({
|
||||
meta: {
|
||||
...field.meta,
|
||||
interface: 'text-input',
|
||||
special: 'uuid',
|
||||
special: ['uuid'],
|
||||
},
|
||||
schema: {
|
||||
...field.schema,
|
||||
@@ -341,7 +341,7 @@ export default defineComponent({
|
||||
field: systemFields.userCreated.name,
|
||||
type: 'uuid',
|
||||
meta: {
|
||||
special: 'user-created',
|
||||
special: ['user-created'],
|
||||
interface: 'user',
|
||||
options: {
|
||||
template: '{{first_name}} {{last_name}}',
|
||||
@@ -360,7 +360,7 @@ export default defineComponent({
|
||||
field: systemFields.dateCreated.name,
|
||||
type: 'timestamp',
|
||||
meta: {
|
||||
special: 'date-created',
|
||||
special: ['date-created'],
|
||||
interface: 'datetime',
|
||||
readonly: true,
|
||||
hidden: true,
|
||||
@@ -375,7 +375,7 @@ export default defineComponent({
|
||||
field: systemFields.userUpdated.name,
|
||||
type: 'uuid',
|
||||
meta: {
|
||||
special: 'user-updated',
|
||||
special: ['user-updated'],
|
||||
interface: 'user',
|
||||
options: {
|
||||
template: '{{first_name}} {{last_name}}',
|
||||
@@ -394,7 +394,7 @@ export default defineComponent({
|
||||
field: systemFields.dateUpdated.name,
|
||||
type: 'timestamp',
|
||||
meta: {
|
||||
special: 'date-updated',
|
||||
special: ['date-updated'],
|
||||
interface: 'datetime',
|
||||
readonly: true,
|
||||
hidden: true,
|
||||
|
||||
@@ -60,7 +60,7 @@ export type FieldMeta = {
|
||||
readonly: boolean;
|
||||
required: boolean;
|
||||
sort: number | null;
|
||||
special: string | null;
|
||||
special: string[] | null;
|
||||
translation: null | Translation[];
|
||||
width: Width | null;
|
||||
note: string | TranslateResult | null;
|
||||
|
||||
@@ -79,7 +79,7 @@ export default defineComponent({
|
||||
const fieldTree = computed<FieldTree[]>(() => {
|
||||
return fieldsStore
|
||||
.getFieldsForCollection(props.collection)
|
||||
.filter((field: Field) => field.meta?.hidden !== true && field.meta?.special?.toLowerCase() !== 'alias')
|
||||
.filter((field: Field) => field.meta?.hidden !== true && field.meta?.special?.includes('alias') === false)
|
||||
.map((field: Field) => parseField(field, []));
|
||||
|
||||
function parseField(field: Field, parents: Field[]) {
|
||||
@@ -108,7 +108,7 @@ export default defineComponent({
|
||||
.getFieldsForCollection(relatedCollection)
|
||||
.filter(
|
||||
(field: Field) =>
|
||||
field.meta?.hidden !== true && field.meta?.special?.toLowerCase() !== 'alias'
|
||||
field.meta?.hidden !== true && field.meta?.special?.includes('alias') === false
|
||||
);
|
||||
})
|
||||
.flat()
|
||||
|
||||
Reference in New Issue
Block a user