@@ -198,3 +198,184 @@ public class FooBarAction extends AbstractDatabaseObjectAction implements IMessa
198198 }
199199}
200200```
201+
202+ ## Migration to ` FileProcessorFormField `
203+
204+ Previously, the ` UploadFormField ` class was used to create file upload fields in forms.
205+ Now, the new ` FileProcessorFormField ` should be used,
206+ which separates file validation and processing into a dedicated class, the ` IFileProcessor ` .
207+
208+ #### Example using ` FileProcessorFormField `
209+
210+ With the new ` FileProcessorFormField ` , file validation and processing is handled by a separate class,
211+ the ` IFileProcessor ` .
212+ The form field now provides information about which ` IFileProcessor ` should be used for the file upload,
213+ by specifying the object type of ` com.woltlab.wcf.file ` .
214+
215+ ``` PHP
216+ #[\Override]
217+ protected function createForm(): void
218+ {
219+ parent::createForm();
220+ $this->form->appendChildren([
221+ FormContainer::create('imageContainer')
222+ ->appendChildren([
223+ FileProcessorFormField::create('imageID')
224+ ->singleFileUpload()
225+ ->required()
226+ ->context($this->getContent())
227+ ->objectType('foo.bar.image')
228+ ]),
229+ ]);
230+ }
231+
232+ protected function getContent(): array
233+ {
234+ if ($this->formObject !== null) {
235+ return [
236+ 'fooID' => $this->formObject->fooID,
237+ ];
238+ }
239+ return [];
240+ }
241+ ```
242+
243+ #### Example for implementing an ` IFileProcessor `
244+
245+ ``` PHP
246+ final class FooImageFileProcessor extends AbstractFileProcessor
247+ {
248+ #[\Override]
249+ public function acceptUpload(string $filename, int $fileSize, array $context): FileProcessorPreflightResult
250+ {
251+ if (isset($context['fooID'])) {
252+ $foo = $this->getFoo($context);
253+ if ($foo === null) {
254+ return FileProcessorPreflightResult::InvalidContext;
255+ }
256+
257+ if (!$foo->canEdit()) {
258+ return FileProcessorPreflightResult::InsufficientPermissions;
259+ }
260+ } elseif (!WCF::getSession()->getPermission('foo.bar.canAdd')) {
261+ return FileProcessorPreflightResult::InsufficientPermissions;
262+ }
263+
264+ if ($fileSize > $this->getMaximumSize($context)) {
265+ return FileProcessorPreflightResult::FileSizeTooLarge;
266+ }
267+
268+ if (!FileUtil::endsWithAllowedExtension($filename, $this->getAllowedFileExtensions($context))) {
269+ return FileProcessorPreflightResult::FileExtensionNotPermitted;
270+ }
271+
272+ return FileProcessorPreflightResult::Passed;
273+ }
274+
275+ #[\Override]
276+ public function getMaximumSize(array $context): ?int
277+ {
278+ return WCF::getSession()->getPermission('foo.bar.image.maxSize');
279+ }
280+
281+ #[\Override]
282+ public function getAllowedFileExtensions(array $context): array
283+ {
284+ return \explode("\n", WCF::getSession()->getPermission('foo.bar.image.allowedFileExtensions'));
285+ }
286+
287+ #[\Override]
288+ public function canAdopt(File $file, array $context): bool
289+ {
290+ $fooFromContext = $this->getFoo($context);
291+ $fooFromCoreFile = $this->getFooByFile($file);
292+
293+ if ($fooFromCoreFile === null) {
294+ return true;
295+ }
296+
297+ if ($fooFromCoreFile->fooID === $fooFromContext->fooID) {
298+ return true;
299+ }
300+
301+ return false;
302+ }
303+
304+ #[\Override]
305+ public function adopt(File $file, array $context): void
306+ {
307+ $foo = $this->getFoo($context);
308+ if ($foo === null) {
309+ return;
310+ }
311+
312+ (new FooEditor($foo))->update([
313+ 'imageID' => $file->fileID,
314+ ]);
315+ }
316+
317+ #[\Override]
318+ public function canDelete(File $file): bool
319+ {
320+ $foo = $this->getFooByFile($file);
321+ if ($foo === null) {
322+ return WCF::getSession()->getPermission('foo.bar.canAdd');
323+ }
324+
325+ return false;
326+ }
327+
328+ #[\Override]
329+ public function canDownload(File $file): bool
330+ {
331+ $foo = $this->getFooByFile($file);
332+ if ($foo === null) {
333+ return WCF::getSession()->getPermission('foo.bar.canAdd');
334+ }
335+
336+ return $foo->canRead();
337+ }
338+
339+ #[\Override]
340+ public function delete(array $fileIDs, array $thumbnailIDs): void
341+ {
342+ $fooList = new FooList();
343+ $fooList->getConditionBuilder()->add('imageID IN (?)', [$fileIDs]);
344+ $fooList->readObjects();
345+
346+ if ($fooList->count() === 0) {
347+ return;
348+ }
349+
350+ (new FooAction($fooList->getObjects(), 'delete'))->executeAction();
351+ }
352+
353+ #[\Override]
354+ public function getObjectTypeName(): string
355+ {
356+ return 'foo.bar.image';
357+ }
358+
359+ #[\Override]
360+ public function countExistingFiles(array $context): ?int
361+ {
362+ $foo = $this->getFoo($context);
363+ if ($foo === null) {
364+ return null;
365+ }
366+
367+ return $foo->imageID === null ? 0 : 1;
368+ }
369+
370+ private function getFoo(array $context): ?Foo
371+ {
372+ // extract foo from context
373+ }
374+
375+ private function getFooByFile(File $file): ?Foo
376+ {
377+ // search foo in database by given file
378+ }
379+ }
380+ ```
381+
0 commit comments