Passed
Push — master ( 4dfe0c...5e249b )
by
unknown
13:51 queued 01:38
created

ExtendedTemplateService::ext_lnBreakPointWrap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\TypoScript;
17
18
use TYPO3\CMS\Backend\Routing\UriBuilder;
19
use TYPO3\CMS\Backend\Utility\BackendUtility;
20
use TYPO3\CMS\Core\Context\Context;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
25
use TYPO3\CMS\Core\Exception;
26
use TYPO3\CMS\Core\Imaging\Icon;
27
use TYPO3\CMS\Core\Imaging\IconFactory;
28
use TYPO3\CMS\Core\Localization\LanguageService;
29
use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser;
30
use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
31
use TYPO3\CMS\Core\Utility\ArrayUtility;
32
use TYPO3\CMS\Core\Utility\GeneralUtility;
33
use TYPO3\CMS\Core\Utility\MathUtility;
34
use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
35
36
/**
37
 * TSParser extension class to TemplateService
38
 * Contains functions for the TS module in TYPO3 backend
39
 *
40
 * @internal this is only used for the TYPO3 TypoScript Template module, which should not be used in Extensions
41
 */
42
class ExtendedTemplateService extends TemplateService
43
{
44
    /**
45
     * @var array
46
     */
47
    protected $categories = [
48
        'basic' => [],
49
        // Constants of superior importance for the template-layout. This is dimensions, imagefiles and enabling of various features. The most basic constants, which you would almost always want to configure.
50
        'menu' => [],
51
        // Menu setup. This includes fontfiles, sizes, background images. Depending on the menutype.
52
        'content' => [],
53
        // All constants related to the display of pagecontent elements
54
        'page' => [],
55
        // General configuration like metatags, link targets
56
        'advanced' => [],
57
        // Advanced functions, which are used very seldom.
58
        'all' => []
59
    ];
60
61
    /**
62
     * Tsconstanteditor
63
     *
64
     * @var int
65
     */
66
    public $ext_inBrace = 0;
67
68
    /**
69
     * Tsbrowser
70
     *
71
     * @var array
72
     */
73
    public $tsbrowser_searchKeys = [];
74
75
    /**
76
     * @var array
77
     */
78
    public $tsbrowser_depthKeys = [];
79
80
    /**
81
     * @var string
82
     */
83
    public $constantMode = '';
84
85
    /**
86
     * @var string
87
     */
88
    public $regexMode = '';
89
90
    /**
91
     * @var int
92
     */
93
    public $ext_lineNumberOffset = 0;
94
95
    /**
96
     * @var int
97
     */
98
    public $ext_expandAllNotes = 0;
99
100
    /**
101
     * @var int
102
     */
103
    public $ext_noPMicons = 0;
104
105
    /**
106
     * Ts analyzer
107
     *
108
     * @var array
109
     */
110
    public $templateTitles = [];
111
112
    /**
113
     * @var array|null
114
     */
115
    protected $lnToScript;
116
117
    /**
118
     * @var array
119
     */
120
    public $clearList_const_temp;
121
122
    /**
123
     * @var array
124
     */
125
    public $clearList_setup_temp;
126
127
    /**
128
     * @var string
129
     */
130
    public $bType = '';
131
132
    /**
133
     * @var bool
134
     */
135
    public $linkObjects = false;
136
137
    /**
138
     * @var bool
139
     */
140
    public $changed = false;
141
142
    /**
143
     * @var int[]
144
     */
145
    protected $objReg = [];
146
147
    /**
148
     * @var array
149
     */
150
    public $raw = [];
151
152
    /**
153
     * @var int
154
     */
155
    public $rawP = 0;
156
157
    /**
158
     * @var string
159
     */
160
    public $lastComment = '';
161
162
    /**
163
     * @var array
164
     */
165
    protected $inlineJavaScript = [];
166
    /**
167
     * @var \TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser
168
     */
169
    private $constantParser;
170
171
    /**
172
     * @param Context|null $context
173
     * @param \TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser $constantParser
174
     */
175
    public function __construct(Context $context = null, ConstantConfigurationParser $constantParser = null)
176
    {
177
        parent::__construct($context);
178
        $this->constantParser = $constantParser ?? GeneralUtility::makeInstance(ConstantConfigurationParser::class);
179
        // Disabled in backend context
180
        $this->tt_track = false;
181
        $this->verbose = false;
182
    }
183
184
    /**
185
     * Gets the inline JavaScript.
186
     *
187
     * @return array
188
     */
189
    public function getInlineJavaScript()
190
    {
191
        return $this->inlineJavaScript;
192
    }
193
194
    /**
195
     * Substitute constant
196
     *
197
     * @param string $all
198
     * @return string
199
     */
200
    public function substituteConstants($all)
201
    {
202
        return preg_replace_callback('/\\{\\$(.[^}]+)\\}/', [$this, 'substituteConstantsCallBack'], $all);
203
    }
204
205
    /**
206
     * Call back method for preg_replace_callback in substituteConstants
207
     *
208
     * @param array $matches Regular expression matches
209
     * @return string Replacement
210
     * @see substituteConstants()
211
     */
212
    public function substituteConstantsCallBack($matches)
213
    {
214
        $marker = substr(md5($matches[0]), 0, 6);
215
        switch ($this->constantMode) {
216
            case 'const':
217
                $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];
218
                break;
219
            case 'subst':
220
                $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];
221
                break;
222
            case 'untouched':
223
                $ret_val = $matches[0];
224
                break;
225
            default:
226
                $ret_val = isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? $this->flatSetup[$matches[1]] : $matches[0];
227
        }
228
        return $ret_val;
229
    }
230
231
    /**
232
     * Substitute markers added in substituteConstantsCallBack()
233
     * with ##6chars_B##value1##6chars_M##value2##6chars_E##
234
     *
235
     * @param string $all
236
     * @return string
237
     */
238
    public function substituteCMarkers($all)
239
    {
240
        switch ($this->constantMode) {
241
            case 'const':
242
            case 'subst':
243
                $all = preg_replace(
244
                    '/##[a-z0-9]{6}_B##(.*?)##[a-z0-9]{6}_M##(.*?)##[a-z0-9]{6}_E##/',
245
                    '<strong class="text-success" data-bs-toggle="tooltip" data-bs-placement="top" data-title="$1" title="$1">$2</strong>',
246
                    $all
247
                );
248
                break;
249
            default:
250
        }
251
        return $all;
252
    }
253
254
    /**
255
     * Parse constants with respect to the constant-editor in this module.
256
     * In particular comments in the code are registered and the edit_divider is taken into account.
257
     *
258
     * @return array
259
     */
260
    public function generateConfig_constants()
261
    {
262
        // Parse constants
263
        $constants = GeneralUtility::makeInstance(TypoScriptParser::class);
264
        // Register comments!
265
        $constants->regComments = true;
266
        /** @var ConditionMatcher $matchObj */
267
        $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
268
        // Matches ALL conditions in TypoScript
269
        $matchObj->setSimulateMatchResult(true);
270
        $c = 0;
271
        $cc = count($this->constants);
272
        $defaultConstants = [];
273
        foreach ($this->constants as $str) {
274
            $c++;
275
            if ($c == $cc) {
276
                $defaultConstants = ArrayUtility::flatten($constants->setup, '', true);
277
            }
278
            $constants->parse($str, $matchObj);
279
        }
280
        $this->setup['constants'] = $constants->setup;
281
        $flatSetup = ArrayUtility::flatten($constants->setup, '', true);
282
        return $this->constantParser->parseComments(
283
            $flatSetup,
284
            $defaultConstants
285
        );
286
    }
287
288
    /**
289
     * @param array $theSetup
290
     * @param string $theKey
291
     * @return array
292
     */
293
    public function ext_getSetup($theSetup, $theKey)
294
    {
295
        $parts = explode('.', $theKey, 2);
296
        if ((string)$parts[0] !== '' && is_array($theSetup[$parts[0] . '.'])) {
297
            if (trim($parts[1]) !== '') {
298
                return $this->ext_getSetup($theSetup[$parts[0] . '.'], trim($parts[1]));
299
            }
300
            return [$theSetup[$parts[0] . '.'], $theSetup[$parts[0]]];
301
        }
302
        if (trim($theKey) !== '') {
303
            return [[], $theSetup[$theKey]];
304
        }
305
        return [$theSetup, ''];
306
    }
307
308
    /**
309
     * Get object tree
310
     *
311
     * @param array $arr
312
     * @param string $depth_in
313
     * @param string $depthData
314
     * @param string $parentType (unused)
315
     * @param string $parentValue (unused)
316
     * @param string $alphaSort sorts the array keys / tree by alphabet when set to 1
317
     * @return string
318
     */
319
    public function ext_getObjTree($arr, $depth_in, $depthData, $parentType = '', $parentValue = '', $alphaSort = '0')
320
    {
321
        $HTML = '';
322
        if ($alphaSort == '1') {
323
            ksort($arr);
324
        }
325
        $keyArr_num = [];
326
        $keyArr_alpha = [];
327
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
328
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
329
        foreach ($arr as $key => $value) {
330
            // Don't do anything with comments / linenumber registrations...
331
            if (substr($key, -2) !== '..') {
332
                $key = preg_replace('/\\.$/', '', $key) ?? '';
333
                if (substr($key, -1) !== '.') {
334
                    if (MathUtility::canBeInterpretedAsInteger($key)) {
335
                        $keyArr_num[$key] = $arr[$key];
336
                    } else {
337
                        $keyArr_alpha[$key] = $arr[$key];
338
                    }
339
                }
340
            }
341
        }
342
        ksort($keyArr_num);
343
        $keyArr = $keyArr_num + $keyArr_alpha;
344
        if ($depth_in) {
345
            $depth_in = $depth_in . '.';
346
        }
347
        foreach ($keyArr as $key => $value) {
348
            $depth = $depth_in . $key;
349
            // This excludes all constants starting with '_' from being shown.
350
            if ($this->bType !== 'const' || $depth[0] !== '_') {
351
                $goto = substr(md5($depth), 0, 6);
352
                $deeper = is_array($arr[$key . '.']) && ($this->tsbrowser_depthKeys[$depth] || $this->ext_expandAllNotes);
353
                $PM = is_array($arr[$key . '.']) && !$this->ext_noPMicons ? ($deeper ? 'minus' : 'plus') : 'join';
354
                $HTML .= $depthData . '<li><span class="list-tree-group">';
355
                if ($PM !== 'join') {
356
                    $urlParameters = [
357
                        'id' => (int)GeneralUtility::_GP('id'),
358
                        'tsbr[' . $depth . ']' => $deeper ? 0 : 1
359
                    ];
360
                    $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters) . '#' . $goto;
361
                    $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>';
362
                }
363
                $label = $key;
364
                // Read only...
365
                if (($depth === 'types') && $this->bType === 'setup') {
366
                    $label = '<span style="color: #666666;">' . $label . '</span>';
367
                } else {
368
                    if ($this->linkObjects) {
369
                        $urlParameters = [
370
                            'id' => (int)GeneralUtility::_GP('id'),
371
                            'sObj' => $depth
372
                        ];
373
                        $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
374
                        if ($this->bType !== 'const') {
375
                            $ln = is_array($arr[$key . '.ln..']) ? 'Defined in: ' . $this->lineNumberToScript($arr[$key . '.ln..']) : 'N/A';
376
                        } else {
377
                            $ln = '';
378
                        }
379
                        if ($this->tsbrowser_searchKeys[$depth] & 4) {
380
                            // The key has matched the search string
381
                            $label = '<strong class="text-danger">' . $label . '</strong>';
382
                        }
383
                        $label = '<a href="' . htmlspecialchars($aHref) . '" title="' . htmlspecialchars($depth_in . $key . ' ' . $ln) . '">' . $label . '</a>';
384
                    }
385
                }
386
                $HTML .= '<span class="list-tree-label" title="' . htmlspecialchars($depth_in . $key) . '">[' . $label . ']</span>';
387
                if (isset($arr[$key])) {
388
                    $theValue = $arr[$key];
389
                    // The value has matched the search string
390
                    if ($this->tsbrowser_searchKeys[$depth] & 2) {
391
                        $HTML .= ' = <span class="list-tree-value text-danger">' . htmlspecialchars($theValue) . '</span>';
392
                    } else {
393
                        $HTML .= ' = <span class="list-tree-value">' . htmlspecialchars($theValue) . '</span>';
394
                    }
395
                    if ($this->ext_regComments && isset($arr[$key . '..'])) {
396
                        $comment = (string)$arr[$key . '..'];
397
                        // Skip INCLUDE_TYPOSCRIPT comments, they are almost useless
398
                        if (!preg_match('/### <INCLUDE_TYPOSCRIPT:.*/', $comment)) {
399
                            // Remove linebreaks, replace with ' '
400
                            $comment = preg_replace('/[\\r\\n]/', ' ', $comment) ?? '';
401
                            // Remove # and * if more than twice in a row
402
                            $comment = preg_replace('/[#\\*]{2,}/', '', $comment) ?? '';
403
                            // Replace leading # (just if it exists) and add it again. Result: Every comment should be prefixed by a '#'.
404
                            $comment = preg_replace('/^[#\\*\\s]+/', '# ', $comment) ?? '';
405
                            // Masking HTML Tags: Replace < with &lt; and > with &gt;
406
                            $comment = htmlspecialchars($comment);
407
                            $HTML .= ' <i class="text-muted">' . trim($comment) . '</i>';
408
                        }
409
                    }
410
                }
411
                $HTML .= '</span>';
412
                if ($deeper) {
413
                    $HTML .= $this->ext_getObjTree($arr[$key . '.'], $depth, $depthData, '', $arr[$key], $alphaSort);
414
                }
415
            }
416
        }
417
        if ($HTML !== '') {
418
            $HTML = '<ul class="list-tree text-monospace">' . $HTML . '</ul>';
419
        }
420
421
        return $HTML;
422
    }
423
424
    /**
425
     * Find the originating template name for an array of line numbers (TypoScript setup only!)
426
     * Given an array of linenumbers the method will try to find the corresponding template where this line originated
427
     * The linenumber indicates the *last* lineNumber that is part of the template
428
     *
429
     * lineNumbers are in sync with the calculated lineNumbers '.ln..' in TypoScriptParser
430
     *
431
     * @param array $lnArr Array with linenumbers (might have some extra symbols, for example for unsetting) to be processed
432
     * @return string Imploded array of line number and template title
433
     */
434
    public function lineNumberToScript(array $lnArr)
435
    {
436
        // On the first call, construct the lnToScript array.
437
        if (!is_array($this->lnToScript)) {
438
            $this->lnToScript = [];
439
440
            // aggregatedTotalLineCount
441
            $c = 0;
442
            foreach ($this->hierarchyInfo as $templateNumber => $info) {
443
                // hierarchyInfo has the number of lines in configLines, but unfortunately this value
444
                // was calculated *before* processing of any INCLUDE instructions
445
                // for some yet unknown reason we have to add an extra +2 offset
446
                $linecountAfterIncludeProcessing = substr_count($this->config[$templateNumber], LF) + 2;
447
                $c += $linecountAfterIncludeProcessing;
448
                $this->lnToScript[$c] = $info['title'];
449
            }
450
        }
451
452
        foreach ($lnArr as $k => $ln) {
453
            foreach ($this->lnToScript as $endLn => $title) {
454
                if ($endLn >= (int)$ln) {
455
                    $lnArr[$k] = '"' . $title . '", ' . $ln;
456
                    break;
457
                }
458
            }
459
        }
460
461
        return implode('; ', $lnArr);
462
    }
463
464
    /**
465
     * @param array $arr
466
     * @param string $depth_in
467
     * @param string $searchString
468
     * @param array $keyArray
469
     * @return array
470
     * @throws Exception
471
     */
472
    public function ext_getSearchKeys($arr, $depth_in, $searchString, $keyArray)
473
    {
474
        $keyArr = [];
475
        foreach ($arr as $key => $value) {
476
            $key = preg_replace('/\\.$/', '', $key) ?? '';
477
            if (substr($key, -1) !== '.') {
478
                $keyArr[$key] = 1;
479
            }
480
        }
481
        if ($depth_in) {
482
            $depth_in = $depth_in . '.';
483
        }
484
        $searchPattern = '';
485
        if ($this->regexMode) {
486
            $searchPattern = '/' . addcslashes($searchString, '/') . '/';
487
            $matchResult = @preg_match($searchPattern, '');
488
            if ($matchResult === false) {
489
                throw new Exception(sprintf('Error evaluating regular expression "%s".', $searchPattern), 1446559458);
490
            }
491
        }
492
        foreach ($keyArr as $key => $value) {
493
            $depth = $depth_in . $key;
494
            $deeper = is_array($arr[$key . '.']);
495
            if ($this->regexMode) {
496
                // The value has matched
497
                if (preg_match($searchPattern, $arr[$key])) {
498
                    $this->tsbrowser_searchKeys[$depth] += 2;
499
                }
500
                // The key has matched
501
                if (preg_match($searchPattern, $key)) {
502
                    $this->tsbrowser_searchKeys[$depth] += 4;
503
                }
504
                // Just open this subtree if the parent key has matched the search
505
                if (preg_match($searchPattern, $depth_in)) {
506
                    $this->tsbrowser_searchKeys[$depth] = 1;
507
                }
508
            } else {
509
                // The value has matched
510
                if (stripos($arr[$key], $searchString) !== false) {
511
                    $this->tsbrowser_searchKeys[$depth] += 2;
512
                }
513
                // The key has matches
514
                if (stripos($key, $searchString) !== false) {
515
                    $this->tsbrowser_searchKeys[$depth] += 4;
516
                }
517
                // Just open this subtree if the parent key has matched the search
518
                if (stripos($depth_in, $searchString) !== false) {
519
                    $this->tsbrowser_searchKeys[$depth] = 1;
520
                }
521
            }
522
            if ($deeper) {
523
                $cS = count($this->tsbrowser_searchKeys);
524
                $keyArray = $this->ext_getSearchKeys($arr[$key . '.'], $depth, $searchString, $keyArray);
525
                if ($cS != count($this->tsbrowser_searchKeys)) {
526
                    $keyArray[$depth] = 1;
527
                }
528
            }
529
        }
530
        return $keyArray;
531
    }
532
533
    /**
534
     * @param int $pid
535
     * @return int
536
     */
537
    public function ext_getRootlineNumber($pid)
538
    {
539
        if ($pid) {
540
            foreach ($this->getRootLine() as $key => $val) {
541
                if ((int)$val['uid'] === (int)$pid) {
542
                    return (int)$key;
543
                }
544
            }
545
        }
546
        return -1;
547
    }
548
549
    /**
550
     * @param array $arr
551
     * @param string $depthData
552
     * @param array $keyArray
553
     * @param int $first
554
     * @return array
555
     */
556
    public function ext_getTemplateHierarchyArr($arr, $depthData, $keyArray, $first = 0)
557
    {
558
        $keyArr = [];
559
        foreach ($arr as $key => $value) {
560
            $key = preg_replace('/\\.$/', '', $key) ?? '';
561
            if (substr($key, -1) !== '.') {
562
                $keyArr[$key] = 1;
563
            }
564
        }
565
        $a = 0;
566
        $c = count($keyArr);
567
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
568
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
569
        /** @var IconFactory $iconFactory */
570
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
571
        foreach ($keyArr as $key => $value) {
572
            $HTML = '';
573
            $a++;
574
            $deeper = is_array($arr[$key . '.']);
575
            $row = $arr[$key];
576
            $LN = $a == $c ? 'blank' : 'line';
577
            $BTM = $a == $c ? 'top' : '';
578
            $HTML .= $depthData;
579
            $alttext = '[' . $row['templateID'] . ']';
580
            $alttext .= $row['pid'] ? ' - ' . BackendUtility::getRecordPath($row['pid'], '1=1', 20) : '';
0 ignored issues
show
Bug introduced by
Are you sure TYPO3\CMS\Backend\Utilit...$row['pid'], '1=1', 20) of type array<integer,string>|string can be used in concatenation? ( Ignorable by Annotation )

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

580
            $alttext .= $row['pid'] ? ' - ' . /** @scrutinizer ignore-type */ BackendUtility::getRecordPath($row['pid'], '1=1', 20) : '';
Loading history...
581
            $icon = strpos($row['templateID'], 'sys') === 0
582
                ? '<span title="' . htmlspecialchars($alttext) . '">' . $iconFactory->getIconForRecord('sys_template', $row, Icon::SIZE_SMALL)->render() . '</span>'
583
                : '<span title="' . htmlspecialchars($alttext) . '">' . $iconFactory->getIcon('mimetypes-x-content-template-static', Icon::SIZE_SMALL)->render() . '</span>';
584
            if (in_array($row['templateID'], $this->clearList_const) || in_array($row['templateID'], $this->clearList_setup)) {
585
                $urlParameters = [
586
                    'id' => (int)GeneralUtility::_GP('id'),
587
                    'template' => $row['templateID']
588
                ];
589
                $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
590
                $A_B = '<a href="' . htmlspecialchars($aHref) . '">';
591
                $A_E = '</a>';
592
                if (GeneralUtility::_GP('template') == $row['templateID']) {
593
                    $A_B = '<strong>' . $A_B;
594
                    $A_E .= '</strong>';
595
                }
596
            } else {
597
                $A_B = '';
598
                $A_E = '';
599
            }
600
            $HTML .= ($first ? '' : '<span class="treeline-icon treeline-icon-join' . $BTM . '"></span>') . $icon . ' ' . $A_B
601
                . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $GLOBALS['BE_USER']->uc['titleLen']))
602
                . $A_E . '&nbsp;&nbsp;';
603
            $RL = $this->ext_getRootlineNumber($row['pid']);
604
            $statusCheckedIcon = $iconFactory->getIcon('status-status-checked', Icon::SIZE_SMALL)->render();
605
            $keyArray[] = '<tr>
606
							<td class="nowrap">' . $HTML . '</td>
607
							<td align="center">' . ($row['root'] ? $statusCheckedIcon : '') . '</td>
608
							<td align="center">' . ($row['clConf'] ? $statusCheckedIcon : '') . '</td>
609
							<td align="center">' . ($row['clConst'] ? $statusCheckedIcon : '') . '</td>
610
							<td align="center">' . ($row['pid'] ?: '') . '</td>
611
							<td align="center">' . ($RL >= 0 ? $RL : '') . '</td>
612
						</tr>';
613
            if ($deeper) {
614
                $keyArray = $this->ext_getTemplateHierarchyArr($arr[$key . '.'], $depthData . ($first ? '' : '<span class="treeline-icon treeline-icon-' . $LN . '"></span>'), $keyArray);
615
            }
616
        }
617
        return $keyArray;
618
    }
619
620
    /**
621
     * Processes the flat array from TemplateService->hierarchyInfo
622
     * and turns it into a hierarchical array to show dependencies (used by TemplateAnalyzer)
623
     *
624
     * @param array $depthDataArr (empty array on external call)
625
     * @param int $pointer Element number (1! to count()) of $this->hierarchyInfo that should be processed.
626
     * @return array Processed hierachyInfo.
627
     */
628
    public function ext_process_hierarchyInfo(array $depthDataArr, &$pointer)
629
    {
630
        $parent = $this->hierarchyInfo[$pointer - 1]['templateParent'];
631
        while ($pointer > 0 && $this->hierarchyInfo[$pointer - 1]['templateParent'] == $parent) {
632
            $pointer--;
633
            $row = $this->hierarchyInfo[$pointer];
634
            $depthDataArr[$row['templateID']] = $row;
635
            unset($this->clearList_setup_temp[$row['templateID']]);
636
            unset($this->clearList_const_temp[$row['templateID']]);
637
            $this->templateTitles[$row['templateID']] = $row['title'];
638
            if ($row['templateID'] == $this->hierarchyInfo[$pointer - 1]['templateParent']) {
639
                $depthDataArr[$row['templateID'] . '.'] = $this->ext_process_hierarchyInfo([], $pointer);
640
            }
641
        }
642
        return $depthDataArr;
643
    }
644
645
    /**
646
     * Get formatted HTML output for TypoScript either with Syntaxhighlighting or in plain mode
647
     *
648
     * @param array $config Array with simple strings of typoscript code.
649
     * @param bool $lineNumbers Prepend linNumbers to each line.
650
     * @param bool $comments Enable including comments in output.
651
     * @param bool $crop Enable cropping of long lines.
652
     * @param bool $syntaxHL Enrich output with syntaxhighlighting.
653
     * @return string
654
     */
655
    public function ext_outputTS(
656
        array $config,
657
        $lineNumbers = false,
658
        $comments = false,
659
        $crop = false,
660
        $syntaxHL = false
661
    ) {
662
        $all = '';
663
        foreach ($config as $str) {
664
            $all .= '[GLOBAL]' . LF . $str;
665
        }
666
        if ($syntaxHL) {
667
            $tsparser = GeneralUtility::makeInstance(TypoScriptParser::class);
668
            $tsparser->lineNumberOffset = $this->ext_lineNumberOffset + 1;
669
            return $tsparser->doSyntaxHighlight($all, $lineNumbers ? [$this->ext_lineNumberOffset + 1] : '');
670
        }
671
        return $this->ext_formatTS($all, $lineNumbers, $comments, $crop);
672
    }
673
674
    /**
675
     * Returns a new string of max. $chars length
676
     * If the string is longer, it will be truncated and prepended with '...'
677
     * $chars must be an integer of at least 4
678
     *
679
     * @param string $string
680
     * @param int $chars
681
     * @return string
682
     */
683
    public function ext_fixed_lgd($string, $chars)
684
    {
685
        if ($chars >= 4) {
686
            if (strlen($string) > $chars) {
687
                if (strlen($string) > 24 && preg_match('/^##[a-z0-9]{6}_B##$/', substr($string, 0, 12))) {
688
                    $string = GeneralUtility::fixed_lgd_cs(substr($string, 12, -12), $chars - 3);
689
                    $marker = substr(md5($string), 0, 6);
690
                    return '##' . $marker . '_B##' . $string . '##' . $marker . '_E##';
691
                }
692
                return GeneralUtility::fixed_lgd_cs($string, $chars - 3);
693
            }
694
        }
695
        return $string;
696
    }
697
698
    /**
699
     * @param string $input
700
     * @param bool $ln
701
     * @param bool $comments
702
     * @param bool $crop
703
     * @return string
704
     */
705
    public function ext_formatTS($input, $ln, $comments = true, $crop = false)
706
    {
707
        $cArr = explode(LF, $input);
708
        $n = ceil(log10(count($cArr) + $this->ext_lineNumberOffset));
709
        $lineNum = '';
710
        foreach ($cArr as $k => $v) {
711
            $lln = $k + $this->ext_lineNumberOffset + 1;
712
            if ($ln) {
713
                $lineNum = str_replace(' ', '&nbsp;', sprintf('% ' . $n . 'd', $lln)) . ':   ';
714
            }
715
            $v = htmlspecialchars($v);
716
            if ($crop) {
717
                $v = $this->ext_fixed_lgd($v, $ln ? 71 : 77);
718
            }
719
            $cArr[$k] = $lineNum . str_replace(' ', '&nbsp;', $v);
720
            $firstChar = substr(trim($v), 0, 1);
721
            if ($firstChar === '[') {
722
                $cArr[$k] = '<strong class="text-success">' . $cArr[$k] . '</strong>';
723
            } elseif ($firstChar === '/' || $firstChar === '#') {
724
                if ($comments) {
725
                    $cArr[$k] = '<span class="text-muted">' . $cArr[$k] . '</span>';
726
                } else {
727
                    unset($cArr[$k]);
728
                }
729
            }
730
        }
731
        $output = implode('<br />', $cArr) . '<br />';
732
        return $output;
733
    }
734
735
    /**
736
     * Get a single sys_template record attached to a single page.
737
     * If multiple template records are on this page, the first (order by sorting)
738
     * record will be returned, unless a specific template uid is specified via $templateUid
739
     *
740
     * @param int $pid The pid to select sys_template records from
741
     * @param int $templateUid Optional template uid
742
     * @return array|null Returns the template record or null if none was found
743
     */
744
    public function ext_getFirstTemplate($pid, $templateUid = 0)
745
    {
746
        if (empty($pid)) {
747
            return null;
748
        }
749
750
        // Query is taken from the runThroughTemplates($theRootLine) function in the parent class.
751
        $queryBuilder = $this->getTemplateQueryBuilder($pid)
752
            ->setMaxResults(1);
753
        if ($templateUid) {
754
            $queryBuilder->andWhere(
755
                $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($templateUid, \PDO::PARAM_INT))
756
            );
757
        }
758
        $row = $queryBuilder->execute()->fetch();
759
        BackendUtility::workspaceOL('sys_template', $row);
760
761
        return $row;
762
    }
763
764
    /**
765
     * Get an array of all template records on a page.
766
     *
767
     * @param int $pid Pid to fetch sys_template records for
768
     * @return array[] Array of template records
769
     */
770
    public function ext_getAllTemplates($pid): array
771
    {
772
        if (empty($pid)) {
773
            return [];
774
        }
775
        $result = $this->getTemplateQueryBuilder($pid)->execute();
776
        $outRes = [];
777
        while ($row = $result->fetch()) {
778
            BackendUtility::workspaceOL('sys_template', $row);
779
            if (is_array($row)) {
780
                $outRes[] = $row;
781
            }
782
        }
783
        return $outRes;
784
    }
785
786
    /**
787
     * Internal helper method to prepare the query builder for
788
     * getting sys_template records from a given pid
789
     *
790
     * @param int $pid The pid to select sys_template records from
791
     * @return QueryBuilder Returns a QueryBuilder
792
     */
793
    protected function getTemplateQueryBuilder(int $pid): QueryBuilder
794
    {
795
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
796
            ->getQueryBuilderForTable('sys_template');
797
        $queryBuilder->getRestrictions()
798
            ->removeAll()
799
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
800
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $GLOBALS['BE_USER']->workspace));
801
802
        $queryBuilder->select('*')
803
            ->from('sys_template')
804
            ->where(
805
                $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT))
806
            );
807
        if (!empty($GLOBALS['TCA']['sys_template']['ctrl']['sortby'])) {
808
            $queryBuilder->orderBy($GLOBALS['TCA']['sys_template']['ctrl']['sortby']);
809
        }
810
811
        return $queryBuilder;
812
    }
813
814
    /**
815
     * @param array $editConstArray
816
     */
817
    public function ext_categorizeEditableConstants($editConstArray)
818
    {
819
        // Runs through the available constants and fills the $this->categories array with pointers and priority-info
820
        foreach ($editConstArray as $constName => $constData) {
821
            if (!$constData['type']) {
822
                $constData['type'] = 'string';
823
            }
824
            $cats = explode(',', $constData['cat']);
825
            // if = only one category, while allows for many. We have agreed on only one category is the most basic way...
826
            foreach ($cats as $theCat) {
827
                $theCat = trim($theCat);
828
                if ($theCat) {
829
                    $this->categories[$theCat][$constName] = $constData['subcat'];
830
                }
831
            }
832
        }
833
    }
834
835
    /**
836
     * @return array
837
     */
838
    public function ext_getCategoryLabelArray()
839
    {
840
        // Returns array used for labels in the menu.
841
        $retArr = [];
842
        foreach ($this->categories as $k => $v) {
843
            if (!empty($v)) {
844
                $retArr[$k] = strtoupper($k) . ' (' . count($v) . ')';
845
            }
846
        }
847
        return $retArr;
848
    }
849
850
    /**
851
     * @param string $type
852
     * @return array
853
     */
854
    public function ext_getTypeData($type)
855
    {
856
        $retArr = [];
857
        $type = trim($type);
858
        if (!$type) {
859
            $retArr['type'] = 'string';
860
        } else {
861
            $m = strcspn($type, ' [');
862
            $retArr['type'] = strtolower(substr($type, 0, $m));
863
            $types = ['int' => 1, 'options' => 1, 'file' => 1, 'boolean' => 1, 'offset' => 1, 'user' => 1];
864
            if (isset($types[$retArr['type']])) {
865
                $p = trim(substr($type, $m));
866
                $reg = [];
867
                preg_match('/\\[(.*)\\]/', $p, $reg);
868
                $p = trim($reg[1]);
869
                if ($p) {
870
                    $retArr['paramstr'] = $p;
871
                    switch ($retArr['type']) {
872
                        case 'int':
873
                            if ($retArr['paramstr'][0] === '-') {
874
                                $retArr['params'] = GeneralUtility::intExplode('-', substr($retArr['paramstr'], 1));
875
                                $retArr['params'][0] = (int)('-' . $retArr['params'][0]);
876
                            } else {
877
                                $retArr['params'] = GeneralUtility::intExplode('-', $retArr['paramstr']);
878
                            }
879
                            $retArr['min'] = $retArr['params'][0];
880
                            $retArr['max'] = $retArr['params'][1];
881
                            $retArr['paramstr'] = $retArr['params'][0] . ' - ' . $retArr['params'][1];
882
                            break;
883
                        case 'options':
884
                            $retArr['params'] = explode(',', $retArr['paramstr']);
885
                            break;
886
                    }
887
                }
888
            }
889
        }
890
        return $retArr;
891
    }
892
893
    /**
894
     * @param array $params
895
     * @return array
896
     */
897
    public function ext_fNandV($params)
898
    {
899
        $fN = 'data[' . $params['name'] . ']';
900
        $idName = str_replace('.', '-', $params['name']);
901
        $fV = $params['value'];
902
        // Values entered from the constantsedit cannot be constants!	230502; removed \{ and set {
903
        if (preg_match('/^{[\\$][a-zA-Z0-9\\.]*}$/', trim($fV), $reg)) {
904
            $fV = '';
905
        }
906
        $fV = htmlspecialchars($fV);
907
        return [$fN, $fV, $params, $idName];
908
    }
909
910
    /**
911
     * This functions returns the HTML-code that creates the editor-layout of the module.
912
     *
913
     * @param array $theConstants
914
     * @param string $category
915
     * @return array
916
     */
917
    public function ext_printFields($theConstants, $category): array
918
    {
919
        reset($theConstants);
920
        $groupedOutput = [];
921
        $subcat = '';
922
        if (is_array($this->categories[$category])) {
923
            asort($this->categories[$category]);
924
            /** @var IconFactory $iconFactory */
925
            $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
926
            $categoryLoop = 0;
927
            foreach ($this->categories[$category] as $name => $type) {
928
                $params = $theConstants[$name];
929
                if (is_array($params)) {
930
                    if ($subcat != $params['subcat_name']) {
931
                        $categoryLoop++;
932
                        $subcat = $params['subcat_name'];
933
                        $subcat_name = $params['subcat_name'] ? $this->constantParser->getSubCategories()[$params['subcat_name']][0] : 'Others';
934
                        $groupedOutput[$categoryLoop] = [
935
                            'label' => $subcat_name,
936
                            'fields' => []
937
                        ];
938
                    }
939
                    $label = $this->getLanguageService()->sL($params['label']);
940
                    $label_parts = explode(':', $label, 2);
941
                    if (count($label_parts) === 2) {
942
                        $head = trim($label_parts[0]);
943
                        $body = trim($label_parts[1]);
944
                    } else {
945
                        $head = trim($label_parts[0]);
946
                        $body = '';
947
                    }
948
                    $typeDat = $this->ext_getTypeData($params['type']);
949
                    $p_field = '';
950
                    $raname = substr(md5($params['name']), 0, 10);
951
                    $aname = '\'' . $raname . '\'';
952
                    [$fN, $fV, $params, $idName] = $this->ext_fNandV($params);
953
                    $idName = htmlspecialchars($idName);
954
                    $hint = '';
955
                    switch ($typeDat['type']) {
956
                        case 'int':
957
                        case 'int+':
958
                            $additionalAttributes = '';
959
                            if ($typeDat['paramstr']) {
960
                                $hint = ' Range: ' . $typeDat['paramstr'];
961
                            } elseif ($typeDat['type'] === 'int+') {
962
                                $hint = ' Range: 0 - ';
963
                                $typeDat['min'] = 0;
964
                            } else {
965
                                $hint = ' (Integer)';
966
                            }
967
968
                            if (isset($typeDat['min'])) {
969
                                $additionalAttributes .= ' min="' . (int)$typeDat['min'] . '" ';
970
                            }
971
                            if (isset($typeDat['max'])) {
972
                                $additionalAttributes .= ' max="' . (int)$typeDat['max'] . '" ';
973
                            }
974
975
                            $p_field =
976
                                '<input class="form-control" id="' . $idName . '" type="number"'
977
                                . ' name="' . $fN . '" value="' . $fV . '" onChange="uFormUrl(' . $aname . ')"' . $additionalAttributes . ' />';
978
                            break;
979
                        case 'color':
980
                            $p_field = '
981
                                <input class="form-control formengine-colorpickerelement t3js-color-picker" type="text" id="input-' . $idName . '" rel="' . $idName .
982
                                '" name="' . $fN . '" value="' . $fV . '" onChange="uFormUrl(' . $aname . ')" />';
983
984
                            if (empty($this->inlineJavaScript[$typeDat['type']])) {
985
                                $this->inlineJavaScript[$typeDat['type']] = 'require([\'TYPO3/CMS/Backend/ColorPicker\'], function(ColorPicker){ColorPicker.initialize()});';
986
                            }
987
                            break;
988
                        case 'wrap':
989
                            $wArr = explode('|', $fV);
990
                            $p_field = '<div class="input-group">
991
                                            <input class="form-control form-control-adapt" type="text" id="' . $idName . '" name="' . $fN . '" value="' . $wArr[0] . '" onChange="uFormUrl(' . $aname . ')" />
992
                                            <span class="input-group-addon input-group-icon">|</span>
993
                                            <input class="form-control form-control-adapt" type="text" name="W' . $fN . '" value="' . $wArr[1] . '" onChange="uFormUrl(' . $aname . ')" />
994
                                         </div>';
995
                            break;
996
                        case 'offset':
997
                            $wArr = explode(',', $fV);
998
                            $labels = GeneralUtility::trimExplode(',', $typeDat['paramstr']);
999
                            $p_field = '<span class="input-group-addon input-group-icon">' . ($labels[0] ?: 'x') . '</span><input type="text" class="form-control form-control-adapt" name="' . $fN . '" value="' . $wArr[0] . '" onChange="uFormUrl(' . $aname . ')" />';
1000
                            $p_field .= '<span class="input-group-addon input-group-icon">' . ($labels[1] ?: 'y') . '</span><input type="text" name="W' . $fN . '" value="' . $wArr[1] . '" class="form-control form-control-adapt" onChange="uFormUrl(' . $aname . ')" />';
1001
                            $labelsCount = count($labels);
1002
                            for ($aa = 2; $aa < $labelsCount; $aa++) {
1003
                                if ($labels[$aa]) {
1004
                                    $p_field .= '<span class="input-group-addon input-group-icon">' . $labels[$aa] . '</span><input type="text" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '" class="form-control form-control-adapt" onChange="uFormUrl(' . $aname . ')" />';
1005
                                } else {
1006
                                    $p_field .= '<input type="hidden" name="W' . $aa . $fN . '" value="' . $wArr[$aa] . '" />';
1007
                                }
1008
                            }
1009
                            $p_field = '<div class="input-group">' . $p_field . '</div>';
1010
                            break;
1011
                        case 'options':
1012
                            if (is_array($typeDat['params'])) {
1013
                                $p_field = '';
1014
                                foreach ($typeDat['params'] as $val) {
1015
                                    $vParts = explode('=', $val, 2);
1016
                                    $label = $vParts[0];
1017
                                    $val = $vParts[1] ?? $vParts[0];
1018
                                    // option tag:
1019
                                    $sel = '';
1020
                                    if ($val === $params['value']) {
1021
                                        $sel = ' selected';
1022
                                    }
1023
                                    $p_field .= '<option value="' . htmlspecialchars($val) . '"' . $sel . '>' . $this->getLanguageService()->sL($label) . '</option>';
1024
                                }
1025
                                $p_field = '<select class="form-select" id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1026
                            }
1027
                            break;
1028
                        case 'boolean':
1029
                            $sel = $fV ? 'checked' : '';
1030
                            $p_field =
1031
                                '<input type="hidden" name="' . $fN . '" value="0" />'
1032
                                . '<label class="btn btn-default btn-checkbox">'
1033
                                . '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value="' . ($typeDat['paramstr'] ?: 1) . '" ' . $sel . ' onClick="uFormUrl(' . $aname . ')" />'
1034
                                . '<span class="t3-icon fa"></span>'
1035
                                . '</label>';
1036
                            break;
1037
                        case 'comment':
1038
                            $sel = $fV ? '' : 'checked';
1039
                            $p_field =
1040
                                '<input type="hidden" name="' . $fN . '" value="" />'
1041
                                . '<label class="btn btn-default btn-checkbox">'
1042
                                . '<input id="' . $idName . '" type="checkbox" name="' . $fN . '" value="1" ' . $sel . ' onClick="uFormUrl(' . $aname . ')" />'
1043
                                . '<span class="t3-icon fa"></span>'
1044
                                . '</label>';
1045
                            break;
1046
                        case 'file':
1047
                            // extensionlist
1048
                            $extList = $typeDat['paramstr'];
1049
                            if ($extList === 'IMAGE_EXT') {
1050
                                $extList = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
1051
                            }
1052
                            $p_field = '<option value="">(' . $extList . ')</option>';
1053
                            if (trim($params['value'])) {
1054
                                $val = $params['value'];
1055
                                $p_field .= '<option value=""></option>';
1056
                                $p_field .= '<option value="' . htmlspecialchars($val) . '" selected>' . $val . '</option>';
1057
                            }
1058
                            $p_field = '<select class="form-select" id="' . $idName . '" name="' . $fN . '" onChange="uFormUrl(' . $aname . ')">' . $p_field . '</select>';
1059
                            break;
1060
                        case 'user':
1061
                            $userFunction = $typeDat['paramstr'];
1062
                            $userFunctionParams = ['fieldName' => $fN, 'fieldValue' => $fV];
1063
                            $p_field = GeneralUtility::callUserFunction($userFunction, $userFunctionParams, $this);
1064
                            break;
1065
                        default:
1066
                            $p_field = '<input class="form-control" id="' . $idName . '" type="text" name="' . $fN . '" value="' . $fV . '"'
1067
                                . ' onChange="uFormUrl(' . $aname . ')" />';
1068
                    }
1069
                    // Define default names and IDs
1070
                    $userTyposcriptID = 'userTS-' . $idName;
1071
                    $defaultTyposcriptID = 'defaultTS-' . $idName;
1072
                    $userTyposcriptStyle = '';
1073
                    // Set the default styling options
1074
                    if (isset($this->objReg[$params['name']])) {
1075
                        $checkboxValue = 'checked';
1076
                        $defaultTyposcriptStyle = 'style="display:none;"';
1077
                    } else {
1078
                        $checkboxValue = '';
1079
                        $userTyposcriptStyle = 'style="display:none;"';
1080
                        $defaultTyposcriptStyle = '';
1081
                    }
1082
                    $deleteIconHTML =
1083
                        '<button type="button" class="btn btn-default t3js-toggle" data-bs-toggle="undo" rel="' . $idName . '">'
1084
                            . '<span title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.deleteTitle')) . '">'
1085
                                . $iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL)->render()
1086
                            . '</span>'
1087
                        . '</button>';
1088
                    $editIconHTML =
1089
                        '<button type="button" class="btn btn-default t3js-toggle" data-bs-toggle="edit"  rel="' . $idName . '">'
1090
                            . '<span title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.editTitle')) . '">'
1091
                                . $iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render()
1092
                            . '</span>'
1093
                        . '</button>';
1094
                    $constantCheckbox = '<input type="hidden" name="check[' . $params['name'] . ']" id="check-' . $idName . '" value="' . $checkboxValue . '"/>';
1095
                    // If there's no default value for the field, use a static label.
1096
                    if (!$params['default_value']) {
1097
                        $params['default_value'] = '[Empty]';
1098
                    }
1099
                    $constantDefaultRow =
1100
                        '<div class="input-group defaultTS" id="' . $defaultTyposcriptID . '" ' . $defaultTyposcriptStyle . '>'
1101
                            . '<span class="input-group-btn">' . $editIconHTML . '</span>'
1102
                            . '<input class="form-control" type="text" placeholder="' . htmlspecialchars($params['default_value']) . '" readonly>'
1103
                        . '</div>';
1104
                    $constantEditRow =
1105
                        '<div class="input-group userTS" id="' . $userTyposcriptID . '" ' . $userTyposcriptStyle . '>'
1106
                            . '<span class="input-group-btn">' . $deleteIconHTML . '</span>'
1107
                            . $p_field
1108
                        . '</div>';
1109
                    $constantData =
1110
                        $constantCheckbox
1111
                        . $constantEditRow
1112
                        . $constantDefaultRow;
1113
1114
                    $groupedOutput[$categoryLoop]['items'][] = [
1115
                        'identifier' => $raname,
1116
                        'label' => $head,
1117
                        'name' => $params['name'],
1118
                        'description' => $body,
1119
                        'hint' => $hint,
1120
                        'data' => $constantData
1121
                    ];
1122
                } else {
1123
                    debug('Error. Constant did not exist. Should not happen.');
1124
                }
1125
            }
1126
        }
1127
        return $groupedOutput;
1128
    }
1129
1130
    /***************************
1131
     *
1132
     * Processing input values
1133
     *
1134
     ***************************/
1135
    /**
1136
     * @param string $constants
1137
     */
1138
    public function ext_regObjectPositions($constants)
1139
    {
1140
        // This runs through the lines of the constants-field of the active template and registers the constants-names
1141
        // and line positions in an array, $this->objReg
1142
        $this->raw = explode(LF, $constants);
1143
        $this->rawP = 0;
1144
        // Resetting the objReg if the divider is found!!
1145
        $this->objReg = [];
1146
        $this->ext_regObjects('');
1147
    }
1148
1149
    /**
1150
     * @param string $pre
1151
     */
1152
    public function ext_regObjects($pre)
1153
    {
1154
        // Works with regObjectPositions. "expands" the names of the TypoScript objects
1155
        while (isset($this->raw[$this->rawP])) {
1156
            $line = ltrim($this->raw[$this->rawP]);
1157
            $this->rawP++;
1158
            if ($line) {
1159
                if ($line[0] === '[') {
1160
                } elseif (strcspn($line, '}#/') != 0) {
1161
                    $varL = strcspn($line, ' {=<');
1162
                    $var = substr($line, 0, $varL);
1163
                    $line = ltrim(substr($line, $varL));
1164
                    switch ($line[0]) {
1165
                        case '=':
1166
                            $this->objReg[$pre . $var] = $this->rawP - 1;
1167
                            break;
1168
                        case '{':
1169
                            $this->ext_inBrace++;
1170
                            $this->ext_regObjects($pre . $var . '.');
1171
                            break;
1172
                    }
1173
                    $this->lastComment = '';
1174
                } elseif ($line[0] === '}') {
1175
                    $this->lastComment = '';
1176
                    $this->ext_inBrace--;
1177
                    if ($this->ext_inBrace < 0) {
1178
                        $this->ext_inBrace = 0;
1179
                    } else {
1180
                        break;
1181
                    }
1182
                }
1183
            }
1184
        }
1185
    }
1186
1187
    /**
1188
     * @param string $key
1189
     * @param string $var
1190
     */
1191
    public function ext_putValueInConf($key, $var)
1192
    {
1193
        // Puts the value $var to the TypoScript value $key in the current lines of the templates.
1194
        // If the $key is not found in the template constants field, a new line is inserted in the bottom.
1195
        $theValue = ' ' . trim($var);
1196
        if (isset($this->objReg[$key])) {
1197
            $lineNum = $this->objReg[$key];
1198
            $parts = explode('=', $this->raw[$lineNum], 2);
1199
            if (count($parts) === 2) {
1200
                $parts[1] = $theValue;
1201
            }
1202
            $this->raw[$lineNum] = implode('=', $parts);
1203
        } else {
1204
            $this->raw[] = $key . ' =' . $theValue;
1205
        }
1206
        $this->changed = true;
1207
    }
1208
1209
    /**
1210
     * @param string $key
1211
     */
1212
    public function ext_removeValueInConf($key)
1213
    {
1214
        // Removes the value in the configuration
1215
        if (isset($this->objReg[$key])) {
1216
            $lineNum = $this->objReg[$key];
1217
            unset($this->raw[$lineNum]);
1218
        }
1219
        $this->changed = true;
1220
    }
1221
1222
    /**
1223
     * @param array $arr
1224
     * @param array $settings
1225
     * @return array
1226
     */
1227
    public function ext_depthKeys($arr, $settings)
1228
    {
1229
        $tsbrArray = [];
1230
        foreach ($arr as $theK => $theV) {
1231
            $theKeyParts = explode('.', $theK);
1232
            $depth = '';
1233
            $c = count($theKeyParts);
1234
            $a = 0;
1235
            foreach ($theKeyParts as $p) {
1236
                $a++;
1237
                $depth .= ($depth ? '.' : '') . $p;
1238
                $tsbrArray[$depth] = $c == $a ? $theV : 1;
1239
            }
1240
        }
1241
        // Modify settings
1242
        foreach ($tsbrArray as $theK => $theV) {
1243
            if ($theV) {
1244
                $settings[$theK] = 1;
1245
            } else {
1246
                unset($settings[$theK]);
1247
            }
1248
        }
1249
        return $settings;
1250
    }
1251
1252
    /**
1253
     * Process input
1254
     *
1255
     * @param array $http_post_vars
1256
     * @param array $http_post_files (not used anymore)
1257
     * @param array $theConstants
1258
     * @param array $tplRow Not used
1259
     */
1260
    public function ext_procesInput($http_post_vars, $http_post_files, $theConstants, $tplRow)
1261
    {
1262
        $data = $http_post_vars['data'];
1263
        $check = $http_post_vars['check'];
1264
        $Wdata = $http_post_vars['Wdata'];
1265
        $W2data = $http_post_vars['W2data'];
1266
        $W3data = $http_post_vars['W3data'];
1267
        $W4data = $http_post_vars['W4data'];
1268
        $W5data = $http_post_vars['W5data'];
1269
        if (is_array($data)) {
1270
            foreach ($data as $key => $var) {
1271
                if (isset($theConstants[$key])) {
1272
                    // If checkbox is set, update the value
1273
                    if (isset($check[$key])) {
1274
                        // Exploding with linebreak, just to make sure that no multiline input is given!
1275
                        [$var] = explode(LF, $var);
1276
                        $typeDat = $this->ext_getTypeData($theConstants[$key]['type']);
1277
                        switch ($typeDat['type']) {
1278
                            case 'int':
1279
                                if ($typeDat['paramstr']) {
1280
                                    $var = MathUtility::forceIntegerInRange((int)$var, $typeDat['params'][0], $typeDat['params'][1]);
1281
                                } else {
1282
                                    $var = (int)$var;
1283
                                }
1284
                                break;
1285
                            case 'int+':
1286
                                $var = max(0, (int)$var);
1287
                                break;
1288
                            case 'color':
1289
                                $col = [];
1290
                                if ($var) {
1291
                                    $var = preg_replace('/[^A-Fa-f0-9]*/', '', $var) ?? '';
1292
                                    $useFulHex = strlen($var) > 3;
1293
                                    $col[] = (int)hexdec($var[0]);
1294
                                    $col[] = (int)hexdec($var[1]);
1295
                                    $col[] = (int)hexdec($var[2]);
1296
                                    if ($useFulHex) {
1297
                                        $col[] = (int)hexdec($var[3]);
1298
                                        $col[] = (int)hexdec($var[4]);
1299
                                        $col[] = (int)hexdec($var[5]);
1300
                                    }
1301
                                    $var = substr('0' . dechex($col[0]), -1) . substr('0' . dechex($col[1]), -1) . substr('0' . dechex($col[2]), -1);
1302
                                    if ($useFulHex) {
1303
                                        $var .= substr('0' . dechex($col[3]), -1) . substr('0' . dechex($col[4]), -1) . substr('0' . dechex($col[5]), -1);
1304
                                    }
1305
                                    $var = '#' . strtoupper($var);
1306
                                }
1307
                                break;
1308
                            case 'comment':
1309
                                if ($var) {
1310
                                    $var = '';
1311
                                } else {
1312
                                    $var = '#';
1313
                                }
1314
                                break;
1315
                            case 'wrap':
1316
                                if (isset($Wdata[$key])) {
1317
                                    $var .= '|' . $Wdata[$key];
1318
                                }
1319
                                break;
1320
                            case 'offset':
1321
                                if (isset($Wdata[$key])) {
1322
                                    $var = (int)$var . ',' . (int)$Wdata[$key];
1323
                                    if (isset($W2data[$key])) {
1324
                                        $var .= ',' . (int)$W2data[$key];
1325
                                        if (isset($W3data[$key])) {
1326
                                            $var .= ',' . (int)$W3data[$key];
1327
                                            if (isset($W4data[$key])) {
1328
                                                $var .= ',' . (int)$W4data[$key];
1329
                                                if (isset($W5data[$key])) {
1330
                                                    $var .= ',' . (int)$W5data[$key];
1331
                                                }
1332
                                            }
1333
                                        }
1334
                                    }
1335
                                }
1336
                                break;
1337
                            case 'boolean':
1338
                                if ($var) {
1339
                                    $var = $typeDat['paramstr'] ?: 1;
1340
                                }
1341
                                break;
1342
                        }
1343
                        if ((string)$theConstants[$key]['value'] !== (string)$var) {
1344
                            // Put value in, if changed.
1345
                            $this->ext_putValueInConf($key, $var);
1346
                        }
1347
                        // Remove the entry because it has been "used"
1348
                        unset($check[$key]);
1349
                    } else {
1350
                        $this->ext_removeValueInConf($key);
1351
                    }
1352
                }
1353
            }
1354
        }
1355
        // Remaining keys in $check indicates fields that are just clicked "on" to be edited.
1356
        // Therefore we get the default value and puts that in the template as a start...
1357
        foreach ($check ?? [] as $key => $var) {
1358
            if (isset($theConstants[$key])) {
1359
                $dValue = $theConstants[$key]['default_value'];
1360
                $this->ext_putValueInConf($key, $dValue);
1361
            }
1362
        }
1363
    }
1364
1365
    /**
1366
     * @param int $id
1367
     * @param string $perms_clause
1368
     * @return array
1369
     */
1370
    public function ext_prevPageWithTemplate($id, $perms_clause)
1371
    {
1372
        $rootLine = BackendUtility::BEgetRootLine($id, $perms_clause ? ' AND ' . $perms_clause : '');
1373
        foreach ($rootLine as $p) {
1374
            if ($this->ext_getFirstTemplate($p['uid'])) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->ext_getFirstTemplate($p['uid']) targeting TYPO3\CMS\Core\TypoScrip...:ext_getFirstTemplate() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1375
                return $p;
1376
            }
1377
        }
1378
        return [];
1379
    }
1380
1381
    /**
1382
     * Is set by runThroughTemplates(), previously set via TemplateAnalyzerModuleFunctionController from the outside
1383
     *
1384
     * @return array
1385
     */
1386
    protected function getRootLine()
1387
    {
1388
        return is_array($this->absoluteRootLine) ? $this->absoluteRootLine : [];
0 ignored issues
show
introduced by
The condition is_array($this->absoluteRootLine) is always true.
Loading history...
1389
    }
1390
1391
    /**
1392
     * @return LanguageService
1393
     */
1394
    protected function getLanguageService()
1395
    {
1396
        return $GLOBALS['LANG'];
1397
    }
1398
}
1399