• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / handlebars-forms / 26085646722

19 May 2026 08:29AM UTC coverage: 0.522% (-0.02%) from 0.538%
26085646722

Pull #42

github

web-flow
Merge 0c216da43 into 3f232160f
Pull Request #42: [!!!][FEATURE] Support `multiple` attribute in `FileUpload` element

0 of 52 new or added lines in 1 file covered. (0.0%)

2 existing lines in 2 files now uncovered.

7 of 1342 relevant lines covered (0.52%)

0.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/Domain/ViewModel/Builder/FileUploadViewModelBuilder.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "handlebars_forms".
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace CPSIT\Typo3HandlebarsForms\Domain\ViewModel\Builder;
19

20
use CPSIT\Typo3HandlebarsForms\Domain;
21
use CPSIT\Typo3HandlebarsForms\Fluid\ViewHelperInvoker;
22
use TYPO3\CMS\Core;
23
use TYPO3\CMS\Extbase;
24
use TYPO3\CMS\Fluid;
25
use TYPO3\CMS\Form;
26
use TYPO3Fluid\Fluid as FluidStandalone;
27

28
/**
29
 * FileUploadViewModelBuilder
30
 *
31
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
32
 * @license GPL-2.0-or-later
33
 *
34
 * @extends AbstractViewModelBuilder<Form\Domain\Model\FormElements\FileUpload>
35
 */
36
final class FileUploadViewModelBuilder extends AbstractViewModelBuilder
37
{
38
    protected array $supportedTypes = [
39
        'FileUpload',
40
        'ImageUpload',
41
    ];
42

43
    private readonly Core\Information\Typo3Version $typo3Version;
44

NEW
45
    public function __construct(ViewHelperInvoker $viewHelperInvoker)
×
46
    {
NEW
47
        parent::__construct($viewHelperInvoker);
×
48

NEW
49
        $this->typo3Version = new Core\Information\Typo3Version();
×
50
    }
51

UNCOV
52
    public function renderRenderable(
×
53
        Form\Domain\Model\Renderable\RootRenderableInterface $renderable,
54
        Fluid\Core\Rendering\RenderingContext $renderingContext,
55
    ): Domain\ViewModel\ViewModelCollection|Domain\ViewModel\ViewHelperContainedViewModel {
56
        $resource = null;
×
57
        $resourceVariableName = 'resource';
×
NEW
58
        $arguments = [
×
NEW
59
            'property' => $renderable->getIdentifier(),
×
NEW
60
            'as' => $resourceVariableName,
×
NEW
61
            'id' => $renderable->getUniqueIdentifier(),
×
NEW
62
            'class' => $renderable->getProperties()['elementClassAttribute'] ?? null,
×
NEW
63
            'errorClass' => $renderable->getProperties()['elementErrorClassAttribute'] ?? null,
×
NEW
64
            'additionalAttributes' => $this->renderAdditionalAttributes($renderable, $renderingContext),
×
NEW
65
            'accept' => $renderable->getProperties()['allowedMimeTypes'] ?? null,
×
NEW
66
        ];
×
67

68
        // @todo Remove condition once support for TYPO3 v13 is dropped
NEW
69
        if ($this->typo3Version->getMajorVersion() >= 14) {
×
NEW
70
            $arguments['multiple'] = $renderable->getProperties()['multiple'] ?? false;
×
71
        }
72

73
        $result = $this->viewHelperInvoker->invoke(
×
74
            $renderingContext,
×
75
            Form\ViewHelpers\Form\UploadedResourceViewHelper::class,
×
NEW
76
            $arguments,
×
77
            static function () use ($renderingContext, &$resource, $resourceVariableName) {
×
78
                $resource = $renderingContext->getVariableProvider()->get($resourceVariableName);
×
79
            },
×
80
        );
×
81
        $inputViewModel = new Domain\ViewModel\ViewHelperContainedViewModel($renderable, $result);
×
82

NEW
83
        if (!$this->isValidResource($resource)) {
×
84
            return $inputViewModel;
×
85
        }
86

87
        $hiddenFields = $result->extractChildNodes('input[@type="hidden"]');
×
88
        $viewModels = [
×
89
            'uploadField' => $inputViewModel,
×
90
            'resource' => new Domain\ViewModel\FileResourceViewModel($renderable, $resource),
×
91
        ];
×
92

93
        if ($hiddenFields !== []) {
×
NEW
94
            $viewModels['resourcePointerFields'] = $this->buildResourcePointerFields($renderable, $hiddenFields);
×
95
        }
96

97
        // @todo Remove first condition once support for TYPO3 v13 is dropped
NEW
98
        if ($this->typo3Version->getMajorVersion() >= 14 && (bool)($renderable->getProperties()['allowRemoval'] ?? false)) {
×
NEW
99
            $viewModels['deleteCheckboxes'] = $this->buildDeleteCheckboxes($renderable, $renderingContext, $resource);
×
100
        }
101

102
        return new Domain\ViewModel\ViewModelCollection($renderable, $viewModels);
×
103
    }
104

105
    /**
106
     * @param list<FluidStandalone\Core\ViewHelper\TagBuilder> $hiddenFields
107
     */
NEW
108
    private function buildResourcePointerFields(
×
109
        Form\Domain\Model\FormElements\FileUpload $renderable,
110
        array $hiddenFields,
111
    ): Domain\ViewModel\ViewModelCollection {
NEW
112
        $resourcePointerFields = [];
×
113

NEW
114
        foreach ($hiddenFields as $hiddenField) {
×
NEW
115
            $resourcePointerFields[] = new Domain\ViewModel\StandaloneTagViewModel($renderable, $hiddenField);
×
116
        }
117

NEW
118
        return new Domain\ViewModel\ViewModelCollection($renderable, $resourcePointerFields);
×
119
    }
120

121
    /**
122
     * @param Extbase\Domain\Model\FileReference|Extbase\Persistence\ObjectStorage<Extbase\Domain\Model\FileReference> $resource
123
     */
NEW
124
    private function buildDeleteCheckboxes(
×
125
        Form\Domain\Model\FormElements\FileUpload $renderable,
126
        Fluid\Core\Rendering\RenderingContext $renderingContext,
127
        Extbase\Domain\Model\FileReference|Extbase\Persistence\ObjectStorage $resource,
128
    ): Domain\ViewModel\ViewModelCollection {
NEW
129
        if (!is_iterable($resource)) {
×
NEW
130
            $resource = [$resource];
×
131
        }
132

NEW
133
        $viewModels = [];
×
NEW
134
        $index = 0;
×
135

136
        // Map file references to view models
NEW
137
        foreach ($resource as $fileReference) {
×
NEW
138
            $result = $this->viewHelperInvoker->invoke(
×
NEW
139
                $renderingContext,
×
NEW
140
                Form\ViewHelpers\Form\UploadDeleteCheckboxViewHelper::class,
×
NEW
141
                [
×
NEW
142
                    'property' => $renderable->getIdentifier(),
×
NEW
143
                    'fileReference' => $fileReference,
×
NEW
144
                    'fileIndex' => $index++,
×
NEW
145
                ],
×
NEW
146
            );
×
147

NEW
148
            $viewModels[] = Domain\ViewModel\FormFieldViewModel::forLabelAndElement(
×
NEW
149
                $fileReference->getOriginalResource()->getOriginalFile()->getName(),
×
NEW
150
                new Domain\ViewModel\ViewHelperContainedViewModel($renderable, $result),
×
NEW
151
            );
×
152
        }
153

NEW
154
        return new Domain\ViewModel\ViewModelCollection($renderable, $viewModels);
×
155
    }
156

157
    /**
158
     * @phpstan-assert-if-true Extbase\Domain\Model\FileReference|Extbase\Persistence\ObjectStorage<Extbase\Domain\Model\FileReference> $resource
159
     */
NEW
160
    private function isValidResource(mixed $resource): bool
×
161
    {
NEW
162
        if ($resource instanceof Extbase\Domain\Model\FileReference) {
×
NEW
163
            return true;
×
164
        }
165

166
        // @todo Combine with previous condition once support for TYPO3 v13 is dropped
NEW
167
        if ($this->typo3Version->getMajorVersion() >= 14
×
NEW
168
            && $resource instanceof Extbase\Persistence\ObjectStorage
×
NEW
169
            && count($resource) > 0
×
170
        ) {
NEW
171
            return true;
×
172
        }
173

NEW
174
        return false;
×
175
    }
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc