Skip to content

Commit f924d38

Browse files
authored
Merge pull request #469 from WoltLab/61-upload-pipeline-v2
Document the Upload Pipeline
2 parents 798f895 + aecd502 commit f924d38

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

docs/php/api/file_uploads.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# File Uploads
2+
3+
Uploading files is handled through the unified upload pipeline introduced in WoltLab Suite 6.1.
4+
The API covers the upload process, storage, thumbnail generation and serving of files to the browser.
5+
Attachments are implemented as an extra layer on top of the upload pipeline.
6+
7+
The most common use cases are the attachment system that relies on the WYSIWYG editor as well as the `FileProcessorFormField`.
8+
9+
# Provide the `IFileProcessor`
10+
11+
At the very core of each uploadable type is an implementation of `IFileProcessor` that handles the validation and handling of new files.
12+
13+
It is strongly recommended to derive from `AbstractFileProcessor` that makes it easy to opt out of some extra features and will provide backwards compatibility with new features.
14+
15+
## Important Methods and Features
16+
17+
Please refer to the documentation in `IFileProcessor` for an explanation of methods that are not explained here.
18+
19+
### Thumbnails
20+
21+
It is possible to automatically generate thumbnails for any uploaded file.
22+
The number of thumbnail variants is not limited but each thumbnail must have an identifier that is unique for your file processor.
23+
24+
```php
25+
#[\Override]
26+
public function getThumbnailFormats(): array
27+
{
28+
return [
29+
new ThumbnailFormat(
30+
'', // An empty string is a valid identifier.
31+
800,
32+
600,
33+
true, // Retaining the dimensions can cause _one_ of the sides to underflow the configured width or height.
34+
),
35+
new ThumbnailFormat(
36+
'square',
37+
100,
38+
100,
39+
false, // This will generate a 100x100 thumbnail where the longest size will be cropped from the center.
40+
),
41+
];
42+
}
43+
```
44+
45+
The abstract implementation returns an empty array which will disable thumbnails entirely.
46+
Changes to the thumbnail configuration, for example, updating the dimensions or adding new thumbnail formats are not applied to existing files automatically.
47+
48+
The system tracks the configuration used to generate a thumbnail.
49+
The existing rebuild data worker for files will check the existing thumbnails against the formats provided by your processor and regenerate any thumbnail when possible.
50+
51+
The identifier must be stable because it is used to verify if a specific thumbnail still matches the configured settings.
52+
53+
### Resizing Images Before Uploading
54+
55+
You can opt-in to the resize feature by returning a custom `ResizeConfiguration` from `getResizeConfiguration`, otherwise images of arbitrary size will be accepted.
56+
57+
```php
58+
#[\Override]
59+
public function getResizeConfiguration(): ResizeConfiguration
60+
{
61+
if (!\ATTACHMENT_IMAGE_AUTOSCALE) {
62+
// The resizing has been disabled through the options.
63+
return ResizeConfiguration::unbounded();
64+
}
65+
66+
return new ResizeConfiguration(
67+
\ATTACHMENT_IMAGE_AUTOSCALE_MAX_WIDTH,
68+
\ATTACHMENT_IMAGE_AUTOSCALE_MAX_HEIGHT,
69+
ResizeFileType::fromString(\ATTACHMENT_IMAGE_AUTOSCALE_FILE_TYPE),
70+
\ATTACHMENT_IMAGE_AUTOSCALE_QUALITY
71+
);
72+
}
73+
```
74+
75+
The `ResizeFileType` controls the output format of the resized image.
76+
The available options are `jpeg` and `webp` but it is also possible to keep the existing image format.
77+
78+
### Adopting Files and Thumbnails
79+
80+
Files are associated with your object type after being uploaded but you possibly want to store the file id in your database table.
81+
This is where `adopt(File $file, array $context): void` comes into play which notifies you of the successful upload of a file while providing the context that is used to upload the file in the first place.
82+
83+
Thumbnails are generated in a separate request for performance reasons and you are being notified through `adoptThumbnail(FileThumbnail $thumbnail): void`.
84+
This is meant to allow you to track the thumbnail id in the database.
85+
86+
### Tracking Downloads
87+
88+
File downloads are handled through the `FileDownloadAction` which validates the requested file and permissions to download it.
89+
Every time a file is being downloaded, `trackDownload(File $file): void` is invoked to allow you to update any counters.
90+
91+
Static images are served directly by the web server for performance reasons and it is not possible to track those accesses.
92+
93+
## Registering the File Processor
94+
95+
The file processor is registered as an object type for `com.woltlab.wcf.file` through the `objectType.xml`:
96+
97+
```xml
98+
<type>
99+
<name>com.woltlab.wcf.attachment</name>
100+
<definitionname>com.woltlab.wcf.file</definitionname>
101+
<classname>wcf\system\file\processor\AttachmentFileProcessor</classname>
102+
</type>
103+
```
104+
105+
# `FileProcessorFormField`
106+
107+
It is highly recommended that you take advantage of the existing form builder field `FileProcessorFormField`.
108+
The integration with the form builder enables you to focus on the file processing and does not require you to manually handle the integration of the upload field.
109+
110+
Please see documentation for [FileProcessorFormField](form_fields.md#fileprocessorformfield) to learn more.
111+
112+
# Implementing an Unmanaged File Upload
113+
114+
If you cannot use or want to use the existing form builder implementation you can still implement the UI yourself following this guide.
115+
116+
## Creating the Context for New Files
117+
118+
The HTML element for the file upload is generated through the helper method `FileProcessor::getHtmlElement()` that expects a reference to your `IFileProcessor` as well as a context for new files.
119+
120+
The context is an array with arbitrary values that will be provided to your `IFileProcessor` when processing uploaded files.
121+
You can provide anything you need in order to recognize what the file belongs to, for example, an identifier, object ids, et cetera.
122+
123+
```php
124+
final class ExampleFileProcessor extends AbstractFileProcessor {
125+
public function toHtmlElement(string $someIdentifier, int $someObjectID): string {
126+
return FileProcessor::getInstance()->getHtmlElement(
127+
$this,
128+
[
129+
'identifier' => $someIdentifier,
130+
'objectID' => $someObjectID,
131+
],
132+
);
133+
}
134+
}
135+
```
136+
137+
This code will generate a `<woltlab-core-file-upload>` HTML element that can be inserted anywhere on the page.
138+
You do not need to initialize any extra JavaScript to make the element work, it will be initialized dynamically.
139+
140+
## Lifecycle of Uploaded Files
141+
142+
Any file that passes the pre-upload validation will be uploaded to the server.
143+
This will trigger the `uploadStart` event on the `<woltlab-core-file-upload>` element, exposing a `<woltlab-core-file>` element as the only detail.
144+
145+
It is your responsibility to insert this element at a location of your choice on the page, it represents the upload progress, reports any errors when uploading the file and shows the uploaded file on completion.
146+
147+
The `<woltlab-core-file>` element exposes the `.ready` property that is a Promise representing the state of the upload.
148+
A successful upload will resolve the promise without any value, you can then access the file id through the `.fileId` property.
149+
150+
A failed upload will reject the `.ready` promise without any value, instead you can retrieve the error through the `.apiError` property if you want to further process it.
151+
The UI is automatically updated with the error message, you only need to handle the `.apiError` property if you need to inspect the root cause.
152+
153+
## Deleting a File
154+
155+
You can delete a file from the UI by invoking `deleteFile()` from `WoltLabSuite/Core/Api/Files` which takes the value of `.fileId`.
156+
157+
## Render a Previously Uploaded File
158+
159+
You can render the `<woltlab-core-file>` element through `File::toHtmlElement()`.
160+
This method accepts an optional list of meta data that is serialized to JSON and exposed on the `data-meta-data` property.
161+
162+
The `.ready` promise exists for these files too and will resolve immediately.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ nav:
3232
- 'Comments': 'php/api/comments.md'
3333
- 'Cronjobs': 'php/api/cronjobs.md'
3434
- 'Events': 'php/api/events.md'
35+
- 'File Uploads': 'php/api/file_uploads.md'
3536
- 'Form Builder':
3637
- 'Overview': 'php/api/form_builder/overview.md'
3738
- 'Structure': 'php/api/form_builder/structure.md'

0 commit comments

Comments
 (0)