Completed
Push — master ( 1c797c...9045f9 )
by
unknown
15:15
created

QueryGenerator::procesData()   F

Complexity

Conditions 18
Paths 224

Size

Total Lines 98
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 18
eloc 66
c 2
b 0
f 0
nc 224
nop 1
dl 0
loc 98
rs 3.7333

How to fix   Long Method    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\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 int
279
     */
280
    protected $limitBegin;
281
282
    /**
283
     * @var int
284
     */
285
    protected $limitLength;
286
287
    /**
288
     * @var string
289
     */
290
    protected $fieldName;
291
292
    public function __construct(array $settings, array $menuItems, string $moduleName)
293
    {
294
        $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf');
295
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
296
        $this->settings = $settings;
297
        $this->menuItems = $menuItems;
298
        $this->moduleName = $moduleName;
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']) {
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 = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $enablePrefix was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
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()->fetchColumn(0)];
368
                    } else {
369
                        $fullQueryString = $selectQueryString;
370
                        $dataRows = $connection->executeQuery($selectQueryString)->fetchAll();
371
                    }
372
                    if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
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
                        . $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()->fetchColumn(0);
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="form-group form-inline">';
509
        $markup[] = '    <div class="form-group">';
510
        $markup[] = '      <select class="form-control" name="storeControl[STORE]" data-assign-store-control-title>' . implode(LF, $opt) . '</select>';
511
        $markup[] = '      <input class="form-control" name="storeControl[title]" value="" type="text" max="80">';
512
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[LOAD]" value="Load">';
513
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[SAVE]" value="Save">';
514
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[REMOVE]" value="Remove">';
515
        $markup[] = '    </div>';
516
        $markup[] = '  </div>';
517
        $markup[] = '</div>';
518
519
        return implode(LF, $markup);
520
    }
521
522
    /**
523
     * Init store array
524
     *
525
     * @return array
526
     */
527
    protected function initStoreArray()
528
    {
529
        $storeArray = [
530
            '0' => '[New]'
531
        ];
532
        $savedStoreArray = unserialize($this->settings['storeArray'], ['allowed_classes' => false]);
533
        if (is_array($savedStoreArray)) {
534
            $storeArray = array_merge($storeArray, $savedStoreArray);
535
        }
536
        return $storeArray;
537
    }
538
539
    /**
540
     * Clean store query configs
541
     *
542
     * @param array $storeQueryConfigs
543
     * @param array $storeArray
544
     * @return array
545
     */
546
    protected function cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
547
    {
548
        if (is_array($storeQueryConfigs)) {
0 ignored issues
show
introduced by
The condition is_array($storeQueryConfigs) is always true.
Loading history...
549
            foreach ($storeQueryConfigs as $k => $v) {
550
                if (!isset($storeArray[$k])) {
551
                    unset($storeQueryConfigs[$k]);
552
                }
553
            }
554
        }
555
        return $storeQueryConfigs;
556
    }
557
558
    /**
559
     * Add to store query configs
560
     *
561
     * @param array $storeQueryConfigs
562
     * @param int $index
563
     * @return array
564
     */
565
    protected function addToStoreQueryConfigs($storeQueryConfigs, $index)
566
    {
567
        $keyArr = explode(',', $this->storeList);
568
        $storeQueryConfigs[$index] = [];
569
        foreach ($keyArr as $k) {
570
            $storeQueryConfigs[$index][$k] = $this->settings[$k];
571
        }
572
        return $storeQueryConfigs;
573
    }
574
575
    /**
576
     * Save query in action
577
     *
578
     * @param int $uid
579
     * @return int
580
     */
581
    protected function saveQueryInAction($uid)
582
    {
583
        if (ExtensionManagementUtility::isLoaded('sys_action')) {
584
            $keyArr = explode(',', $this->storeList);
585
            $saveArr = [];
586
            foreach ($keyArr as $k) {
587
                $saveArr[$k] = $this->settings[$k];
588
            }
589
            // Show query
590
            if ($saveArr['queryTable']) {
591
                $this->init('queryConfig', $saveArr['queryTable'], '', $this->settings);
592
                $this->makeSelectorTable($saveArr);
593
                $this->enablePrefix = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $enablePrefix was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
594
                $queryString = $this->getQuery($this->queryConfig);
595
596
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
597
                    ->getQueryBuilderForTable($this->table);
598
                $queryBuilder->getRestrictions()->removeAll()
599
                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
600
                $rowCount = $queryBuilder->count('*')
601
                    ->from($this->table)
602
                    ->where(QueryHelper::stripLogicalOperatorPrefix($queryString))
603
                    ->execute()->fetchColumn(0);
604
605
                $t2DataValue = [
606
                    'qC' => $saveArr,
607
                    'qCount' => $rowCount,
608
                    'qSelect' => $this->getSelectQuery($queryString),
609
                    'qString' => $queryString
610
                ];
611
                GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_action')
612
                    ->update(
613
                        'sys_action',
614
                        ['t2_data' => serialize($t2DataValue)],
615
                        ['uid' => (int)$uid],
616
                        ['t2_data' => Connection::PARAM_LOB]
617
                    );
618
            }
619
            return 1;
620
        }
621
        return null;
622
    }
623
    /**
624
     * Load store query configs
625
     *
626
     * @param array $storeQueryConfigs
627
     * @param int $storeIndex
628
     * @param array $writeArray
629
     * @return array
630
     */
631
    protected function loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
632
    {
633
        if ($storeQueryConfigs[$storeIndex]) {
634
            $keyArr = explode(',', $this->storeList);
635
            foreach ($keyArr as $k) {
636
                $writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
637
            }
638
        }
639
        return $writeArray;
640
    }
641
642
    /**
643
     * Process store control
644
     *
645
     * @return string
646
     */
647
    protected function procesStoreControl()
648
    {
649
        $languageService = $this->getLanguageService();
650
        $flashMessage = null;
651
        $storeArray = $this->initStoreArray();
652
        $storeQueryConfigs = unserialize($this->settings['storeQueryConfigs'], ['allowed_classes' => false]);
653
        $storeControl = GeneralUtility::_GP('storeControl');
654
        $storeIndex = (int)$storeControl['STORE'];
655
        $saveStoreArray = 0;
656
        $writeArray = [];
657
        $msg = '';
658
        if (is_array($storeControl)) {
659
            if ($storeControl['LOAD']) {
660
                if ($storeIndex > 0) {
661
                    $writeArray = $this->loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
662
                    $saveStoreArray = 1;
663
                    $flashMessage = GeneralUtility::makeInstance(
664
                        FlashMessage::class,
665
                        sprintf($languageService->getLL('query_loaded'), $storeArray[$storeIndex])
666
                    );
667
                } elseif ($storeIndex < 0 && ExtensionManagementUtility::isLoaded('sys_action')) {
668
                    $actionRecord = BackendUtility::getRecord('sys_action', abs($storeIndex));
0 ignored issues
show
Bug introduced by
It seems like abs($storeIndex) can also be of type double; however, parameter $uid of TYPO3\CMS\Backend\Utilit...endUtility::getRecord() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

668
                    $actionRecord = BackendUtility::getRecord('sys_action', /** @scrutinizer ignore-type */ abs($storeIndex));
Loading history...
669
                    if (is_array($actionRecord)) {
670
                        $dA = unserialize($actionRecord['t2_data'], ['allowed_classes' => false]);
671
                        $dbSC = [];
672
                        if (is_array($dA['qC'])) {
673
                            $dbSC[0] = $dA['qC'];
674
                        }
675
                        $writeArray = $this->loadStoreQueryConfigs($dbSC, 0, $writeArray);
676
                        $saveStoreArray = 1;
677
                        $flashMessage = GeneralUtility::makeInstance(
678
                            FlashMessage::class,
679
                            sprintf($languageService->getLL('query_from_action_loaded'), $actionRecord['title'])
680
                        );
681
                    }
682
                }
683
            } elseif ($storeControl['SAVE']) {
684
                if ($storeIndex < 0) {
685
                    $qOK = $this->saveQueryInAction(abs($storeIndex));
0 ignored issues
show
Bug introduced by
It seems like abs($storeIndex) can also be of type double; however, parameter $uid of TYPO3\CMS\Lowlevel\Datab...or::saveQueryInAction() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

685
                    $qOK = $this->saveQueryInAction(/** @scrutinizer ignore-type */ abs($storeIndex));
Loading history...
686
                    if ($qOK) {
687
                        $flashMessage = GeneralUtility::makeInstance(
688
                            FlashMessage::class,
689
                            $languageService->getLL('query_saved')
690
                        );
691
                    } else {
692
                        $flashMessage = GeneralUtility::makeInstance(
693
                            FlashMessage::class,
694
                            $languageService->getLL('query_notsaved'),
695
                            '',
696
                            FlashMessage::ERROR
697
                        );
698
                    }
699
                } else {
700
                    if (trim($storeControl['title'])) {
701
                        if ($storeIndex > 0) {
702
                            $storeArray[$storeIndex] = $storeControl['title'];
703
                        } else {
704
                            $storeArray[] = $storeControl['title'];
705
                            end($storeArray);
706
                            $storeIndex = key($storeArray);
707
                        }
708
                        $storeQueryConfigs = $this->addToStoreQueryConfigs($storeQueryConfigs, (int)$storeIndex);
709
                        $saveStoreArray = 1;
710
                        $flashMessage = GeneralUtility::makeInstance(
711
                            FlashMessage::class,
712
                            $languageService->getLL('query_saved')
713
                        );
714
                    }
715
                }
716
            } elseif ($storeControl['REMOVE']) {
717
                if ($storeIndex > 0) {
718
                    $flashMessage = GeneralUtility::makeInstance(
719
                        FlashMessage::class,
720
                        sprintf($languageService->getLL('query_removed'), $storeArray[$storeControl['STORE']])
721
                    );
722
                    // Removing
723
                    unset($storeArray[$storeControl['STORE']]);
724
                    $saveStoreArray = 1;
725
                }
726
            }
727
            if (!empty($flashMessage)) {
728
                $msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
729
                    ->resolve()
730
                    ->render([$flashMessage]);
731
            }
732
        }
733
        if ($saveStoreArray) {
734
            // Making sure, index 0 is not set!
735
            unset($storeArray[0]);
736
            $writeArray['storeArray'] = serialize($storeArray);
737
            $writeArray['storeQueryConfigs'] =
738
                serialize($this->cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
739
            $this->settings = BackendUtility::getModuleData(
740
                $this->menuItems,
741
                $writeArray,
742
                $this->moduleName,
743
                'ses'
744
            );
745
        }
746
        return $msg;
747
    }
748
749
    /**
750
     * Get query result code
751
     *
752
     * @param string $type
753
     * @param array $dataRows Rows to display
754
     * @param string $table
755
     * @return array HTML-code for "header" and "content"
756
     * @throws \TYPO3\CMS\Core\Exception
757
     */
758
    protected function getQueryResultCode($type, array $dataRows, $table)
759
    {
760
        $out = '';
761
        $cPR = [];
762
        switch ($type) {
763
            case 'count':
764
                $cPR['header'] = 'Count';
765
                $cPR['content'] = '<br><strong>' . (int)$dataRows[0] . '</strong> records selected.';
766
                break;
767
            case 'all':
768
                $rowArr = [];
769
                $dataRow = null;
770
                foreach ($dataRows as $dataRow) {
771
                    $rowArr[] = $this->resultRowDisplay($dataRow, $GLOBALS['TCA'][$table], $table);
772
                }
773
                if (is_array($this->hookArray['beforeResultTable'])) {
774
                    foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
775
                        $out .= GeneralUtility::callUserFunction($_funcRef, $this->settings);
776
                    }
777
                }
778
                if (!empty($rowArr)) {
779
                    $cPR['header'] = 'Result';
780
                    $out .= '<table class="table table-striped table-hover">'
781
                        . $this->resultRowTitles($dataRow, $GLOBALS['TCA'][$table]) . implode(LF, $rowArr)
782
                        . '</table>';
783
                } else {
784
                    $this->renderNoResultsFoundMessage();
785
                }
786
787
                $cPR['content'] = $out;
788
                break;
789
            case 'csv':
790
                $rowArr = [];
791
                $first = 1;
792
                foreach ($dataRows as $dataRow) {
793
                    if ($first) {
794
                        $rowArr[] = $this->csvValues(array_keys($dataRow), ',', '');
795
                        $first = 0;
796
                    }
797
                    $rowArr[] = $this->csvValues($dataRow, ',', '"', $GLOBALS['TCA'][$table], $table);
798
                }
799
                if (!empty($rowArr)) {
800
                    $cPR['header'] = 'Result';
801
                    $out .= '<textarea name="whatever" rows="20" class="text-monospace" style="width:100%">'
802
                        . htmlspecialchars(implode(LF, $rowArr))
803
                        . '</textarea>';
804
                    if (!$this->noDownloadB) {
805
                        $out .= '<br><input class="btn btn-default" type="submit" name="download_file" '
806
                            . 'value="Click to download file">';
807
                    }
808
                    // Downloads file:
809
                    // @todo: args. routing anyone?
810
                    if (GeneralUtility::_GP('download_file')) {
811
                        $filename = 'TYPO3_' . $table . '_export_' . date('dmy-Hi') . '.csv';
812
                        $mimeType = 'application/octet-stream';
813
                        header('Content-Type: ' . $mimeType);
814
                        header('Content-Disposition: attachment; filename=' . $filename);
815
                        echo implode(CRLF, $rowArr);
816
                        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...
817
                    }
818
                } else {
819
                    $this->renderNoResultsFoundMessage();
820
                }
821
                $cPR['content'] = $out;
822
                break;
823
            case 'explain':
824
            default:
825
                foreach ($dataRows as $dataRow) {
826
                    $out .= '<br />' . DebugUtility::viewArray($dataRow);
827
                }
828
                $cPR['header'] = 'Explain SQL query';
829
                $cPR['content'] = $out;
830
        }
831
        return $cPR;
832
    }
833
    /**
834
     * CSV values
835
     *
836
     * @param array $row
837
     * @param string $delim
838
     * @param string $quote
839
     * @param array $conf
840
     * @param string $table
841
     * @return string A single line of CSV
842
     */
843
    protected function csvValues($row, $delim = ',', $quote = '"', $conf = [], $table = '')
844
    {
845
        $valueArray = $row;
846
        if ($this->settings['search_result_labels'] && $table) {
847
            foreach ($valueArray as $key => $val) {
848
                $valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';');
849
            }
850
        }
851
        return CsvUtility::csvValues($valueArray, $delim, $quote);
852
    }
853
854
    /**
855
     * Result row display
856
     *
857
     * @param array $row
858
     * @param array $conf
859
     * @param string $table
860
     * @return string
861
     */
862
    protected function resultRowDisplay($row, $conf, $table)
863
    {
864
        $languageService = $this->getLanguageService();
865
        $out = '<tr>';
866
        foreach ($row as $fieldName => $fieldValue) {
867
            if (GeneralUtility::inList($this->settings['queryFields'], $fieldName)
868
                || !$this->settings['queryFields']
869
                && $fieldName !== 'pid'
870
                && $fieldName !== 'deleted'
871
            ) {
872
                if ($this->settings['search_result_labels']) {
873
                    $fVnew = $this->getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, '<br />');
874
                } else {
875
                    $fVnew = htmlspecialchars($fieldValue);
876
                }
877
                $out .= '<td>' . $fVnew . '</td>';
878
            }
879
        }
880
        $out .= '<td>';
881
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
882
883
        if (!$row['deleted']) {
884
            $out .= '<div class="btn-group" role="group">';
885
            $url = (string)$uriBuilder->buildUriFromRoute('record_edit', [
886
                'edit' => [
887
                    $table => [
888
                        $row['uid'] => 'edit'
889
                    ]
890
                ],
891
                'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
892
                    . HttpUtility::buildQueryString(['SET' => (array)GeneralUtility::_POST('SET')], '&')
893
            ]);
894
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars($url) . '">'
895
                . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
896
            $out .= '</div><div class="btn-group" role="group">';
897
            $out .= sprintf(
898
                '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
899
                'TYPO3.InfoWindow.showItem',
900
                htmlspecialchars($table . ',' . $row['uid']),
901
                $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
902
            );
903
            $out .= '</div>';
904
        } else {
905
            $out .= '<div class="btn-group" role="group">';
906
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
907
                        'cmd' => [
908
                            $table => [
909
                                $row['uid'] => [
910
                                    'undelete' => 1
911
                                ]
912
                            ]
913
                        ],
914
                        'redirect' => GeneralUtility::linkThisScript()
915
                    ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_only')) . '">';
916
            $out .= $this->iconFactory->getIcon('actions-edit-restore', Icon::SIZE_SMALL)->render() . '</a>';
917
            $formEngineParameters = [
918
                'edit' => [
919
                    $table => [
920
                        $row['uid'] => 'edit'
921
                    ]
922
                ],
923
                'returnUrl' => GeneralUtility::linkThisScript()
924
            ];
925
            $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $formEngineParameters);
926
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
927
                    'cmd' => [
928
                        $table => [
929
                            $row['uid'] => [
930
                                'undelete' => 1
931
                            ]
932
                        ]
933
                    ],
934
                    'redirect' => $redirectUrl
935
                ])) . '" title="' . htmlspecialchars($languageService->getLL('undelete_and_edit')) . '">';
936
            $out .= $this->iconFactory->getIcon('actions-edit-restore-edit', Icon::SIZE_SMALL)->render() . '</a>';
937
            $out .= '</div>';
938
        }
939
        $_params = [$table => $row];
940
        if (is_array($this->hookArray['additionalButtons'])) {
941
            foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
942
                $out .= GeneralUtility::callUserFunction($_funcRef, $_params);
943
            }
944
        }
945
        $out .= '</td></tr>';
946
        return $out;
947
    }
948
949
    /**
950
     * Get processed value extra
951
     *
952
     * @param string $table
953
     * @param string $fieldName
954
     * @param string $fieldValue
955
     * @param array $conf Not used
956
     * @param string $splitString
957
     * @return string
958
     */
959
    protected function getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, $splitString)
960
    {
961
        $out = '';
962
        $fields = [];
963
        // Analysing the fields in the table.
964
        if (is_array($GLOBALS['TCA'][$table])) {
965
            $fC = $GLOBALS['TCA'][$table]['columns'][$fieldName];
966
            $fields = $fC['config'];
967
            $fields['exclude'] = $fC['exclude'];
968
            if (is_array($fC) && $fC['label']) {
969
                $fields['label'] = preg_replace('/:$/', '', trim($this->getLanguageService()->sL($fC['label'])));
970
                switch ($fields['type']) {
971
                    case 'input':
972
                        if (preg_match('/int|year/i', $fields['eval'])) {
973
                            $fields['type'] = 'number';
974
                        } elseif (preg_match('/time/i', $fields['eval'])) {
975
                            $fields['type'] = 'time';
976
                        } elseif (preg_match('/date/i', $fields['eval'])) {
977
                            $fields['type'] = 'date';
978
                        } else {
979
                            $fields['type'] = 'text';
980
                        }
981
                        break;
982
                    case 'check':
983
                        if (!$fields['items']) {
984
                            $fields['type'] = 'boolean';
985
                        } else {
986
                            $fields['type'] = 'binary';
987
                        }
988
                        break;
989
                    case 'radio':
990
                        $fields['type'] = 'multiple';
991
                        break;
992
                    case 'select':
993
                        $fields['type'] = 'multiple';
994
                        if ($fields['foreign_table']) {
995
                            $fields['type'] = 'relation';
996
                        }
997
                        if ($fields['special']) {
998
                            $fields['type'] = 'text';
999
                        }
1000
                        break;
1001
                    case 'group':
1002
                        if ($fields['internal_type'] === 'db') {
1003
                            $fields['type'] = 'relation';
1004
                        }
1005
                        break;
1006
                    case 'user':
1007
                    case 'flex':
1008
                    case 'passthrough':
1009
                    case 'none':
1010
                    case 'text':
1011
                    default:
1012
                        $fields['type'] = 'text';
1013
                }
1014
            } else {
1015
                $fields['label'] = '[FIELD: ' . $fieldName . ']';
1016
                switch ($fieldName) {
1017
                    case 'pid':
1018
                        $fields['type'] = 'relation';
1019
                        $fields['allowed'] = 'pages';
1020
                        break;
1021
                    case 'cruser_id':
1022
                        $fields['type'] = 'relation';
1023
                        $fields['allowed'] = 'be_users';
1024
                        break;
1025
                    case 'tstamp':
1026
                    case 'crdate':
1027
                        $fields['type'] = 'time';
1028
                        break;
1029
                    default:
1030
                        $fields['type'] = 'number';
1031
                }
1032
            }
1033
        }
1034
        switch ($fields['type']) {
1035
            case 'date':
1036
                if ($fieldValue != -1) {
1037
                    $out = strftime('%d-%m-%Y', (int)$fieldValue);
1038
                }
1039
                break;
1040
            case 'time':
1041
                if ($fieldValue != -1) {
1042
                    if ($splitString === '<br />') {
1043
                        $out = strftime('%H:%M' . $splitString . '%d-%m-%Y', (int)$fieldValue);
1044
                    } else {
1045
                        $out = strftime('%H:%M %d-%m-%Y', (int)$fieldValue);
1046
                    }
1047
                }
1048
                break;
1049
            case 'multiple':
1050
            case 'binary':
1051
            case 'relation':
1052
                $out = $this->makeValueList($fieldName, $fieldValue, $fields, $table, $splitString);
1053
                break;
1054
            case 'boolean':
1055
                $out = $fieldValue ? 'True' : 'False';
1056
                break;
1057
            default:
1058
                $out = htmlspecialchars($fieldValue);
1059
        }
1060
        return $out;
1061
    }
1062
1063
    /**
1064
     * Recursively fetch all descendants of a given page
1065
     *
1066
     * @param int $id uid of the page
1067
     * @param int $depth
1068
     * @param int $begin
1069
     * @param string $permsClause
1070
     * @return string comma separated list of descendant pages
1071
     */
1072
    protected function getTreeList($id, $depth, $begin = 0, $permsClause = '')
1073
    {
1074
        $depth = (int)$depth;
1075
        $begin = (int)$begin;
1076
        $id = (int)$id;
1077
        if ($id < 0) {
1078
            $id = abs($id);
1079
        }
1080
        if ($begin == 0) {
1081
            $theList = $id;
1082
        } else {
1083
            $theList = '';
1084
        }
1085
        if ($id && $depth > 0) {
1086
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1087
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1088
            $statement = $queryBuilder->select('uid')
1089
                ->from('pages')
1090
                ->where(
1091
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
1092
                    $queryBuilder->expr()->eq('sys_language_uid', 0),
1093
                    QueryHelper::stripLogicalOperatorPrefix($permsClause)
1094
                )
1095
                ->execute();
1096
            while ($row = $statement->fetch()) {
1097
                if ($begin <= 0) {
1098
                    $theList .= ',' . $row['uid'];
1099
                }
1100
                if ($depth > 1) {
1101
                    $theSubList = $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
1102
                    if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
1103
                        $theList .= ',';
1104
                    }
1105
                    $theList .= $theSubList;
1106
                }
1107
            }
1108
        }
1109
        return $theList;
1110
    }
1111
1112
    /**
1113
     * Make value list
1114
     *
1115
     * @param string $fieldName
1116
     * @param string $fieldValue
1117
     * @param array $conf
1118
     * @param string $table
1119
     * @param string $splitString
1120
     * @return string
1121
     */
1122
    protected function makeValueList($fieldName, $fieldValue, $conf, $table, $splitString)
1123
    {
1124
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1125
        $languageService = $this->getLanguageService();
1126
        $from_table_Arr = [];
1127
        $fieldSetup = $conf;
1128
        $out = '';
1129
        if ($fieldSetup['type'] === 'multiple') {
1130
            foreach ($fieldSetup['items'] as $key => $val) {
1131
                if (strpos($val[0], 'LLL:') === 0) {
1132
                    $value = $languageService->sL($val[0]);
1133
                } else {
1134
                    $value = $val[0];
1135
                }
1136
                if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) {
1137
                    if ($out !== '') {
1138
                        $out .= $splitString;
1139
                    }
1140
                    $out .= htmlspecialchars($value);
1141
                }
1142
            }
1143
        }
1144
        if ($fieldSetup['type'] === 'binary') {
1145
            foreach ($fieldSetup['items'] as $Key => $val) {
1146
                if (strpos($val[0], 'LLL:') === 0) {
1147
                    $value = $languageService->sL($val[0]);
1148
                } else {
1149
                    $value = $val[0];
1150
                }
1151
                if ($out !== '') {
1152
                    $out .= $splitString;
1153
                }
1154
                $out .= htmlspecialchars($value);
1155
            }
1156
        }
1157
        if ($fieldSetup['type'] === 'relation') {
1158
            $dontPrefixFirstTable = 0;
1159
            $useTablePrefix = 0;
1160
            if ($fieldSetup['items']) {
1161
                foreach ($fieldSetup['items'] as $key => $val) {
1162
                    if (strpos($val[0], 'LLL:') === 0) {
1163
                        $value = $languageService->sL($val[0]);
1164
                    } else {
1165
                        $value = $val[0];
1166
                    }
1167
                    if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
1168
                        if ($out !== '') {
1169
                            $out .= $splitString;
1170
                        }
1171
                        $out .= htmlspecialchars($value);
1172
                    }
1173
                }
1174
            }
1175
            if (strpos($fieldSetup['allowed'], ',') !== false) {
1176
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
1177
                $useTablePrefix = 1;
1178
                if (!$fieldSetup['prepend_tname']) {
1179
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1180
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1181
                    $statement = $queryBuilder->select($fieldName)->from($table)->execute();
1182
                    while ($row = $statement->fetch()) {
1183
                        if (strpos($row[$fieldName], ',') !== false) {
1184
                            $checkContent = explode(',', $row[$fieldName]);
1185
                            foreach ($checkContent as $singleValue) {
1186
                                if (strpos($singleValue, '_') === false) {
1187
                                    $dontPrefixFirstTable = 1;
1188
                                }
1189
                            }
1190
                        } else {
1191
                            $singleValue = $row[$fieldName];
1192
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
1193
                                $dontPrefixFirstTable = 1;
1194
                            }
1195
                        }
1196
                    }
1197
                }
1198
            } else {
1199
                $from_table_Arr[0] = $fieldSetup['allowed'];
1200
            }
1201
            if ($fieldSetup['prepend_tname']) {
1202
                $useTablePrefix = 1;
1203
            }
1204
            if ($fieldSetup['foreign_table']) {
1205
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
1206
            }
1207
            $counter = 0;
1208
            $useSelectLabels = 0;
1209
            $useAltSelectLabels = 0;
1210
            $tablePrefix = '';
1211
            $labelFieldSelect = [];
1212
            foreach ($from_table_Arr as $from_table) {
1213
                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...
1214
                    $tablePrefix = $from_table . '_';
1215
                }
1216
                $counter = 1;
1217
                if (is_array($GLOBALS['TCA'][$from_table])) {
1218
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
1219
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
1220
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
1221
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'];
1222
                        foreach ($items as $labelArray) {
1223
                            if (strpos($labelArray[0], 'LLL:') === 0) {
1224
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
1225
                            } else {
1226
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
1227
                            }
1228
                        }
1229
                        $useSelectLabels = 1;
1230
                    }
1231
                    $altLabelFieldSelect = [];
1232
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
1233
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
1234
                        foreach ($items as $altLabelArray) {
1235
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
1236
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
1237
                            } else {
1238
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
1239
                            }
1240
                        }
1241
                        $useAltSelectLabels = 1;
1242
                    }
1243
1244
                    if (!$this->tableArray[$from_table]) {
1245
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1246
                        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1247
                        $selectFields = ['uid', $labelField];
1248
                        if ($altLabelField) {
1249
                            $selectFields[] = $altLabelField;
1250
                        }
1251
                        $queryBuilder->select(...$selectFields)
1252
                            ->from($from_table)
1253
                            ->orderBy('uid');
1254
                        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
1255
                            $webMounts = $backendUserAuthentication->returnWebmounts();
1256
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1257
                            $webMountPageTree = '';
1258
                            $webMountPageTreePrefix = '';
1259
                            foreach ($webMounts as $webMount) {
1260
                                if ($webMountPageTree) {
1261
                                    $webMountPageTreePrefix = ',';
1262
                                }
1263
                                $webMountPageTree .= $webMountPageTreePrefix
1264
                                    . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause);
1265
                            }
1266
                            if ($from_table === 'pages') {
1267
                                $queryBuilder->where(
1268
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1269
                                    $queryBuilder->expr()->in(
1270
                                        'uid',
1271
                                        $queryBuilder->createNamedParameter(
1272
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1273
                                            Connection::PARAM_INT_ARRAY
1274
                                        )
1275
                                    )
1276
                                );
1277
                            } else {
1278
                                $queryBuilder->where(
1279
                                    $queryBuilder->expr()->in(
1280
                                        'pid',
1281
                                        $queryBuilder->createNamedParameter(
1282
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1283
                                            Connection::PARAM_INT_ARRAY
1284
                                        )
1285
                                    )
1286
                                );
1287
                            }
1288
                        }
1289
                        $statement = $queryBuilder->execute();
1290
                        $this->tableArray[$from_table] = [];
1291
                        while ($row = $statement->fetch()) {
1292
                            $this->tableArray[$from_table][] = $row;
1293
                        }
1294
                    }
1295
1296
                    foreach ($this->tableArray[$from_table] as $key => $val) {
1297
                        $this->settings['labels_noprefix'] =
1298
                            $this->settings['labels_noprefix'] == 1
1299
                                ? 'on'
1300
                                : $this->settings['labels_noprefix'];
1301
                        $prefixString =
1302
                            $this->settings['labels_noprefix'] === 'on'
1303
                                ? ''
1304
                                : ' [' . $tablePrefix . $val['uid'] . '] ';
1305
                        if ($out !== '') {
1306
                            $out .= $splitString;
1307
                        }
1308
                        if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
1309
                            || $fieldValue == $tablePrefix . $val['uid']) {
1310
                            if ($useSelectLabels) {
1311
                                $out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
1312
                            } elseif ($val[$labelField]) {
1313
                                $out .= htmlspecialchars($prefixString . $val[$labelField]);
1314
                            } elseif ($useAltSelectLabels) {
1315
                                $out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
1316
                            } else {
1317
                                $out .= htmlspecialchars($prefixString . $val[$altLabelField]);
1318
                            }
1319
                        }
1320
                    }
1321
                }
1322
            }
1323
        }
1324
        return $out;
1325
    }
1326
1327
    /**
1328
     * Render table header
1329
     *
1330
     * @param array $row Table columns
1331
     * @param array $conf Table TCA
1332
     * @return string HTML of table header
1333
     */
1334
    protected function resultRowTitles($row, $conf)
1335
    {
1336
        $languageService = $this->getLanguageService();
1337
        $tableHeader = [];
1338
        // Start header row
1339
        $tableHeader[] = '<thead><tr>';
1340
        // Iterate over given columns
1341
        foreach ($row as $fieldName => $fieldValue) {
1342
            if (GeneralUtility::inList($this->settings['queryFields'], $fieldName)
1343
                || !$this->settings['queryFields']
1344
                && $fieldName !== 'pid'
1345
                && $fieldName !== 'deleted'
1346
            ) {
1347
                if ($this->settings['search_result_labels']) {
1348
                    $title = $languageService->sL($conf['columns'][$fieldName]['label']
1349
                        ?: $fieldName);
1350
                } else {
1351
                    $title = $languageService->sL($fieldName);
1352
                }
1353
                $tableHeader[] = '<th>' . htmlspecialchars($title) . '</th>';
1354
            }
1355
        }
1356
        // Add empty icon column
1357
        $tableHeader[] = '<th></th>';
1358
        // Close header row
1359
        $tableHeader[] = '</tr></thead>';
1360
        return implode(LF, $tableHeader);
1361
    }
1362
    /**
1363
     * @throws \InvalidArgumentException
1364
     * @throws \TYPO3\CMS\Core\Exception
1365
     */
1366
    private function renderNoResultsFoundMessage()
1367
    {
1368
        $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', FlashMessage::INFO);
1369
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1370
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1371
        $defaultFlashMessageQueue->enqueue($flashMessage);
1372
    }
1373
1374
    /**
1375
     * Make a list of fields for current table
1376
     *
1377
     * @return string Separated list of fields
1378
     */
1379
    protected function makeFieldList()
1380
    {
1381
        $fieldListArr = [];
1382
        if (is_array($GLOBALS['TCA'][$this->table])) {
1383
            $fieldListArr = array_keys($GLOBALS['TCA'][$this->table]['columns']);
1384
            $fieldListArr[] = 'uid';
1385
            $fieldListArr[] = 'pid';
1386
            $fieldListArr[] = 'deleted';
1387
            if ($GLOBALS['TCA'][$this->table]['ctrl']['tstamp']) {
1388
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['tstamp'];
1389
            }
1390
            if ($GLOBALS['TCA'][$this->table]['ctrl']['crdate']) {
1391
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['crdate'];
1392
            }
1393
            if ($GLOBALS['TCA'][$this->table]['ctrl']['cruser_id']) {
1394
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['cruser_id'];
1395
            }
1396
            if ($GLOBALS['TCA'][$this->table]['ctrl']['sortby']) {
1397
                $fieldListArr[] = $GLOBALS['TCA'][$this->table]['ctrl']['sortby'];
1398
            }
1399
        }
1400
        return implode(',', $fieldListArr);
1401
    }
1402
1403
    /**
1404
     * Init function
1405
     *
1406
     * @param string $name The name
1407
     * @param string $table The table name
1408
     * @param string $fieldList The field list
1409
     * @param array $settings Module settings like checkboxes in the interface
1410
     */
1411
    protected function init($name, $table, $fieldList = '', array $settings = [])
1412
    {
1413
        // Analysing the fields in the table.
1414
        if (is_array($GLOBALS['TCA'][$table])) {
1415
            $this->name = $name;
1416
            $this->table = $table;
1417
            $this->fieldList = $fieldList ?: $this->makeFieldList();
1418
            $this->settings = $settings;
1419
            $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
1420
            foreach ($fieldArr as $fieldName) {
1421
                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName];
1422
                $this->fields[$fieldName] = $fC['config'];
1423
                $this->fields[$fieldName]['exclude'] = $fC['exclude'];
1424
                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...ame]['type'] === 'none', Probably Intended Meaning: $this->fields[$fieldName...me]['type'] === 'none')
Loading history...
1425
                    || $this->fields[$fieldName]['type'] === 'none'
1426
                ) {
1427
                    // Do not list type=none "virtual" fields or query them from db,
1428
                    // and if type is user without defined userFunc
1429
                    unset($this->fields[$fieldName]);
1430
                    continue;
1431
                }
1432
                if (is_array($fC) && $fC['label']) {
1433
                    $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
1434
                    switch ($this->fields[$fieldName]['type']) {
1435
                        case 'input':
1436
                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
1437
                                $this->fields[$fieldName]['type'] = 'number';
1438
                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
1439
                                $this->fields[$fieldName]['type'] = 'time';
1440
                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
1441
                                $this->fields[$fieldName]['type'] = 'date';
1442
                            } else {
1443
                                $this->fields[$fieldName]['type'] = 'text';
1444
                            }
1445
                            break;
1446
                        case 'check':
1447
                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
1448
                                $this->fields[$fieldName]['type'] = 'boolean';
1449
                            } else {
1450
                                $this->fields[$fieldName]['type'] = 'binary';
1451
                            }
1452
                            break;
1453
                        case 'radio':
1454
                            $this->fields[$fieldName]['type'] = 'multiple';
1455
                            break;
1456
                        case 'select':
1457
                            $this->fields[$fieldName]['type'] = 'multiple';
1458
                            if ($this->fields[$fieldName]['foreign_table']) {
1459
                                $this->fields[$fieldName]['type'] = 'relation';
1460
                            }
1461
                            if ($this->fields[$fieldName]['special']) {
1462
                                $this->fields[$fieldName]['type'] = 'text';
1463
                            }
1464
                            break;
1465
                        case 'group':
1466
                            if ($this->fields[$fieldName]['internal_type'] === 'db') {
1467
                                $this->fields[$fieldName]['type'] = 'relation';
1468
                            }
1469
                            break;
1470
                        case 'user':
1471
                        case 'flex':
1472
                        case 'passthrough':
1473
                        case 'none':
1474
                        case 'text':
1475
                        default:
1476
                            $this->fields[$fieldName]['type'] = 'text';
1477
                    }
1478
                } else {
1479
                    $this->fields[$fieldName]['label'] = '[FIELD: ' . $fieldName . ']';
1480
                    switch ($fieldName) {
1481
                        case 'pid':
1482
                            $this->fields[$fieldName]['type'] = 'relation';
1483
                            $this->fields[$fieldName]['allowed'] = 'pages';
1484
                            break;
1485
                        case 'cruser_id':
1486
                            $this->fields[$fieldName]['type'] = 'relation';
1487
                            $this->fields[$fieldName]['allowed'] = 'be_users';
1488
                            break;
1489
                        case 'tstamp':
1490
                        case 'crdate':
1491
                            $this->fields[$fieldName]['type'] = 'time';
1492
                            break;
1493
                        case 'deleted':
1494
                            $this->fields[$fieldName]['type'] = 'boolean';
1495
                            break;
1496
                        default:
1497
                            $this->fields[$fieldName]['type'] = 'number';
1498
                    }
1499
                }
1500
            }
1501
        }
1502
        /*	// EXAMPLE:
1503
        $this->queryConfig = array(
1504
        array(
1505
        'operator' => 'AND',
1506
        'type' => 'FIELD_space_before_class',
1507
        ),
1508
        array(
1509
        'operator' => 'AND',
1510
        'type' => 'FIELD_records',
1511
        'negate' => 1,
1512
        'inputValue' => 'foo foo'
1513
        ),
1514
        array(
1515
        'type' => 'newlevel',
1516
        'nl' => array(
1517
        array(
1518
        'operator' => 'AND',
1519
        'type' => 'FIELD_space_before_class',
1520
        'negate' => 1,
1521
        'inputValue' => 'foo foo'
1522
        ),
1523
        array(
1524
        'operator' => 'AND',
1525
        'type' => 'FIELD_records',
1526
        'negate' => 1,
1527
        'inputValue' => 'foo foo'
1528
        )
1529
        )
1530
        ),
1531
        array(
1532
        'operator' => 'OR',
1533
        'type' => 'FIELD_maillist',
1534
        )
1535
        );
1536
         */
1537
    }
1538
1539
    /**
1540
     * Set and clean up external lists
1541
     *
1542
     * @param string $name The name
1543
     * @param string $list The list
1544
     * @param string $force
1545
     */
1546
    protected function setAndCleanUpExternalLists($name, $list, $force = '')
1547
    {
1548
        $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
1549
        $reList = [];
1550
        foreach ($fields as $fieldName) {
1551
            if ($this->fields[$fieldName]) {
1552
                $reList[] = $fieldName;
1553
            }
1554
        }
1555
        $this->extFieldLists[$name] = implode(',', $reList);
1556
    }
1557
1558
    /**
1559
     * Process data
1560
     *
1561
     * @param string $qC Query config
1562
     */
1563
    protected function procesData($qC = '')
1564
    {
1565
        $this->queryConfig = $qC;
0 ignored issues
show
Documentation Bug introduced by
It seems like $qC of type string is incompatible with the declared type array of property $queryConfig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1566
        $POST = GeneralUtility::_POST();
1567
        // If delete...
1568
        if ($POST['qG_del']) {
1569
            // Initialize array to work on, save special parameters
1570
            $ssArr = $this->getSubscript($POST['qG_del']);
1571
            $workArr = &$this->queryConfig;
1572
            $ssArrSize = count($ssArr) - 1;
1573
            $i = 0;
1574
            for (; $i < $ssArrSize; $i++) {
1575
                $workArr = &$workArr[$ssArr[$i]];
1576
            }
1577
            // Delete the entry and move the other entries
1578
            unset($workArr[$ssArr[$i]]);
1579
            $workArrSize = count((array)$workArr);
1580
            for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
1581
                $workArr[$j] = $workArr[$j + 1];
1582
                unset($workArr[$j + 1]);
1583
            }
1584
        }
1585
        // If insert...
1586
        if ($POST['qG_ins']) {
1587
            // Initialize array to work on, save special parameters
1588
            $ssArr = $this->getSubscript($POST['qG_ins']);
1589
            $workArr = &$this->queryConfig;
1590
            $ssArrSize = count($ssArr) - 1;
1591
            $i = 0;
1592
            for (; $i < $ssArrSize; $i++) {
1593
                $workArr = &$workArr[$ssArr[$i]];
1594
            }
1595
            // Move all entries above position where new entry is to be inserted
1596
            $workArrSize = count((array)$workArr);
1597
            for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
1598
                $workArr[$j] = $workArr[$j - 1];
1599
            }
1600
            // Clear new entry position
1601
            unset($workArr[$ssArr[$i] + 1]);
1602
            $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
1603
        }
1604
        // If move up...
1605
        if ($POST['qG_up']) {
1606
            // Initialize array to work on
1607
            $ssArr = $this->getSubscript($POST['qG_up']);
1608
            $workArr = &$this->queryConfig;
1609
            $ssArrSize = count($ssArr) - 1;
1610
            $i = 0;
1611
            for (; $i < $ssArrSize; $i++) {
1612
                $workArr = &$workArr[$ssArr[$i]];
1613
            }
1614
            // Swap entries
1615
            $qG_tmp = $workArr[$ssArr[$i]];
1616
            $workArr[$ssArr[$i]] = $workArr[$ssArr[$i] - 1];
1617
            $workArr[$ssArr[$i] - 1] = $qG_tmp;
1618
        }
1619
        // If new level...
1620
        if ($POST['qG_nl']) {
1621
            // Initialize array to work on
1622
            $ssArr = $this->getSubscript($POST['qG_nl']);
1623
            $workArr = &$this->queryConfig;
1624
            $ssArraySize = count($ssArr) - 1;
1625
            $i = 0;
1626
            for (; $i < $ssArraySize; $i++) {
1627
                $workArr = &$workArr[$ssArr[$i]];
1628
            }
1629
            // Do stuff:
1630
            $tempEl = $workArr[$ssArr[$i]];
1631
            if (is_array($tempEl)) {
1632
                if ($tempEl['type'] !== 'newlevel') {
1633
                    $workArr[$ssArr[$i]] = [
1634
                        'type' => 'newlevel',
1635
                        'operator' => $tempEl['operator'],
1636
                        'nl' => [$tempEl]
1637
                    ];
1638
                }
1639
            }
1640
        }
1641
        // If collapse level...
1642
        if ($POST['qG_remnl']) {
1643
            // Initialize array to work on
1644
            $ssArr = $this->getSubscript($POST['qG_remnl']);
1645
            $workArr = &$this->queryConfig;
1646
            $ssArrSize = count($ssArr) - 1;
1647
            $i = 0;
1648
            for (; $i < $ssArrSize; $i++) {
1649
                $workArr = &$workArr[$ssArr[$i]];
1650
            }
1651
            // Do stuff:
1652
            $tempEl = $workArr[$ssArr[$i]];
1653
            if (is_array($tempEl)) {
1654
                if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
1655
                    $a1 = array_slice($workArr, 0, $ssArr[$i]);
1656
                    $a2 = array_slice($workArr, $ssArr[$i]);
1657
                    array_shift($a2);
1658
                    $a3 = $tempEl['nl'];
1659
                    $a3[0]['operator'] = $tempEl['operator'];
1660
                    $workArr = array_merge($a1, $a3, $a2);
1661
                }
1662
            }
1663
        }
1664
    }
1665
1666
    /**
1667
     * Clean up query config
1668
     *
1669
     * @param array $queryConfig Query config
1670
     * @return array
1671
     */
1672
    protected function cleanUpQueryConfig($queryConfig)
1673
    {
1674
        // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
1675
        if (is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always true.
Loading history...
1676
            ksort($queryConfig);
1677
        } else {
1678
            // queryConfig should never be empty!
1679
            if (!isset($queryConfig[0]) || empty($queryConfig[0]['type'])) {
1680
                // Make sure queryConfig is an array
1681
                $queryConfig = [];
1682
                $queryConfig[0] = ['type' => 'FIELD_'];
1683
            }
1684
        }
1685
        // Traverse:
1686
        foreach ($queryConfig as $key => $conf) {
1687
            $fieldName = '';
1688
            if (strpos($conf['type'], 'FIELD_') === 0) {
1689
                $fieldName = substr($conf['type'], 6);
1690
                $fieldType = $this->fields[$fieldName]['type'];
1691
            } elseif ($conf['type'] === 'newlevel') {
1692
                $fieldType = $conf['type'];
1693
            } else {
1694
                $fieldType = 'ignore';
1695
            }
1696
            switch ($fieldType) {
1697
                case 'newlevel':
1698
                    if (!$queryConfig[$key]['nl']) {
1699
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1700
                    }
1701
                    $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
1702
                    break;
1703
                case 'userdef':
1704
                    break;
1705
                case 'ignore':
1706
                default:
1707
                    $verifiedName = $this->verifyType($fieldName);
1708
                    $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
1709
                    if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
1710
                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
1711
                    }
1712
                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
1713
                    $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
1714
                    $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], '1');
1715
            }
1716
        }
1717
        return $queryConfig;
1718
    }
1719
1720
    /**
1721
     * Get form elements
1722
     *
1723
     * @param int $subLevel
1724
     * @param string $queryConfig
1725
     * @param string $parent
1726
     * @return array
1727
     */
1728
    protected function getFormElements($subLevel = 0, $queryConfig = '', $parent = '')
1729
    {
1730
        $codeArr = [];
1731
        if (!is_array($queryConfig)) {
0 ignored issues
show
introduced by
The condition is_array($queryConfig) is always false.
Loading history...
1732
            $queryConfig = $this->queryConfig;
1733
        }
1734
        $c = 0;
1735
        $arrCount = 0;
1736
        $loopCount = 0;
1737
        foreach ($queryConfig as $key => $conf) {
1738
            $fieldName = '';
1739
            $subscript = $parent . '[' . $key . ']';
1740
            $lineHTML = [];
1741
            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], (bool)$c, $conf['type'] !== 'FIELD_');
1742
            if (strpos($conf['type'], 'FIELD_') === 0) {
1743
                $fieldName = substr($conf['type'], 6);
1744
                $this->fieldName = $fieldName;
1745
                $fieldType = $this->fields[$fieldName]['type'];
1746
                if ($conf['comparison'] >> 5 != $this->comp_offsets[$fieldType]) {
1747
                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
1748
                }
1749
                //nasty nasty...
1750
                //make sure queryConfig contains _actual_ comparevalue.
1751
                //mkCompSelect don't care, but getQuery does.
1752
                $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
1753
            } elseif ($conf['type'] === 'newlevel') {
1754
                $fieldType = $conf['type'];
1755
            } else {
1756
                $fieldType = 'ignore';
1757
            }
1758
            $fieldPrefix = htmlspecialchars($this->name . $subscript);
1759
            switch ($fieldType) {
1760
                case 'ignore':
1761
                    break;
1762
                case 'newlevel':
1763
                    if (!$queryConfig[$key]['nl']) {
1764
                        $queryConfig[$key]['nl'][0]['type'] = 'FIELD_';
1765
                    }
1766
                    $lineHTML[] = '<input type="hidden" name="' . $fieldPrefix . '[type]" value="newlevel">';
1767
                    $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
1768
                    break;
1769
                case 'userdef':
1770
                    $lineHTML[] = '';
1771
                    break;
1772
                case 'date':
1773
                    $lineHTML[] = '<div class="form-inline">';
1774
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1775
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1776
                        // between
1777
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1778
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'date');
1779
                    } else {
1780
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'date');
1781
                    }
1782
                    $lineHTML[] = '</div>';
1783
                    break;
1784
                case 'time':
1785
                    $lineHTML[] = '<div class="form-inline">';
1786
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1787
                    if ($conf['comparison'] === 100 || $conf['comparison'] === 101) {
1788
                        // between:
1789
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1790
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue1]', $conf['inputValue1'], 'datetime');
1791
                    } else {
1792
                        $lineHTML[] = $this->getDateTimePickerField($fieldPrefix . '[inputValue]', $conf['inputValue'], 'datetime');
1793
                    }
1794
                    $lineHTML[] = '</div>';
1795
                    break;
1796
                case 'multiple':
1797
                case 'binary':
1798
                case 'relation':
1799
                    $lineHTML[] = '<div class="form-inline">';
1800
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1801
                    if ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
1802
                        $lineHTML[] = '<select class="form-control" name="' . $fieldPrefix . '[inputValue][]" multiple="multiple">';
1803
                    } elseif ($conf['comparison'] === 66 || $conf['comparison'] === 67) {
1804
                        if (is_array($conf['inputValue'])) {
1805
                            $conf['inputValue'] = implode(',', $conf['inputValue']);
1806
                        }
1807
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1808
                    } elseif ($conf['comparison'] === 64) {
1809
                        if (is_array($conf['inputValue'])) {
1810
                            $conf['inputValue'] = $conf['inputValue'][0];
1811
                        }
1812
                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1813
                    } else {
1814
                        $lineHTML[] = '<select class="form-control t3js-submit-change" name="' . $fieldPrefix . '[inputValue]">';
1815
                    }
1816
                    if ($conf['comparison'] != 66 && $conf['comparison'] != 67) {
1817
                        $lineHTML[] = $this->makeOptionList($fieldName, $conf, $this->table);
1818
                        $lineHTML[] = '</select>';
1819
                    }
1820
                    $lineHTML[] = '</div>';
1821
                    break;
1822
                case 'boolean':
1823
                    $lineHTML[] = '<div class="form-inline">';
1824
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1825
                    $lineHTML[] = '<input type="hidden" value="1" name="' . $fieldPrefix . '[inputValue]">';
1826
                    $lineHTML[] = '</div>';
1827
                    break;
1828
                default:
1829
                    $lineHTML[] = '<div class="form-inline">';
1830
                    $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
1831
                    if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
1832
                        // between:
1833
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1834
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]">';
1835
                    } else {
1836
                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
1837
                    }
1838
                    $lineHTML[] = '</div>';
1839
            }
1840
            if ($fieldType !== 'ignore') {
1841
                $lineHTML[] = '<div class="btn-group" style="margin-top: .5em;">';
1842
                $lineHTML[] = $this->updateIcon();
1843
                if ($loopCount) {
1844
                    $lineHTML[] = '<button class="btn btn-default" title="Remove condition" name="qG_del' . htmlspecialchars($subscript) . '"><i class="fa fa-trash fa-fw"></i></button>';
1845
                }
1846
                $lineHTML[] = '<button class="btn btn-default" title="Add condition" name="qG_ins' . htmlspecialchars($subscript) . '"><i class="fa fa-plus fa-fw"></i></button>';
1847
                if ($c != 0) {
1848
                    $lineHTML[] = '<button class="btn btn-default" title="Move up" name="qG_up' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-up fa-fw"></i></button>';
1849
                }
1850
                if ($c != 0 && $fieldType !== 'newlevel') {
1851
                    $lineHTML[] = '<button class="btn btn-default" title="New level" name="qG_nl' . htmlspecialchars($subscript) . '"><i class="fa fa-chevron-right fa-fw"></i></button>';
1852
                }
1853
                if ($fieldType === 'newlevel') {
1854
                    $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>';
1855
                }
1856
                $lineHTML[] = '</div>';
1857
                $codeArr[$arrCount]['html'] = implode(LF, $lineHTML);
1858
                $codeArr[$arrCount]['query'] = $this->getQuerySingle($conf, $c === 0);
1859
                $arrCount++;
1860
                $c++;
1861
            }
1862
            $loopCount = 1;
1863
        }
1864
        $this->queryConfig = $queryConfig;
1865
        return $codeArr;
1866
    }
1867
1868
    /**
1869
     * @param string $subscript
1870
     * @param string $fieldName
1871
     * @param array $conf
1872
     *
1873
     * @return string
1874
     */
1875
    protected function makeComparisonSelector($subscript, $fieldName, $conf)
1876
    {
1877
        $fieldPrefix = $this->name . $subscript;
1878
        $lineHTML = [];
1879
        $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
1880
        $lineHTML[] = '	<div class="input-group">';
1881
        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
1882
        $lineHTML[] = '	<div class="input-group-addon">';
1883
        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
1884
        $lineHTML[] = '	</div>';
1885
        $lineHTML[] = '	</div>';
1886
        return implode(LF, $lineHTML);
1887
    }
1888
1889
    /**
1890
     * Make option list
1891
     *
1892
     * @param string $fieldName
1893
     * @param array $conf
1894
     * @param string $table
1895
     * @return string
1896
     */
1897
    protected function makeOptionList($fieldName, $conf, $table)
1898
    {
1899
        $backendUserAuthentication = $this->getBackendUserAuthentication();
1900
        $from_table_Arr = [];
1901
        $out = [];
1902
        $fieldSetup = $this->fields[$fieldName];
1903
        $languageService = $this->getLanguageService();
1904
        if ($fieldSetup['type'] === 'multiple') {
1905
            $optGroupOpen = false;
1906
            foreach ($fieldSetup['items'] as $key => $val) {
1907
                if (strpos($val[0], 'LLL:') === 0) {
1908
                    $value = $languageService->sL($val[0]);
1909
                } else {
1910
                    $value = $val[0];
1911
                }
1912
                if ($val[1] === '--div--') {
1913
                    if ($optGroupOpen) {
1914
                        $out[] = '</optgroup>';
1915
                    }
1916
                    $optGroupOpen = true;
1917
                    $out[] = '<optgroup label="' . htmlspecialchars($value) . '">';
1918
                } elseif (GeneralUtility::inList($conf['inputValue'], $val[1])) {
1919
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
1920
                } else {
1921
                    $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
1922
                }
1923
            }
1924
            if ($optGroupOpen) {
1925
                $out[] = '</optgroup>';
1926
            }
1927
        }
1928
        if ($fieldSetup['type'] === 'binary') {
1929
            foreach ($fieldSetup['items'] as $key => $val) {
1930
                if (strpos($val[0], 'LLL:') === 0) {
1931
                    $value = $languageService->sL($val[0]);
1932
                } else {
1933
                    $value = $val[0];
1934
                }
1935
                if (GeneralUtility::inList($conf['inputValue'], (string)(2 ** $key))) {
1936
                    $out[] = '<option value="' . 2 ** $key . '" selected>' . htmlspecialchars($value) . '</option>';
1937
                } else {
1938
                    $out[] = '<option value="' . 2 ** $key . '">' . htmlspecialchars($value) . '</option>';
1939
                }
1940
            }
1941
        }
1942
        if ($fieldSetup['type'] === 'relation') {
1943
            $useTablePrefix = 0;
1944
            $dontPrefixFirstTable = 0;
1945
            if ($fieldSetup['items']) {
1946
                foreach ($fieldSetup['items'] as $key => $val) {
1947
                    if (strpos($val[0], 'LLL:') === 0) {
1948
                        $value = $languageService->sL($val[0]);
1949
                    } else {
1950
                        $value = $val[0];
1951
                    }
1952
                    if (GeneralUtility::inList($conf['inputValue'], $val[1])) {
1953
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '" selected>' . htmlspecialchars($value) . '</option>';
1954
                    } else {
1955
                        $out[] = '<option value="' . htmlspecialchars($val[1]) . '">' . htmlspecialchars($value) . '</option>';
1956
                    }
1957
                }
1958
            }
1959
            if (strpos($fieldSetup['allowed'], ',') !== false) {
1960
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
1961
                $useTablePrefix = 1;
1962
                if (!$fieldSetup['prepend_tname']) {
1963
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1964
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1965
                    $statement = $queryBuilder->select($fieldName)
1966
                        ->from($table)
1967
                        ->execute();
1968
                    while ($row = $statement->fetch()) {
1969
                        if (strpos($row[$fieldName], ',') !== false) {
1970
                            $checkContent = explode(',', $row[$fieldName]);
1971
                            foreach ($checkContent as $singleValue) {
1972
                                if (strpos($singleValue, '_') === false) {
1973
                                    $dontPrefixFirstTable = 1;
1974
                                }
1975
                            }
1976
                        } else {
1977
                            $singleValue = $row[$fieldName];
1978
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
1979
                                $dontPrefixFirstTable = 1;
1980
                            }
1981
                        }
1982
                    }
1983
                }
1984
            } else {
1985
                $from_table_Arr[0] = $fieldSetup['allowed'];
1986
            }
1987
            if ($fieldSetup['prepend_tname']) {
1988
                $useTablePrefix = 1;
1989
            }
1990
            if ($fieldSetup['foreign_table']) {
1991
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
1992
            }
1993
            $counter = 0;
1994
            $tablePrefix = '';
1995
            $outArray = [];
1996
            $labelFieldSelect = [];
1997
            foreach ($from_table_Arr as $from_table) {
1998
                $useSelectLabels = false;
1999
                $useAltSelectLabels = false;
2000
                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...
2001
                    $tablePrefix = $from_table . '_';
2002
                }
2003
                $counter = 1;
2004
                if (is_array($GLOBALS['TCA'][$from_table])) {
2005
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
2006
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
2007
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
2008
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
2009
                            if (strpos($labelArray[0], 'LLL:') === 0) {
2010
                                $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
2011
                            } else {
2012
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
2013
                            }
2014
                        }
2015
                        $useSelectLabels = true;
2016
                    }
2017
                    $altLabelFieldSelect = [];
2018
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
2019
                        foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
2020
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
2021
                                $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
2022
                            } else {
2023
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
2024
                            }
2025
                        }
2026
                        $useAltSelectLabels = true;
2027
                    }
2028
2029
                    if (!$this->tableArray[$from_table]) {
2030
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
2031
                        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
2032
                            $queryBuilder->getRestrictions()->removeAll();
2033
                        } else {
2034
                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2035
                        }
2036
                        $selectFields = ['uid', $labelField];
2037
                        if ($altLabelField) {
2038
                            $selectFields[] = $altLabelField;
2039
                        }
2040
                        $queryBuilder->select(...$selectFields)
2041
                            ->from($from_table)
2042
                            ->orderBy('uid');
2043
                        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
2044
                            $webMounts = $backendUserAuthentication->returnWebmounts();
2045
                            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
2046
                            $webMountPageTree = '';
2047
                            $webMountPageTreePrefix = '';
2048
                            foreach ($webMounts as $webMount) {
2049
                                if ($webMountPageTree) {
2050
                                    $webMountPageTreePrefix = ',';
2051
                                }
2052
                                $webMountPageTree .= $webMountPageTreePrefix
2053
                                    . $this->getTreeList($webMount, 999, 0, $perms_clause);
2054
                            }
2055
                            if ($from_table === 'pages') {
2056
                                $queryBuilder->where(
2057
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
2058
                                    $queryBuilder->expr()->in(
2059
                                        'uid',
2060
                                        $queryBuilder->createNamedParameter(
2061
                                            GeneralUtility::intExplode(',', $webMountPageTree),
2062
                                            Connection::PARAM_INT_ARRAY
2063
                                        )
2064
                                    )
2065
                                );
2066
                            } else {
2067
                                $queryBuilder->where(
2068
                                    $queryBuilder->expr()->in(
2069
                                        'pid',
2070
                                        $queryBuilder->createNamedParameter(
2071
                                            GeneralUtility::intExplode(',', $webMountPageTree),
2072
                                            Connection::PARAM_INT_ARRAY
2073
                                        )
2074
                                    )
2075
                                );
2076
                            }
2077
                        }
2078
                        $statement = $queryBuilder->execute();
2079
                        $this->tableArray[$from_table] = [];
2080
                        while ($row = $statement->fetch()) {
2081
                            $this->tableArray[$from_table][] = $row;
2082
                        }
2083
                    }
2084
2085
                    foreach ($this->tableArray[$from_table] as $key => $val) {
2086
                        if ($useSelectLabels) {
2087
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
2088
                        } elseif ($val[$labelField]) {
2089
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$labelField]);
2090
                        } elseif ($useAltSelectLabels) {
2091
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($altLabelFieldSelect[$val[$altLabelField]]);
2092
                        } else {
2093
                            $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($val[$altLabelField]);
2094
                        }
2095
                    }
2096
                    if (isset($this->settings['options_sortlabel']) && $this->settings['options_sortlabel'] && is_array($outArray)) {
2097
                        natcasesort($outArray);
2098
                    }
2099
                }
2100
            }
2101
            foreach ($outArray as $key2 => $val2) {
2102
                if (GeneralUtility::inList($conf['inputValue'], $key2)) {
2103
                    $out[] = '<option value="' . htmlspecialchars($key2) . '" selected>[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2104
                } else {
2105
                    $out[] = '<option value="' . htmlspecialchars($key2) . '">[' . htmlspecialchars($key2) . '] ' . htmlspecialchars($val2) . '</option>';
2106
                }
2107
            }
2108
        }
2109
        return implode(LF, $out);
2110
    }
2111
2112
    /**
2113
     * Print code array
2114
     *
2115
     * @param array $codeArr
2116
     * @param int $recursionLevel
2117
     * @return string
2118
     */
2119
    protected function printCodeArray($codeArr, $recursionLevel = 0)
2120
    {
2121
        $out = [];
2122
        foreach ($codeArr as $k => $v) {
2123
            $out[] = '<div class="card">';
2124
            $out[] = '<div class="card-content">';
2125
            $out[] = $v['html'];
2126
2127
            if ($this->enableQueryParts) {
2128
                $out[] = '<pre>';
2129
                $out[] = htmlspecialchars($v['query']);
2130
                $out[] = '</pre>';
2131
            }
2132
            if (is_array($v['sub'])) {
2133
                $out[] = '<div>';
2134
                $out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
2135
                $out[] = '</div>';
2136
            }
2137
            $out[] = '</div>';
2138
            $out[] = '</div>';
2139
        }
2140
        return implode(LF, $out);
2141
    }
2142
2143
    /**
2144
     * Make operator select
2145
     *
2146
     * @param string $name
2147
     * @param string $op
2148
     * @param bool $draw
2149
     * @param bool $submit
2150
     * @return string
2151
     */
2152
    protected function mkOperatorSelect($name, $op, $draw, $submit)
2153
    {
2154
        $out = [];
2155
        if ($draw) {
2156
            $out[] = '<div class="form-inline">';
2157
            $out[] = '<select class="form-control' . ($submit ? ' t3js-submit-change' : '') . '" name="' . htmlspecialchars($name) . '[operator]">';
2158
            $out[] = '	<option value="AND"' . (!$op || $op === 'AND' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['AND']) . '</option>';
2159
            $out[] = '	<option value="OR"' . ($op === 'OR' ? ' selected' : '') . '>' . htmlspecialchars($this->lang['OR']) . '</option>';
2160
            $out[] = '</select>';
2161
            $out[] = '</div>';
2162
        } else {
2163
            $out[] = '<input type="hidden" value="' . htmlspecialchars($op) . '" name="' . htmlspecialchars($name) . '[operator]">';
2164
        }
2165
        return implode(LF, $out);
2166
    }
2167
2168
    /**
2169
     * Make type select
2170
     *
2171
     * @param string $name
2172
     * @param string $fieldName
2173
     * @param string $prepend
2174
     * @return string
2175
     */
2176
    protected function mkTypeSelect($name, $fieldName, $prepend = 'FIELD_')
2177
    {
2178
        $out = [];
2179
        $out[] = '<select class="form-control t3js-submit-change" name="' . htmlspecialchars($name) . '">';
2180
        $out[] = '<option value=""></option>';
2181
        foreach ($this->fields as $key => $value) {
2182
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2183
                $label = $this->fields[$key]['label'];
2184
                $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2185
            }
2186
        }
2187
        $out[] = '</select>';
2188
        return implode(LF, $out);
2189
    }
2190
2191
    /**
2192
     * Verify type
2193
     *
2194
     * @param string $fieldName
2195
     * @return string
2196
     */
2197
    protected function verifyType($fieldName)
2198
    {
2199
        $first = '';
2200
        foreach ($this->fields as $key => $value) {
2201
            if (!$first) {
2202
                $first = $key;
2203
            }
2204
            if ($key === $fieldName) {
2205
                return $key;
2206
            }
2207
        }
2208
        return $first;
2209
    }
2210
2211
    /**
2212
     * Verify comparison
2213
     *
2214
     * @param string $comparison
2215
     * @param int $neg
2216
     * @return int
2217
     */
2218
    protected function verifyComparison($comparison, $neg)
2219
    {
2220
        $compOffSet = $comparison >> 5;
2221
        $first = -1;
2222
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2223
            if ($first === -1) {
2224
                $first = $i;
2225
            }
2226
            if ($i >> 1 === $comparison >> 1) {
2227
                return $i;
2228
            }
2229
        }
2230
        return $first;
2231
    }
2232
2233
    /**
2234
     * Make field to input select
2235
     *
2236
     * @param string $name
2237
     * @param string $fieldName
2238
     * @return string
2239
     */
2240
    protected function mkFieldToInputSelect($name, $fieldName)
2241
    {
2242
        $out = [];
2243
        $out[] = '<div class="input-group" style="margin-bottom: .5em;">';
2244
        $out[] = '	<span class="input-group-btn">';
2245
        $out[] = $this->updateIcon();
2246
        $out[] = ' 	</span>';
2247
        $out[] = '	<input type="text" class="form-control t3js-clearable" value="' . htmlspecialchars($fieldName) . '" name="' . htmlspecialchars($name) . '">';
2248
        $out[] = '</div>';
2249
2250
        $out[] = '<select class="form-control t3js-addfield" name="_fieldListDummy" size="5" data-field="' . htmlspecialchars($name) . '">';
2251
        foreach ($this->fields as $key => $value) {
2252
            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
2253
                $label = $this->fields[$key]['label'];
2254
                $out[] = '<option value="' . htmlspecialchars($key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
2255
            }
2256
        }
2257
        $out[] = '</select>';
2258
        return implode(LF, $out);
2259
    }
2260
2261
    /**
2262
     * Make table select
2263
     *
2264
     * @param string $name
2265
     * @param string $cur
2266
     * @return string
2267
     */
2268
    protected function mkTableSelect($name, $cur)
2269
    {
2270
        $out = [];
2271
        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
2272
        $out[] = '<option value=""></option>';
2273
        foreach ($GLOBALS['TCA'] as $tN => $value) {
2274
            if ($this->getBackendUserAuthentication()->check('tables_select', $tN)) {
2275
                $out[] = '<option value="' . htmlspecialchars($tN) . '"' . ($tN === $cur ? ' selected' : '') . '>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$tN]['ctrl']['title'])) . '</option>';
2276
            }
2277
        }
2278
        $out[] = '</select>';
2279
        return implode(LF, $out);
2280
    }
2281
2282
    /**
2283
     * Make comparison select
2284
     *
2285
     * @param string $name
2286
     * @param string $comparison
2287
     * @param int $neg
2288
     * @return string
2289
     */
2290
    protected function mkCompSelect($name, $comparison, $neg)
2291
    {
2292
        $compOffSet = $comparison >> 5;
2293
        $out = [];
2294
        $out[] = '<select class="form-control t3js-submit-change" name="' . $name . '">';
2295
        for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
2296
            if ($this->lang['comparison'][$i . '_']) {
2297
                $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
2298
            }
2299
        }
2300
        $out[] = '</select>';
2301
        return implode(LF, $out);
2302
    }
2303
2304
    /**
2305
     * Get subscript
2306
     *
2307
     * @param array $arr
2308
     * @return array
2309
     */
2310
    protected function getSubscript($arr): array
2311
    {
2312
        $retArr = [];
2313
        while (\is_array($arr)) {
2314
            reset($arr);
2315
            $key = key($arr);
2316
            $retArr[] = $key;
2317
            if (isset($arr[$key])) {
2318
                $arr = $arr[$key];
2319
            } else {
2320
                break;
2321
            }
2322
        }
2323
        return $retArr;
2324
    }
2325
2326
    /**
2327
     * Get query
2328
     *
2329
     * @param array $queryConfig
2330
     * @param string $pad
2331
     * @return string
2332
     */
2333
    protected function getQuery($queryConfig, $pad = '')
2334
    {
2335
        $qs = '';
2336
        // Since we don't traverse the array using numeric keys in the upcoming whileloop make sure it's fresh and clean
2337
        ksort($queryConfig);
2338
        $first = true;
2339
        foreach ($queryConfig as $key => $conf) {
2340
            $conf = $this->convertIso8601DatetimeStringToUnixTimestamp($conf);
2341
            switch ($conf['type']) {
2342
                case 'newlevel':
2343
                    $qs .= LF . $pad . trim($conf['operator']) . ' (' . $this->getQuery(
2344
                        $queryConfig[$key]['nl'],
2345
                        $pad . '   '
2346
                    ) . LF . $pad . ')';
2347
                    break;
2348
                default:
2349
                    $qs .= LF . $pad . $this->getQuerySingle($conf, $first);
2350
            }
2351
            $first = false;
2352
        }
2353
        return $qs;
2354
    }
2355
2356
    /**
2357
     * Convert ISO-8601 timestamp (string) into unix timestamp (int)
2358
     *
2359
     * @param array $conf
2360
     * @return array
2361
     */
2362
    protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
2363
    {
2364
        if ($this->isDateOfIso8601Format($conf['inputValue'])) {
2365
            $conf['inputValue'] = strtotime($conf['inputValue']);
2366
            if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
2367
                $conf['inputValue1'] = strtotime($conf['inputValue1']);
2368
            }
2369
        }
2370
2371
        return $conf;
2372
    }
2373
2374
    /**
2375
     * Checks if the given value is of the ISO 8601 format.
2376
     *
2377
     * @param mixed $date
2378
     * @return bool
2379
     */
2380
    protected function isDateOfIso8601Format($date): bool
2381
    {
2382
        if (!is_int($date) && !is_string($date)) {
2383
            return false;
2384
        }
2385
        $format = 'Y-m-d\\TH:i:s\\Z';
2386
        $formattedDate = \DateTime::createFromFormat($format, (string)$date);
2387
        return $formattedDate && $formattedDate->format($format) === $date;
2388
    }
2389
2390
    /**
2391
     * Get single query
2392
     *
2393
     * @param array $conf
2394
     * @param bool $first
2395
     * @return string
2396
     */
2397
    protected function getQuerySingle($conf, $first)
2398
    {
2399
        $qs = '';
2400
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
2401
        $prefix = $this->enablePrefix ? $this->table . '.' : '';
2402
        if (!$first) {
2403
            // Is it OK to insert the AND operator if none is set?
2404
            $operator = strtoupper(trim($conf['operator']));
2405
            if (!in_array($operator, ['AND', 'OR'], true)) {
2406
                $operator = 'AND';
2407
            }
2408
            $qs .= $operator . ' ';
2409
        }
2410
        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
2411
        $inputVal = $this->cleanInputVal($conf);
2412
        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
2413
            $inputVal = explode(',', $inputVal);
2414
            foreach ($inputVal as $key => $fileName) {
2415
                $inputVal[$key] = $queryBuilder->quote($fileName);
2416
            }
2417
            $inputVal = implode(',', $inputVal);
2418
            $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
2419
        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
2420
            $inputValArray = explode(',', $inputVal);
2421
            $inputVal = 0;
2422
            foreach ($inputValArray as $fileName) {
2423
                $inputVal += (int)$fileName;
2424
            }
2425
            $qsTmp = str_replace('#VALUE#', (string)$inputVal, $qsTmp);
2426
        } else {
2427
            if (is_array($inputVal)) {
0 ignored issues
show
introduced by
The condition is_array($inputVal) is always false.
Loading history...
2428
                $inputVal = $inputVal[0];
2429
            }
2430
            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
2431
        }
2432
        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
2433
            // between:
2434
            $inputVal = $this->cleanInputVal($conf, '1');
2435
            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
2436
        }
2437
        $qs .= trim((string)$qsTmp);
2438
        return $qs;
2439
    }
2440
2441
    /**
2442
     * Clear input value
2443
     *
2444
     * @param array $conf
2445
     * @param string $suffix
2446
     * @return string
2447
     */
2448
    protected function cleanInputVal($conf, $suffix = '')
2449
    {
2450
        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)) {
2451
            $inputVal = $conf['inputValue' . $suffix];
2452
        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
2453
            // in list:
2454
            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
2455
        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
2456
            // in list:
2457
            if (is_array($conf['inputValue' . $suffix])) {
2458
                $inputVal = implode(',', $conf['inputValue' . $suffix]);
2459
            } elseif ($conf['inputValue' . $suffix]) {
2460
                $inputVal = $conf['inputValue' . $suffix];
2461
            } else {
2462
                $inputVal = 0;
2463
            }
2464
        } elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
2465
            $inputVal = $conf['inputValue' . $suffix];
2466
        } elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
2467
            $inputVal = (int)$conf['inputValue' . $suffix];
2468
        } else {
2469
            // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
2470
            // fallback to float casting, the whole class smells like it needs a refactoring.
2471
            $inputVal = (float)$conf['inputValue' . $suffix];
2472
        }
2473
        return $inputVal;
2474
    }
2475
2476
    /**
2477
     * Update icon
2478
     *
2479
     * @return string
2480
     */
2481
    protected function updateIcon()
2482
    {
2483
        return '<button class="btn btn-default" title="Update" name="just_update"><i class="fa fa-refresh fa-fw"></i></button>';
2484
    }
2485
2486
    /**
2487
     * Get label column
2488
     *
2489
     * @return string
2490
     */
2491
    protected function getLabelCol()
2492
    {
2493
        return $GLOBALS['TCA'][$this->table]['ctrl']['label'];
2494
    }
2495
2496
    /**
2497
     * Make selector table
2498
     *
2499
     * @param array $modSettings
2500
     * @param string $enableList
2501
     * @return string
2502
     */
2503
    protected function makeSelectorTable($modSettings, $enableList = 'table,fields,query,group,order,limit')
2504
    {
2505
        $out = [];
2506
        $enableArr = explode(',', $enableList);
2507
        $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
2508
2509
        // Make output
2510
        if (in_array('table', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectATable']) {
2511
            $out[] = '<div class="form-group">';
2512
            $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
2513
            $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
2514
            $out[] = '</div>';
2515
        }
2516
        if ($this->table) {
2517
            // Init fields:
2518
            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'], 'uid,' . $this->getLabelCol());
2519
            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup']);
2520
            $this->setAndCleanUpExternalLists('queryOrder', $modSettings['queryOrder'] . ',' . $modSettings['queryOrder2']);
2521
            // Limit:
2522
            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'];
2523
            if (!$this->extFieldLists['queryLimit']) {
2524
                $this->extFieldLists['queryLimit'] = 100;
2525
            }
2526
            $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
2527
            if ($parts[1]) {
2528
                $this->limitBegin = $parts[0];
2529
                $this->limitLength = $parts[1];
2530
            } else {
2531
                $this->limitLength = $this->extFieldLists['queryLimit'];
2532
            }
2533
            $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
2534
            // Insert Descending parts
2535
            if ($this->extFieldLists['queryOrder']) {
2536
                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
2537
                $orderParts = explode(',', $this->extFieldLists['queryOrder']);
2538
                $reList = [];
2539
                foreach ($orderParts as $kk => $vv) {
2540
                    $reList[] = $vv . ($descParts[$kk] ? ' DESC' : '');
2541
                }
2542
                $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
2543
            }
2544
            // Query Generator:
2545
            $this->procesData($modSettings['queryConfig'] ? unserialize($modSettings['queryConfig'], ['allowed_classes' => false]) : '');
2546
            $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
0 ignored issues
show
Bug introduced by
It seems like $this->queryConfig can also be of type string; however, parameter $queryConfig of TYPO3\CMS\Lowlevel\Datab...r::cleanUpQueryConfig() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

2546
            $this->queryConfig = $this->cleanUpQueryConfig(/** @scrutinizer ignore-type */ $this->queryConfig);
Loading history...
2547
            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
2548
            $codeArr = $this->getFormElements();
2549
            $queryCode = $this->printCodeArray($codeArr);
2550
            if (in_array('fields', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectFields']) {
2551
                $out[] = '<div class="form-group form-group-with-button-addon">';
2552
                $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
2553
                $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
2554
                $out[] = '</div>';
2555
            }
2556
            if (in_array('query', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableMakeQuery']) {
2557
                $out[] = '<div class="form-group">';
2558
                $out[] = '	<label>Make Query:</label>';
2559
                $out[] =    $queryCode;
2560
                $out[] = '</div>';
2561
            }
2562
            if (in_array('group', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableGroupBy']) {
2563
                $out[] = '<div class="form-group form-inline">';
2564
                $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
2565
                $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
2566
                $out[] = '</div>';
2567
            }
2568
            if (in_array('order', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableOrderBy']) {
2569
                $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
2570
                $orderBy = [];
2571
                $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
2572
                $orderBy[] = '<div class="checkbox">';
2573
                $orderBy[] = '	<label for="checkQueryOrderDesc">';
2574
                $orderBy[] =        BackendUtility::getFuncCheck(0, 'SET[queryOrderDesc]', $modSettings['queryOrderDesc'], '', '', 'id="checkQueryOrderDesc"') . ' Descending';
2575
                $orderBy[] = '	</label>';
2576
                $orderBy[] = '</div>';
2577
2578
                if ($orderByArr[0]) {
2579
                    $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
2580
                    $orderBy[] = '<div class="checkbox">';
2581
                    $orderBy[] = '	<label for="checkQueryOrder2Desc">';
2582
                    $orderBy[] =        BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"') . ' Descending';
2583
                    $orderBy[] = '	</label>';
2584
                    $orderBy[] = '</div>';
2585
                }
2586
                $out[] = '<div class="form-group form-inline">';
2587
                $out[] = '	<label>Order By:</label>';
2588
                $out[] =     implode(LF, $orderBy);
2589
                $out[] = '</div>';
2590
            }
2591
            if (in_array('limit', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableLimit']) {
2592
                $limit = [];
2593
                $limit[] = '<div class="input-group">';
2594
                $limit[] = '	<span class="input-group-btn">';
2595
                $limit[] = $this->updateIcon();
2596
                $limit[] = '	</span>';
2597
                $limit[] = '	<input type="text" class="form-control" value="' . htmlspecialchars($this->extFieldLists['queryLimit']) . '" name="SET[queryLimit]" id="queryLimit">';
2598
                $limit[] = '</div>';
2599
2600
                $prevLimit = $this->limitBegin - $this->limitLength < 0 ? 0 : $this->limitBegin - $this->limitLength;
2601
                $prevButton = '';
2602
                $nextButton = '';
2603
2604
                if ($this->limitBegin) {
2605
                    $prevButton = '<input type="button" class="btn btn-default" value="previous ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($prevLimit . ',' . $this->limitLength) . '">';
2606
                }
2607
                if (!$this->limitLength) {
2608
                    $this->limitLength = 100;
2609
                }
2610
2611
                $nextLimit = $this->limitBegin + $this->limitLength;
2612
                if ($nextLimit < 0) {
2613
                    $nextLimit = 0;
2614
                }
2615
                if ($nextLimit) {
2616
                    $nextButton = '<input type="button" class="btn btn-default" value="next ' . htmlspecialchars($this->limitLength) . '" data-value="' . htmlspecialchars($nextLimit . ',' . $this->limitLength) . '">';
2617
                }
2618
2619
                $out[] = '<div class="form-group">';
2620
                $out[] = '	<label>Limit:</label>';
2621
                $out[] = '	<div class="form-inline">';
2622
                $out[] =        implode(LF, $limit);
2623
                $out[] = '		<div class="btn-group t3js-limit-submit">';
2624
                $out[] =            $prevButton;
2625
                $out[] =            $nextButton;
2626
                $out[] = '		</div>';
2627
                $out[] = '		<div class="btn-group t3js-limit-submit">';
2628
                $out[] = '			<input type="button" class="btn btn-default" data-value="10" value="10">';
2629
                $out[] = '			<input type="button" class="btn btn-default" data-value="20" value="20">';
2630
                $out[] = '			<input type="button" class="btn btn-default" data-value="50" value="50">';
2631
                $out[] = '			<input type="button" class="btn btn-default" data-value="100" value="100">';
2632
                $out[] = '		</div>';
2633
                $out[] = '	</div>';
2634
                $out[] = '</div>';
2635
            }
2636
        }
2637
        return implode(LF, $out);
2638
    }
2639
2640
    /**
2641
     * Get select query
2642
     *
2643
     * @param string $qString
2644
     * @return string
2645
     */
2646
    protected function getSelectQuery($qString = ''): string
2647
    {
2648
        $backendUserAuthentication = $this->getBackendUserAuthentication();
2649
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
2650
        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
2651
            $queryBuilder->getRestrictions()->removeAll();
2652
        } else {
2653
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
2654
        }
2655
        $fieldList = GeneralUtility::trimExplode(
2656
            ',',
2657
            $this->extFieldLists['queryFields']
2658
            . ',pid'
2659
            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
2660
        );
2661
        $queryBuilder->select(...$fieldList)
2662
            ->from($this->table);
2663
2664
        if ($this->extFieldLists['queryGroup']) {
2665
            $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
2666
        }
2667
        if ($this->extFieldLists['queryOrder']) {
2668
            foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
2669
                [$fieldName, $order] = $orderPair;
2670
                $queryBuilder->addOrderBy($fieldName, $order);
2671
            }
2672
        }
2673
        if ($this->extFieldLists['queryLimit']) {
2674
            $queryBuilder->setMaxResults((int)$this->extFieldLists['queryLimit']);
2675
        }
2676
2677
        if (!$backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
2678
            $webMounts = $backendUserAuthentication->returnWebmounts();
2679
            $perms_clause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
2680
            $webMountPageTree = '';
2681
            $webMountPageTreePrefix = '';
2682
            foreach ($webMounts as $webMount) {
2683
                if ($webMountPageTree) {
2684
                    $webMountPageTreePrefix = ',';
2685
                }
2686
                $webMountPageTree .= $webMountPageTreePrefix
2687
                    . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause);
2688
            }
2689
            // createNamedParameter() is not used here because the SQL fragment will only include
2690
            // the :dcValueX placeholder when the query is returned as a string. The value for the
2691
            // placeholder would be lost in the process.
2692
            if ($this->table === 'pages') {
2693
                $queryBuilder->where(
2694
                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
2695
                    $queryBuilder->expr()->in(
2696
                        'uid',
2697
                        GeneralUtility::intExplode(',', $webMountPageTree)
2698
                    )
2699
                );
2700
            } else {
2701
                $queryBuilder->where(
2702
                    $queryBuilder->expr()->in(
2703
                        'pid',
2704
                        GeneralUtility::intExplode(',', $webMountPageTree)
2705
                    )
2706
                );
2707
            }
2708
        }
2709
        if (!$qString) {
2710
            $qString = $this->getQuery($this->queryConfig);
2711
        }
2712
        $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($qString));
2713
2714
        return $queryBuilder->getSQL();
2715
    }
2716
2717
    /**
2718
     * @param string $name the field name
2719
     * @param string $timestamp ISO-8601 timestamp
2720
     * @param string $type [datetime, date, time, timesec, year]
2721
     *
2722
     * @return string
2723
     */
2724
    protected function getDateTimePickerField($name, $timestamp, $type)
2725
    {
2726
        $value = strtotime($timestamp) ? date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], (int)strtotime($timestamp)) : '';
2727
        $id = StringUtility::getUniqueId('dt_');
2728
        $html = [];
2729
        $html[] = '<div class="input-group" id="' . $id . '-wrapper">';
2730
        $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 . '">';
2731
        $html[] = '		<input name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($timestamp) . '" type="hidden">';
2732
        $html[] = '		<span class="input-group-btn">';
2733
        $html[] = '			<label class="btn btn-default" for="' . $id . '">';
2734
        $html[] = '				<span class="fa fa-calendar"></span>';
2735
        $html[] = '			</label>';
2736
        $html[] = ' 	</span>';
2737
        $html[] = '</div>';
2738
        return implode(LF, $html);
2739
    }
2740
2741
    protected function getBackendUserAuthentication(): BackendUserAuthentication
2742
    {
2743
        return $GLOBALS['BE_USER'];
2744
    }
2745
2746
    protected function getLanguageService(): LanguageService
2747
    {
2748
        return $GLOBALS['LANG'];
2749
    }
2750
}
2751