Skip to content

Commit 8067cd7

Browse files
authored
Merge pull request #714 from code16/cwe-434
Fix upload validation
2 parents 45aeb8b + 4d24153 commit 8067cd7

19 files changed

Lines changed: 591 additions & 90 deletions

resources/js/form/components/Field.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import Editor from "./fields/editor/Editor.vue";
99
import Geolocation from "./fields/geolocation/Geolocation.vue";
1010
import Html from "./fields/Html.vue";
11-
import List from "./fields/List.vue";
11+
import List from "./fields/list/List.vue";
1212
import Number from "./fields/Number.vue";
1313
import Select from "./fields/select/Select.vue";
1414
import Tags from "./fields/Tags.vue";

resources/js/form/components/Form.vue

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import debounce from "lodash/debounce";
3030
import { api } from "@/api/api";
3131
import { route } from "@/utils/url";
32-
import { useParentCommands } from "@/commands/useCommands";
3332
import merge from 'lodash/merge';
33+
import { useFieldContainerData } from "@/form/useFieldContainerData";
3434
3535
const props = defineProps<{
3636
form: Form
@@ -82,14 +82,11 @@
8282
props.form.setMeta(fieldKey, { uploading });
8383
}
8484
85-
const parentCommands = useParentCommands();
85+
const fieldContainerData = useFieldContainerData(props.form);
8686
const refresh = debounce((data) => {
8787
api.post(route('code16.sharp.api.form.refresh.update', {
8888
entityKey: props.form.entityKey,
89-
instance_id: props.form.instanceId,
90-
embed_key: props.form.embedKey,
91-
entity_list_command_key: parentCommands?.commandContainer === 'entityList' ? props.form.commandKey : null,
92-
show_command_key: parentCommands?.commandContainer === 'show' ? props.form.commandKey : null,
89+
...fieldContainerData,
9390
}), data)
9491
.then(response => {
9592
merge(props.form.data, response.data.form.data);

resources/js/form/components/fields/Autocomplete.vue

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import {
55
FormAutocompleteItemData,
66
FormAutocompleteLocalFieldData,
7-
FormAutocompleteRemoteFieldData,
7+
FormAutocompleteRemoteFieldData
88
} from "@/types";
99
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
1010
import { computed, ref } from "vue";
@@ -21,12 +21,12 @@
2121
import { route } from "@/utils/url";
2222
import { api } from "@/api/api";
2323
import { useParentForm } from "@/form/useParentForm";
24-
import { isCancel } from "axios";
2524
import { ComboboxItemIndicator } from "reka-ui";
26-
import { useParentCommands } from "@/commands/useCommands";
2725
import { useIsInDialog } from "@/components/ui/dialog/Dialog.vue";
2826
import { useFullTextSearch } from "@/composables/useFullTextSearch";
2927
import { useRemoteAutocomplete } from "@/composables/useRemoteAutocomplete";
28+
import { useFieldContainerData } from "@/form/useFieldContainerData";
29+
import { useParentListField } from "@/form/components/fields/list/useParentListField";
3030
3131
const props = defineProps<FormFieldProps<FormAutocompleteLocalFieldData | FormAutocompleteRemoteFieldData>>();
3232
const emit = defineEmits<FormFieldEmits<FormAutocompleteLocalFieldData | FormAutocompleteRemoteFieldData>>();
@@ -36,7 +36,6 @@
3636
const searchTerm = ref('');
3737
const results = ref<FormAutocompleteItemData[]>([]);
3838
39-
const parentCommands = useParentCommands();
4039
const isInDialog = useIsInDialog();
4140
const { fullTextSearch } = useFullTextSearch(
4241
() => props.field.mode === 'local' ? props.field.localValues : null,
@@ -45,19 +44,19 @@
4544
searchKeys: props.field.mode === 'local' ? props.field.searchKeys : [],
4645
}
4746
);
47+
const parentListField = useParentListField();
48+
const fieldContainerData = useFieldContainerData(form);
4849
const { loading, search: remoteSearch } = useRemoteAutocomplete(({ query, signal, onSuccess, onError }) => {
4950
const field = props.field as FormAutocompleteRemoteFieldData;
5051
return api.post(
5152
route('code16.sharp.api.form.autocomplete.index', {
5253
entityKey: form.entityKey,
53-
autocompleteFieldKey: props.parentField ? `${props.parentField.key}.${field.key}` : field.key,
54-
embed_key: form.embedKey,
55-
entity_list_command_key: parentCommands?.commandContainer === 'entityList' ? form.commandKey : null,
56-
show_command_key: parentCommands?.commandContainer === 'show' ? form.commandKey : null,
57-
dashboard_command_key: parentCommands?.commandContainer === 'dashboard' ? form.commandKey : null,
58-
instance_id: form.instanceId,
54+
autocompleteFieldKey: parentListField && parentListField.form === form
55+
? `${parentListField.props.field.key}.${field.key}`
56+
: field.key,
5957
endpoint: field.remoteEndpoint,
6058
search: query,
59+
...fieldContainerData,
6160
}), {
6261
formData: field.callbackLinkedFields
6362
? Object.fromEntries(

resources/js/form/components/fields/editor/Editor.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@
8686
uploadModal,
8787
embedManager,
8888
embedModal,
89-
} satisfies ParentEditor);
89+
form,
90+
});
9091
9192
watch(() => [embedManager.contentEmbeds, uploadManager.contentUploads], () => {
9293
emit('input', {

resources/js/form/components/fields/editor/useParentEditor.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ import EditorUploadModal from "@/form/components/fields/editor/extensions/upload
88
import EditorEmbedModal from "@/form/components/fields/editor/extensions/embed/EditorEmbedModal.vue";
99

1010
/**
11-
* @see import('./Editor.vue') -> provide('editor')
11+
* @see import('./Editor.vue')
1212
*/
1313
export type ParentEditor = {
1414
props: FormFieldProps<FormEditorFieldData>,
1515
embedManager: ContentEmbedManager<Form>,
1616
embedModal: Ref<InstanceType<typeof EditorEmbedModal>>
1717
uploadManager: ContentUploadManager<Form>,
1818
uploadModal: Ref<InstanceType<typeof EditorUploadModal>>,
19+
form: Form,
1920
};
2021

2122
export function useParentEditor() {
22-
return inject<ParentEditor>('editor');
23+
return inject<ParentEditor>('editor', null);
2324
}

resources/js/form/components/fields/List.vue renamed to resources/js/form/components/fields/list/List.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useParentForm } from "@/form/useParentForm";
44
import { FormFieldData, FormListFieldData, FormUploadFieldData, FormUploadFieldValueData } from "@/types";
55
import { getDependantFieldsResetData } from "@/form/util";
6-
import { computed, nextTick, ref, watch, watchEffect } from "vue";
6+
import { computed, nextTick, provide, ref, watch, watchEffect } from "vue";
77
import { Button, buttonVariants } from '@/components/ui/button';
88
import { showAlert } from "@/utils/dialogs";
99
import { FieldMeta, FieldsMeta, FormFieldEmitInputOptions, FormFieldEmits, FormFieldProps } from "@/form/types";
@@ -22,11 +22,18 @@
2222
import { useSortable } from "@vueuse/integrations/useSortable";
2323
import { useEventListener, watchArray } from "@vueuse/core";
2424
import { FormEvents } from "@/form/Form";
25+
import { ParentListField } from "@/form/components/fields/list/useParentListField";
2526
2627
const props = defineProps<FormFieldProps<FormListFieldData>>();
2728
const emit = defineEmits<FormFieldEmits<FormListFieldData>>();
2829
2930
const form = useParentForm();
31+
32+
provide<ParentListField>('listField', {
33+
props,
34+
form,
35+
});
36+
3037
const canAddItem = computed(() => {
3138
const { field, value } = props;
3239
return field.addable &&
@@ -226,7 +233,6 @@
226233
:field="form.getField(itemFieldLayout.key, field.itemFields, item, props.field.readOnly)"
227234
:field-layout="itemFieldLayout"
228235
:field-error-key="`${field.key}.${item[errorIndex] ?? item[itemKey]}.${itemFieldLayout.key}`"
229-
:parent-field="field"
230236
:value="item[itemFieldLayout.key]"
231237
:locale="(form.getMeta(`${field.key}.${item[itemKey]}.${itemFieldLayout.key}`) as FieldMeta)?.locale ?? form.defaultLocale"
232238
:parent-data="item"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { FormFieldProps } from "@/form/types";
2+
import { FormListFieldData } from "@/types";
3+
import { inject } from "vue";
4+
import { Form } from "@/form/Form";
5+
6+
export type ParentListField = {
7+
props: FormFieldProps<FormListFieldData>,
8+
form: Form,
9+
}
10+
11+
/**
12+
* @see import('./List.vue')
13+
*/
14+
export function useParentListField() {
15+
return inject<ParentListField>('listField', null);
16+
}

resources/js/form/components/fields/upload/Upload.vue

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<script setup lang="ts">
22
import { FormUploadFieldData } from "@/types";
3-
import Uppy, { MinimalRequiredUppyFile } from '@uppy/core';
3+
import Uppy from '@uppy/core';
44
import type { UppyFile } from "@uppy/core";
5-
import ThumbnailGenerator from '@uppy/thumbnail-generator';
65
import XHRUpload from '@uppy/xhr-upload';
76
import DropTarget from '@uppy/drop-target';
87
import Cropper from 'cropperjs';
@@ -42,6 +41,9 @@
4241
} from "@/components/ui/dialog";
4342
import { rotate, rotateTo } from "@/form/components/fields/upload/util/rotate";
4443
import { createThumbnail } from "@/form/components/fields/upload/util/thumbnail";
44+
import { useFieldContainerData } from "@/form/useFieldContainerData";
45+
import { useParentEditor } from "@/form/components/fields/editor/useParentEditor";
46+
import { useParentListField } from "@/form/components/fields/list/useParentListField";
4547
4648
const props = defineProps<FormFieldProps<FormUploadFieldData> & {
4749
asEditorEmbed?: boolean,
@@ -67,6 +69,8 @@
6769
(e: 'edit', event: CustomEvent): void
6870
}>();
6971
const form = useParentForm();
72+
const parentEditor = useParentEditor();
73+
const parentListField = useParentListField();
7074
const transformedImg = ref<string>();
7175
const persistedEditableImg = ref<string>();
7276
const playablePreviewUrl = ref<string>();
@@ -75,6 +79,22 @@
7579
return props.value && canTransform(props.value.name, props.value.mime_type) && !props.hasError
7680
|| !!props.dropdownEditLabel;
7781
});
82+
const fieldContainerData = useFieldContainerData(form);
83+
const uploadEndpoint = computed(() => {
84+
// we check form equality because the field may be in an editor embed
85+
// so parentEditor can be the embed parent editor and not an embed form field
86+
let uploadFieldKey = parentEditor && parentEditor.form === form
87+
? parentEditor.props.field.key
88+
: props.field.key;
89+
if(parentListField && parentListField.form === form) {
90+
uploadFieldKey = `${parentListField.props.field.key}.${uploadFieldKey}`;
91+
}
92+
return route('code16.sharp.api.form.upload', {
93+
entityKey: form.entityKey,
94+
uploadFieldKey,
95+
...fieldContainerData,
96+
});
97+
});
7898
const uppy = new Uppy({
7999
id: props.fieldErrorKey,
80100
restrictions: {
@@ -92,12 +112,9 @@
92112
pluralize: () => 1,
93113
},
94114
autoProceed: true,
95-
meta: {
96-
'validation_rule[]': props.field.validationRule,
97-
},
98115
})
99116
.use(XHRUpload, {
100-
endpoint: route('code16.sharp.api.form.upload'),
117+
endpoint: uploadEndpoint.value,
101118
fieldName: 'file',
102119
headers: {
103120
'Accept': 'application/json',

resources/js/form/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export type WithDynamicAttributesApplied<Data extends FormFieldData> =
2323

2424
export type FormFieldProps<Data extends FormFieldData = FormFieldData, Value = Data['value']> = {
2525
field: WithDynamicAttributesApplied<Data>,
26-
parentField?: FormListFieldData
2726
fieldLayout?: LayoutFieldData,
2827
fieldErrorKey?: string,
2928
parentData?: FormFieldData | FormListFieldData['value'][number],
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { RequestFieldContainerData } from "@/types";
2+
import { useParentCommands } from "@/commands/useCommands";
3+
import { Form } from "@/form/Form";
4+
5+
6+
export function useFieldContainerData(form: Form): RequestFieldContainerData {
7+
const parentCommands = useParentCommands();
8+
9+
return {
10+
embed_key: form.embedKey,
11+
entity_list_command_key: parentCommands?.commandContainer === 'entityList' ? form.commandKey : null,
12+
show_command_key: parentCommands?.commandContainer === 'show' ? form.commandKey : null,
13+
dashboard_command_key: parentCommands?.commandContainer === 'dashboard' ? form.commandKey : null,
14+
instance_id: form.instanceId,
15+
};
16+
}

0 commit comments

Comments
 (0)