Completed
Pull Request — master (#14)
by
unknown
02:13
created

FormAssetHandler::setAssetHandlerFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/*
3
 * 2016 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 Formz project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\AssetHandler;
15
16
use Romm\Formz\AssetHandler\Css\ErrorContainerDisplayCssAssetHandler;
17
use Romm\Formz\AssetHandler\Css\FieldsActivationCssAssetHandler;
18
use Romm\Formz\AssetHandler\JavaScript\FieldsActivationJavaScriptAssetHandler;
19
use Romm\Formz\AssetHandler\JavaScript\FieldsValidationActivationJavaScriptAssetHandler;
20
use Romm\Formz\AssetHandler\JavaScript\FieldsValidationJavaScriptAssetHandler;
21
use Romm\Formz\AssetHandler\JavaScript\FormInitializationJavaScriptAssetHandler;
22
use Romm\Formz\AssetHandler\JavaScript\FormRequestDataJavaScriptAssetHandler;
23
use Romm\Formz\AssetHandler\JavaScript\FormzConfigurationJavaScriptAssetHandler;
24
use Romm\Formz\AssetHandler\JavaScript\FormzLocalizationJavaScriptAssetHandler;
25
use Romm\Formz\Condition\Items\AbstractConditionItem;
26
use Romm\Formz\Condition\Node\ConditionNode;
27
use Romm\Formz\Core\Core;
28
use Romm\Formz\ViewHelpers\FormViewHelper;
29
use TYPO3\CMS\Core\Page\PageRenderer;
30
use TYPO3\CMS\Core\SingletonInterface;
31
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
use TYPO3\CMS\Core\Utility\PathUtility;
34
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
35
36
class FormAssetHandler implements SingletonInterface
37
{
38
39
    /**
40
     * List of JavaScript files which will be included whenever this view helper
41
     * is used.
42
     *
43
     * @var array
44
     */
45
    protected static $javaScriptFiles = [
46
        'Formz.Main.js',
47
        'Formz.Misc.js',
48
        'Formz.EventsManager.js',
49
        'Formz.Result.js',
50
        'Formz.Localization.js',
51
        'Form/Formz.Form.js',
52
        'Form/Formz.Form.SubmissionService.js',
53
        'Field/Formz.Field.js',
54
        'Field/Formz.Field.DataAttributesService.js',
55
        'Field/Formz.Field.ValidationService.js',
56
        'Conditions/Formz.Condition.js',
57
        'Validators/Formz.Validation.js',
58
        'Validators/Formz.Validator.Ajax.js'
59
    ];
60
61
    /**
62
     * List of CSS files which will be included whenever this view helper is
63
     * used.
64
     *
65
     * @var array
66
     */
67
    protected static $cssFiles = [
68
        'Form.Main.css'
69
    ];
70
71
    /**
72
     * @var bool
73
     */
74
    protected $assetsIncluded = false;
75
76
    /**
77
     * Storage for JavaScript files which were already included. It will handle
78
     * multiple instance of forms in the same page, by avoiding multiple
79
     * inclusions of the same JavaScript files.
80
     *
81
     * @var array
82
     */
83
    protected static $alreadyIncludedValidationJavaScriptFiles = [];
84
85
    /**
86
     * @var PageRenderer
87
     */
88
    protected $pageRenderer;
89
90
    /**
91
     * @var AssetHandlerFactory
92
     */
93
    protected $assetHandlerFactory;
94
95
    /**
96
     * @param PageRenderer        $pageRenderer
97
     * @param AssetHandlerFactory $assetHandlerFactory
98
     * @return FormAssetHandler
99
     */
100
    public static function get(PageRenderer $pageRenderer, AssetHandlerFactory $assetHandlerFactory)
101
    {
102
        /** @var FormAssetHandler $instance */
103
        $instance = GeneralUtility::makeInstance(self::class);
104
        $instance->setPageRenderer($pageRenderer);
105
        $instance->setAssetHandlerFactory($assetHandlerFactory);
106
107
        return $instance;
108
    }
109
110
    /**
111
     * @param PageRenderer $pageRenderer
112
     */
113
    public function setPageRenderer(PageRenderer $pageRenderer)
114
    {
115
        $this->pageRenderer = $pageRenderer;
116
    }
117
118
    /**
119
     * @param AssetHandlerFactory $assetHandlerFactory
120
     */
121
    public function setAssetHandlerFactory(AssetHandlerFactory $assetHandlerFactory)
122
    {
123
        $this->assetHandlerFactory = $assetHandlerFactory;
124
    }
125
126
    /**
127
     * Will take care of including internal Formz JavaScript and CSS files. They
128
     * will be included only once, even if the view helper is used several times
129
     * in the same page.
130
     *
131
     * @return $this
132
     */
133
    public function includeAssets()
134
    {
135
        if (false === $this->assetsIncluded) {
136
            $this->assetsIncluded = true;
137
138
            if (Core::get()->isInDebugMode()) {
139
                self::$javaScriptFiles[] = 'Formz.Debug.js';
140
            }
141
142
            foreach (self::$javaScriptFiles as $file) {
143
                $filePath = Core::get()->getExtensionRelativePath('Resources/Public/JavaScript/' . $file);
144
                $this->pageRenderer->addJsFile($filePath);
145
            }
146
147
            foreach (self::$cssFiles as $file) {
148
                $filePath = Core::get()->getExtensionRelativePath('Resources/Public/StyleSheets/' . $file);
149
                $this->pageRenderer->addCssFile($filePath);
150
            }
151
152
            $formzConfigurationJavaScriptAssetHandler = FormzConfigurationJavaScriptAssetHandler::with($this->assetHandlerFactory);
153
            $formzConfigurationJavaScriptFileName = $formzConfigurationJavaScriptAssetHandler->getJavaScriptFileName();
154
            if (false === file_exists(GeneralUtility::getFileAbsFileName($formzConfigurationJavaScriptFileName))) {
155
                GeneralUtility::writeFileToTypo3tempDir(
156
                    GeneralUtility::getFileAbsFileName($formzConfigurationJavaScriptFileName),
157
                    $formzConfigurationJavaScriptAssetHandler->getJavaScriptCode()
158
                );
159
            }
160
161
            $this->pageRenderer->addJsFooterFile($formzConfigurationJavaScriptFileName);
162
        }
163
164
        return $this;
165
    }
166
167
    /**
168
     * Will take care of generating the CSS with the AssetHandlerFactory. The
169
     * code will be put in a `.css` file in the `typo3temp` directory.
170
     *
171
     * If the file already exists, it is included directly before the code
172
     * generation.
173
     *
174
     * @return $this
175
     */
176
    public function includeGeneratedCss()
177
    {
178
        $filePath = $this->getFormzGeneratedFilePath() . '.css';
179
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
180
            $css = ErrorContainerDisplayCssAssetHandler::with($this->assetHandlerFactory)->getErrorContainerDisplayCss() . LF;
181
            $css .= FieldsActivationCssAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationCss();
182
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $css);
183
        }
184
        $this->pageRenderer->addCssFile($filePath);
185
186
        return $this;
187
    }
188
189
    /**
190
     * Will take care of generating the JavaScript with the AssetHandlerFactory.
191
     * The code will be put in a `.js` file in the `typo3temp` directory.
192
     *
193
     * If the file already exists, it is included directly before the code
194
     * generation.
195
     *
196
     * @return $this
197
     */
198
    public function includeGeneratedJavaScript()
199
    {
200
        $filePath = $this->getFormzGeneratedFilePath() . '.js';
201
        $cacheInstance = Core::get()->getCacheInstance();
202
        $javaScriptValidationFilesCacheIdentifier = Core::get()->getCacheIdentifier('js-files-', $this->assetHandlerFactory->getFormObject()->getClassName());
203
204
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
205
            ConditionNode::distinctUsedConditions();
206
            $fieldValidationConfigurationAssetHandler = FieldsValidationJavaScriptAssetHandler::with($this->assetHandlerFactory)->process();
207
208
            $javaScriptCode = FormInitializationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFormInitializationJavaScriptCode() . LF;
209
            $javaScriptCode .= $fieldValidationConfigurationAssetHandler->getJavaScriptCode() . LF;
210
            $javaScriptCode .= FieldsActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationJavaScriptCode() . LF;
211
            $javaScriptCode .= FieldsValidationActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsValidationActivationJavaScriptCode();
212
213
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $javaScriptCode);
214
215
            $javaScriptFiles = $this->saveAndGetJavaScriptFiles(
216
                $javaScriptValidationFilesCacheIdentifier,
217
                $fieldValidationConfigurationAssetHandler->getJavaScriptValidationFiles()
218
            );
219
        } else {
220
            // Including all JavaScript files required by used validation rules and conditions.
221
            if ($cacheInstance->has($javaScriptValidationFilesCacheIdentifier)) {
222
                $javaScriptFiles = $cacheInstance->get($javaScriptValidationFilesCacheIdentifier);
223
            } else {
224
                $fieldValidationConfigurationAssetHandler = FieldsValidationJavaScriptAssetHandler::with($this->assetHandlerFactory)->process();
225
                ConditionNode::distinctUsedConditions();
226
                FieldsActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationJavaScriptCode();
227
                FieldsValidationActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsValidationActivationJavaScriptCode();
228
229
                $javaScriptFiles = $this->saveAndGetJavaScriptFiles(
230
                    $javaScriptValidationFilesCacheIdentifier,
231
                    $fieldValidationConfigurationAssetHandler->getJavaScriptValidationFiles()
232
                );
233
            }
234
        }
235
236
        $this->pageRenderer->addJsFooterFile($filePath);
237
238
        $this->includeJavaScriptValidationFiles($javaScriptFiles);
239
240
        // Here we generate the JavaScript code containing the submitted values, and the existing errors.
241
        $javaScriptCode = FormRequestDataJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFormRequestDataJavaScriptCode(FormViewHelper::getVariable(FormViewHelper::FORM_INSTANCE));
242
243
        if (Core::get()->isInDebugMode()) {
244
            $javaScriptCode .= LF;
245
            $javaScriptCode .= 'Formz.Debug.activate();';
246
        }
247
248
        /** @var UriBuilder $uriBuilder */
249
        $uriBuilder = Core::get()->getObjectManager()->get(UriBuilder::class);
250
        $uri = $uriBuilder->reset()
251
            ->setTargetPageType(1473682545)
252
            ->setNoCache(true)
253
            ->setUseCacheHash(false)
254
            ->setCreateAbsoluteUri(true)
255
            ->build();
256
257
        $javaScriptCode .= LF;
258
        $javaScriptCode .= "Formz.setAjaxUrl('$uri');";
259
260
        $this->pageRenderer->addJsFooterInlineCode('Formz - Initialization ' . $this->assetHandlerFactory->getFormObject()->getClassName(), $javaScriptCode);
261
262
        return $this;
263
    }
264
265
    /**
266
     * Will save in cache and return the list of files which must be included in
267
     * order to make validation rules and conditions work properly.
268
     *
269
     * @param string $cacheIdentifier
270
     * @param array  $javaScriptFiles
271
     * @return array
272
     */
273
    protected function saveAndGetJavaScriptFiles($cacheIdentifier, array $javaScriptFiles)
274
    {
275
        /** @var AbstractConditionItem[] $conditions */
276
        $conditions = ConditionNode::getDistinctUsedConditions();
277
278
        foreach ($conditions as $condition) {
279
            $javaScriptFiles = array_merge($javaScriptFiles, $condition::getJavaScriptFiles());
280
        }
281
282
        Core::get()->getCacheInstance()->set($cacheIdentifier, $javaScriptFiles);
283
284
        return $javaScriptFiles;
285
    }
286
287
    /**
288
     * This function will handle the JavaScript localization files.
289
     *
290
     * A file will be created for the current language (there can be as many
291
     * files as languages), containing the translations handling for JavaScript.
292
     * If the file already exists, it is directly included.
293
     *
294
     * @return $this
295
     */
296
    public function handleJavaScriptLocalization()
297
    {
298
        $filePath = $this->getFormzGeneratedFilePath('local-' . Core::get()->getLanguageKey()) . '.js';
299
300
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
301
            $javaScriptCode = FormzLocalizationJavaScriptAssetHandler::with($this->assetHandlerFactory)
302
                ->injectTranslationsForFormFieldsValidation()
303
                ->getJavaScriptCode();
304
305
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $javaScriptCode);
306
        }
307
308
        $this->pageRenderer->addJsFooterFile($filePath);
309
310
        return $this;
311
    }
312
313
    /**
314
     * Returns a file name based on the form object class name.
315
     *
316
     * @param string $prefix
317
     * @return string
318
     */
319
    protected function getFormzGeneratedFilePath($prefix = '')
320
    {
321
        $formObject = $this->assetHandlerFactory->getFormObject();
322
        $prefix = (false === empty($prefix))
323
            ? $prefix . '-'
324
            : '';
325
326
        return Core::GENERATED_FILES_PATH . Core::get()->getCacheIdentifier('formz-' . $prefix, $formObject->getClassName() . '-' . $formObject->getName());
327
    }
328
329
    /**
330
     * Will include all new JavaScript files given, by checking that every given
331
     * file was not already included.
332
     *
333
     * @param array $javaScriptValidationFiles List of JavaScript validation files.
334
     */
335
    protected function includeJavaScriptValidationFiles(array $javaScriptValidationFiles)
336
    {
337
        $javaScriptValidationFiles = array_unique($javaScriptValidationFiles);
338
339
        foreach ($javaScriptValidationFiles as $file) {
340
            if (false === in_array($file, self::$alreadyIncludedValidationJavaScriptFiles)) {
341
                $path = self::getResourceRelativePath($file);
342
                $this->pageRenderer->addJsFooterFile($path);
343
                self::$alreadyIncludedValidationJavaScriptFiles[] = $file;
344
            }
345
        }
346
    }
347
348
    /**
349
     * @param string $path
350
     * @return string
351
     */
352
    public static function getResourceRelativePath($path)
353
    {
354
        return rtrim(
355
            PathUtility::getRelativePath(
356
                GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'),
357
                GeneralUtility::getFileAbsFileName($path)
358
            ),
359
            '/'
360
        );
361
    }
362
}
363