Passed
Push — master ( 6f1745...57b5b6 )
by
unknown
34:44 queued 14:32
created

checkSaveFileMountAccess()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 18
rs 9.9
cc 4
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
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 TYPO3\CMS\Form\Mvc\Property;
19
20
use TYPO3\CMS\Core\Resource\ResourceFactory;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\PathUtility;
23
use TYPO3\CMS\Extbase\Object\ObjectManager;
24
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
25
use TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator;
26
use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload;
27
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
28
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
29
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime\Lifecycle\AfterFormStateInitializedInterface;
30
use TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter;
31
use TYPO3\CMS\Form\Mvc\Validation\MimeTypeValidator;
32
33
/**
34
 * Scope: frontend
35
 * @internal
36
 */
37
class PropertyMappingConfiguration implements AfterFormStateInitializedInterface
38
{
39
40
    /**
41
     * This hook is called for each form element after the class
42
     * TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory has built the entire form.
43
     *
44
     * It is invoked after the static form definition is ready, but without knowing
45
     * about the individual state organized in `FormRuntime` and `FormState`.
46
     *
47
     * @param RenderableInterface $renderable
48
     * @internal
49
     */
50
    public function afterBuildingFinished(RenderableInterface $renderable)
51
    {
52
        if ($renderable instanceof FileUpload) {
53
            // Set the property mapping configuration for the file upload element.
54
            // * Add the UploadedFileReferenceConverter to convert an uploaded file to a
55
            //   FileReference.
56
            // * Add the MimeTypeValidator to the UploadedFileReferenceConverter to
57
            //   delete non-valid file types directly.
58
            // * Setup the storage:
59
            //   If the property "saveToFileMount" exist for this element it will be used.
60
            //   If this file mount or the property "saveToFileMount" does not exist
61
            //   the default storage "1:/user_uploads/" will be used. Uploads are placed
62
            //   in a dedicated sub-folder (e.g. ".../form_<40-chars-hash>/actual.file").
63
64
            /** @var UploadedFileReferenceConverter $typeConverter */
65
            $typeConverter = GeneralUtility::makeInstance(ObjectManager::class)
66
                ->get(UploadedFileReferenceConverter::class);
67
            /** @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration */
68
            $propertyMappingConfiguration = $renderable->getRootForm()
69
                ->getProcessingRule($renderable->getIdentifier())
70
                ->getPropertyMappingConfiguration()
71
                ->setTypeConverter($typeConverter);
72
73
            $allowedMimeTypes = [];
74
            $validators = [];
75
            if (isset($renderable->getProperties()['allowedMimeTypes']) && \is_array($renderable->getProperties()['allowedMimeTypes'])) {
76
                $allowedMimeTypes = array_filter($renderable->getProperties()['allowedMimeTypes']);
77
            }
78
            if (!empty($allowedMimeTypes)) {
79
                $mimeTypeValidator = GeneralUtility::makeInstance(ObjectManager::class)
80
                    ->get(MimeTypeValidator::class, ['allowedMimeTypes' => $allowedMimeTypes]);
81
                $validators = [$mimeTypeValidator];
82
            }
83
84
            $processingRule = $renderable->getRootForm()->getProcessingRule($renderable->getIdentifier());
85
            foreach ($processingRule->getValidators() as $validator) {
86
                if (!($validator instanceof NotEmptyValidator)) {
87
                    $validators[] = $validator;
88
                    $processingRule->removeValidator($validator);
89
                }
90
            }
91
92
            $uploadConfiguration = [
93
                UploadedFileReferenceConverter::CONFIGURATION_FILE_VALIDATORS => $validators,
94
                UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_CONFLICT_MODE => 'rename',
95
            ];
96
97
            $saveToFileMountIdentifier = $renderable->getProperties()['saveToFileMount'] ?? '';
98
            if ($this->checkSaveFileMountAccess($saveToFileMountIdentifier)) {
99
                $uploadConfiguration[UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_FOLDER] = $saveToFileMountIdentifier;
100
            } else {
101
                // @todo Why should uploaded files be stored to the same directory as the *.form.yaml definitions?
102
                $persistenceIdentifier = $renderable->getRootForm()->getPersistenceIdentifier();
103
                if (!empty($persistenceIdentifier)) {
104
                    $pathinfo = PathUtility::pathinfo($persistenceIdentifier);
105
                    $saveToFileMountIdentifier = $pathinfo['dirname'];
106
                    if ($this->checkSaveFileMountAccess($saveToFileMountIdentifier)) {
107
                        $uploadConfiguration[UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_FOLDER] = $saveToFileMountIdentifier;
108
                    }
109
                }
110
            }
111
            $propertyMappingConfiguration->setTypeConverterOptions(UploadedFileReferenceConverter::class, $uploadConfiguration);
112
            return;
113
        }
114
115
        if ($renderable->getType() === 'Date') {
116
            // Set the property mapping configuration for the `Date` element.
117
118
            /** @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration */
119
            $propertyMappingConfiguration = $renderable->getRootForm()->getProcessingRule($renderable->getIdentifier())->getPropertyMappingConfiguration();
0 ignored issues
show
Bug introduced by
The method getRootForm() does not exist on TYPO3\CMS\Form\Domain\Mo...ble\RenderableInterface. It seems like you code against a sub-type of said class. However, the method does not exist in TYPO3\CMS\Form\Domain\Mo...siteRenderableInterface or TYPO3\CMS\Form\Domain\Mo...ts\FormElementInterface or TYPO3\CMS\Form\Domain\Mo...ements\GridRowInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
            $propertyMappingConfiguration = $renderable->/** @scrutinizer ignore-call */ getRootForm()->getProcessingRule($renderable->getIdentifier())->getPropertyMappingConfiguration();
Loading history...
120
            // @see https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html#input.date.attrs.value
121
            // 'Y-m-d' = https://tools.ietf.org/html/rfc3339#section-5.6 -> full-date
122
            $propertyMappingConfiguration->setTypeConverterOption(DateTimeConverter::class, DateTimeConverter::CONFIGURATION_DATE_FORMAT, 'Y-m-d');
123
        }
124
    }
125
126
    /**
127
     * @param string $saveToFileMountIdentifier
128
     * @return bool
129
     * @internal
130
     */
131
    protected function checkSaveFileMountAccess(string $saveToFileMountIdentifier): bool
132
    {
133
        if (empty($saveToFileMountIdentifier)) {
134
            return false;
135
        }
136
137
        if (strpos($saveToFileMountIdentifier, 'EXT:') === 0) {
138
            return false;
139
        }
140
141
        $resourceFactory = GeneralUtility::makeInstance(ObjectManager::class)
142
            ->get(ResourceFactory::class);
143
144
        try {
145
            $resourceFactory->getFolderObjectFromCombinedIdentifier($saveToFileMountIdentifier);
146
            return true;
147
        } catch (\InvalidArgumentException $e) {
148
            return false;
149
        }
150
    }
151
152
    /**
153
     * @param FormRuntime $formRuntime holding current form state and static form definition
154
     */
155
    public function afterFormStateInitialized(FormRuntime $formRuntime): void
156
    {
157
        foreach ($formRuntime->getFormDefinition()->getRenderablesRecursively() as $renderable) {
158
            $this->adjustPropertyMappingForFileUploadsAtRuntime($formRuntime, $renderable);
159
        }
160
    }
161
162
    /**
163
     * If the form runtime is able to process form submissions
164
     * (determined by $formRuntime->canProcessFormSubmission()) then a
165
     * 'form session' is available.
166
     * This form session identifier will be used to deriving storage sub-folders
167
     * for the file uploads.
168
     * This is done by setting `UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_SEED`
169
     * type converter option.
170
     *
171
     * @param FormRuntime $formRuntime
172
     * @param RenderableInterface $renderable
173
     */
174
    protected function adjustPropertyMappingForFileUploadsAtRuntime(
175
        FormRuntime $formRuntime,
176
        RenderableInterface $renderable
177
    ): void {
178
        if (!$renderable instanceof FileUpload
179
            || $formRuntime->getFormSession() === null
180
            || !$formRuntime->canProcessFormSubmission()
181
        ) {
182
            return;
183
        }
184
        $renderable->getRootForm()
185
            ->getProcessingRule($renderable->getIdentifier())
186
            ->getPropertyMappingConfiguration()
187
            ->setTypeConverterOption(
188
                UploadedFileReferenceConverter::class,
189
                UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_SEED,
190
                $formRuntime->getFormSession()->getIdentifier()
191
            );
192
    }
193
}
194