From a87facf4a24e80f78cc3c7800df50e219a6a2808 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Wed, 19 Jun 2024 11:37:59 +0200 Subject: [PATCH] Allow the `FileProcessorFormField` to handle only one file. --- .../shared_fileProcessorFormField.tpl | 10 ++- .../Builder/Field/Controller/FileProcessor.ts | 64 +++++++++-------- .../Builder/Field/Controller/FileProcessor.js | 68 +++++++++++-------- .../field/FileProcessorFormField.class.php | 33 +++++++-- 4 files changed, 114 insertions(+), 61 deletions(-) diff --git a/com.woltlab.wcf/templates/shared_fileProcessorFormField.tpl b/com.woltlab.wcf/templates/shared_fileProcessorFormField.tpl index 110fb7ac77..0d0077f2d1 100644 --- a/com.woltlab.wcf/templates/shared_fileProcessorFormField.tpl +++ b/com.woltlab.wcf/templates/shared_fileProcessorFormField.tpl @@ -1,5 +1,5 @@ {assign var="files" value=$field->getFiles()} -{if $maxUploads === 1 && $imageOnly} +{if $field->isSingleFileUpload() === 1 && $imageOnly}
{if $field->getValue()} {assign var="file" value=$files|reset} @@ -8,14 +8,22 @@
{else} {/if} + {unsafe:$fileProcessorHtmlElement} diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts index 889b17533f..b83e456a26 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts @@ -15,9 +15,13 @@ export class FileProcessor { readonly #fieldId: string; #replaceElement: WoltlabCoreFileElement | undefined = undefined; readonly #fileInput: HTMLInputElement; + readonly #imageOnly: boolean; + readonly #singleFileUpload: boolean; - constructor(fieldId: string) { + constructor(fieldId: string, singleFileUpload: boolean = false, imageOnly: boolean = false) { this.#fieldId = fieldId; + this.#imageOnly = imageOnly; + this.#singleFileUpload = singleFileUpload; this.#container = document.getElementById(fieldId + "Container")!; if (this.#container === null) { @@ -31,33 +35,35 @@ export class FileProcessor { this.#fileInput = this.#uploadButton.shadowRoot!.querySelector('input[type="file"]')!; this.#container.querySelectorAll("woltlab-core-file").forEach((element) => { - void this.#registerFile(element); + void this.#registerFile(element, element.parentElement); }); } - get isSingleFileUpload(): boolean { - // TODO check if only images are allowed - return this.#uploadButton.maximumCount === 1; + get classPrefix(): string { + return this.showBigPreview ? "fileUpload__preview__" : "fileUpload__fileList__"; } - async #registerFile(element: WoltlabCoreFileElement): Promise { - let elementContainer: HTMLElement | null; + get showBigPreview(): boolean { + return this.#singleFileUpload && this.#imageOnly; + } - if (this.isSingleFileUpload) { - elementContainer = this.#container.querySelector(".fileUpload__preview"); - if (elementContainer === null) { - elementContainer = document.createElement("div"); - elementContainer.classList.add("fileUpload__preview"); - this.#uploadButton.insertAdjacentElement("beforebegin", elementContainer); + async #registerFile(element: WoltlabCoreFileElement, elementContainer: HTMLElement | null = null): Promise { + if (elementContainer === null) { + if (this.showBigPreview) { + elementContainer = this.#container.querySelector(".fileUpload__preview"); + if (elementContainer === null) { + elementContainer = document.createElement("div"); + elementContainer.classList.add("fileUpload__preview"); + this.#uploadButton.insertAdjacentElement("beforebegin", elementContainer); + } + } else { + elementContainer = document.createElement("li"); + elementContainer.classList.add("fileUpload__fileList__item"); + this.#container.querySelector(".fileUpload__fileList")!.append(elementContainer); } - } else { - elementContainer = document.createElement("li"); - elementContainer.classList.add("fileUpload__fileList__item"); - this.#container.querySelector(".fileUpload__fileList")!.append(elementContainer); + elementContainer.append(element); } - elementContainer.append(element); - try { await element.ready; @@ -75,14 +81,22 @@ export class FileProcessor { return; } - if (this.isSingleFileUpload) { + if (this.showBigPreview) { element.dataset.previewUrl = element.link!; element.unbounded = true; + } else if (element.isImage()) { + const thumbnail = element.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny"); + if (thumbnail !== undefined) { + element.thumbnail = thumbnail; + } else { + element.dataset.previewUrl = element.link!; + element.unbounded = false; + } } const input = document.createElement("input"); input.type = "hidden"; - input.name = this.isSingleFileUpload ? this.#fieldId : this.#fieldId + "[]"; + input.name = this.#singleFileUpload ? this.#fieldId : this.#fieldId + "[]"; input.value = element.fileId!.toString(); elementContainer.append(input); @@ -125,11 +139,7 @@ export class FileProcessor { #addButtons(element: WoltlabCoreFileElement): void { const buttons = document.createElement("ul"); buttons.classList.add("buttonList"); - if (this.isSingleFileUpload) { - buttons.classList.add("fileUpload__preview__buttons"); - } else { - buttons.classList.add("fileUpload__fileList__buttons"); - } + buttons.classList.add(this.classPrefix + "buttons"); this.#addDeleteButton(element, buttons); this.#addReplaceButton(element, buttons); @@ -154,7 +164,7 @@ export class FileProcessor { } #unregisterFile(element: WoltlabCoreFileElement): void { - if (this.isSingleFileUpload) { + if (this.showBigPreview) { element.parentElement!.innerHTML = ""; } else { element.parentElement!.remove(); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js index 1dd15ec25d..a3fcd5b153 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js @@ -14,8 +14,12 @@ define(["require", "exports", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/A #fieldId; #replaceElement = undefined; #fileInput; - constructor(fieldId) { + #imageOnly; + #singleFileUpload; + constructor(fieldId, singleFileUpload = false, imageOnly = false) { this.#fieldId = fieldId; + this.#imageOnly = imageOnly; + this.#singleFileUpload = singleFileUpload; this.#container = document.getElementById(fieldId + "Container"); if (this.#container === null) { throw new Error("Unknown field with id '" + fieldId + "'"); @@ -26,29 +30,32 @@ define(["require", "exports", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/A }); this.#fileInput = this.#uploadButton.shadowRoot.querySelector('input[type="file"]'); this.#container.querySelectorAll("woltlab-core-file").forEach((element) => { - void this.#registerFile(element); + void this.#registerFile(element, element.parentElement); }); } - get isSingleFileUpload() { - // TODO check if only images are allowed - return this.#uploadButton.maximumCount === 1; + get classPrefix() { + return this.showBigPreview ? "fileUpload__preview__" : "fileUpload__fileList__"; } - async #registerFile(element) { - let elementContainer; - if (this.isSingleFileUpload) { - elementContainer = this.#container.querySelector(".fileUpload__preview"); - if (elementContainer === null) { - elementContainer = document.createElement("div"); - elementContainer.classList.add("fileUpload__preview"); - this.#uploadButton.insertAdjacentElement("beforebegin", elementContainer); + get showBigPreview() { + return this.#singleFileUpload && this.#imageOnly; + } + async #registerFile(element, elementContainer = null) { + if (elementContainer === null) { + if (this.showBigPreview) { + elementContainer = this.#container.querySelector(".fileUpload__preview"); + if (elementContainer === null) { + elementContainer = document.createElement("div"); + elementContainer.classList.add("fileUpload__preview"); + this.#uploadButton.insertAdjacentElement("beforebegin", elementContainer); + } } + else { + elementContainer = document.createElement("li"); + elementContainer.classList.add("fileUpload__fileList__item"); + this.#container.querySelector(".fileUpload__fileList").append(elementContainer); + } + elementContainer.append(element); } - else { - elementContainer = document.createElement("li"); - elementContainer.classList.add("fileUpload__fileList__item"); - this.#container.querySelector(".fileUpload__fileList").append(elementContainer); - } - elementContainer.append(element); try { await element.ready; if (this.#replaceElement !== undefined) { @@ -65,13 +72,23 @@ define(["require", "exports", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/A this.#markElementUploadHasFailed(elementContainer, element, reason); return; } - if (this.isSingleFileUpload) { + if (this.showBigPreview) { element.dataset.previewUrl = element.link; element.unbounded = true; } + else if (element.isImage()) { + const thumbnail = element.thumbnails.find((thumbnail) => thumbnail.identifier === "tiny"); + if (thumbnail !== undefined) { + element.thumbnail = thumbnail; + } + else { + element.dataset.previewUrl = element.link; + element.unbounded = false; + } + } const input = document.createElement("input"); input.type = "hidden"; - input.name = this.isSingleFileUpload ? this.#fieldId : this.#fieldId + "[]"; + input.name = this.#singleFileUpload ? this.#fieldId : this.#fieldId + "[]"; input.value = element.fileId.toString(); elementContainer.append(input); this.#addButtons(element); @@ -107,12 +124,7 @@ define(["require", "exports", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/A #addButtons(element) { const buttons = document.createElement("ul"); buttons.classList.add("buttonList"); - if (this.isSingleFileUpload) { - buttons.classList.add("fileUpload__preview__buttons"); - } - else { - buttons.classList.add("fileUpload__fileList__buttons"); - } + buttons.classList.add(this.classPrefix + "buttons"); this.#addDeleteButton(element, buttons); this.#addReplaceButton(element, buttons); element.parentElement.append(buttons); @@ -131,7 +143,7 @@ define(["require", "exports", "WoltLabSuite/Core/Language", "WoltLabSuite/Core/A buttons.append(listItem); } #unregisterFile(element) { - if (this.isSingleFileUpload) { + if (this.showBigPreview) { element.parentElement.innerHTML = ""; } else { diff --git a/wcfsetup/install/files/lib/system/form/builder/field/FileProcessorFormField.class.php b/wcfsetup/install/files/lib/system/form/builder/field/FileProcessorFormField.class.php index 9ee31e5037..3a60744312 100644 --- a/wcfsetup/install/files/lib/system/form/builder/field/FileProcessorFormField.class.php +++ b/wcfsetup/install/files/lib/system/form/builder/field/FileProcessorFormField.class.php @@ -4,6 +4,7 @@ namespace wcf\system\form\builder\field; use wcf\data\file\File; use wcf\data\file\FileList; +use wcf\data\file\thumbnail\FileThumbnailList; use wcf\system\file\processor\FileProcessor; use wcf\system\file\processor\IFileProcessor; use wcf\system\form\builder\TObjectTypeFormNode; @@ -32,6 +33,7 @@ final class FileProcessorFormField extends AbstractFormField * @var File[] */ private array $files = []; + private bool $singleFileUpload = false; #[\Override] public function readValue() @@ -76,9 +78,21 @@ final class FileProcessorFormField extends AbstractFormField return $this->getObjectType()->getProcessor(); } - private function isSingleFileUpload(): bool + public function isSingleFileUpload(): bool { - return $this->getFileProcessor()->getMaximumCount($this->context) === 1; + return $this->singleFileUpload; + } + + /** + * Sets whether only a single file can be uploaded. + * If set to true, the value of the field will be an integer. + * Otherwise, the value will be an array of integers. + */ + public function setSingleFileUpload(bool $singleFileUpload): self + { + $this->singleFileUpload = $singleFileUpload; + + return $this; } #[\Override] @@ -95,13 +109,13 @@ final class FileProcessorFormField extends AbstractFormField #[\Override] public function value($value) { + $fileIDs = []; if ($this->isSingleFileUpload()) { $file = new File($value); if ($file->fileID === $value) { $this->files = [$file]; + $fileIDs[] = $value; } - - return parent::value($value); } else { if (!\is_array($value)) { $value = [$value]; @@ -112,8 +126,17 @@ final class FileProcessorFormField extends AbstractFormField $fileList->readObjects(); $this->files = $fileList->getObjects(); - return parent::value($value); + $fileIDs = $fileList->getObjectIDs(); } + + $thumbnailList = new FileThumbnailList(); + $thumbnailList->getConditionBuilder()->add("fileID IN (?)", [$fileIDs]); + $thumbnailList->readObjects(); + foreach ($thumbnailList as $thumbnail) { + $this->files[$thumbnail->fileID]->addThumbnail($thumbnail); + } + + return parent::value($value); } /** -- 2.20.1