Allow the `FileProcessorFormField` to handle only one file.
authorCyperghost <olaf_schmitz_1@t-online.de>
Wed, 19 Jun 2024 09:37:59 +0000 (11:37 +0200)
committerCyperghost <olaf_schmitz_1@t-online.de>
Wed, 19 Jun 2024 09:37:59 +0000 (11:37 +0200)
com.woltlab.wcf/templates/shared_fileProcessorFormField.tpl
ts/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor.js
wcfsetup/install/files/lib/system/form/builder/field/FileProcessorFormField.class.php

index 110fb7ac77daf54e547d1e6f1afb3cf7035b956d..0d0077f2d10e509ac7a4f9d9c015fc626bc3d461 100644 (file)
@@ -1,5 +1,5 @@
 {assign var="files" value=$field->getFiles()}
-{if $maxUploads === 1 && $imageOnly}
+{if $field->isSingleFileUpload() === 1 && $imageOnly}
        <div class="fileUpload__preview">
                {if $field->getValue()}
                        {assign var="file" value=$files|reset}
@@ -8,14 +8,22 @@
        </div>
 {else}
        <ul class="fileUpload__fileList">
+               {foreach from=$files item=file}
+                       <li>
+                               {unsafe:$file->toHtmlElement()}
+                       </li>
+               {/foreach}
        </ul>
 {/if}
+
 {unsafe:$fileProcessorHtmlElement}
 
 <script data-relocate="true">
        require(["WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor"], ({ FileProcessor }) => {
                new FileProcessor(
                        '{@$field->getPrefixedId()|encodeJS}',
+                       {if $field->isSingleFileUpload()}true{else}false{/if},
+                       {if $imageOnly}true{else}false{/if}
                );
        });
 </script>
index 889b17533feb78c8f7eb96984f6e29c804d23058..b83e456a26d44f100c7d4f482a68f36979dc5732 100644 (file)
@@ -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<HTMLInputElement>('input[type="file"]')!;
 
     this.#container.querySelectorAll<WoltlabCoreFileElement>("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<void> {
-    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<void> {
+    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();
index 1dd15ec25dfbac8a74abeeb344e934175cd56bc3..a3fcd5b15332c4923105114f098935eb8c3b8167 100644 (file)
@@ -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 {
index 9ee31e5037f3ea5a596c6f685bed060bfe57f1a2..3a60744312ac650e766f189ddfd1f29a7d27f034 100644 (file)
@@ -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);
     }
 
     /**