ExtendedTemplateService.php 28.5 KB
Newer Older
1
<?php
2

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

16
17
namespace TYPO3\CMS\Core\TypoScript;

18
use TYPO3\CMS\Backend\Routing\UriBuilder;
19
use TYPO3\CMS\Core\Context\Context;
20
use TYPO3\CMS\Core\Exception;
21
use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser;
22
use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
23
use TYPO3\CMS\Core\Utility\ArrayUtility;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
27

28
/**
Christian Kuhn's avatar
Christian Kuhn committed
29
 * TSParser extension class to TemplateService
30
 * Contains functions for the TS module in TYPO3 backend
31
32
 *
 * @internal this is only used for the TYPO3 TypoScript Template module, which should not be used in Extensions
33
 */
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class ExtendedTemplateService extends TemplateService
{
    /**
     * Tsconstanteditor
     *
     * @var int
     */
    public $ext_inBrace = 0;

    /**
     * Tsbrowser
     *
     * @var array
     */
48
    public $tsbrowser_searchKeys = [];
49
50
51
52

    /**
     * @var array
     */
53
    public $tsbrowser_depthKeys = [];
54
55
56
57
58
59
60

    /**
     * @var string
     */
    public $constantMode = '';

    /**
61
     * @var bool
62
     */
63
    public $regexMode = false;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

    /**
     * @var int
     */
    public $ext_expandAllNotes = 0;

    /**
     * @var int
     */
    public $ext_noPMicons = 0;

    /**
     * Ts analyzer
     *
     * @var array
     */
80
    public $templateTitles = [];
81
82

    /**
83
     * @var array|null
84
     */
85
    protected $lnToScript;
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

    /**
     * @var array
     */
    public $clearList_const_temp;

    /**
     * @var array
     */
    public $clearList_setup_temp;

    /**
     * @var string
     */
    public $bType = '';

    /**
     * @var bool
     */
    public $linkObjects = false;

    /**
     * @var int[]
     */
110
    public $objReg = [];
111
112
113
114

    /**
     * @var array
     */
115
    public $raw = [];
116
117
118
119
120
121
122
123
124
125
126

    /**
     * @var int
     */
    public $rawP = 0;

    /**
     * @var string
     */
    public $lastComment = '';

127
    private ConstantConfigurationParser $constantParser;
128

129
    public function __construct(Context $context = null, ConstantConfigurationParser $constantParser = null)
130
131
    {
        parent::__construct($context);
132
        $this->constantParser = $constantParser ?? GeneralUtility::makeInstance(ConstantConfigurationParser::class);
133
134
135
136
137
        // Disabled in backend context
        $this->tt_track = false;
        $this->verbose = false;
    }

138
139
140
141
142
143
144
145
    /**
     * Substitute constant
     *
     * @param string $all
     * @return string
     */
    public function substituteConstants($all)
    {
146
        return preg_replace_callback('/\\{\\$(.[^}]+)\\}/', [$this, 'substituteConstantsCallBack'], $all);
147
148
149
150
151
152
153
154
155
156
157
    }

    /**
     * Call back method for preg_replace_callback in substituteConstants
     *
     * @param array $matches Regular expression matches
     * @return string Replacement
     * @see substituteConstants()
     */
    public function substituteConstantsCallBack($matches)
    {
158
        $marker = substr(md5($matches[0]), 0, 6);
159
160
        switch ($this->constantMode) {
            case 'const':
161
                $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? '##' . $marker . '_B##' . $this->flatSetup[$matches[1]] . '##' . $marker . '_M##' . $matches[0] . '##' . $marker . '_E##' : $matches[0];
162
163
                break;
            case 'subst':
164
                $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? '##' . $marker . '_B##' . $matches[0] . '##' . $marker . '_M##' . $this->flatSetup[$matches[1]] . '##' . $marker . '_E##' : $matches[0];
165
166
167
168
169
170
171
172
173
174
175
                break;
            case 'untouched':
                $ret_val = $matches[0];
                break;
            default:
                $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? $this->flatSetup[$matches[1]] : $matches[0];
        }
        return $ret_val;
    }

    /**
176
177
     * Substitute markers added in substituteConstantsCallBack()
     * with ##6chars_B##value1##6chars_M##value2##6chars_E##
178
179
180
181
182
183
184
185
186
     *
     * @param string $all
     * @return string
     */
    public function substituteCMarkers($all)
    {
        switch ($this->constantMode) {
            case 'const':
            case 'subst':
187
                $all = preg_replace(
188
                    '/##[a-z0-9]{6}_B##(.*?)##[a-z0-9]{6}_M##(.*?)##[a-z0-9]{6}_E##/',
189
                    '<strong class="text-success" data-bs-toggle="tooltip" data-bs-placement="top" data-title="$1" title="$1">$2</strong>',
190
                    $all
191
192
193
194
195
196
197
198
199
                );
                break;
            default:
        }
        return $all;
    }

    /**
     * Parse constants with respect to the constant-editor in this module.
200
     * In particular comments in the code are registered and the edit_divider is taken into account.
201
202
203
204
205
206
     *
     * @return array
     */
    public function generateConfig_constants()
    {
        // Parse constants
207
        $constants = GeneralUtility::makeInstance(TypoScriptParser::class);
208
        // Register comments!
209
        $constants->regComments = true;
210
211
212
213
214
        $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
        // Matches ALL conditions in TypoScript
        $matchObj->setSimulateMatchResult(true);
        $c = 0;
        $cc = count($this->constants);
215
        $defaultConstants = [];
216
217
218
        foreach ($this->constants as $str) {
            $c++;
            if ($c == $cc) {
219
                $defaultConstants = ArrayUtility::flatten($constants->setup, '', true);
220
221
222
223
            }
            $constants->parse($str, $matchObj);
        }
        $this->setup['constants'] = $constants->setup;
224
225
226
227
228
        $flatSetup = ArrayUtility::flatten($constants->setup, '', true);
        return $this->constantParser->parseComments(
            $flatSetup,
            $defaultConstants
        );
229
230
231
232
233
234
235
236
    }

    /**
     * Get object tree
     *
     * @param array $arr
     * @param string $depth_in
     * @param string $depthData
237
     * @param bool $alphaSort sorts the array keys / tree by alphabet when set
238
     * @return string
239
     */
240
    public function ext_getObjTree($arr, $depth_in, $depthData, bool $alphaSort = false, string $targetRoute = 'web_ts')
241
242
    {
        $HTML = '';
243
        if ($alphaSort) {
244
245
            ksort($arr);
        }
246
247
        $keyArr_num = [];
        $keyArr_alpha = [];
248
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
249
250
        foreach ($arr as $key => $value) {
            // Don't do anything with comments / linenumber registrations...
251
            if (substr($key, -2) !== '..') {
252
                $key = preg_replace('/\\.$/', '', $key) ?? '';
253
                if (substr($key, -1) !== '.') {
254
                    if (MathUtility::canBeInterpretedAsInteger($key)) {
255
                        $keyArr_num[$key] = $arr[$key] ?? '';
256
                    } else {
257
                        $keyArr_alpha[$key] = $arr[$key] ?? '';
258
259
260
261
262
263
264
265
266
267
268
269
270
271
                    }
                }
            }
        }
        ksort($keyArr_num);
        $keyArr = $keyArr_num + $keyArr_alpha;
        if ($depth_in) {
            $depth_in = $depth_in . '.';
        }
        foreach ($keyArr as $key => $value) {
            $depth = $depth_in . $key;
            // This excludes all constants starting with '_' from being shown.
            if ($this->bType !== 'const' || $depth[0] !== '_') {
                $goto = substr(md5($depth), 0, 6);
272
273
                $deeper = is_array($arr[$key . '.'] ?? null) && (($this->tsbrowser_depthKeys[$depth] ?? false) || $this->ext_expandAllNotes);
                $PM = is_array($arr[$key . '.'] ?? null) && !$this->ext_noPMicons ? ($deeper ? 'minus' : 'plus') : 'join';
274
                $HTML .= $depthData . '<li><span class="list-tree-group">';
275
                if ($PM !== 'join') {
276
                    $urlParameters = [
277
                        'id' => (int)GeneralUtility::_GP('id'),
278
                        'tsbr[' . $depth . ']' => $deeper ? 0 : 1,
279
                    ];
280
                    $aHref = $uriBuilder->buildUriFromRoute($targetRoute, $urlParameters) . '#' . $goto;
281
282
283
284
                    $HTML .= '<a class="list-tree-control' . ($PM === 'minus' ? ' list-tree-control-open' : ' list-tree-control-closed') . '" name="' . $goto . '" href="' . htmlspecialchars($aHref) . '"><i class="fa"></i></a>';
                }
                $label = $key;
                // Read only...
285
                if (($depth === 'types') && $this->bType === 'setup') {
286
287
288
                    $label = '<span style="color: #666666;">' . $label . '</span>';
                } else {
                    if ($this->linkObjects) {
289
                        $urlParameters = [
290
                            'id' => (int)GeneralUtility::_GP('id'),
291
                            'sObj' => $depth,
292
                        ];
293
                        $aHref = (string)$uriBuilder->buildUriFromRoute($targetRoute, $urlParameters);
294
                        if ($this->bType !== 'const') {
295
                            $ln = is_array($arr[$key . '.ln..'] ?? null) ? 'Defined in: ' . $this->lineNumberToScript($arr[$key . '.ln..']) : 'N/A';
296
297
298
                        } else {
                            $ln = '';
                        }
299
                        if (($this->tsbrowser_searchKeys[$depth] ?? 0) & 4) {
300
                            // The key has matched the search string
301
302
                            $label = '<strong class="text-danger">' . $label . '</strong>';
                        }
303
                        $label = '<a href="' . htmlspecialchars($aHref) . '" title="' . htmlspecialchars($depth_in . $key . ' ' . $ln) . '">' . $label . '</a>';
304
305
                    }
                }
306
                $HTML .= '<span class="list-tree-label" title="' . htmlspecialchars($depth_in . $key) . '">[' . $label . ']</span>';
307
308
309
                if (isset($arr[$key])) {
                    $theValue = $arr[$key];
                    // The value has matched the search string
310
                    if (($this->tsbrowser_searchKeys[$depth] ?? 0) & 2) {
311
312
313
314
315
                        $HTML .= ' = <span class="list-tree-value text-danger">' . htmlspecialchars($theValue) . '</span>';
                    } else {
                        $HTML .= ' = <span class="list-tree-value">' . htmlspecialchars($theValue) . '</span>';
                    }
                    if ($this->ext_regComments && isset($arr[$key . '..'])) {
316
                        $comment = (string)$arr[$key . '..'];
317
318
319
                        // Skip INCLUDE_TYPOSCRIPT comments, they are almost useless
                        if (!preg_match('/### <INCLUDE_TYPOSCRIPT:.*/', $comment)) {
                            // Remove linebreaks, replace with ' '
320
                            $comment = preg_replace('/[\\r\\n]/', ' ', $comment) ?? '';
321
                            // Remove # and * if more than twice in a row
322
                            $comment = preg_replace('/[#\\*]{2,}/', '', $comment) ?? '';
323
                            // Replace leading # (just if it exists) and add it again. Result: Every comment should be prefixed by a '#'.
324
                            $comment = preg_replace('/^[#\\*\\s]+/', '# ', $comment) ?? '';
325
326
327
328
329
330
331
332
                            // Masking HTML Tags: Replace < with &lt; and > with &gt;
                            $comment = htmlspecialchars($comment);
                            $HTML .= ' <i class="text-muted">' . trim($comment) . '</i>';
                        }
                    }
                }
                $HTML .= '</span>';
                if ($deeper) {
333
                    $HTML .= $this->ext_getObjTree($arr[$key . '.'] ?? [], $depth, $depthData, $alphaSort, $targetRoute);
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
                }
            }
        }
        if ($HTML !== '') {
            $HTML = '<ul class="list-tree text-monospace">' . $HTML . '</ul>';
        }

        return $HTML;
    }

    /**
     * Find the originating template name for an array of line numbers (TypoScript setup only!)
     * Given an array of linenumbers the method will try to find the corresponding template where this line originated
     * The linenumber indicates the *last* lineNumber that is part of the template
     *
     * lineNumbers are in sync with the calculated lineNumbers '.ln..' in TypoScriptParser
     *
     * @param array $lnArr Array with linenumbers (might have some extra symbols, for example for unsetting) to be processed
352
     * @return string Imploded array of line number and template title
353
354
355
356
357
     */
    public function lineNumberToScript(array $lnArr)
    {
        // On the first call, construct the lnToScript array.
        if (!is_array($this->lnToScript)) {
358
            $this->lnToScript = [];
359
360
361
362

            // aggregatedTotalLineCount
            $c = 0;
            foreach ($this->hierarchyInfo as $templateNumber => $info) {
363
                // hierarchyInfo has the number of lines in configLines, but unfortunately this value
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
                // was calculated *before* processing of any INCLUDE instructions
                // for some yet unknown reason we have to add an extra +2 offset
                $linecountAfterIncludeProcessing = substr_count($this->config[$templateNumber], LF) + 2;
                $c += $linecountAfterIncludeProcessing;
                $this->lnToScript[$c] = $info['title'];
            }
        }

        foreach ($lnArr as $k => $ln) {
            foreach ($this->lnToScript as $endLn => $title) {
                if ($endLn >= (int)$ln) {
                    $lnArr[$k] = '"' . $title . '", ' . $ln;
                    break;
                }
            }
        }

        return implode('; ', $lnArr);
    }

    /**
     * @param array $arr
     * @param string $depth_in
     * @param string $searchString
     * @param array $keyArray
     * @return array
390
     * @throws Exception
391
392
393
     */
    public function ext_getSearchKeys($arr, $depth_in, $searchString, $keyArray)
    {
394
        $keyArr = [];
395
        foreach ($arr as $key => $value) {
396
            $key = preg_replace('/\\.$/', '', $key) ?? '';
397
            if (substr($key, -1) !== '.') {
398
399
400
401
402
403
                $keyArr[$key] = 1;
            }
        }
        if ($depth_in) {
            $depth_in = $depth_in . '.';
        }
404
405
406
407
408
409
410
411
        $searchPattern = '';
        if ($this->regexMode) {
            $searchPattern = '/' . addcslashes($searchString, '/') . '/';
            $matchResult = @preg_match($searchPattern, '');
            if ($matchResult === false) {
                throw new Exception(sprintf('Error evaluating regular expression "%s".', $searchPattern), 1446559458);
            }
        }
412
413
414
415
        foreach ($keyArr as $key => $value) {
            $depth = $depth_in . $key;
            if ($this->regexMode) {
                // The value has matched
416
417
                if (($arr[$key] ?? false) && preg_match($searchPattern, $arr[$key])) {
                    $this->tsbrowser_searchKeys[$depth] = ($this->tsbrowser_searchKeys[$depth] ?? 0) + 2;
418
419
                }
                // The key has matched
420
                if (preg_match($searchPattern, $key)) {
421
                    $this->tsbrowser_searchKeys[$depth] = ($this->tsbrowser_searchKeys[$depth] ?? 0) + 4;
422
423
                }
                // Just open this subtree if the parent key has matched the search
424
                if (preg_match($searchPattern, $depth_in)) {
425
426
427
428
                    $this->tsbrowser_searchKeys[$depth] = 1;
                }
            } else {
                // The value has matched
429
430
                if (($arr[$key] ?? false) && stripos($arr[$key], $searchString) !== false) {
                    $this->tsbrowser_searchKeys[$depth] = ($this->tsbrowser_searchKeys[$depth] ?? 0) + 2;
431
432
                }
                // The key has matches
433
                if (stripos($key, $searchString) !== false) {
434
                    $this->tsbrowser_searchKeys[$depth] = ($this->tsbrowser_searchKeys[$depth] ?? 0) + 4;
435
436
                }
                // Just open this subtree if the parent key has matched the search
437
                if (stripos($depth_in, $searchString) !== false) {
438
439
440
                    $this->tsbrowser_searchKeys[$depth] = 1;
                }
            }
441
            if (is_array($arr[$key . '.'] ?? null)) {
442
443
                $cS = count($this->tsbrowser_searchKeys);
                $keyArray = $this->ext_getSearchKeys($arr[$key . '.'], $depth, $searchString, $keyArray);
444
                if ($cS !== count($this->tsbrowser_searchKeys)) {
445
446
447
448
449
450
451
452
453
                    $keyArray[$depth] = 1;
                }
            }
        }
        return $keyArray;
    }

    /**
     * Processes the flat array from TemplateService->hierarchyInfo
454
     * and turns it into a hierarchical array to show dependencies (used by TemplateAnalyzer)
455
456
     *
     * @param array $depthDataArr (empty array on external call)
457
     * @param int $pointer Element number (1! to count()) of $this->hierarchyInfo that should be processed.
458
459
460
461
462
463
464
465
466
467
468
469
     * @return array Processed hierachyInfo.
     */
    public function ext_process_hierarchyInfo(array $depthDataArr, &$pointer)
    {
        $parent = $this->hierarchyInfo[$pointer - 1]['templateParent'];
        while ($pointer > 0 && $this->hierarchyInfo[$pointer - 1]['templateParent'] == $parent) {
            $pointer--;
            $row = $this->hierarchyInfo[$pointer];
            $depthDataArr[$row['templateID']] = $row;
            unset($this->clearList_setup_temp[$row['templateID']]);
            unset($this->clearList_const_temp[$row['templateID']]);
            $this->templateTitles[$row['templateID']] = $row['title'];
470
            if ($row['templateID'] == ($this->hierarchyInfo[$pointer - 1]['templateParent'] ?? '')) {
471
                $depthDataArr[$row['templateID'] . '.'] = $this->ext_process_hierarchyInfo([], $pointer);
472
473
474
475
476
477
478
479
480
481
482
            }
        }
        return $depthDataArr;
    }

    /**
     * @param string $type
     * @return array
     */
    public function ext_getTypeData($type)
    {
483
        $retArr = [];
484
485
486
487
488
489
        $type = trim($type);
        if (!$type) {
            $retArr['type'] = 'string';
        } else {
            $m = strcspn($type, ' [');
            $retArr['type'] = strtolower(substr($type, 0, $m));
490
491
            $types = ['int' => 1, 'options' => 1, 'file' => 1, 'boolean' => 1, 'offset' => 1, 'user' => 1];
            if (isset($types[$retArr['type']])) {
492
                $p = trim(substr($type, $m));
493
                $reg = [];
494
                preg_match('/\\[(.*)\\]/', $p, $reg);
495
                $p = trim($reg[1] ?? '');
496
497
498
499
500
501
502
503
504
505
                if ($p) {
                    $retArr['paramstr'] = $p;
                    switch ($retArr['type']) {
                        case 'int':
                            if ($retArr['paramstr'][0] === '-') {
                                $retArr['params'] = GeneralUtility::intExplode('-', substr($retArr['paramstr'], 1));
                                $retArr['params'][0] = (int)('-' . $retArr['params'][0]);
                            } else {
                                $retArr['params'] = GeneralUtility::intExplode('-', $retArr['paramstr']);
                            }
Georg Ringer's avatar
Georg Ringer committed
506
507
                            $retArr['min'] = $retArr['params'][0];
                            $retArr['max'] = $retArr['params'][1];
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
                            $retArr['paramstr'] = $retArr['params'][0] . ' - ' . $retArr['params'][1];
                            break;
                        case 'options':
                            $retArr['params'] = explode(',', $retArr['paramstr']);
                            break;
                    }
                }
            }
        }
        return $retArr;
    }

    /***************************
     *
     * Processing input values
     *
     ***************************/
    /**
     * @param string $constants
     */
528
    public function ext_regObjectPositions(string $constants): void
529
530
531
532
533
534
    {
        // This runs through the lines of the constants-field of the active template and registers the constants-names
        // and line positions in an array, $this->objReg
        $this->raw = explode(LF, $constants);
        $this->rawP = 0;
        // Resetting the objReg if the divider is found!!
535
        $this->objReg = [];
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
        $this->ext_regObjects('');
    }

    /**
     * @param string $pre
     */
    public function ext_regObjects($pre)
    {
        // Works with regObjectPositions. "expands" the names of the TypoScript objects
        while (isset($this->raw[$this->rawP])) {
            $line = ltrim($this->raw[$this->rawP]);
            $this->rawP++;
            if ($line) {
                if ($line[0] === '[') {
                } elseif (strcspn($line, '}#/') != 0) {
                    $varL = strcspn($line, ' {=<');
                    $var = substr($line, 0, $varL);
                    $line = ltrim(substr($line, $varL));
                    switch ($line[0]) {
                        case '=':
                            $this->objReg[$pre . $var] = $this->rawP - 1;
                            break;
                        case '{':
                            $this->ext_inBrace++;
                            $this->ext_regObjects($pre . $var . '.');
                            break;
                    }
                    $this->lastComment = '';
                } elseif ($line[0] === '}') {
                    $this->lastComment = '';
                    $this->ext_inBrace--;
                    if ($this->ext_inBrace < 0) {
                        $this->ext_inBrace = 0;
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * @param string $key
     * @param string $var
     */
    public function ext_putValueInConf($key, $var)
    {
        // Puts the value $var to the TypoScript value $key in the current lines of the templates.
        // If the $key is not found in the template constants field, a new line is inserted in the bottom.
        $theValue = ' ' . trim($var);
        if (isset($this->objReg[$key])) {
            $lineNum = $this->objReg[$key];
            $parts = explode('=', $this->raw[$lineNum], 2);
            if (count($parts) === 2) {
                $parts[1] = $theValue;
            }
592
            $this->raw[$lineNum] = implode('=', $parts);
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
        } else {
            $this->raw[] = $key . ' =' . $theValue;
        }
    }

    /**
     * @param string $key
     */
    public function ext_removeValueInConf($key)
    {
        // Removes the value in the configuration
        if (isset($this->objReg[$key])) {
            $lineNum = $this->objReg[$key];
            unset($this->raw[$lineNum]);
        }
    }

610
    public function ext_procesInput(array $http_post_vars, array $theConstants)
611
    {
612
        $valuesHaveChanged = false;
613
614
        $data = $http_post_vars['data'] ?? null;
        $check = $http_post_vars['check'] ?? [];
615
616
617
618
619
        $Wdata = $http_post_vars['Wdata'] ?? [];
        $W2data = $http_post_vars['W2data'] ?? [];
        $W3data = $http_post_vars['W3data'] ?? [];
        $W4data = $http_post_vars['W4data'] ?? [];
        $W5data = $http_post_vars['W5data'] ?? [];
620
621
622
623
        if (is_array($data)) {
            foreach ($data as $key => $var) {
                if (isset($theConstants[$key])) {
                    // If checkbox is set, update the value
624
                    if (isset($check[$key])) {
625
                        // Exploding with linebreak, just to make sure that no multiline input is given!
626
                        [$var] = explode(LF, $var);
627
628
629
                        $typeDat = $this->ext_getTypeData($theConstants[$key]['type']);
                        switch ($typeDat['type']) {
                            case 'int':
630
                                if ($typeDat['paramstr'] ?? false) {
631
                                    $var = MathUtility::forceIntegerInRange((int)$var, $typeDat['params'][0], $typeDat['params'][1]);
632
633
634
635
636
637
638
639
                                } else {
                                    $var = (int)$var;
                                }
                                break;
                            case 'int+':
                                $var = max(0, (int)$var);
                                break;
                            case 'color':
640
                                $col = [];
641
                                if ($var) {
642
                                    $var = preg_replace('/[^A-Fa-f0-9]*/', '', $var) ?? '';
643
                                    $useFulHex = strlen($var) > 3;
644
645
646
                                    $col[] = (int)hexdec($var[0]);
                                    $col[] = (int)hexdec($var[1]);
                                    $col[] = (int)hexdec($var[2]);
647
                                    if ($useFulHex) {
648
649
650
                                        $col[] = (int)hexdec($var[3]);
                                        $col[] = (int)hexdec($var[4]);
                                        $col[] = (int)hexdec($var[5]);
651
                                    }
652
                                    $var = substr('0' . dechex($col[0]), -1) . substr('0' . dechex($col[1]), -1) . substr('0' . dechex($col[2]), -1);
653
                                    if ($useFulHex) {
654
                                        $var .= substr('0' . dechex($col[3]), -1) . substr('0' . dechex($col[4]), -1) . substr('0' . dechex($col[5]), -1);
655
656
657
658
659
660
661
                                    }
                                    $var = '#' . strtoupper($var);
                                }
                                break;
                            case 'comment':
                                if ($var) {
                                    $var = '';
662
663
                                } else {
                                    $var = '#';
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
                                }
                                break;
                            case 'wrap':
                                if (isset($Wdata[$key])) {
                                    $var .= '|' . $Wdata[$key];
                                }
                                break;
                            case 'offset':
                                if (isset($Wdata[$key])) {
                                    $var = (int)$var . ',' . (int)$Wdata[$key];
                                    if (isset($W2data[$key])) {
                                        $var .= ',' . (int)$W2data[$key];
                                        if (isset($W3data[$key])) {
                                            $var .= ',' . (int)$W3data[$key];
                                            if (isset($W4data[$key])) {
                                                $var .= ',' . (int)$W4data[$key];
                                                if (isset($W5data[$key])) {
                                                    $var .= ',' . (int)$W5data[$key];
                                                }
                                            }
                                        }
                                    }
                                }
                                break;
                            case 'boolean':
                                if ($var) {
690
                                    $var = ($typeDat['paramstr'] ?? false) ?: 1;
691
692
693
                                }
                                break;
                        }
694
                        if ((string)($theConstants[$key]['value'] ?? '') !== (string)$var) {
695
696
                            // Put value in, if changed.
                            $this->ext_putValueInConf($key, $var);
697
                            $valuesHaveChanged = true;
698
699
700
701
702
                        }
                        // Remove the entry because it has been "used"
                        unset($check[$key]);
                    } else {
                        $this->ext_removeValueInConf($key);
703
                        $valuesHaveChanged = true;
704
705
706
707
708
709
                    }
                }
            }
        }
        // Remaining keys in $check indicates fields that are just clicked "on" to be edited.
        // Therefore we get the default value and puts that in the template as a start...
710
711
712
713
        foreach ($check ?? [] as $key => $var) {
            if (isset($theConstants[$key])) {
                $dValue = $theConstants[$key]['default_value'];
                $this->ext_putValueInConf($key, $dValue);
714
                $valuesHaveChanged = true;
715
716
            }
        }
717
        return $valuesHaveChanged;
718
719
720
    }

    /**
721
     * Is set by runThroughTemplates(), previously set via TemplateAnalyzerModuleFunctionController from the outside
722
     */
723
    public function getRootLine(): array
724
    {
725
        return $this->absoluteRootLine;
726
    }
727
}