Passed
Push — master ( d6fa37...897295 )
by
unknown
18:32
created

QueryGenerator::getTreeList()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 41
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 41
rs 6.9666
c 0
b 0
f 0
cc 12
nc 12
nop 4

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Lowlevel\Database;
17
18
use Doctrine\DBAL\Exception as DBALException;
19
use TYPO3\CMS\Backend\Routing\UriBuilder;
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22
use TYPO3\CMS\Core\Database\Connection;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Database\Query\QueryHelper;
25
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
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\Messaging\FlashMessage;
30
use TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver;
31
use TYPO3\CMS\Core\Messaging\FlashMessageService;
32
use TYPO3\CMS\Core\Type\Bitmask\Permission;
33
use TYPO3\CMS\Core\Utility\CsvUtility;
34
use TYPO3\CMS\Core\Utility\DebugUtility;
35
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Core\Utility\HttpUtility;
38
use TYPO3\CMS\Core\Utility\MathUtility;
39
use TYPO3\CMS\Core\Utility\StringUtility;
40
41
/**
42
 * Class used in module tools/dbint (advanced search) and which may hold code specific for that module
43
 *
44
 * @internal This class is a specific implementation for the lowlevel extension and is not part of the TYPO3's Core API.
45
 */
46
class QueryGenerator
47
{
48
    /**
49
     * @var string
50
     */
51
    protected $storeList = 'search_query_smallparts,search_result_labels,labels_noprefix,show_deleted,queryConfig,queryTable,queryFields,queryLimit,queryOrder,queryOrderDesc,queryOrder2,queryOrder2Desc,queryGroup,search_query_makeQuery';
52
53
    /**
54
     * @var int
55
     */
56
    protected $noDownloadB = 0;
57
58
    /**
59
     * @var array
60
     */
61
    protected $hookArray = [];
62
63
    /**
64
     * @var string
65
     */
66
    protected $formName = '';
67
68
    /**
69
     * @var IconFactory
70
     */
71
    protected $iconFactory;
72
73
    /**
74
     * @var array
75
     */
76
    protected $tableArray = [];
77
78
    /**
79
     * @var array Settings, usually from the controller
80
     */
81
    protected $settings = [];
82
83
    /**
84
     * @var array information on the menu of this module
85
     */
86
    protected $menuItems = [];
87
88
    /**
89
     * @var string
90
     */
91
    protected $moduleName;
92
93
    /**
94
     * @var array
95
     */
96
    protected $lang = [
97
        'OR' => 'or',
98
        'AND' => 'and',
99
        'comparison' => [
100
            // Type = text	offset = 0
101
            '0_' => 'contains',
102
            '1_' => 'does not contain',
103
            '2_' => 'starts with',
104
            '3_' => 'does not start with',
105
            '4_' => 'ends with',
106
            '5_' => 'does not end with',
107
            '6_' => 'equals',
108
            '7_' => 'does not equal',
109
            // Type = number , offset = 32
110
            '32_' => 'equals',
111
            '33_' => 'does not equal',
112
            '34_' => 'is greater than',
113
            '35_' => 'is less than',
114
            '36_' => 'is between',
115
            '37_' => 'is not between',
116
            '38_' => 'is in list',
117
            '39_' => 'is not in list',
118
            '40_' => 'binary AND equals',
119
            '41_' => 'binary AND does not equal',
120
            '42_' => 'binary OR equals',
121
            '43_' => 'binary OR does not equal',
122
            // Type = multiple, relation, offset = 64
123
            '64_' => 'equals',
124
            '65_' => 'does not equal',
125
            '66_' => 'contains',
126
            '67_' => 'does not contain',
127
            '68_' => 'is in list',
128
            '69_' => 'is not in list',
129
            '70_' => 'binary AND equals',
130
            '71_' => 'binary AND does not equal',
131
            '72_' => 'binary OR equals',
132
            '73_' => 'binary OR does not equal',
133
            // Type = date,time  offset = 96
134
            '96_' => 'equals',
135
            '97_' => 'does not equal',
136
            '98_' => 'is greater than',
137
            '99_' => 'is less than',
138
            '100_' => 'is between',
139
            '101_' => 'is not between',
140
            '102_' => 'binary AND equals',
141
            '103_' => 'binary AND does not equal',
142
            '104_' => 'binary OR equals',
143
            '105_' => 'binary OR does not equal',
144
            // Type = boolean,  offset = 128
145
            '128_' => 'is True',
146
            '129_' => 'is False',
147
            // Type = binary , offset = 160
148
            '160_' => 'equals',
149
            '161_' => 'does not equal',
150
            '162_' => 'contains',
151
            '163_' => 'does not contain'
152
        ]
153
    ];
154
155
    /**
156
     * @var array
157
     */
158
    protected $compSQL = [
159
        // Type = text	offset = 0
160
        '0' => '#FIELD# LIKE \'%#VALUE#%\'',
161
        '1' => '#FIELD# NOT LIKE \'%#VALUE#%\'',
162
        '2' => '#FIELD# LIKE \'#VALUE#%\'',
163
        '3' => '#FIELD# NOT LIKE \'#VALUE#%\'',
164
        '4' => '#FIELD# LIKE \'%#VALUE#\'',
165
        '5' => '#FIELD# NOT LIKE \'%#VALUE#\'',
166
        '6' => '#FIELD# = \'#VALUE#\'',
167
        '7' => '#FIELD# != \'#VALUE#\'',
168
        // Type = number, offset = 32
169
        '32' => '#FIELD# = \'#VALUE#\'',
170
        '33' => '#FIELD# != \'#VALUE#\'',
171
        '34' => '#FIELD# > #VALUE#',
172
        '35' => '#FIELD# < #VALUE#',
173
        '36' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
174
        '37' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
175
        '38' => '#FIELD# IN (#VALUE#)',
176
        '39' => '#FIELD# NOT IN (#VALUE#)',
177
        '40' => '(#FIELD# & #VALUE#)=#VALUE#',
178
        '41' => '(#FIELD# & #VALUE#)!=#VALUE#',
179
        '42' => '(#FIELD# | #VALUE#)=#VALUE#',
180
        '43' => '(#FIELD# | #VALUE#)!=#VALUE#',
181
        // Type = multiple, relation, offset = 64
182
        '64' => '#FIELD# = \'#VALUE#\'',
183
        '65' => '#FIELD# != \'#VALUE#\'',
184
        '66' => '#FIELD# LIKE \'%#VALUE#%\' AND #FIELD# LIKE \'%#VALUE1#%\'',
185
        '67' => '(#FIELD# NOT LIKE \'%#VALUE#%\' OR #FIELD# NOT LIKE \'%#VALUE1#%\')',
186
        '68' => '#FIELD# IN (#VALUE#)',
187
        '69' => '#FIELD# NOT IN (#VALUE#)',
188
        '70' => '(#FIELD# & #VALUE#)=#VALUE#',
189
        '71' => '(#FIELD# & #VALUE#)!=#VALUE#',
190
        '72' => '(#FIELD# | #VALUE#)=#VALUE#',
191
        '73' => '(#FIELD# | #VALUE#)!=#VALUE#',
192
        // Type = date, offset = 32
193
        '96' => '#FIELD# = \'#VALUE#\'',
194
        '97' => '#FIELD# != \'#VALUE#\'',
195
        '98' => '#FIELD# > #VALUE#',
196
        '99' => '#FIELD# < #VALUE#',
197
        '100' => '#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#',
198
        '101' => 'NOT (#FIELD# >= #VALUE# AND #FIELD# <= #VALUE1#)',
199
        '102' => '(#FIELD# & #VALUE#)=#VALUE#',
200
        '103' => '(#FIELD# & #VALUE#)!=#VALUE#',
201
        '104' => '(#FIELD# | #VALUE#)=#VALUE#',
202
        '105' => '(#FIELD# | #VALUE#)!=#VALUE#',
203
        // Type = boolean, offset = 128
204
        '128' => '#FIELD# = \'1\'',
205
        '129' => '#FIELD# != \'1\'',
206
        // Type = binary = 160
207
        '160' => '#FIELD# = \'#VALUE#\'',
208
        '161' => '#FIELD# != \'#VALUE#\'',
209
        '162' => '(#FIELD# & #VALUE#)=#VALUE#',
210
        '163' => '(#FIELD# & #VALUE#)=0'
211
    ];
212
213
    /**
214
     * @var array
215
     */
216
    protected $comp_offsets = [
217
        'text' => 0,
218
        'number' => 1,
219
        'multiple' => 2,
220
        'relation' => 2,
221
        'date' => 3,
222
        'time' => 3,
223
        'boolean' => 4,
224
        'binary' => 5
225
    ];
226
227
    /**
228
     * Form data name prefix
229
     *
230
     * @var string
231
     */
232
    protected $name;
233
234
    /**
235
     * Table for the query
236
     *
237
     * @var string
238
     */
239
    protected $table;
240
241
    /**
242
     * Field list
243
     *
244
     * @var string
245
     */
246
    protected $fieldList;
247
248
    /**
249
     * Array of the fields possible
250
     *
251
     * @var array
252
     */
253
    protected $fields = [];
254
255
    /**
256
     * @var array
257
     */
258
    protected $extFieldLists = [];
259
260
    /**
261
     * The query config
262
     *
263
     * @var array
264
     */
265
    protected $queryConfig = [];
266
267
    /**
268
     * @var bool
269
     */
270
    protected $enablePrefix = false;
271
272
    /**
273
     * @var bool
274
     */
275
    protected $enableQueryParts = false;
276
277
    /**
278
     * @var string
279
     */
280
    protected $fieldName;
281
282
    /**
283
     * If the current user is an admin and $GLOBALS['TYPO3_CONF_VARS']['BE']['debug']
284
     * is set to true, the names of fields and tables are displayed.
285
     *
286
     * @var bool
287
     */
288
    protected $showFieldAndTableNames = false;
289
290
    public function __construct(array $settings, array $menuItems, string $moduleName)
291
    {
292
        $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf');
293
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
294
        $this->settings = $settings;
295
        $this->menuItems = $menuItems;
296
        $this->moduleName = $moduleName;
297
        $this->showFieldAndTableNames = ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] ?? false)
298
            && $this->getBackendUserAuthentication()->isAdmin();
299
    }
300
301
    /**
302
     * Get form
303
     *
304
     * @return string
305
     */
306
    public function form()
307
    {
308
        $markup = [];
309
        $markup[] = '<div class="form-group">';
310
        $markup[] = '<input placeholder="Search Word" class="form-control" type="search" name="SET[sword]" value="'
311
            . htmlspecialchars($this->settings['sword'] ?? '') . '">';
312
        $markup[] = '</div>';
313
        $markup[] = '<div class="form-group">';
314
        $markup[] = '<input class="btn btn-default" type="submit" name="submit" value="Search All Records">';
315
        $markup[] = '</div>';
316
        return implode(LF, $markup);
317
    }
318
319
    /**
320
     * Query marker
321
     *
322
     * @return string
323
     */
324
    public function queryMaker()
325
    {
326
        $output = '';
327
        $this->hookArray = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
328
        $msg = $this->procesStoreControl();
329
        $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
330
        if (!($userTsConfig['mod.']['dbint.']['disableStoreControl'] ?? false)) {
331
            $output .= '<h2>Load/Save Query</h2>';
332
            $output .= '<div>' . $this->makeStoreControl() . '</div>';
333
            $output .= $msg;
334
        }
335
        // Query Maker:
336
        $this->init('queryConfig', $this->settings['queryTable'] ?? '', '', $this->settings);
337
        if ($this->formName) {
338
            $this->setFormName($this->formName);
339
        }
340
        $tmpCode = $this->makeSelectorTable($this->settings);
341
        $output .= '<div id="query"></div><h2>Make query</h2><div>' . $tmpCode . '</div>';
342
        $mQ = $this->settings['search_query_makeQuery'];
343
        // Make form elements:
344
        if ($this->table && is_array($GLOBALS['TCA'][$this->table])) {
345
            if ($mQ) {
346
                // Show query
347
                $this->enablePrefix = true;
348
                $queryString = $this->getQuery($this->queryConfig);
349
                $selectQueryString = $this->getSelectQuery($queryString);
350
                $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
351
352
                $isConnectionMysql = strpos($connection->getServerVersion(), 'MySQL') === 0;
353
                $fullQueryString = '';
354
                try {
355
                    if ($mQ === 'explain' && $isConnectionMysql) {
356
                        // EXPLAIN is no ANSI SQL, for now this is only executed on mysql
357
                        // @todo: Move away from getSelectQuery() or model differently
358
                        $fullQueryString = 'EXPLAIN ' . $selectQueryString;
359
                        $dataRows = $connection->executeQuery('EXPLAIN ' . $selectQueryString)->fetchAll();
360
                    } elseif ($mQ === 'count') {
361
                        $queryBuilder = $connection->createQueryBuilder();
362
                        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
363
                        $queryBuilder->count('*')
364
                            ->from($this->table)
365
                            ->where(QueryHelper::stripLogicalOperatorPrefix($queryString));
366
                        $fullQueryString = $queryBuilder->getSQL();
367
                        $dataRows = [$queryBuilder->execute()->fetchOne()];
368
                    } else {
369
                        $fullQueryString = $selectQueryString;
370
                        $dataRows = $connection->executeQuery($selectQueryString)->fetchAll();
371
                    }
372
                    if (!($userTsConfig['mod.']['dbint.']['disableShowSQLQuery'] ?? false)) {
373
                        $output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
374
                    }
375
                    $cPR = $this->getQueryResultCode($mQ, $dataRows, $this->table);
376
                    $output .= '<h2>' . $cPR['header'] . '</h2><div>' . $cPR['content'] . '</div>';
377
                } catch (DBALException $e) {
378
                    if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
379
                        $output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
380
                    }
381
                    $out = '<p><strong>Error: <span class="text-danger">'
382
                        . htmlspecialchars($e->getMessage())
383
                        . '</span></strong></p>';
384
                    $output .= '<h2>SQL error</h2><div>' . $out . '</div>';
385
                }
386
            }
387
        }
388
        return '<div class="database-query-builder">' . $output . '</div>';
389
    }
390
391
    /**
392
     * Search
393
     *
394
     * @return string
395
     */
396
    public function search()
397
    {
398
        $swords = $this->settings['sword'] ?? '';
399
        $out = '';
400
        if ($swords) {
401
            foreach ($GLOBALS['TCA'] as $table => $value) {
402
                // Get fields list
403
                $conf = $GLOBALS['TCA'][$table];
404
                // Avoid querying tables with no columns
405
                if (empty($conf['columns'])) {
406
                    continue;
407
                }
408
                $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
409
                $tableColumns = $connection->getSchemaManager()->listTableColumns($table);
410
                $fieldsInDatabase = [];
411
                foreach ($tableColumns as $column) {
412
                    $fieldsInDatabase[] = $column->getName();
413
                }
414
                $fields = array_intersect(array_keys($conf['columns']), $fieldsInDatabase);
415
416
                $queryBuilder = $connection->createQueryBuilder();
417
                $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
418
                $queryBuilder->count('*')->from($table);
419
                $likes = [];
420
                $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($swords) . '%';
421
                foreach ($fields as $field) {
422
                    $likes[] = $queryBuilder->expr()->like(
423
                        $field,
424
                        $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
425
                    );
426
                }
427
                $count = $queryBuilder->orWhere(...$likes)->execute()->fetchOne();
428
429
                if ($count > 0) {
430
                    $queryBuilder = $connection->createQueryBuilder();
431
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
432
                    $queryBuilder->select('uid', $conf['ctrl']['label'])
433
                        ->from($table)
434
                        ->setMaxResults(200);
435
                    $likes = [];
436
                    foreach ($fields as $field) {
437
                        $likes[] = $queryBuilder->expr()->like(
438
                            $field,
439
                            $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
440
                        );
441
                    }
442
                    $statement = $queryBuilder->orWhere(...$likes)->execute();
443
                    $lastRow = null;
444
                    $rowArr = [];
445
                    while ($row = $statement->fetch()) {
446
                        $rowArr[] = $this->resultRowDisplay($row, $conf, $table);
447
                        $lastRow = $row;
448
                    }
449
                    $markup = [];
450
                    $markup[] = '<div class="panel panel-default">';
451
                    $markup[] = '  <div class="panel-heading">';
452
                    $markup[] = htmlspecialchars($this->getLanguageService()->sL($conf['ctrl']['title'])) . ' (' . $count . ')';
453
                    $markup[] = '  </div>';
454
                    $markup[] = '  <table class="table table-striped table-hover">';
455
                    $markup[] = $this->resultRowTitles($lastRow, $conf);
456
                    $markup[] = implode(LF, $rowArr);
457
                    $markup[] = '  </table>';
458
                    $markup[] = '</div>';
459
460
                    $out .= implode(LF, $markup);
461
                }
462
            }
463
        }
464
        return $out;
465
    }
466
467
    /**
468
     * Sets the current name of the input form.
469
     *
470
     * @param string $formName The name of the form.
471
     */
472
    public function setFormName($formName)
473
    {
474
        $this->formName = trim($formName);
475
    }
476
477
    /**
478
     * Make store control
479
     *
480
     * @return string
481
     */
482
    protected function makeStoreControl()
483
    {
484
        // Load/Save
485
        $storeArray = $this->initStoreArray();
486
487
        $opt = [];
488
        foreach ($storeArray as $k => $v) {
489
            $opt[] = '<option value="' . htmlspecialchars($k) . '">' . htmlspecialchars($v) . '</option>';
490
        }
491
        // Actions:
492
        if (ExtensionManagementUtility::isLoaded('sys_action') && $this->getBackendUserAuthentication()->isAdmin()) {
493
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_action');
494
            $queryBuilder->getRestrictions()->removeAll();
495
            $statement = $queryBuilder->select('uid', 'title')
496
                ->from('sys_action')
497
                ->where($queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(2, \PDO::PARAM_INT)))
498
                ->orderBy('title')
499
                ->execute();
500
            $opt[] = '<option value="0">__Save to Action:__</option>';
501
            while ($row = $statement->fetch()) {
502
                $opt[] = '<option value="-' . (int)$row['uid'] . '">' . htmlspecialchars($row['title']
503
                        . ' [' . (int)$row['uid'] . ']') . '</option>';
504
            }
505
        }
506
        $markup = [];
507
        $markup[] = '<div class="load-queries">';
508
        $markup[] = '  <div class="row row-cols-auto">';
509
        $markup[] = '    <div class="col">';
510
        $markup[] = '      <select class="form-select" name="storeControl[STORE]" data-assign-store-control-title>' . implode(LF, $opt) . '</select>';
511
        $markup[] = '    </div>';
512
        $markup[] = '    <div class="col">';
513
        $markup[] = '      <input class="form-control" name="storeControl[title]" value="" type="text" max="80">';
514
        $markup[] = '    </div>';
515
        $markup[] = '    <div class="col">';
516
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[LOAD]" value="Load">';
517
        $markup[] = '    </div>';
518
        $markup[] = '    <div class="col">';
519
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[SAVE]" value="Save">';
520
        $markup[] = '    </div>';
521
        $markup[] = '    <div class="col">';
522
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[REMOVE]" value="Remove">';
523
        $markup[] = '    </div>';
524
        $markup[] = '  </div>';
525
        $markup[] = '</div>';
526
527
        return implode(LF, $markup);
528
    }
529
530
    /**
531
     * Init store array
532
     *
533
     * @return array
534
     */
535
    protected function initStoreArray()
536
    {
537
        $storeArray = [
538
            '0' => '[New]'
539
        ];
540
        $savedStoreArray = unserialize($this->settings['storeArray'] ?? '', ['allowed_classes' => false]);
541
        if (is_array($savedStoreArray)) {
542
            $storeArray = array_merge($storeArray, $savedStoreArray);
543
        }
544
        return $storeArray;
545
    }
546
547
    /**
548
     * Clean store query configs
549
     *
550
     * @param array $storeQueryConfigs
551
     * @param array $storeArray
552
     * @return array
553
     */
554
    protected function cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
555
    {
556
        if (is_array($storeQueryConfigs)) {
0 ignored issues
show
introduced by
The condition is_array($storeQueryConfigs) is always true.
Loading history...
557
            foreach ($storeQueryConfigs as $k => $v) {
558
                if (!isset($storeArray[$k])) {
559
                    unset($storeQueryConfigs[$k]);
560
                }
561
            }
562
        }
563
        return $storeQueryConfigs;
564
    }
565
566
    /**
567
     * Add to store query configs
568
     *
569
     * @param array $storeQueryConfigs
570
     * @param int $index
571
     * @return array
572
     */
573
    protected function addToStoreQueryConfigs($storeQueryConfigs, $index)
574
    {
575
        $keyArr = explode(',', $this->storeList);
576
        $storeQueryConfigs[$index] = [];
577
        foreach ($keyArr as $k) {
578
            $storeQueryConfigs[$index][$k] = $this->settings[$k];
579
        }
580
        return $storeQueryConfigs;
581
    }
582
583
    /**
584
     * Save query in action
585
     *
586
     * @param int $uid
587
     * @return bool
588
     */
589
    protected function saveQueryInAction($uid)
590
    {
591
        if (ExtensionManagementUtility::isLoaded('sys_action')) {
592
            $keyArr = explode(',', $this->storeList);
593
            $saveArr = [];
594
            foreach ($keyArr as $k) {
595
                $saveArr[$k] = $this->settings[$k];
596
            }
597
            // Show query
598
            if ($saveArr['queryTable']) {
599
                $this->init('queryConfig', $saveArr['queryTable'], '', $this->settings);
600
                $this->makeSelectorTable($saveArr);
601
                $this->enablePrefix = true;
602
                $queryString = $this->getQuery($this->queryConfig);
603
604
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
605
                    ->getQueryBuilderForTable($this->table);
606
                $queryBuilder->getRestrictions()->removeAll()
607
                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
608
                $rowCount = $queryBuilder->count('*')
609
                    ->from($this->table)
610
                    ->where(QueryHelper::stripLogicalOperatorPrefix($queryString))
611
                    ->execute()->fetchOne();
612
613
                $t2DataValue = [
614
                    'qC' => $saveArr,
615
                    'qCount' => $rowCount,
616
                    'qSelect' => $this->getSelectQuery($queryString),
617
                    'qString' => $queryString
618
                ];
619
                GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_action')
620
                    ->update(
621
                        'sys_action',
622
                        ['t2_data' => serialize($t2DataValue)],
623
                        ['uid' => (int)$uid],
624
                        ['t2_data' => Connection::PARAM_LOB]
625
                    );
626
            }
627
            return true;
628
        }
629
        return false;
630
    }
631
    /**
632
     * Load store query configs
633
     *
634
     * @param array $storeQueryConfigs
635
     * @param int $storeIndex
636
     * @param array $writeArray
637
     * @return array
638
     */
639
    protected function loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
640
    {
641
        if ($storeQueryConfigs[$storeIndex]) {
642
            $keyArr = explode(',', $this->storeList);
643
            foreach ($keyArr as $k) {
644
                $writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
645
            }
646
        }
647
        return $writeArray;
648
    }
649
650
    /**
651
     * Process store control
652
     *
653
     * @return string
654
     */
655
    protected function procesStoreControl()
656
    {
657
        $languageService = $this->getLanguageService();
658
        $flashMessage = null;
659
        $storeArray = $this->initStoreArray();
660
        $storeQueryConfigs = unserialize($this->settings['storeQueryConfigs'] ?? '', ['allowed_classes' => false]);
661
        $storeControl = GeneralUtility::_GP('storeControl');
662
        $storeIndex = (int)($storeControl['STORE'] ?? 0);
663
        $saveStoreArray = 0;
664
        $writeArray = [];
665
        $msg = '';
666
        if (is_array($storeControl)) {
0 ignored issues
show
introduced by
The condition is_array($storeControl) is always false.
Loading history...
667
            if ($storeControl['LOAD'] ?? false) {
668
                if ($storeIndex > 0) {
669
                    $writeArray = $this->loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
670
                    $saveStoreArray = 1;
671
                    $flashMessage = GeneralUtility::makeInstance(
672
                        FlashMessage::class,
673
                        sprintf($languageService->getLL('query_loaded'), $storeArray[$storeIndex])
674
                    );
675
                } elseif ($storeIndex < 0 && ExtensionManagementUtility::isLoaded('sys_action')) {
676
                    $actionRecord = BackendUtility::getRecord('sys_action', abs($storeIndex));
677
                    if (is_array($actionRecord)) {
678
                        $dA = unserialize($actionRecord['t2_data'], ['allowed_classes' => false]);
679
                        $dbSC = [];
680
                        if (is_array($dA['qC'])) {
681
                            $dbSC[0] = $dA['qC'];
682
                        }
683
                        $writeArray = $this->loadStoreQueryConfigs($dbSC, 0, $writeArray);
684
                        $saveStoreArray = 1;
685
                        $flashMessage = GeneralUtility::makeInstance(
686
                            FlashMessage::class,
687
                            sprintf($languageService->getLL('query_from_action_loaded'), $actionRecord['title'])
688
                        );
689
                    }
690
                }
691
            } elseif ($storeControl['SAVE'] ?? false) {
692
                if ($storeIndex < 0) {
693
                    $qOK = $this->saveQueryInAction(abs($storeIndex));
694
                    if ($qOK) {
695
                        $flashMessage = GeneralUtility::makeInstance(
696
                            FlashMessage::class,
697
                            $languageService->getLL('query_saved')
698
                        );
699
                    } else {
700
                        $flashMessage = GeneralUtility::makeInstance(
701
                            FlashMessage::class,
702
                            $languageService->getLL('query_notsaved'),
703
                            '',
704
                            FlashMessage::ERROR
705
                        );
706
                    }
707
                } else {
708
                    if (trim($storeControl['title'])) {
709
                        if ($storeIndex > 0) {
710
                            $storeArray[$storeIndex] = $storeControl['title'];
711
                        } else {
712
                            $storeArray[] = $storeControl['title'];
713
                            end($storeArray);
714
                            $storeIndex = key($storeArray);
715
                        }
716
                        $storeQueryConfigs = $this->addToStoreQueryConfigs($storeQueryConfigs, (int)$storeIndex);
717
                        $saveStoreArray = 1;
718
                        $flashMessage = GeneralUtility::makeInstance(
719
                            FlashMessage::class,
720
                            $languageService->getLL('query_saved')
721
                        );
722
                    }
723
                }
724
            } elseif ($storeControl['REMOVE'] ?? false) {
725
                if ($storeIndex > 0) {
726
                    $flashMessage = GeneralUtility::makeInstance(
727
                        FlashMessage::class,
728
                        sprintf($languageService->getLL('query_removed'), $storeArray[$storeControl['STORE']])
729
                    );
730
                    // Removing
731
                    unset($storeArray[$storeControl['STORE']]);
732
                    $saveStoreArray = 1;
733
                }
734
            }
735
            if (!empty($flashMessage)) {
736
                $msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
737
                    ->resolve()
738
                    ->render([$flashMessage]);
739
            }
740
        }
741
        if ($saveStoreArray) {
742
            // Making sure, index 0 is not set!
743
            unset($storeArray[0]);
744
            $writeArray['storeArray'] = serialize($storeArray);
745
            $writeArray['storeQueryConfigs'] =
746
                serialize($this->cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
747
            $this->settings = BackendUtility::getModuleData(
748
                $this->menuItems,
749
                $writeArray,
750
                $this->moduleName,
751
                'ses'
752
            );
753
        }
754
        return $msg;
755
    }
756
757
    /**
758
     * Get query result code
759
     *
760
     * @param string $type
761
     * @param array $dataRows Rows to display
762
     * @param string $table
763
     * @return array HTML-code for "header" and "content"
764
     * @throws \TYPO3\CMS\Core\Exception
765
     */
766
    protected function getQueryResultCode($type, array $dataRows, $table)
767
    {
768
        $out = '';
769
        $cPR = [];
770
        switch ($type) {
771
            case 'count':
772
                $cPR['header'] = 'Count';
773
                $cPR['content'] = '<br><strong>' . (int)$dataRows[0] . '</strong> records selected.';
774
                break;
775
            case 'all':
776
                $rowArr = [];
777
                $dataRow = null;
778
                foreach ($dataRows as $dataRow) {
779
                    $rowArr[] = $this->resultRowDisplay($dataRow, $GLOBALS['TCA'][$table], $table);
780
                }
781
                if (is_array($this->hookArray['beforeResultTable'] ?? false)) {
782
                    foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
783
                        $out .= GeneralUtility::callUserFunction($_funcRef, $this->settings);
784
                    }
785
                }
786
                if (!empty($rowArr)) {
787
                    $cPR['header'] = 'Result';
788
                    $out .= '<table class="table table-striped table-hover">'
789
                        . $this->resultRowTitles($dataRow, $GLOBALS['TCA'][$table]) . implode(LF, $rowArr)
790
                        . '</table>';
791
                } else {
792
                    $this->renderNoResultsFoundMessage();
793
                }
794
795
                $cPR['content'] = $out;
796
                break;
797
            case 'csv':
798
                $rowArr = [];
799
                $first = 1;
800
                foreach ($dataRows as $dataRow) {
801
                    if ($first) {
802
                        $rowArr[] = $this->csvValues(array_keys($dataRow), ',', '');
803
                        $first = 0;
804
                    }
805
                    $rowArr[] = $this->csvValues($dataRow, ',', '"', $GLOBALS['TCA'][$table], $table);
806
                }
807
                if (!empty($rowArr)) {
808
                    $cPR['header'] = 'Result';
809
                    $out .= '<textarea name="whatever" rows="20" class="text-monospace" style="width:100%">'
810
                        . htmlspecialchars(implode(LF, $rowArr))
811
                        . '</textarea>';
812
                    if (!$this->noDownloadB) {
813
                        $out .= '<br><input class="btn btn-default" type="submit" name="download_file" '
814
                            . 'value="Click to download file">';
815
                    }
816
                    // Downloads file:
817
                    // @todo: args. routing anyone?
818
                    if (GeneralUtility::_GP('download_file')) {
819
                        $filename = 'TYPO3_' . $table . '_export_' . date('dmy-Hi') . '.csv';
820
                        $mimeType = 'application/octet-stream';
821
                        header('Content-Type: ' . $mimeType);
822
                        header('Content-Disposition: attachment; filename=' . $filename);
823
                        echo implode(CRLF, $rowArr);
824
                        die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
825
                    }
826
                } else {
827
                    $this->renderNoResultsFoundMessage();
828
                }
829
                $cPR['content'] = $out;
830
                break;
831
            case 'explain':
832
            default:
833
                foreach ($dataRows as $dataRow) {
834
                    $out .= '<br />' . DebugUtility::viewArray($dataRow);
835
                }
836
                $cPR['header'] = 'Explain SQL query';
837
                $cPR['content'] = $out;
838
        }
839
        return $cPR;
840
    }
841
    /**
842
     * CSV values
843
     *
844
     * @param array $row
845
     * @param string $delim
846
     * @param string $quote
847
     * @param array $conf
848
     * @param string $table
849
     * @return string A single line of CSV
850
     */
851
    protected function csvValues($row, $delim = ',', $quote = '"', $conf = [], $table = '')
852
    {
853
        $valueArray = $row;
854
        if ($this->settings['search_result_labels'] && $table) {
855
            foreach ($valueArray as $key => $val) {
856
                $valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';');
857
            }
858
        }
859
        return CsvUtility::csvValues($valueArray, $delim, $quote);
860
    }
861
862
    /**
863
     * Result row display
864
     *
865
     * @param array $row
866
     * @param array $conf
867
     * @param string $table
868
     * @return string
869
     */
870
    protected function resultRowDisplay($row, $conf, $table)
871
    {
872
        $languageService = $this->getLanguageService();
873
        $out = '<tr>';
874
        foreach ($row as $fieldName => $fieldValue) {
875
            if (GeneralUtility::inList($this->settings['queryFields'] ?? '', $fieldName)
876
                || !($this->settings['queryFields'] ?? false)
877
                && $fieldName !== 'pid'
878
                && $fieldName !== 'deleted'
879
            ) {
880
                if ($this->settings['search_result_labels'] ?? false) {
881
                    $fVnew = $this->getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, '<br />');
882
                } else {
883
                    $fVnew = htmlspecialchars($fieldValue);
884
                }
885
                $out .= '<td>' . $fVnew . '</td>';
886
            }
887
        }
888
        $out .= '<td>';
889
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
890
891
        if (!($row['deleted'] ?? false)) {
892
            $out .= '<div class="btn-group" role="group">';
893
            $url = (string)$uriBuilder->buildUriFromRoute('record_edit', [
894
                'edit' => [
895
                    $table => [
896
                        $row['uid'] => 'edit'
897
                    ]
898
                ],
899
                'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri()
900
                    . HttpUtility::buildQueryString(['SET' => (array)GeneralUtility::_POST('SET')], '&')
901
            ]);
902
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars($url) . '">'
903
                . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
904
            $out .= '</div><div class="btn-group" role="group">';
905
            $out .= sprintf(
906
                '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
907
                'TYPO3.InfoWindow.showItem',
908
                htmlspecialchars($table . ',' . $row['uid']),
909
                $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
910
            );
911
            $out .= '</div>';
912
        } else {
913
            $out .= '<div class="btn-group" role="group">';
914
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
915
                        'cmd' => [
916
                            $table => [
917
                                $row['uid'] => [
918
                                    'undelete' => 1
919
                                ]
920
                            ]
921
                        ],
922
                        'redirect' => GeneralUtility::linkThisScript()
923
                    ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_only')) . '">';
924
            $out .= $this->iconFactory->getIcon('actions-edit-restore', Icon::SIZE_SMALL)->render() . '</a>';
925
            $formEngineParameters = [
926
                'edit' => [
927
                    $table => [
928
                        $row['uid'] => 'edit'
929
                    ]
930
                ],
931
                'returnUrl' => GeneralUtility::linkThisScript()
932
            ];
933
            $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $formEngineParameters);
934
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
935
                    'cmd' => [
936
                        $table => [
937
                            $row['uid'] => [
938
                                'undelete' => 1
939
                            ]
940
                        ]
941
                    ],
942
                    'redirect' => $redirectUrl
943
                ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_and_edit')) . '">';
944
            $out .= $this->iconFactory->getIcon('actions-edit-restore-edit', Icon::SIZE_SMALL)->render() . '</a>';
945
            $out .= '</div>';
946
        }
947
        $_params = [$table => $row];
948
        if (is_array($this->hookArray['additionalButtons'] ?? false)) {
949
            foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
950
                $out .= GeneralUtility::callUserFunction($_funcRef, $_params);
951
            }
952
        }
953
        $out .= '</td></tr>';
954
        return $out;
955
    }
956
957
    /**
958
     * Get processed value extra
959
     *
960
     * @param string $table
961
     * @param string $fieldName
962
     * @param string $fieldValue
963
     * @param array $conf Not used
964
     * @param string $splitString
965
     * @return string
966
     */
967
    protected function getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, $splitString)
968
    {
969
        $out = '';
970
        $fields = [];
971
        // Analysing the fields in the table.
972
        if (is_array($GLOBALS['TCA'][$table])) {
973
            $fC = $GLOBALS['TCA'][$table]['columns'][$fieldName];
974
            $fields = $fC['config'];
975
            $fields['exclude'] = $fC['exclude'];
976
            if (is_array($fC) && $fC['label']) {
977
                $fields['label'] = preg_replace('/:$/', '', trim($this->getLanguageService()->sL($fC['label'])));
978
                switch ($fields['type']) {
979
                    case 'input':
980
                        if (preg_match('/int|year/i', $fields['eval'])) {
981
                            $fields['type'] = 'number';
982
                        } elseif (preg_match('/time/i', $fields['eval'])) {
983
                            $fields['type'] = 'time';
984
                        } elseif (preg_match('/date/i', $fields['eval'])) {
985
                            $fields['type'] = 'date';
986
                        } else {
987
                            $fields['type'] = 'text';
988
                        }
989
                        break;
990
                    case 'check':
991
                        if (!$fields['items']) {
992
                            $fields['type'] = 'boolean';
993
                        } else {
994
                            $fields['type'] = 'binary';
995
                        }
996
                        break;
997
                    case 'radio':
998
                        $fields['type'] = 'multiple';
999
                        break;
1000
                    case 'select':
1001
                        $fields['type'] = 'multiple';
1002
                        if ($fields['foreign_table']) {
1003
                            $fields['type'] = 'relation';
1004
                        }
1005
                        if ($fields['special']) {
1006
                            $fields['type'] = 'text';
1007
                        }
1008
                        break;
1009
                    case 'group':
1010
                        if ($fields['internal_type'] === 'db') {
1011
                            $fields['type'] = 'relation';
1012
                        }
1013
                        break;
1014
                    case 'user':
1015
                    case 'flex':
1016
                    case 'passthrough':
1017
                    case 'none':
1018
                    case 'text':
1019
                    default:
1020
                        $fields['type'] = 'text';
1021
                }
1022
            } else {
1023
                $fields['label'] = '[FIELD: ' . $fieldName . ']';
1024
                switch ($fieldName) {
1025
                    case 'pid':
1026
                        $fields['type'] = 'relation';
1027
                        $fields['allowed'] = 'pages';
1028
                        break;
1029
                    case 'cruser_id':
1030
                        $fields['type'] = 'relation';
1031
                        $fields['allowed'] = 'be_users';
1032
                        break;
1033
                    case 'tstamp':
1034
                    case 'crdate':
1035
                        $fields['type'] = 'time';
1036
                        break;
1037
                    default:
1038
                        $fields['type'] = 'number';
1039
                }
1040
            }
1041
        }
1042
        switch ($fields['type']) {
1043
            case 'date':
1044
                if ($fieldValue != -1) {
1045
                    $out = (string)strftime('%d-%m-%Y', (int)$fieldValue);
1046
                }
1047
                break;
1048
            case 'time':
1049
                if ($fieldValue != -1) {
1050
                    if ($splitString === '<br />') {
1051
                        $out = (string)strftime('%H:%M' . $splitString . '%d-%m-%Y', (int)$fieldValue);
1052
                    } else {
1053
                        $out = (string)strftime('%H:%M %d-%m-%Y', (int)$fieldValue);
1054
                    }
1055
                }
1056
                break;
1057
            case 'multiple':
1058
            case 'binary':
1059
            case 'relation':
1060
                $out = $this->makeValueList($fieldName, $fieldValue, $fields, $table, $splitString);
1061
                break;
1062
            case 'boolean':
1063
                $out = $fieldValue ? 'True' : 'False';
1064
                break;
1065
            default:
1066
                $out = htmlspecialchars($fieldValue);
1067
        }
1068
        return $out;
1069
    }
1070
1071
    /**
1072
     * Recursively fetch all descendants of a given page
1073
     *
1074
     * @param int $id uid of the page
1075
     * @param int $depth
1076
     * @param int $begin
1077
     * @param string $permsClause
1078
     * @return string comma separated list of descendant pages
1079
     */
1080
    protected function getTreeList($id, $depth, $begin = 0, $permsClause = '')
1081
    {
1082
        $depth = (int)$depth;
1083
        $begin = (int)$begin;
1084
        $id = (int)$id;
1085
        if ($id < 0) {
1086
            $id = abs($id);
1087
        }
1088
        if ($begin == 0) {
1089
            $theList = (string)$id;
1090
        } else {
1091
            $theList = '';
1092
        }
1093
        if ($id && $depth > 0) {
1094
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1095
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1096
            $statement = $queryBuilder->select('uid')
0 ignored issues
show
Unused Code introduced by
The assignment to $statement is dead and can be removed.
Loading history...
1097
                ->from('pages')
1098
                ->where(
1099
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
1100
                    $queryBuilder->expr()->eq('sys_language_uid', 0)
1101
                )
1102
                ->orderBy('uid');
1103
            if ($permsClause !== '') {
1104
                $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($permsClause));
1105
            }
1106
            $statement = $queryBuilder->execute();
1107
            while ($row = $statement->fetch()) {
1108
                if ($begin <= 0) {
1109
                    $theList .= ',' . $row['uid'];
1110
                }
1111
                if ($depth > 1) {
1112
                    $theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
1113
                    if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
1114
                        $theList .= ',';
1115
                    }
1116
                    $theList .= $theSubList;
1117
                }
1118
            }
1119
        }
1120
        return $theList;
1121
    }
1122
1123
    /**
1124
     * Make value list
1125
     *
1126
     * @param string $fieldName
1127
     * @param string $fieldValue
1128
     * @param array $conf
1129
     * @param string $table
1130
     * @param string $splitString
1131
     * @return string
1132
     */
1133
    protected function makeValueList($fieldName, $fieldValue, $conf, $table, $splitString)
1134
    {
1135
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1136
        $languageService = $this->getLanguageService();
1137
        $from_table_Arr = [];
1138
        $fieldSetup = $conf;
1139
        $out = '';
1140
        if ($fieldSetup['type'] === 'multiple') {
1141
            foreach ($fieldSetup['items'] as $key => $val) {
1142
                if (strpos($val[0], 'LLL:') === 0) {
1143
                    $value = $languageService->sL($val[0]);
1144
                } else {
1145
                    $value = $val[0];
1146
                }
1147
                if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) {
1148
                    if ($out !== '') {
1149
                        $out .= $splitString;
1150
                    }
1151
                    $out .= htmlspecialchars($value);
1152
                }
1153
            }
1154
        }
1155
        if ($fieldSetup['type'] === 'binary') {
1156
            foreach ($fieldSetup['items'] as $Key => $val) {
1157
                if (strpos($val[0], 'LLL:') === 0) {
1158
                    $value = $languageService->sL($val[0]);
1159
                } else {
1160
                    $value = $val[0];
1161
                }
1162
                if ($out !== '') {
1163
                    $out .= $splitString;
1164
                }
1165
                $out .= htmlspecialchars($value);
1166
            }
1167
        }
1168
        if ($fieldSetup['type'] === 'relation') {
1169
            $dontPrefixFirstTable = 0;
1170
            $useTablePrefix = 0;
1171
            if ($fieldSetup['items']) {
1172
                foreach ($fieldSetup['items'] as $key => $val) {
1173
                    if (strpos($val[0], 'LLL:') === 0) {
1174
                        $value = $languageService->sL($val[0]);
1175
                    } else {
1176
                        $value = $val[0];
1177
                    }
1178
                    if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
1179
                        if ($out !== '') {
1180
                            $out .= $splitString;
1181
                        }
1182
                        $out .= htmlspecialchars($value);
1183
                    }
1184
                }
1185
            }
1186
            if (strpos($fieldSetup['allowed'], ',') !== false) {
1187
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
1188
                $useTablePrefix = 1;
1189
                if (!$fieldSetup['prepend_tname']) {
1190
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1191
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1192
                    $statement = $queryBuilder->select($fieldName)->from($table)->execute();
1193
                    while ($row = $statement->fetch()) {
1194
                        if (strpos($row[$fieldName], ',') !== false) {
1195
                            $checkContent = explode(',', $row[$fieldName]);
1196
                            foreach ($checkContent as $singleValue) {
1197
                                if (strpos($singleValue, '_') === false) {
1198
                                    $dontPrefixFirstTable = 1;
1199
                                }
1200
                            }
1201
                        } else {
1202
                            $singleValue = $row[$fieldName];
1203
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
1204
                                $dontPrefixFirstTable = 1;
1205
                            }
1206
                        }
1207
                    }
1208
                }
1209
            } else {
1210
                $from_table_Arr[0] = $fieldSetup['allowed'];
1211
            }
1212
            if ($fieldSetup['prepend_tname']) {
1213
                $useTablePrefix = 1;
1214
            }
1215
            if ($fieldSetup['foreign_table']) {
1216
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
1217
            }
1218
            $counter = 0;
1219
            $useSelectLabels = 0;
1220
            $useAltSelectLabels = 0;
1221
            $tablePrefix = '';
1222
            $labelFieldSelect = [];
1223
            foreach ($from_table_Arr as $from_table) {
1224
                if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter == 1) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($useTablePrefix && ! $d... != 1) || $counter == 1, Probably Intended Meaning: $useTablePrefix && ! $do... != 1 || $counter == 1)
Loading history...
1225
                    $tablePrefix = $from_table . '_';
1226
                }
1227
                $counter = 1;
1228
                if (is_array($GLOBALS['TCA'][$from_table])) {
1229
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
1230
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
1231
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
1232
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'];
1233
                        foreach ($items as $labelArray) {
1234
                            if (strpos($labelArray[0], 'LLL:') === 0) {
1235
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
1236
                            } else {
1237
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
1238
                            }
1239
                        }
1240
                        $useSelectLabels = 1;
1241
                    }
1242
                    $altLabelFieldSelect = [];
1243
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
1244
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
1245
                        foreach ($items as $altLabelArray) {
1246
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
1247
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
1248
                            } else {
1249
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
1250
                            }
1251
                        }
1252
                        $useAltSelectLabels = 1;
1253
                    }
1254
1255
                    if (!$this->tableArray[$from_table]) {
1256
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1257
                        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1258
                        $selectFields = ['uid', $labelField];
1259
                        if ($altLabelField) {
1260
                            $selectFields[] = $altLabelField;
1261
                        }
1262
                        $queryBuilder->select(...$selectFields)
1263
                            ->from($from_table)
1264
                            ->orderBy('uid');
1265
                        if (!$backendUserAuthentication->isAdmin()) {
1266
                            $webMounts = $backendUserAuthentication->returnWebmounts();
1267
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1268
                            $webMountPageTree = '';
1269
                            $webMountPageTreePrefix = '';
1270
                            foreach ($webMounts as $webMount) {
1271
                                if ($webMountPageTree) {
1272
                                    $webMountPageTreePrefix = ',';
1273
                                }
1274
                                $webMountPageTree .= $webMountPageTreePrefix
1275
                                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
1276
                            }
1277
                            if ($from_table === 'pages') {
1278
                                $queryBuilder->where(
1279
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1280
                                    $queryBuilder->expr()->in(
1281
                                        'uid',
1282
                                        $queryBuilder->createNamedParameter(
1283
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1284
                                            Connection::PARAM_INT_ARRAY
1285
                                        )
1286
                                    )
1287
                                );
1288
                            } else {
1289
                                $queryBuilder->where(
1290
                                    $queryBuilder->expr()->in(
1291
                                        'pid',
1292
                                        $queryBuilder->createNamedParameter(
1293
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1294
                                            Connection::PARAM_INT_ARRAY
1295
                                        )
1296
                                    )
1297
                                );
1298
                            }
1299
                        }
1300
                        $statement = $queryBuilder->execute();
1301
                        $this->tableArray[$from_table] = [];
1302
                        while ($row = $statement->fetch()) {
1303
                            $this->tableArray[$from_table][] = $row;
1304
                        }
1305
                    }
1306
1307
                    foreach ($this->tableArray[$from_table] as $key => $val) {
1308
                        $this->settings['labels_noprefix'] =
1309
                            $this->settings['labels_noprefix'] == 1
1310
                                ? 'on'
1311
                                : $this->settings['labels_noprefix'];
1312
                        $prefixString =
1313
                            $this->settings['labels_noprefix'] === 'on'
1314
                                ? ''
1315
                                : ' [' . $tablePrefix . $val['uid'] . '] ';
1316
                        if ($out !== '') {
1317
                            $out .= $splitString;
1318
                        }
1319
                        if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
1320
                            || $fieldValue == $tablePrefix . $val['uid']) {
1321
                            if ($useSelectLabels) {
1322
                                $out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
1323
                            } elseif ($val[$labelField]) {
1324
                                $out .= htmlspecialchars($prefixString . $val[$labelField]);
1325
                            } elseif ($useAltSelectLabels) {
1326
                                $out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
1327
                            } else {
1328
                                $out .= htmlspecialchars($prefixString . $val[$altLabelField]);
1329
                            }
1330
                        }
1331
                    }
1332
                }
1333
            }
1334
        }
1335
        return $out;
1336
    }
1337
1338
    /**
1339
     * Render table header
1340
     *
1341
     * @param array $row Table columns
1342
     * @param array $conf Table TCA
1343
     * @return string HTML of table header
1344
     */
1345
    protected function resultRowTitles($row, $conf)
1346
    {
1347
        $languageService = $this->getLanguageService();
1348
        $tableHeader = [];
1349
        // Start header row
1350
        $tableHeader[] = '<thead><tr>';
1351
        // Iterate over given columns
1352
        foreach ($row as $fieldName => $fieldValue) {
1353
            if (GeneralUtility::inList($this->settings['queryFields'] ?? '', $fieldName)
1354
                || !($this->settings['queryFields'] ?? false)
1355
                && $fieldName !== 'pid'
1356
                && $fieldName !== 'deleted'
1357
            ) {
1358
                if ($this->settings['search_result_labels'] ?? false) {
1359
                    $title = $languageService->sL($conf['columns'][$fieldName]['label']
1360
                        ?: $fieldName);
1361
                } else {
1362
                    $title = $languageService->sL($fieldName);
1363
                }
1364
                $tableHeader[] = '<th>' . htmlspecialchars($title) . '</th>';
1365
            }
1366
        }
1367
        // Add empty icon column
1368
        $tableHeader[] = '<th></th>';
1369
        // Close header row
1370
        $tableHeader[] = '</tr></thead>';
1371
        return implode(LF, $tableHeader);
1372
    }
1373
    /**
1374
     * @throws \InvalidArgumentException
1375
     * @throws \TYPO3\CMS\Core\Exception
1376
     */
1377
    private function renderNoResultsFoundMessage()
1378
    {
1379
        $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', FlashMessage::INFO);
1380
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1381
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1382
        $defaultFlashMessageQueue->enqueue($flashMessage);
1383
    }
1384
1385
    /**
1386
     * Make a list of fields for current table
1387
     *
1388
     * @return string Separated list of fields
1389
     */
1390
    protected function makeFieldList()
1391
    {
1392
        $fieldListArr = [];
1393
        if (is_array($GLOBALS['TCA'][$this->table])) {
1394
            $fieldListArr = array_keys($GLOBALS['TCA'][$this->table]['columns'] ?? []);
1395
            $fieldListArr[] = 'uid';
1396
            $fieldListArr[] = 'pid';
1397
            $fieldListArr[] = 'deleted';
1398
            if ($GLOBALS['TCA'][$this->table]['ctrl']['tstamp'] ?? false) {
1399
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
1400
            }
1401
            if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate'] ?? false) {
1402
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
1403
            }
1404
            if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'] ?? false) {
1405
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
1406
            }
1407
            if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby'] ?? false) {
1408
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['sortby'];
1409
            }
1410
        }
1411
        return implode(',', $fieldListArr);
1412
    }
1413
1414
    /**
1415
     * Init function
1416
     *
1417
     * @param string $name The name
1418
     * @param string $table The table name
1419
     * @param string $fieldList The field list
1420
     * @param array $settings Module settings like checkboxes in the interface
1421
     */
1422
    protected function init($name, $table, $fieldList = '', array $settings = [])
1423
    {
1424
        // Analysing the fields in the table.
1425
        if (is_array($GLOBALS['TCA'][$table] ?? false)) {
1426
            $this->name = $name;
1427
            $this->table = $table;
1428
            $this->fieldList = $fieldList ?: $this->makeFieldList();
1429
            $this->settings = $settings;
1430
            $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
1431
            foreach ($fieldArr as $fieldName) {
1432
                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName] ?? [];
1433
                $this->fields[$fieldName] = $fC['config'] ?? [];
1434
                $this->fields[$fieldName]['exclude'] = $fC['exclude'] ?? '';
1435
                if (($this->fields[$fieldName]['type'] ?? '') === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->fields[$fieldNam...type'] ?? '' === 'none', Probably Intended Meaning: $this->fields[$fieldName...ype'] ?? '' === 'none')
Loading history...
1436
                    || ($this->fields[$fieldName]['type'] ?? '') === 'none'
1437
                ) {
1438
                    // Do not list type=none "virtual" fields or query them from db,
1439
                    // and if type is user without defined userFunc
1440
                    unset($this->fields[$fieldName]);
1441
                    continue;
1442
                }
1443
                if (is_array($fC) && ($fC['label'] ?? false)) {
1444
                    $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
1445
                    switch ($this->fields[$fieldName]['type']) {
1446
                        case 'input':
1447
                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
1448
                                $this->fields[$fieldName]['type'] = 'number';
1449
                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
1450
                                $this->fields[$fieldName]['type'] = 'time';
1451
                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
1452
                                $this->fields[$fieldName]['type'] = 'date';
1453
                            } else {
1454
                                $this->fields[$fieldName]['type'] = 'text';
1455
                            }
1456
                            break;
1457
                        case 'check':
1458
                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
1459
                                $this->fields[$fieldName]['type'] = 'boolean';
1460
                            } else {
1461
                                $this->fields[$fieldName]['type'] = 'binary';
1462
                            }
1463
                            break;
1464
                        case 'radio':
1465
                            $this->fields[$fieldName]['type'] = 'multiple';
1466
                            break;
1467
                        case 'select':
1468
                            $this->fields[$fieldName]['type'] = 'multiple';
1469
                            if ($this->fields[$fieldName]['foreign_table'] ?? false) {
1470
                                $this->fields[$fieldName]['type'] = 'relation';
1471
                            }
1472
                            if ($this->fields[$fieldName]['special'] ?? false) {
1473
                                $this->fields[$fieldName]['type'] = 'text';
1474
                            }
1475
                            break;
1476
                        case 'group':
1477
                            if ($this->fields[$fieldName]['internal_type'] === 'db') {
1478
                                $this->fields[$fieldName]['type'] = 'relation';
1479
                            }
1480
                            break;
1481
                        case 'user':
1482
                        case 'flex':
1483
                        case 'passthrough':
1484
                        case 'none':
1485
                        case 'text':
1486
                        default:
1487
                            $this->fields[$fieldName]['type'] = 'text';
1488
                    }
1489
                } else {
1490
                    $this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
1491
                    switch ($fieldName) {
1492
                        case 'pid':
1493
                            $this->fields[$fieldName]['type'] = 'relation';
1494
                            $this->fields[$fieldName]['allowed'] = 'pages';
1495
                            break;
1496
                        case 'cruser_id':
1497
                            $this->fields[$fieldName]['type'] = 'relation';
1498
                            $this->fields[$fieldName]['allowed'] = 'be_users';
1499
                            break;
1500
                        case 'tstamp':
1501
                        case 'crdate':
1502
                            $this->fields[$fieldName]['type'] = 'time';
1503
                            break;
1504
                        case 'deleted':
1505
                            $this->fields[$fieldName]['type'] = 'boolean';
1506
                            break;
1507
                        default:
1508
                            $this->fields[$fieldName]['type'] = 'number';
1509
                    }
1510
                }
1511
            }
1512
        }
1513
        /*	// EXAMPLE:
1514
        $this->queryConfig = array(
1515
        array(
1516
        'operator' => 'AND',
1517
        'type' => 'FIELD_space_before_class',
1518
        ),
1519
        array(
1520
        'operator' => 'AND',
1521
        'type' => 'FIELD_records',
1522
        'negate' => 1,
1523
        'inputValue' => 'foo foo'
1524
        ),
1525
        array(
1526
        'type' => 'newlevel',
1527
        'nl' => array(
1528
        array(
1529
        'operator' => 'AND',
1530
        'type' => 'FIELD_space_before_class',
1531
        'negate' => 1,
1532
        'inputValue' => 'foo foo'
1533
        ),
1534
        array(
1535
        'operator' => 'AND',
1536
        'type' => 'FIELD_records',
1537
        'negate' => 1,
1538
        'inputValue' => 'foo foo'
1539
        )
1540
        )
1541
        ),
1542
        array(
1543
        'operator' => 'OR',
1544
        'type' => 'FIELD_maillist',
1545
        )
1546
        );
1547
         */
1548
    }
1549
1550
    /**
1551
     * Set and clean up external lists
1552
     *
1553
     * @param string $name The name
1554
     * @param string $list The list
1555
     * @param string $force
1556
     */
1557
    protected function setAndCleanUpExternalLists($name, $list, $force = '')
1558
    {
1559
        $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
1560
        $reList = [];
1561
        foreach ($fields as $fieldName) {
1562
            if ($this->fields[$fieldName]) {
1563
                $reList[] = $fieldName;
1564
            }
1565
        }
1566
        $this->extFieldLists[$name] = implode(',', $reList);
1567
    }
1568
1569
    /**
1570
     * Process data
1571
     *
1572
     * @param array $qC Query config
1573
     */
1574
    protected function procesData($qC = [])
1575
    {
1576
        $this->queryConfig = $qC;
1577
        $POST = GeneralUtility::_POST();
1578
        // If delete...
1579
        if ($POST['qG_del'] ?? false) {
1580
            // Initialize array to work on, save special parameters
1581
            $ssArr = $this->getSubscript($POST['qG_del']);
1582
            $workArr = &$this->queryConfig;
1583
            $ssArrSize = count($ssArr) - 1;
1584
            $i = 0;
1585
            for (; $i < $ssArrSize; $i++) {
1586
                $workArr = &$workArr[$ssArr[$i]];
1587
            }
1588
            // Delete the entry and move the other entries
1589
            unset($workArr[$ssArr[$i]]);
1590
            $workArrSize = count((array)$workArr);
1591
            for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
1592
                $workArr[$j] = $workArr[$j + 1];
1593
                unset($workArr[$j + 1]);
1594
            }
1595
        }
1596
        // If insert...
1597
        if ($POST['qG_ins'] ?? false) {
1598
            // Initialize array to work on, save special parameters
1599
            $ssArr = $this->getSubscript($POST['qG_ins']);
1600
            $workArr = &$this->queryConfig;
1601
            $ssArrSize = count($ssArr) - 1;
1602
            $i = 0;
1603
            for (; $i < $ssArrSize; $i++) {
1604
                $workArr = &$workArr[$ssArr[$i]];
1605
            }
1606
            // Move all entries above position where new entry is to be inserted
1607
            $workArrSize = count((array)$workArr);
1608
            for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
1609
                $workArr[$j] = $workArr[$j - 1];
1610
            }
1611
            // Clear new entry position
1612
            unset($workArr[$ssArr[$i] + 1]);
1613
            $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
1614
        }
1615
        // If move up...
1616
        if ($POST['qG_up'] ?? false) {
1617
            // Initialize array to work on
1618
            $ssArr = $this->getSubscript($POST['qG_up']);
1619
            $workArr = &$this->queryConfig;
1620
            $ssArrSize = count($ssArr) - 1;
1621
            $i = 0;
1622
            for (; $i < $ssArrSize; $i++) {
1623
                $workArr = &$workArr[$ssArr[$i]];
1624
            }
1625
            // Swap entries
1626
            $qG_tmp = $workArr[$ssArr[$i]];
1627
            $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
1628
            $workArr[$ssArr[$i] - 1] = $qG_tmp;
1629
        }
1630
        // If new level...
1631
        if ($POST['qG_nl'] ?? false) {
1632
            // Initialize array to work on
1633
            $ssArr = $this->getSubscript($POST['qG_nl']);
1634
            $workArr = &$this->queryConfig;
1635
            $ssArraySize = count($ssArr) - 1;
1636
            $i = 0;
1637
            for (; $i < $ssArraySize; $i++) {
1638
                $workArr = &$workArr[$ssArr[$i]];
1639
            }
1640
            // Do stuff:
1641
            $tempEl = $workArr[$ssArr[$i]];
1642
            if (is_array($tempEl)) {
1643
                if ($tempEl['type'] !== 'newlevel') {
1644
                    $workArr[$ssArr[$i]] = [
1645
                        'type' => 'newlevel',
1646
                        'operator' => $tempEl['operator'],
1647
                        'nl' => [$tempEl]
1648
                    ];
1649
                }
1650
            }
1651
        }
1652
        // If collapse level...
1653
        if ($POST['qG_remnl'] ?? false) {
1654
            // Initialize array to work on
1655
            $ssArr = $this->getSubscript($POST['qG_remnl']);
1656
            $workArr = &$this->queryConfig;
1657
            $ssArrSize = count($ssArr) - 1;
1658
            $i = 0;
1659
            for (; $i < $ssArrSize; $i++) {
1660
                $workArr = &$workArr[$ssArr[$i]];
1661
            }
1662
            // Do stuff:
1663
            $tempEl = $workArr[$ssArr[$i]];
1664
            if (is_array($tempEl)) {
1665
                if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
1666
                    $a1 = array_slice($workArr, 0, $ssArr[$i]);
1667
                    $a2 = array_slice($workArr, $ssArr[$i]);
1668
                    array_shift($a2);
1669
                    $a3 = $tempEl['nl'];
1670
                    $a3[0]['operator'] = $tempEl['operator'];
1671
                    $workArr = array_merge($a1, $a3, $a2);
1672
                }
1673
            }
1674
        }
1675
    }
1676
1677
    /**
1678
     * Clean up query config
1679
     *
1680
     * @param array $queryConfig Query config
1681
     * @return array
1682
     */
1683
    protected function cleanUpQueryConfig($queryConfig)
1684
    {
1685
        // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
1686
        if (is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always true.
Loading history...
1687
            ksort($queryConfig);
1688
        } else {
1689
            // queryConfig should never be empty!
1690
            if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
1691
                // Make sure queryConfig is an array
1692
                $queryConfig = [];
1693
                $queryConfig[0] = ['type' => 'FIELD_'];
1694
            }
1695
        }
1696
        // Traverse:
1697
        foreach ($queryConfig as $key => $conf) {
1698
            $fieldName = '';
1699
            if (strpos($conf['type'], 'FIELD_') === 0) {
1700
                $fieldName = substr($conf['type'], 6);
1701
                $fieldType = $this->fields[$fieldName]['type'];
1702
            } elseif ($conf['type'] === 'newlevel') {
1703
                $fieldType = $conf['type'];
1704
            } else {
1705
                $fieldType = 'ignore';
1706
            }
1707
            switch ($fieldType) {
1708
                case 'newlevel':
1709
                    if (!$queryConfig[$key]['nl']) {
1710
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1711
                    }
1712
                    $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
1713
                    break;
1714
                case 'userdef':
1715
                    break;
1716
                case 'ignore':
1717
                default:
1718
                    $verifiedName = $this->verifyType($fieldName);
1719
                    $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
1720
                    if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
1721
                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
1722
                    }
1723
                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
1724
                    $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
1725
                    $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], '1');
1726
            }
1727
        }
1728
        return $queryConfig;
1729
    }
1730
1731
    /**
1732
     * Get form elements
1733
     *
1734
     * @param int $subLevel
1735
     * @param string $queryConfig
1736
     * @param string $parent
1737
     * @return array
1738
     */
1739
    protected function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
1740
    {
1741
        $codeArr = [];
1742
        if (!is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always false.
Loading history...
1743
            $queryConfig = $this->queryConfig;
1744
        }
1745
        $c = 0;
1746
        $arrCount = 0;
1747
        $loopCount = 0;
1748
        foreach ($queryConfig as $key => $conf) {
1749
            $fieldName = '';
1750
            $subscript = $parent . '[' . $key . ']';
1751
            $lineHTML = [];
1752
            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], (bool)$c, $conf['type'] !== 'FIELD_');
1753
            if (strpos($conf['type'], 'FIELD_') === 0) {
1754
                $fieldName = substr($conf['type'], 6);
1755
                $this->fieldName = $fieldName;
1756
                $fieldType = $this->fields[$fieldName]['type'];
1757
                if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
1758
                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
1759
                }
1760
                //nasty nasty...
1761
                //make sure queryConfig contains _actual_ comparevalue.
1762
                //mkCompSelect don't care, but getQuery does.
1763
                $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
1764
            } elseif ($conf['type'] === 'newlevel') {
1765
                $fieldType = $conf['type'];
1766
            } else {
1767
                $fieldType = 'ignore';
1768
            }
1769
            $fieldPrefix = htmlspecialchars($this->name . $subscript);
1770
            switch ($fieldType) {
1771
                case 'ignore':
1772
                    break;
1773
                case 'newlevel':
1774
                    if (!$queryConfig[$key]['nl']) {
1775
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1776
                    }
1777
                    $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
1778
                    $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
1779
                    break;
1780
                case 'userdef':
1781
                    $lineHTML[] = '';
1782
                    break;
1783
                case 'date':
1784
                    $lineHTML[] = '<div class="form-inline">';
1785
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1786
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1787
                        // between
1788
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1789
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
1790
                    } else {
1791
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1792
                    }
1793
                    $lineHTML[] = '</div>';
1794
                    break;
1795
                case 'time':
1796
                    $lineHTML[] = '<div class="form-inline">';
1797
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1798
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1799
                        // between:
1800
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1801
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
1802
                    } else {
1803
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1804
                    }
1805
                    $lineHTML[] = '</div>';
1806
                    break;
1807
                case 'multiple':
1808
                case 'binary':
1809
                case 'relation':
1810
                    $lineHTML[] = '<div class="form-inline">';
1811
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1812
                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1813
                        $lineHTML[] = '<select class="form-select" name="' . $fieldPrefix . '[inputValue][]" multiple="multiple">';
1814
                    } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
1815
                        if (is_array($conf['inputValue'])) {
1816
                            $conf['inputValue'] = implode(',', $conf['inputValue']);
1817
                        }
1818
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1819
                    } elseif ($conf['comparison'] === 64) {
1820
                        if (is_array($conf['inputValue'])) {
1821
                            $conf['inputValue'] = $conf['inputValue'][0];
1822
                        }
1823
                        $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1824
                    } else {
1825
                        $lineHTML[] = '<select class="form-select t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1826
                    }
1827
                    if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
1828
                        $lineHTML[] = $this->makeOptionList($fieldName, $conf, $this->table);
1829
                        $lineHTML[] = '</select>';
1830
                    }
1831
                    $lineHTML[] = '</div>';
1832
                    break;
1833
                case 'boolean':
1834
                    $lineHTML[] = '<div class="form-inline">';
1835
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1836
                    $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]">';
1837
                    $lineHTML[] = '</div>';
1838
                    break;
1839
                default:
1840
                    $lineHTML[] = '<div class="form-inline">';
1841
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1842
                    if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
1843
                        // between:
1844
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1845
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]">';
1846
                    } else {
1847
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1848
                    }
1849
                    $lineHTML[] = '</div>';
1850
            }
1851
            if ($fieldType !== 'ignore') {
1852
                $lineHTML[] = '<div class="btn-group" style="margin-top: .5em;">';
1853
                $lineHTML[] = $this->updateIcon();
1854
                if ($loopCount) {
1855
                    $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
1856
                }
1857
                $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
1858
                if ($c != 0) {
1859
                    $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
1860
                }
1861
                if ($c != 0 && $fieldType !== 'newlevel') {
1862
                    $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
1863
                }
1864
                if ($fieldType === 'newlevel') {
1865
                    $lineHTML[] = '<button class="btn btn-default" title="Collapse new level" name="qG_remnl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-left fa-fw"></i></button>';
1866
                }
1867
                $lineHTML[] = '</div>';
1868
                $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
1869
                $codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c === 0);
1870
                $arrCount++;
1871
                $c++;
1872
            }
1873
            $loopCount = 1;
1874
        }
1875
        $this->queryConfig = $queryConfig;
1876
        return $codeArr;
1877
    }
1878
1879
    /**
1880
     * @param string $subscript
1881
     * @param string $fieldName
1882
     * @param array $conf
1883
     *
1884
     * @return string
1885
     */
1886
    protected function makeComparisonSelector($subscript, $fieldName, $conf)
1887
    {
1888
        $fieldPrefix = $this->name . $subscript;
1889
        $lineHTML = [];
1890
        $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
1891
        $lineHTML[] = '	<div class="input-group">';
1892
        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
1893
        $lineHTML[] = '	<div class="input-group-addon">';
1894
        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
1895
        $lineHTML[] = '	</div>';
1896
        $lineHTML[] = '	</div>';
1897
        return implode(LF, $lineHTML);
1898
    }
1899
1900
    /**
1901
     * Make option list
1902
     *
1903
     * @param string $fieldName
1904
     * @param array $conf
1905
     * @param string $table
1906
     * @return string
1907
     */
1908
    protected function makeOptionList($fieldName, $conf, $table)
1909
    {
1910
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1911
        $from_table_Arr = [];
1912
        $out = [];
1913
        $fieldSetup = $this->fields[$fieldName];
1914
        $languageService = $this->getLanguageService();
1915
        if ($fieldSetup['type'] === 'multiple') {
1916
            $optGroupOpen = false;
1917
            foreach ($fieldSetup['items'] as $key => $val) {
1918
                if (strpos($val[0], 'LLL:') === 0) {
1919
                    $value = $languageService->sL($val[0]);
1920
                } else {
1921
                    $value = $val[0];
1922
                }
1923
                if ($val[1] === '--div--') {
1924
                    if ($optGroupOpen) {
1925
                        $out[] = '</optgroup>';
1926
                    }
1927
                    $optGroupOpen = true;
1928
                    $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
1929
                } elseif (GeneralUtility::inList($conf['inputValue'], $val[1])) {
1930
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
1931
                } else {
1932
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
1933
                }
1934
            }
1935
            if ($optGroupOpen) {
1936
                $out[] = '</optgroup>';
1937
            }
1938
        }
1939
        if ($fieldSetup['type'] === 'binary') {
1940
            foreach ($fieldSetup['items'] as $key => $val) {
1941
                if (strpos($val[0], 'LLL:') === 0) {
1942
                    $value = $languageService->sL($val[0]);
1943
                } else {
1944
                    $value = $val[0];
1945
                }
1946
                if (GeneralUtility::inList($conf['inputValue'], (string)(2 ** $key))) {
1947
                    $out[] = '<option value="' . 2 ** $key . '" selected>' . htmlspecialchars($value) . '</option>';
1948
                } else {
1949
                    $out[] = '<option value="' . 2 ** $key . '">' . htmlspecialchars($value) . '</option>';
1950
                }
1951
            }
1952
        }
1953
        if ($fieldSetup['type'] === 'relation') {
1954
            $useTablePrefix = 0;
1955
            $dontPrefixFirstTable = 0;
1956
            if ($fieldSetup['items']) {
1957
                foreach ($fieldSetup['items'] as $key => $val) {
1958
                    if (strpos($val[0], 'LLL:') === 0) {
1959
                        $value = $languageService->sL($val[0]);
1960
                    } else {
1961
                        $value = $val[0];
1962
                    }
1963
                    if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
1964
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
1965
                    } else {
1966
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
1967
                    }
1968
                }
1969
            }
1970
            if (strpos($fieldSetup['allowed'], ',') !== false) {
1971
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
1972
                $useTablePrefix = 1;
1973
                if (!$fieldSetup['prepend_tname']) {
1974
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1975
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1976
                    $statement = $queryBuilder->select($fieldName)
1977
                        ->from($table)
1978
                        ->execute();
1979
                    while ($row = $statement->fetch()) {
1980
                        if (strpos($row[$fieldName], ',') !== false) {
1981
                            $checkContent = explode(',', $row[$fieldName]);
1982
                            foreach ($checkContent as $singleValue) {
1983
                                if (strpos($singleValue, '_') === false) {
1984
                                    $dontPrefixFirstTable = 1;
1985
                                }
1986
                            }
1987
                        } else {
1988
                            $singleValue = $row[$fieldName];
1989
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
1990
                                $dontPrefixFirstTable = 1;
1991
                            }
1992
                        }
1993
                    }
1994
                }
1995
            } else {
1996
                $from_table_Arr[0] = $fieldSetup['allowed'];
1997
            }
1998
            if ($fieldSetup['prepend_tname']) {
1999
                $useTablePrefix = 1;
2000
            }
2001
            if ($fieldSetup['foreign_table']) {
2002
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
2003
            }
2004
            $counter = 0;
2005
            $tablePrefix = '';
2006
            $outArray = [];
2007
            $labelFieldSelect = [];
2008
            foreach ($from_table_Arr as $from_table) {
2009
                $useSelectLabels = false;
2010
                $useAltSelectLabels = false;
2011
                if ($useTablePrefix && !$dontPrefixFirstTable && $counter != 1 || $counter === 1) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($useTablePrefix && ! $d...!= 1) || $counter === 1, Probably Intended Meaning: $useTablePrefix && ! $do...!= 1 || $counter === 1)
Loading history...
2012
                    $tablePrefix = $from_table . '_';
2013
                }
2014
                $counter = 1;
2015
                if (is_array($GLOBALS['TCA'][$from_table])) {
2016
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
2017
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
2018
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
2019
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
2020
                            if (strpos($labelArray[0], 'LLL:') === 0) {
2021
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
2022
                            } else {
2023
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
2024
                            }
2025
                        }
2026
                        $useSelectLabels = true;
2027
                    }
2028
                    $altLabelFieldSelect = [];
2029
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
2030
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
2031
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
2032
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
2033
                            } else {
2034
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
2035
                            }
2036
                        }
2037
                        $useAltSelectLabels = true;
2038
                    }
2039
2040
                    if (!$this->tableArray[$from_table]) {
2041
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
2042
                        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
2043
                            $queryBuilder->getRestrictions()->removeAll();
2044
                        } else {
2045
                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2046
                        }
2047
                        $selectFields = ['uid', $labelField];
2048
                        if ($altLabelField) {
2049
                            $selectFields[] = $altLabelField;
2050
                        }
2051
                        $queryBuilder->select(...$selectFields)
2052
                            ->from($from_table)
2053
                            ->orderBy('uid');
2054
                        if (!$backendUserAuthentication->isAdmin()) {
2055
                            $webMounts = $backendUserAuthentication->returnWebmounts();
2056
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
2057
                            $webMountPageTree = '';
2058
                            $webMountPageTreePrefix = '';
2059
                            foreach ($webMounts as $webMount) {
2060
                                if ($webMountPageTree) {
2061
                                    $webMountPageTreePrefix = ',';
2062
                                }
2063
                                $webMountPageTree .= $webMountPageTreePrefix
2064
                                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
2065
                            }
2066
                            if ($from_table === 'pages') {
2067
                                $queryBuilder->where(
2068
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
2069
                                    $queryBuilder->expr()->in(
2070
                                        'uid',
2071
                                        $queryBuilder->createNamedParameter(
2072
                                            GeneralUtility::intExplode(',', $webMountPageTree),
2073
                                            Connection::PARAM_INT_ARRAY
2074
                                        )
2075
                                    )
2076
                                );
2077
                            } else {
2078
                                $queryBuilder->where(
2079
                                    $queryBuilder->expr()->in(
2080
                                        'pid',
2081
                                        $queryBuilder->createNamedParameter(
2082
                                            GeneralUtility::intExplode(',', $webMountPageTree),
2083
                                            Connection::PARAM_INT_ARRAY
2084
                                        )
2085
                                    )
2086
                                );
2087
                            }
2088
                        }
2089
                        $statement = $queryBuilder->execute();
2090
                        $this->tableArray[$from_table] = [];
2091
                        while ($row = $statement->fetch()) {
2092
                            $this->tableArray[$from_table][] = $row;
2093
                        }
2094
                    }
2095
2096
                    foreach ($this->tableArray[$from_table] as $key => $val) {
2097
                        if ($useSelectLabels) {
2098
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
2099
                        } elseif ($val[$labelField]) {
2100
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
2101
                        } elseif ($useAltSelectLabels) {
2102
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
2103
                        } else {
2104
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
2105
                        }
2106
                    }
2107
                    if (isset($this->settings['options_sortlabel']) && $this->settings['options_sortlabel'] && is_array($outArray)) {
2108
                        natcasesort($outArray);
2109
                    }
2110
                }
2111
            }
2112
            foreach ($outArray as $key2 => $val2) {
2113
                if (GeneralUtility::inList($conf['inputValue'], $key2)) {
2114
                    $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2115
                } else {
2116
                    $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2117
                }
2118
            }
2119
        }
2120
        return implode(LF, $out);
2121
    }
2122
2123
    /**
2124
     * Print code array
2125
     *
2126
     * @param array $codeArr
2127
     * @param int $recursionLevel
2128
     * @return string
2129
     */
2130
    protected function printCodeArray($codeArr, $recursionLevel = 0)
2131
    {
2132
        $out = [];
2133
        foreach ($codeArr as $k => $v) {
2134
            $out[] = '<div class="card">';
2135
            $out[] = '<div class="card-content">';
2136
            $out[] = $v['html'];
2137
2138
            if ($this->enableQueryParts) {
2139
                $out[] = '<pre>';
2140
                $out[] = htmlspecialchars($v['query']);
2141
                $out[] = '</pre>';
2142
            }
2143
            if (is_array($v['sub'])) {
2144
                $out[] = '<div>';
2145
                $out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
2146
                $out[] = '</div>';
2147
            }
2148
            $out[] = '</div>';
2149
            $out[] = '</div>';
2150
        }
2151
        return implode(LF, $out);
2152
    }
2153
2154
    /**
2155
     * Make operator select
2156
     *
2157
     * @param string $name
2158
     * @param string $op
2159
     * @param bool $draw
2160
     * @param bool $submit
2161
     * @return string
2162
     */
2163
    protected function mkOperatorSelect($name, $op, $draw, $submit)
2164
    {
2165
        $out = [];
2166
        if ($draw) {
2167
            $out[] = '<div class="form-inline">';
2168
            $out[] = '<select class="form-select' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars($name) . '[operator]">';
2169
            $out[] = '	<option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
2170
            $out[] = '	<option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
2171
            $out[] = '</select>';
2172
            $out[] = '</div>';
2173
        } else {
2174
            $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars($name) . '[operator]">';
2175
        }
2176
        return implode(LF, $out);
2177
    }
2178
2179
    /**
2180
     * Make type select
2181
     *
2182
     * @param string $name
2183
     * @param string $fieldName
2184
     * @param string $prepend
2185
     * @return string
2186
     */
2187
    protected function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
2188
    {
2189
        $out = [];
2190
        $out[] = '<select class="form-select t3js-submit-change" name="' . htmlspecialchars($name) . '">';
2191
        $out[] = '<option value=""></option>';
2192
        foreach ($this->fields as $key => $value) {
2193
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2194
                $label = $this->fields[$key]['label'];
2195
                if ($this->showFieldAndTableNames) {
2196
                    $label .= ' [' . $key . ']';
2197
                }
2198
                $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2199
            }
2200
        }
2201
        $out[] = '</select>';
2202
        return implode(LF, $out);
2203
    }
2204
2205
    /**
2206
     * Verify type
2207
     *
2208
     * @param string $fieldName
2209
     * @return string
2210
     */
2211
    protected function verifyType($fieldName)
2212
    {
2213
        $first = '';
2214
        foreach ($this->fields as $key => $value) {
2215
            if (!$first) {
2216
                $first = $key;
2217
            }
2218
            if ($key === $fieldName) {
2219
                return $key;
2220
            }
2221
        }
2222
        return $first;
2223
    }
2224
2225
    /**
2226
     * Verify comparison
2227
     *
2228
     * @param string $comparison
2229
     * @param int $neg
2230
     * @return int
2231
     */
2232
    protected function verifyComparison($comparison, $neg)
2233
    {
2234
        $compOffSet = $comparison >> 5;
2235
        $first = -1;
2236
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2237
            if ($first === -1) {
2238
                $first = $i;
2239
            }
2240
            if ($i >> 1 === $comparison >> 1) {
2241
                return $i;
2242
            }
2243
        }
2244
        return $first;
2245
    }
2246
2247
    /**
2248
     * Make field to input select
2249
     *
2250
     * @param string $name
2251
     * @param string $fieldName
2252
     * @return string
2253
     */
2254
    protected function mkFieldToInputSelect($name, $fieldName)
2255
    {
2256
        $out = [];
2257
        $out[] = '<div class="input-group" style="margin-bottom: .5em;">';
2258
        $out[] = '	<span class="input-group-btn">';
2259
        $out[] = $this->updateIcon();
2260
        $out[] = ' 	</span>';
2261
        $out[] = '	<input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars($fieldName) . '" name="' . htmlspecialchars($name) . '">';
2262
        $out[] = '</div>';
2263
2264
        $out[] = '<select class="form-select t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars($name) . '">';
2265
        foreach ($this->fields as $key => $value) {
2266
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2267
                $label = $this->fields[$key]['label'];
2268
                if ($this->showFieldAndTableNames) {
2269
                    $label .= ' [' . $key . ']';
2270
                }
2271
                $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2272
            }
2273
        }
2274
        $out[] = '</select>';
2275
        return implode(LF, $out);
2276
    }
2277
2278
    /**
2279
     * Make table select
2280
     *
2281
     * @param string $name
2282
     * @param string $cur
2283
     * @return string
2284
     */
2285
    protected function mkTableSelect($name, $cur)
2286
    {
2287
        $out = [];
2288
        $out[] = '<select class="form-select t3js-submit-change" name="' . $name . '">';
2289
        $out[] = '<option value=""></option>';
2290
        foreach ($GLOBALS['TCA'] as $tN => $value) {
2291
            if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
2292
                $label = $this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title']);
2293
                if ($this->showFieldAndTableNames) {
2294
                    $label .= ' [' . $tN . ']';
2295
                }
2296
                $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2297
            }
2298
        }
2299
        $out[] = '</select>';
2300
        return implode(LF, $out);
2301
    }
2302
2303
    /**
2304
     * Make comparison select
2305
     *
2306
     * @param string $name
2307
     * @param string $comparison
2308
     * @param int $neg
2309
     * @return string
2310
     */
2311
    protected function mkCompSelect($name, $comparison, $neg)
2312
    {
2313
        $compOffSet = $comparison >> 5;
2314
        $out = [];
2315
        $out[] = '<select class="form-select t3js-submit-change" name="' . $name . '">';
2316
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2317
            if ($this->lang['comparison'][$i . '_']) {
2318
                $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
2319
            }
2320
        }
2321
        $out[] = '</select>';
2322
        return implode(LF, $out);
2323
    }
2324
2325
    /**
2326
     * Get subscript
2327
     *
2328
     * @param array $arr
2329
     * @return array
2330
     */
2331
    protected function getSubscript($arr): array
2332
    {
2333
        $retArr = [];
2334
        while (\is_array($arr)) {
2335
            reset($arr);
2336
            $key = key($arr);
2337
            $retArr[] = $key;
2338
            if (isset($arr[$key])) {
2339
                $arr = $arr[$key];
2340
            } else {
2341
                break;
2342
            }
2343
        }
2344
        return $retArr;
2345
    }
2346
2347
    /**
2348
     * Get query
2349
     *
2350
     * @param array $queryConfig
2351
     * @param string $pad
2352
     * @return string
2353
     */
2354
    protected function getQuery($queryConfig, $pad = '')
2355
    {
2356
        $qs = '';
2357
        // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
2358
        ksort($queryConfig);
2359
        $first = true;
2360
        foreach ($queryConfig as $key => $conf) {
2361
            $conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
2362
            switch ($conf['type']) {
2363
                case 'newlevel':
2364
                    $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
2365
                        $queryConfig[$key]['nl'],
2366
                        $pad . '   '
2367
                    ) . LF . $pad . ')';
2368
                    break;
2369
                default:
2370
                    $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
2371
            }
2372
            $first = false;
2373
        }
2374
        return $qs;
2375
    }
2376
2377
    /**
2378
     * Convert ISO-8601 timestamp (string) into unix timestamp (int)
2379
     *
2380
     * @param array $conf
2381
     * @return array
2382
     */
2383
    protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
2384
    {
2385
        if ($this->isDateOfIso8601Format($conf['inputValue'])) {
2386
            $conf['inputValue'] = strtotime($conf['inputValue']);
2387
            if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
2388
                $conf['inputValue1'] = strtotime($conf['inputValue1']);
2389
            }
2390
        }
2391
2392
        return $conf;
2393
    }
2394
2395
    /**
2396
     * Checks if the given value is of the ISO 8601 format.
2397
     *
2398
     * @param mixed $date
2399
     * @return bool
2400
     */
2401
    protected function isDateOfIso8601Format($date): bool
2402
    {
2403
        if (!is_int($date) && !is_string($date)) {
2404
            return false;
2405
        }
2406
        $format = 'Y-m-d\\TH:i:s\\Z';
2407
        $formattedDate = \DateTime::createFromFormat($format, (string)$date);
2408
        return $formattedDate && $formattedDate->format($format) === $date;
2409
    }
2410
2411
    /**
2412
     * Get single query
2413
     *
2414
     * @param array $conf
2415
     * @param bool $first
2416
     * @return string
2417
     */
2418
    protected function getQuerySingle($conf, $first)
2419
    {
2420
        $qs = '';
2421
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
2422
        $prefix = $this->enablePrefix ? $this->table . '.' : '';
2423
        if (!$first) {
2424
            // Is it OK to insert the AND operator if none is set?
2425
            $operator = strtoupper(trim($conf['operator']));
2426
            if (!in_array($operator, ['AND', 'OR'], true)) {
2427
                $operator = 'AND';
2428
            }
2429
            $qs .= $operator . ' ';
2430
        }
2431
        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
2432
        $inputVal = $this->cleanInputVal($conf);
2433
        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
2434
            $inputVal = explode(',', $inputVal);
2435
            foreach ($inputVal as $key => $fileName) {
2436
                $inputVal[$key] = $queryBuilder->quote($fileName);
2437
            }
2438
            $inputVal = implode(',', $inputVal);
2439
            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
2440
        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
2441
            $inputValArray = explode(',', $inputVal);
2442
            $inputVal = 0;
2443
            foreach ($inputValArray as $fileName) {
2444
                $inputVal += (int)$fileName;
2445
            }
2446
            $qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
2447
        } else {
2448
            if (is_array($inputVal)) {
0 ignored issues
show
introduced by
The condition is_array($inputVal) is always false.
Loading history...
2449
                $inputVal = $inputVal[0];
2450
            }
2451
            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
2452
        }
2453
        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
2454
            // between:
2455
            $inputVal = $this->cleanInputVal($conf, '1');
2456
            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
2457
        }
2458
        $qs .= trim((string)$qsTmp);
2459
        return $qs;
2460
    }
2461
2462
    /**
2463
     * Clear input value
2464
     *
2465
     * @param array $conf
2466
     * @param string $suffix
2467
     * @return string
2468
     */
2469
    protected function cleanInputVal($conf, $suffix = '')
2470
    {
2471
        if ($conf['comparison'] >> 5 === 0 || ($conf['comparison'] === 32 || $conf['comparison'] === 33 || $conf['comparison'] === 64 || $conf['comparison'] === 65 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 96 || $conf['comparison'] === 97)) {
2472
            $inputVal = $conf['inputValue' . $suffix];
2473
        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
2474
            // in list:
2475
            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
2476
        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
2477
            // in list:
2478
            if (is_array($conf['inputValue' . $suffix])) {
2479
                $inputVal = implode(',', $conf['inputValue' . $suffix]);
2480
            } elseif ($conf['inputValue' . $suffix]) {
2481
                $inputVal = $conf['inputValue' . $suffix];
2482
            } else {
2483
                $inputVal = 0;
2484
            }
2485
        } elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
2486
            $inputVal = $conf['inputValue' . $suffix];
2487
        } elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
2488
            $inputVal = (int)$conf['inputValue' . $suffix];
2489
        } else {
2490
            // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
2491
            // fallback to float casting, the whole class smells like it needs a refactoring.
2492
            $inputVal = (float)$conf['inputValue' . $suffix];
2493
        }
2494
        return $inputVal;
2495
    }
2496
2497
    /**
2498
     * Update icon
2499
     *
2500
     * @return string
2501
     */
2502
    protected function updateIcon()
2503
    {
2504
        return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
2505
    }
2506
2507
    /**
2508
     * Get label column
2509
     *
2510
     * @return string
2511
     */
2512
    protected function getLabelCol()
2513
    {
2514
        return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
2515
    }
2516
2517
    /**
2518
     * Make selector table
2519
     *
2520
     * @param array $modSettings
2521
     * @param string $enableList
2522
     * @return string
2523
     */
2524
    protected function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
2525
    {
2526
        $out = [];
2527
        $enableArr = explode(',', $enableList);
2528
        $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
2529
2530
        // Make output
2531
        if (in_array('table', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectATable'] ?? false)) {
2532
            $out[] = '<div class="form-group">';
2533
            $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
2534
            $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
2535
            $out[] = '</div>';
2536
        }
2537
        if ($this->table) {
2538
            // Init fields:
2539
            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'] ?? '', 'uid,' . $this->getLabelCol());
2540
            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup'] ?? '');
2541
            $this->setAndCleanUpExternalLists('queryOrder', ($modSettings['queryOrder'] ?? '') . ',' . ($modSettings['queryOrder2'] ?? ''));
2542
            // Limit:
2543
            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'] ?? '';
2544
            if (!$this->extFieldLists['queryLimit']) {
2545
                $this->extFieldLists['queryLimit'] = 100;
2546
            }
2547
            $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
2548
            $limitBegin = 0;
2549
            $limitLength = (int)($this->extFieldLists['queryLimit'] ?? 0);
2550
            if ($parts[1]) {
2551
                $limitBegin = (int)$parts[0];
2552
                $limitLength = (int)$parts[1];
2553
            }
2554
            $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
2555
            // Insert Descending parts
2556
            if ($this->extFieldLists['queryOrder']) {
2557
                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
2558
                $orderParts = explode(',', $this->extFieldLists['queryOrder']);
2559
                $reList = [];
2560
                foreach ($orderParts as $kk => $vv) {
2561
                    $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
2562
                }
2563
                $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
2564
            }
2565
            // Query Generator:
2566
            $this->procesData(($modSettings['queryConfig'] ?? false) ? unserialize($modSettings['queryConfig'] ?? '', ['allowed_classes' => false]) : []);
2567
            $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
2568
            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
2569
            $codeArr = $this->getFormElements();
2570
            $queryCode = $this->printCodeArray($codeArr);
2571
            if (in_array('fields', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectFields'] ?? false)) {
2572
                $out[] = '<div class="form-group form-group-with-button-addon">';
2573
                $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
2574
                $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
2575
                $out[] = '</div>';
2576
            }
2577
            if (in_array('query', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableMakeQuery'] ?? false)) {
2578
                $out[] = '<div class="form-group">';
2579
                $out[] = '	<label>Make Query:</label>';
2580
                $out[] =    $queryCode;
2581
                $out[] = '</div>';
2582
            }
2583
            if (in_array('group', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableGroupBy'] ?? false)) {
2584
                $out[] = '<div class="form-group form-inline">';
2585
                $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
2586
                $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
2587
                $out[] = '</div>';
2588
            }
2589
            if (in_array('order', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableOrderBy'] ?? false)) {
2590
                $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
2591
                $orderBy = [];
2592
                $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
2593
                $orderBy[] = '<div class="form-check">';
2594
                $orderBy[] =    BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'] ?? '', '', '', 'id="checkQueryOrderDesc"');
2595
                $orderBy[] = '	<label class="form-check-label" for="checkQueryOrderDesc">Descending</label>';
2596
                $orderBy[] = '</div>';
2597
2598
                if ($orderByArr[0]) {
2599
                    $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
2600
                    $orderBy[] = '<div class="form-check">';
2601
                    $orderBy[] =    BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"') . ' Descending';
2602
                    $orderBy[] = '	<label class="form-check-label" for="checkQueryOrder2Desc">Descending</label>';
2603
                    $orderBy[] = '</div>';
2604
                }
2605
                $out[] = '<div class="form-group form-inline">';
2606
                $out[] = '	<label>Order By:</label>';
2607
                $out[] =     implode(LF, $orderBy);
2608
                $out[] = '</div>';
2609
            }
2610
            if (in_array('limit', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableLimit'] ?? false)) {
2611
                $limit = [];
2612
                $limit[] = '<div class="input-group">';
2613
                $limit[] = '	<span class="input-group-btn">';
2614
                $limit[] = $this->updateIcon();
2615
                $limit[] = '	</span>';
2616
                $limit[] = '	<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
2617
                $limit[] = '</div>';
2618
2619
                $prevLimit = $limitBegin - $limitLength < 0 ? 0 : $limitBegin - $limitLength;
2620
                $prevButton = '';
2621
                $nextButton = '';
2622
2623
                if ($limitBegin) {
2624
                    $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $limitLength) . '">';
2625
                }
2626
                if (!$limitLength) {
2627
                    $limitLength = 100;
2628
                }
2629
2630
                $nextLimit = $limitBegin + $limitLength;
2631
                if ($nextLimit < 0) {
2632
                    $nextLimit = 0;
2633
                }
2634
                if ($nextLimit) {
2635
                    $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars((string)$limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $limitLength) . '">';
2636
                }
2637
2638
                $out[] = '<div class="form-group">';
2639
                $out[] = '	<label>Limit:</label>';
2640
                $out[] = '	<div class="form-inline">';
2641
                $out[] =        implode(LF, $limit);
2642
                $out[] = '		<div class="btn-group t3js-limit-submit">';
2643
                $out[] =            $prevButton;
2644
                $out[] =            $nextButton;
2645
                $out[] = '		</div>';
2646
                $out[] = '		<div class="btn-group t3js-limit-submit">';
2647
                $out[] = '			<input type="button" class="btn btn-default" data-value="10" value="10">';
2648
                $out[] = '			<input type="button" class="btn btn-default" data-value="20" value="20">';
2649
                $out[] = '			<input type="button" class="btn btn-default" data-value="50" value="50">';
2650
                $out[] = '			<input type="button" class="btn btn-default" data-value="100" value="100">';
2651
                $out[] = '		</div>';
2652
                $out[] = '	</div>';
2653
                $out[] = '</div>';
2654
            }
2655
        }
2656
        return implode(LF, $out);
2657
    }
2658
2659
    /**
2660
     * Get select query
2661
     *
2662
     * @param string $qString
2663
     * @return string
2664
     */
2665
    protected function getSelectQuery($qString = ''): string
2666
    {
2667
        $backendUserAuthentication = $this->getBackendUserAuthentication();
2668
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
2669
        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
2670
            $queryBuilder->getRestrictions()->removeAll();
2671
        } else {
2672
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2673
        }
2674
        $fieldList = GeneralUtility::trimExplode(
2675
            ',',
2676
            $this->extFieldLists['queryFields']
2677
            . ',pid'
2678
            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
2679
        );
2680
        $queryBuilder->select(...$fieldList)
2681
            ->from($this->table);
2682
2683
        if ($this->extFieldLists['queryGroup']) {
2684
            $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
2685
        }
2686
        if ($this->extFieldLists['queryOrder']) {
2687
            foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
2688
                [$fieldName, $order] = $orderPair;
2689
                $queryBuilder->addOrderBy($fieldName, $order);
2690
            }
2691
        }
2692
        if ($this->extFieldLists['queryLimit']) {
2693
            $queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
2694
        }
2695
2696
        if (!$backendUserAuthentication->isAdmin()) {
2697
            $webMounts = $backendUserAuthentication->returnWebmounts();
2698
            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
2699
            $webMountPageTree = '';
2700
            $webMountPageTreePrefix = '';
2701
            foreach ($webMounts as $webMount) {
2702
                if ($webMountPageTree) {
2703
                    $webMountPageTreePrefix = ',';
2704
                }
2705
                $webMountPageTree .= $webMountPageTreePrefix
2706
                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
2707
            }
2708
            // createNamedParameter() is not used here because the SQL fragment will only include
2709
            // the :dcValueX placeholder when the query is returned as a string. The value for the
2710
            // placeholder would be lost in the process.
2711
            if ($this->table === 'pages') {
2712
                $queryBuilder->where(
2713
                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
2714
                    $queryBuilder->expr()->in(
2715
                        'uid',
2716
                        GeneralUtility::intExplode(',', $webMountPageTree)
2717
                    )
2718
                );
2719
            } else {
2720
                $queryBuilder->where(
2721
                    $queryBuilder->expr()->in(
2722
                        'pid',
2723
                        GeneralUtility::intExplode(',', $webMountPageTree)
2724
                    )
2725
                );
2726
            }
2727
        }
2728
        if (!$qString) {
2729
            $qString = $this->getQuery($this->queryConfig);
2730
        }
2731
        $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
2732
2733
        return $queryBuilder->getSQL();
2734
    }
2735
2736
    /**
2737
     * @param string $name the field name
2738
     * @param string $timestamp ISO-8601 timestamp
2739
     * @param string $type [datetime, date, time, timesec, year]
2740
     *
2741
     * @return string
2742
     */
2743
    protected function getDateTimePickerField($name, $timestamp, $type)
2744
    {
2745
        $value = strtotime($timestamp) ? date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
2746
        $id = StringUtility::getUniqueId('dt_');
2747
        $html = [];
2748
        $html[] = '<div class="input-group" id="' . $id . '-wrapper">';
2749
        $html[] = '		<input data-formengine-input-name="' . htmlspecialchars($name) . '" value="' . $value . '" class="form-control t3js-datetimepicker t3js-clearable" data-date-type="' . htmlspecialchars($type) . '" type="text" id="' . $id . '">';
2750
        $html[] = '		<input name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
2751
        $html[] = '		<span class="input-group-btn">';
2752
        $html[] = '			<label class="btn btn-default" for="' . $id . '">';
2753
        $html[] = '				<span class="fa fa-calendar"></span>';
2754
        $html[] = '			</label>';
2755
        $html[] = ' 	</span>';
2756
        $html[] = '</div>';
2757
        return implode(LF, $html);
2758
    }
2759
2760
    protected function getBackendUserAuthentication(): BackendUserAuthentication
2761
    {
2762
        return $GLOBALS['BE_USER'];
2763
    }
2764
2765
    protected function getLanguageService(): LanguageService
2766
    {
2767
        return $GLOBALS['LANG'];
2768
    }
2769
}
2770