Skip to content

Commit 499a571

Browse files
committed
Fix upload validation
1 parent ee69d23 commit 499a571

15 files changed

Lines changed: 547 additions & 77 deletions

File tree

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

Lines changed: 5 additions & 7 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";
@@ -27,6 +27,7 @@
2727
import { useIsInDialog } from "@/components/ui/dialog/Dialog.vue";
2828
import { useFullTextSearch } from "@/composables/useFullTextSearch";
2929
import { useRemoteAutocomplete } from "@/composables/useRemoteAutocomplete";
30+
import { useFieldContainerData } from "@/form/useFieldContainerData";
3031
3132
const props = defineProps<FormFieldProps<FormAutocompleteLocalFieldData | FormAutocompleteRemoteFieldData>>();
3233
const emit = defineEmits<FormFieldEmits<FormAutocompleteLocalFieldData | FormAutocompleteRemoteFieldData>>();
@@ -45,19 +46,16 @@
4546
searchKeys: props.field.mode === 'local' ? props.field.searchKeys : [],
4647
}
4748
);
49+
const fieldContainerData = useFieldContainerData();
4850
const { loading, search: remoteSearch } = useRemoteAutocomplete(({ query, signal, onSuccess, onError }) => {
4951
const field = props.field as FormAutocompleteRemoteFieldData;
5052
return api.post(
5153
route('code16.sharp.api.form.autocomplete.index', {
5254
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,
55+
autocompleteFieldKey: props.parentListField ? `${props.parentListField.key}.${field.key}` : field.key,
5956
endpoint: field.remoteEndpoint,
6057
search: query,
58+
...fieldContainerData,
6159
}), {
6260
formData: field.callbackLinkedFields
6361
? Object.fromEntries(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@
226226
:field="form.getField(itemFieldLayout.key, field.itemFields, item, props.field.readOnly)"
227227
:field-layout="itemFieldLayout"
228228
:field-error-key="`${field.key}.${item[errorIndex] ?? item[itemKey]}.${itemFieldLayout.key}`"
229-
:parent-field="field"
229+
:parent-list-field="field"
230230
:value="item[itemFieldLayout.key]"
231231
:locale="(form.getMeta(`${field.key}.${item[itemKey]}.${itemFieldLayout.key}`) as FieldMeta)?.locale ?? form.defaultLocale"
232232
:parent-data="item"

resources/js/form/components/fields/editor/extensions/upload/UploadNode.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
readOnly: parentEditor.props.field.readOnly,
7777
}"
7878
:field-error-key="`${parentEditor.props.fieldErrorKey}-upload-${props.node.attrs['data-key']}`"
79+
:parent-list-field="parentEditor.props.parentListField"
80+
:editor-field="parentEditor.props.field"
7981
:value="upload?.file"
8082
as-editor-embed
8183
persist-thumbnail-url

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<script setup lang="ts">
2-
import { FormUploadFieldData } from "@/types";
3-
import Uppy, { MinimalRequiredUppyFile } from '@uppy/core';
2+
import { FormEditorFieldData, FormUploadFieldData } from "@/types";
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,8 +41,10 @@
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";
4545
4646
const props = defineProps<FormFieldProps<FormUploadFieldData> & {
47+
editorField?: FormEditorFieldData,
4748
asEditorEmbed?: boolean,
4849
legend?: string,
4950
dropdownEditLabel?: string,
@@ -92,12 +93,15 @@
9293
pluralize: () => 1,
9394
},
9495
autoProceed: true,
95-
meta: {
96-
'validation_rule[]': props.field.validationRule,
97-
},
9896
})
9997
.use(XHRUpload, {
100-
endpoint: route('code16.sharp.api.form.upload'),
98+
endpoint: route('code16.sharp.api.form.upload', {
99+
entityKey: form.entityKey,
100+
uploadFieldKey: props.parentListField
101+
? `${props.parentListField.key}.${props.editorField?.key ?? props.field.key}`
102+
: props.editorField?.key ?? props.field.key,
103+
...useFieldContainerData(),
104+
}),
101105
fieldName: 'file',
102106
headers: {
103107
'Accept': 'application/json',

resources/js/form/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ 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
26+
parentListField?: FormListFieldData,
2727
fieldLayout?: LayoutFieldData,
2828
fieldErrorKey?: string,
2929
parentData?: FormFieldData | FormListFieldData['value'][number],
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { RequestFieldContainerData } from "@/types";
2+
import { useParentForm } from "@/form/useParentForm";
3+
import { useParentCommands } from "@/commands/useCommands";
4+
5+
6+
export function useFieldContainerData(): RequestFieldContainerData {
7+
const form = useParentForm();
8+
const parentCommands = useParentCommands();
9+
10+
return {
11+
embed_key: form.embedKey,
12+
entity_list_command_key: parentCommands?.commandContainer === 'entityList' ? form.commandKey : null,
13+
show_command_key: parentCommands?.commandContainer === 'show' ? form.commandKey : null,
14+
dashboard_command_key: parentCommands?.commandContainer === 'dashboard' ? form.commandKey : null,
15+
instance_id: form.instanceId,
16+
};
17+
}

resources/js/types/generated.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export type BreadcrumbData = {
4747
items: Array<BreadcrumbItemData>;
4848
};
4949
export type BreadcrumbItemData = {
50-
type: string;
5150
label: string;
5251
documentTitleLabel: string | null;
5352
entityKey: string;
@@ -812,6 +811,13 @@ export type PieGraphWidgetData = {
812811
ratioY: number | null;
813812
height: number | null;
814813
};
814+
export type RequestFieldContainerData = {
815+
embed_key: string | null;
816+
entity_list_command_key: string | null;
817+
show_command_key: string | null;
818+
dashboard_command_key: string | null;
819+
instance_id: string | number | null;
820+
};
815821
export type SearchResultLinkData = {
816822
link: string;
817823
label: string;
@@ -850,7 +856,6 @@ export type ShowConfigData = {
850856
isSingle: boolean;
851857
commands: ConfigCommandsData | null;
852858
titleAttribute: string | null;
853-
breadcrumbAttribute: string | null;
854859
editButtonLabel: string | null;
855860
state: EntityStateData | null;
856861
};

resources/js/types/routes.d.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,20 @@ declare module 'ziggy-js' {
512512
"required": false
513513
}
514514
],
515-
"code16.sharp.api.form.upload": [],
515+
"code16.sharp.api.form.upload": [
516+
{
517+
"name": "globalFilter"
518+
},
519+
{
520+
"name": "entityKey",
521+
"required": true,
522+
"binding": "key"
523+
},
524+
{
525+
"name": "uploadFieldKey",
526+
"required": true
527+
}
528+
],
516529
"code16.sharp.api.form.autocomplete.index": [
517530
{
518531
"name": "globalFilter"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Code16\Sharp\Data;
4+
5+
/**
6+
* @internal
7+
*/
8+
final class RequestFieldContainerData extends Data
9+
{
10+
public function __construct(
11+
public ?string $embed_key,
12+
public ?string $entity_list_command_key,
13+
public ?string $show_command_key,
14+
public ?string $dashboard_command_key,
15+
public string|int|null $instance_id,
16+
) {}
17+
18+
public static function from(array $request): self
19+
{
20+
return new self(
21+
embed_key: $request['embed_key'] ?? null,
22+
entity_list_command_key: $request['entity_list_command_key'] ?? null,
23+
show_command_key: $request['show_command_key'] ?? null,
24+
dashboard_command_key: $request['dashboard_command_key'] ?? null,
25+
instance_id: $request['instance_id'] ?? null,
26+
);
27+
}
28+
}

src/Http/Controllers/Api/ApiFormUploadController.php

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,34 @@
22

33
namespace Code16\Sharp\Http\Controllers\Api;
44

5+
use Code16\Sharp\Exceptions\SharpInvalidConfigException;
6+
use Code16\Sharp\Form\Fields\SharpFormEditorField;
7+
use Code16\Sharp\Utils\Entities\ValueObjects\EntityKey;
58
use Code16\Sharp\Utils\FileUtil;
69
use Illuminate\Foundation\Validation\ValidatesRequests;
710

811
class ApiFormUploadController extends ApiController
912
{
13+
use HandlesFieldContainer;
1014
use ValidatesRequests;
1115

12-
public function store(FileUtil $fileUtil)
16+
public function store(string $globalFilter, EntityKey $entityKey, string $uploadFieldKey, FileUtil $fileUtil)
1317
{
14-
$this->validate(request(), [
15-
'validation_rule' => ['nullable', 'array'],
16-
'validation_rule.*' => [
17-
'string',
18-
'regex:/^(file$|image:?|mimes:|mimetypes:|extensions:|dimensions:|size:|between:|min:|max:)/',
19-
],
20-
]);
18+
$field = $this->getFieldContainer($entityKey)
19+
->findFieldByKey($uploadFieldKey);
20+
21+
if ($field instanceof SharpFormEditorField) {
22+
$field = $field->uploadsConfig();
23+
}
24+
25+
if (! $field) {
26+
throw new SharpInvalidConfigException('Upload field '.$uploadFieldKey.' was not found in form.');
27+
}
2128

2229
$this->validate(request(), [
2330
'file' => [
2431
'required',
25-
...request()->input('validation_rule') ?? ['file'],
32+
...$field->toArray()['validationRule'],
2633
],
2734
]);
2835

0 commit comments

Comments
 (0)