Completed
Push — master ( 9abde4...30596a )
by
unknown
02:26
created

FormAssetHandler::saveAndGetJavaScriptFiles()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
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 static $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->insertData($pageRenderer, $assetHandlerFactory);
105
106
        return $instance;
107
    }
108
109
    /**
110
     * @param PageRenderer        $pageRenderer
111
     * @param AssetHandlerFactory $assetHandlerFactory
112
     */
113
    public function insertData(PageRenderer $pageRenderer, AssetHandlerFactory $assetHandlerFactory)
114
    {
115
        $this->pageRenderer = $pageRenderer;
116
        $this->assetHandlerFactory = $assetHandlerFactory;
117
    }
118
119
    /**
120
     * Will take care of including internal Formz JavaScript and CSS files. They
121
     * will be included only once, even if the view helper is used several times
122
     * in the same page.
123
     *
124
     * @return $this
125
     */
126
    public function includeAssets()
127
    {
128
        if (false === self::$assetsIncluded) {
129
            self::$assetsIncluded = true;
130
131
            if (Core::get()->isInDebugMode()) {
132
                self::$javaScriptFiles[] = 'Formz.Debug.js';
133
            }
134
135
            foreach (self::$javaScriptFiles as $file) {
136
                $filePath = ExtensionManagementUtility::siteRelPath('formz') . 'Resources/Public/JavaScript/' . $file;
137
                $this->pageRenderer->addJsFile($filePath);
138
            }
139
140
            foreach (self::$cssFiles as $file) {
141
                $filePath = ExtensionManagementUtility::siteRelPath('formz') . 'Resources/Public/StyleSheets/' . $file;
142
                $this->pageRenderer->addCssFile($filePath);
143
            }
144
145
            $formzConfigurationJavaScriptAssetHandler = FormzConfigurationJavaScriptAssetHandler::with($this->assetHandlerFactory);
146
            $formzConfigurationJavaScriptFileName = $formzConfigurationJavaScriptAssetHandler->getJavaScriptFileName();
147
            if (false === file_exists(GeneralUtility::getFileAbsFileName($formzConfigurationJavaScriptFileName))) {
148
                GeneralUtility::writeFileToTypo3tempDir(
149
                    GeneralUtility::getFileAbsFileName($formzConfigurationJavaScriptFileName),
150
                    $formzConfigurationJavaScriptAssetHandler->getJavaScriptCode()
151
                );
152
            }
153
154
            $this->pageRenderer->addJsFooterFile($formzConfigurationJavaScriptFileName);
155
        }
156
157
        return $this;
158
    }
159
160
    /**
161
     * Will take care of generating the CSS with the AssetHandlerFactory. The
162
     * code will be put in a `.css` file in the `typo3temp` directory.
163
     *
164
     * If the file already exists, it is included directly before the code
165
     * generation.
166
     *
167
     * @return $this
168
     */
169
    public function includeGeneratedCss()
170
    {
171
        $filePath = $this->getFormzGeneratedFilePath() . '.css';
172
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
173
            $css = ErrorContainerDisplayCssAssetHandler::with($this->assetHandlerFactory)->getErrorContainerDisplayCss() . LF;
174
            $css .= FieldsActivationCssAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationCss();
175
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $css);
176
        }
177
        $this->pageRenderer->addCssFile($filePath);
178
179
        return $this;
180
    }
181
182
    /**
183
     * Will take care of generating the JavaScript with the AssetHandlerFactory.
184
     * The code will be put in a `.js` file in the `typo3temp` directory.
185
     *
186
     * If the file already exists, it is included directly before the code
187
     * generation.
188
     *
189
     * @return $this
190
     */
191
    public function includeGeneratedJavaScript()
192
    {
193
        $filePath = $this->getFormzGeneratedFilePath() . '.js';
194
        $cacheInstance = Core::get()->getCacheInstance();
195
        $javaScriptValidationFilesCacheIdentifier = Core::get()->getCacheIdentifier('js-files-', $this->assetHandlerFactory->getFormObject()->getClassName());
196
197
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
198
            ConditionNode::distinctUsedConditions();
199
            $fieldValidationConfigurationAssetHandler = FieldsValidationJavaScriptAssetHandler::with($this->assetHandlerFactory)->process();
200
201
            $javaScriptCode = FormInitializationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFormInitializationJavaScriptCode() . LF;
202
            $javaScriptCode .= $fieldValidationConfigurationAssetHandler->getJavaScriptCode() . LF;
203
            $javaScriptCode .= FieldsActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationJavaScriptCode() . LF;
204
            $javaScriptCode .= FieldsValidationActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsValidationActivationJavaScriptCode();
205
206
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $javaScriptCode);
207
208
            $javaScriptFiles = $this->saveAndGetJavaScriptFiles(
209
                $javaScriptValidationFilesCacheIdentifier,
210
                $fieldValidationConfigurationAssetHandler->getJavaScriptValidationFiles()
211
            );
212
        } else {
213
            // Including all JavaScript files required by used validation rules and conditions.
214
            if ($cacheInstance->has($javaScriptValidationFilesCacheIdentifier)) {
215
                $javaScriptFiles = $cacheInstance->get($javaScriptValidationFilesCacheIdentifier);
216
            } else {
217
                $fieldValidationConfigurationAssetHandler = FieldsValidationJavaScriptAssetHandler::with($this->assetHandlerFactory)->process();
218
                ConditionNode::distinctUsedConditions();
219
                FieldsActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsActivationJavaScriptCode();
220
                FieldsValidationActivationJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFieldsValidationActivationJavaScriptCode();
221
222
                $javaScriptFiles = $this->saveAndGetJavaScriptFiles(
223
                    $javaScriptValidationFilesCacheIdentifier,
224
                    $fieldValidationConfigurationAssetHandler->getJavaScriptValidationFiles()
225
                );
226
            }
227
        }
228
229
        $this->pageRenderer->addJsFooterFile($filePath);
230
231
        $this->includeJavaScriptValidationFiles($javaScriptFiles);
232
233
        // Here we generate the JavaScript code containing the submitted values, and the existing errors.
234
        $javaScriptCode = FormRequestDataJavaScriptAssetHandler::with($this->assetHandlerFactory)->getFormRequestDataJavaScriptCode(FormViewHelper::getVariable(FormViewHelper::FORM_INSTANCE));
235
236
        if (Core::get()->isInDebugMode()) {
237
            $javaScriptCode .= LF;
238
            $javaScriptCode .= 'Formz.Debug.activate();';
239
        }
240
241
        /** @var UriBuilder $uriBuilder */
242
        $uriBuilder = Core::get()->getObjectManager()->get(UriBuilder::class);
243
        $uri = $uriBuilder->reset()
244
            ->setTargetPageType(1473682545)
245
            ->setNoCache(true)
246
            ->setUseCacheHash(false)
247
            ->setCreateAbsoluteUri(true)
248
            ->build();
249
250
        $javaScriptCode .= LF;
251
        $javaScriptCode .= "Formz.setAjaxUrl('$uri');";
252
253
        $this->pageRenderer->addJsFooterInlineCode('Formz - Initialization ' . $this->assetHandlerFactory->getFormObject()->getClassName(), $javaScriptCode);
254
255
        return $this;
256
    }
257
258
    /**
259
     * Will save in cache and return the list of files which must be included in
260
     * order to make validation rules and conditions work properly.
261
     *
262
     * @param string $cacheIdentifier
263
     * @param array  $javaScriptFiles
264
     * @return array
265
     */
266
    protected function saveAndGetJavaScriptFiles($cacheIdentifier, array $javaScriptFiles)
267
    {
268
        /** @var AbstractConditionItem[] $conditions */
269
        $conditions = ConditionNode::getDistinctUsedConditions();
270
271
        foreach ($conditions as $condition) {
272
            $javaScriptFiles = array_merge($javaScriptFiles, $condition::getJavaScriptFiles());
273
        }
274
275
        Core::get()->getCacheInstance()->set($cacheIdentifier, $javaScriptFiles);
276
277
        return $javaScriptFiles;
278
    }
279
280
    /**
281
     * This function will handle the JavaScript localization files.
282
     *
283
     * A file will be created for the current language (there can be as many
284
     * files as languages), containing the translations handling for JavaScript.
285
     * If the file already exists, it is directly included.
286
     *
287
     * @return $this
288
     */
289
    public function handleJavaScriptLocalization()
290
    {
291
        $filePath = $this->getFormzGeneratedFilePath('local-' . Core::get()->getLanguageKey()) . '.js';
292
293
        if (false === file_exists(GeneralUtility::getFileAbsFileName($filePath))) {
294
            $javaScriptCode = FormzLocalizationJavaScriptAssetHandler::with($this->assetHandlerFactory)
295
                ->injectTranslationsForFormFieldsValidation()
296
                ->getJavaScriptCode();
297
298
            GeneralUtility::writeFileToTypo3tempDir(GeneralUtility::getFileAbsFileName($filePath), $javaScriptCode);
299
        }
300
301
        $this->pageRenderer->addJsFooterFile($filePath);
302
303
        return $this;
304
    }
305
306
    /**
307
     * Returns a file name based on the form object class name.
308
     *
309
     * @param string $prefix
310
     * @return string
311
     */
312
    protected function getFormzGeneratedFilePath($prefix = '')
313
    {
314
        $formObject = $this->assetHandlerFactory->getFormObject();
315
        $prefix = (false === empty($prefix))
316
            ? $prefix . '-'
317
            : '';
318
319
        return Core::GENERATED_FILES_PATH . Core::get()->getCacheIdentifier('formz-' . $prefix, $formObject->getClassName() . '-' . $formObject->getName());
320
    }
321
322
    /**
323
     * Will include all new JavaScript files given, by checking that every given
324
     * file was not already included.
325
     *
326
     * @param array $javaScriptValidationFiles List of JavaScript validation files.
327
     */
328
    protected function includeJavaScriptValidationFiles(array $javaScriptValidationFiles)
329
    {
330
        $javaScriptValidationFiles = array_unique($javaScriptValidationFiles);
331
332
        foreach ($javaScriptValidationFiles as $file) {
333
            if (false === in_array($file, self::$alreadyIncludedValidationJavaScriptFiles)) {
334
                $path = self::getResourceRelativePath($file);
335
                $this->pageRenderer->addJsFooterFile($path);
336
                self::$alreadyIncludedValidationJavaScriptFiles[] = $file;
337
            }
338
        }
339
    }
340
341
    /**
342
     * @param string $path
343
     * @return string
344
     */
345
    public static function getResourceRelativePath($path)
346
    {
347
        return rtrim(
348
            PathUtility::getRelativePath(
349
                GeneralUtility::getIndpEnv('TYPO3_DOCUMENT_ROOT'),
350
                GeneralUtility::getFileAbsFileName($path)
351
            ),
352
            '/'
353
        );
354
    }
355
}
356