feat(12.2-02): add the fileupload field with deferred uploads committed on save
- type: fileupload compiles the D-08 keys and binds to the model's attach.Relation at boot
- X-Session-Key (cabana.SessionKeyHeader) carries the form session key; RecordInput.SessionKey
- GET and POST .../{id}/files/{field}: list with pending uploads, multipart upload into attach.Store
- the create and update save attaches the session's pending files in its transaction
- swagger2openapi folds formData parameters into a multipart requestBody
- admin OpenAPI, TS types, conformance cases, README and forms docs
This commit is contained in:
@@ -91,8 +91,9 @@ for _, f := range form.Fields {
|
||||
| `relation-manager` | An embedded list of related records; see [Relation manager](relation-manager.md). |
|
||||
| `widget` | A plugin custom element with a server action; see [Partials and widgets](partials-and-widgets.md). |
|
||||
| `partial` | A server-rendered template; see [Partials and widgets](partials-and-widgets.md). |
|
||||
| `fileupload` | Uploads for an attachOne or attachMany relation; see [File uploads](#file-uploads). |
|
||||
|
||||
The WinterCMS widgets that are not in this list (the rich editor, the media finder, the repeater, the file upload and the others) are not provided. A field with one of those types stops the start-up.
|
||||
The WinterCMS widgets that are not in this list (the rich editor, the media finder, the repeater and the others) are not provided. A field with one of those types stops the start-up.
|
||||
|
||||
### Field options
|
||||
|
||||
@@ -100,6 +101,43 @@ A field takes `label`, `comment`, `type`, `required`, `default`, `tab`, `span` (
|
||||
|
||||
`context: update` shows a field only on the update form, and `context: create` only on the create form; a list of contexts is also accepted. The context is enforced on the server too: a field that is hidden on a form is never written by that form's save, whatever the request body holds.
|
||||
|
||||
## File uploads
|
||||
|
||||
A `type: fileupload` field edits one attachment relation of the form's model. The model declares its relations with an `AttachRelations` method (`attach.HasRelations`) and implements `attach.Owner`; see [Attachments](../database/attachments.md). The field name must be one of the declared relation names, otherwise the start-up stops.
|
||||
|
||||
```yaml
|
||||
fields:
|
||||
photos:
|
||||
label: acme.blog::lang.posts.photos
|
||||
type: fileupload
|
||||
mode: image
|
||||
fileTypes: jpg,png,webp
|
||||
maxFilesize: 5
|
||||
maxFiles: 10
|
||||
thumbOptions:
|
||||
mode: crop
|
||||
```
|
||||
|
||||
The field takes the generic keys plus these WinterCMS keys:
|
||||
|
||||
| Key | Meaning |
|
||||
|-----|---------|
|
||||
| `mode` | `image` or `file` (the default). Image mode accepts only jpg, jpeg, png, gif and webp and checks that the bytes decode as such an image. |
|
||||
| `fileTypes` | Allowed extensions, as a comma- or pipe-separated string or a list. |
|
||||
| `mimeTypes` | Allowed MIME types (`image/png`, `image/*`) or extensions. |
|
||||
| `maxFilesize` | The largest file in megabytes. It may not exceed `http.body_limits.upload_bytes`. |
|
||||
| `maxFiles` | The most files an attachMany relation may hold. Refused on attachOne. |
|
||||
| `imageWidth`, `imageHeight` | Preview size, 1 to 4096 pixels (240 by 240 when not set). |
|
||||
| `thumbOptions` | A mapping with `mode`: `auto`, `exact`, `crop` (the default) or `fit`. |
|
||||
| `useCaption` | Lets the administrator edit each file's title and description. |
|
||||
| `prompt` | The upload button text, a translation key. |
|
||||
|
||||
`options`, `emptyOption`, `nameFrom` and every other key stop the start-up.
|
||||
|
||||
Uploads are deferred until the form is saved, on the create form and the update form alike, as WinterCMS's file upload widget does. The admin SPA makes a random session key when it opens a form and sends it in the `X-Session-Key` header with every upload and with the save. The upload stores the file unattached and records a pending binding for that key and the signed-in administrator; the record's create or update save attaches every pending file of the form's fileupload fields inside its own transaction. If the save fails with a 422, the uploads stay pending for the next attempt; if the form is left without saving, the daily `deferred:purge` removes them. A session key is only ever seen by the administrator who used it.
|
||||
|
||||
The limits are enforced on the server: the upload route caps the request body at the smaller of `http.body_limits.upload_bytes` and `maxFilesize` plus 64 KiB (413 `payload_too_large` past it), and a file that is too large, of a type the field does not allow, or not a valid image in image mode is a 422 on the field.
|
||||
|
||||
## What a save may write
|
||||
|
||||
The form's writable fields are bound to model columns at boot. A save passes only those fields that are also in the model's `Fillable` list, drops unknown keys, case variants and nested objects, and fills the model with `lagoon.Fill` (see [Models](../database/models.md)). The model's validation rules (a `Rules` method returning `lagoon.Validate` rule strings) and the form's `required` flags are checked in the save's transaction, and a failure is a 422 with messages per field. A value that does not fit its column is also a 422 on that field.
|
||||
|
||||
Reference in New Issue
Block a user