Completed
Push — master ( 8e305d...1d0f8f )
by
unknown
30:04
created

QueryView::initStoreArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\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\Query\QueryHelper;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Imaging\Icon;
25
use TYPO3\CMS\Core\Imaging\IconFactory;
26
use TYPO3\CMS\Core\Localization\LanguageService;
27
use TYPO3\CMS\Core\Messaging\FlashMessage;
28
use TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver;
29
use TYPO3\CMS\Core\Messaging\FlashMessageService;
30
use TYPO3\CMS\Core\Type\Bitmask\Permission;
31
use TYPO3\CMS\Core\Utility\CsvUtility;
32
use TYPO3\CMS\Core\Utility\DebugUtility;
33
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
34
use TYPO3\CMS\Core\Utility\GeneralUtility;
35
use TYPO3\CMS\Core\Utility\HttpUtility;
36
37
/**
38
 * Class used in module tools/dbint (advanced search) and which may hold code specific for that module
39
 * However the class has a general principle in it which may be used in the web/export module.
40
 */
41
class QueryView
42
{
43
    /**
44
     * @var string
45
     */
46
    public $storeList = 'search_query_smallparts,search_result_labels,labels_noprefix,show_deleted,queryConfig,queryTable,queryFields,queryLimit,queryOrder,queryOrderDesc,queryOrder2,queryOrder2Desc,queryGroup,search_query_makeQuery';
47
48
    /**
49
     * @var int
50
     */
51
    public $noDownloadB = 0;
52
53
    /**
54
     * @var array
55
     */
56
    public $hookArray = [];
57
58
    /**
59
     * @var string
60
     */
61
    protected $formName = '';
62
63
    /**
64
     * @var \TYPO3\CMS\Core\Imaging\IconFactory
65
     */
66
    protected $iconFactory;
67
68
    /**
69
     * @var array
70
     */
71
    protected $tableArray = [];
72
73
    /**
74
     * @var LanguageService
75
     */
76
    protected $languageService;
77
78
    /**
79
     * @var BackendUserAuthentication
80
     */
81
    protected $backendUserAuthentication;
82
83
    /**
84
     * Settings, usually from the controller (previously known from $GLOBALS['SOBE']->MOD_SETTINGS
85
     * @var array
86
     */
87
    protected $settings = [];
88
89
    /**
90
     * @var array information on the menu of this module
91
     */
92
    protected $menuItems = [];
93
94
    /**
95
     * @var string
96
     */
97
    protected $moduleName;
98
99
    /**
100
     * @param array $settings previously stored in $GLOBALS['SOBE']->MOD_SETTINGS
101
     * @param array $menuItems previously stored in $GLOBALS['SOBE']->MOD_MENU
102
     * @param string $moduleName previously stored in $GLOBALS['SOBE']->moduleName
103
     */
104
    public function __construct(array $settings = null, $menuItems = null, $moduleName = null)
105
    {
106
        $this->backendUserAuthentication = $GLOBALS['BE_USER'];
107
        $this->languageService = $GLOBALS['LANG'];
108
        $this->languageService->includeLLFile('EXT:core/Resources/Private/Language/locallang_t3lib_fullsearch.xlf');
109
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
110
        $this->settings = $settings ?: $GLOBALS['SOBE']->MOD_SETTINGS;
111
        $this->menuItems = $menuItems ?: $GLOBALS['SOBE']->MOD_MENU;
112
        $this->moduleName = $moduleName ?: $GLOBALS['SOBE']->moduleName;
113
    }
114
115
    /**
116
     * Get form
117
     *
118
     * @return string
119
     */
120
    public function form()
121
    {
122
        $markup = [];
123
        $markup[] = '<div class="form-group">';
124
        $markup[] = '<input placeholder="Search Word" class="form-control" type="search" name="SET[sword]" value="'
125
            . htmlspecialchars($this->settings['sword']) . '">';
126
        $markup[] = '</div>';
127
        $markup[] = '<div class="form-group">';
128
        $markup[] = '<input class="btn btn-default" type="submit" name="submit" value="Search All Records">';
129
        $markup[] = '</div>';
130
        return implode(LF, $markup);
131
    }
132
133
    /**
134
     * Make store control
135
     *
136
     * @return string
137
     */
138
    public function makeStoreControl()
139
    {
140
        // Load/Save
141
        $storeArray = $this->initStoreArray();
142
143
        $opt = [];
144
        foreach ($storeArray as $k => $v) {
145
            $opt[] = '<option value="' . htmlspecialchars($k) . '">' . htmlspecialchars($v) . '</option>';
146
        }
147
        // Actions:
148
        if (ExtensionManagementUtility::isLoaded('sys_action') && $this->backendUserAuthentication->isAdmin()) {
149
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_action');
150
            $queryBuilder->getRestrictions()->removeAll();
151
            $statement = $queryBuilder->select('uid', 'title')
152
                ->from('sys_action')
153
                ->where($queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(2, \PDO::PARAM_INT)))
154
                ->orderBy('title')
155
                ->execute();
156
            $opt[] = '<option value="0">__Save to Action:__</option>';
157
            while ($row = $statement->fetch()) {
158
                $opt[] = '<option value="-' . (int)$row['uid'] . '">' . htmlspecialchars($row['title']
159
                        . ' [' . (int)$row['uid'] . ']') . '</option>';
160
            }
161
        }
162
        $markup = [];
163
        $markup[] = '<div class="load-queries">';
164
        $markup[] = '  <div class="form-group form-inline">';
165
        $markup[] = '    <div class="form-group">';
166
        $markup[] = '      <select class="form-control" name="storeControl[STORE]" data-assign-store-control-title>' . implode(LF, $opt) . '</select>';
167
        $markup[] = '      <input class="form-control" name="storeControl[title]" value="" type="text" max="80">';
168
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[LOAD]" value="Load">';
169
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[SAVE]" value="Save">';
170
        $markup[] = '      <input class="btn btn-default" type="submit" name="storeControl[REMOVE]" value="Remove">';
171
        $markup[] = '    </div>';
172
        $markup[] = '  </div>';
173
        $markup[] = '</div>';
174
175
        return implode(LF, $markup);
176
    }
177
178
    /**
179
     * Init store array
180
     *
181
     * @return array
182
     */
183
    public function initStoreArray()
184
    {
185
        $storeArray = [
186
            '0' => '[New]'
187
        ];
188
        $savedStoreArray = unserialize($this->settings['storeArray'], ['allowed_classes' => false]);
189
        if (is_array($savedStoreArray)) {
190
            $storeArray = array_merge($storeArray, $savedStoreArray);
191
        }
192
        return $storeArray;
193
    }
194
195
    /**
196
     * Clean store query configs
197
     *
198
     * @param array $storeQueryConfigs
199
     * @param array $storeArray
200
     * @return array
201
     */
202
    public function cleanStoreQueryConfigs($storeQueryConfigs, $storeArray)
203
    {
204
        if (is_array($storeQueryConfigs)) {
0 ignored issues
show
introduced by
The condition is_array($storeQueryConfigs) is always true.
Loading history...
205
            foreach ($storeQueryConfigs as $k => $v) {
206
                if (!isset($storeArray[$k])) {
207
                    unset($storeQueryConfigs[$k]);
208
                }
209
            }
210
        }
211
        return $storeQueryConfigs;
212
    }
213
214
    /**
215
     * Add to store query configs
216
     *
217
     * @param array $storeQueryConfigs
218
     * @param int $index
219
     * @return array
220
     */
221
    public function addToStoreQueryConfigs($storeQueryConfigs, $index)
222
    {
223
        $keyArr = explode(',', $this->storeList);
224
        $storeQueryConfigs[$index] = [];
225
        foreach ($keyArr as $k) {
226
            $storeQueryConfigs[$index][$k] = $this->settings[$k];
227
        }
228
        return $storeQueryConfigs;
229
    }
230
231
    /**
232
     * Save query in action
233
     *
234
     * @param int $uid
235
     * @return int
236
     */
237
    public function saveQueryInAction($uid)
238
    {
239
        if (ExtensionManagementUtility::isLoaded('sys_action')) {
240
            $keyArr = explode(',', $this->storeList);
241
            $saveArr = [];
242
            foreach ($keyArr as $k) {
243
                $saveArr[$k] = $this->settings[$k];
244
            }
245
            // Show query
246
            if ($saveArr['queryTable']) {
247
                /** @var \TYPO3\CMS\Core\Database\QueryGenerator $queryGenerator */
248
                $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
249
                $queryGenerator->init('queryConfig', $saveArr['queryTable']);
250
                $queryGenerator->makeSelectorTable($saveArr);
251
                $queryGenerator->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...
252
                $queryString = $queryGenerator->getQuery($queryGenerator->queryConfig);
253
254
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
255
                    ->getQueryBuilderForTable($queryGenerator->table);
256
                $queryBuilder->getRestrictions()->removeAll()
257
                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
258
                $rowCount = $queryBuilder->count('*')
259
                    ->from($queryGenerator->table)
260
                    ->where(QueryHelper::stripLogicalOperatorPrefix($queryString))
261
                    ->execute()->fetchColumn(0);
262
263
                $t2DataValue = [
264
                    'qC' => $saveArr,
265
                    'qCount' => $rowCount,
266
                    'qSelect' => $queryGenerator->getSelectQuery($queryString),
267
                    'qString' => $queryString
268
                ];
269
                GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_action')
270
                    ->update(
271
                        'sys_action',
272
                        ['t2_data' => serialize($t2DataValue)],
273
                        ['uid' => (int)$uid],
274
                        ['t2_data' => Connection::PARAM_LOB]
275
                    );
276
            }
277
            return 1;
278
        }
279
        return null;
280
    }
281
282
    /**
283
     * Load store query configs
284
     *
285
     * @param array $storeQueryConfigs
286
     * @param int $storeIndex
287
     * @param array $writeArray
288
     * @return array
289
     */
290
    public function loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray)
291
    {
292
        if ($storeQueryConfigs[$storeIndex]) {
293
            $keyArr = explode(',', $this->storeList);
294
            foreach ($keyArr as $k) {
295
                $writeArray[$k] = $storeQueryConfigs[$storeIndex][$k];
296
            }
297
        }
298
        return $writeArray;
299
    }
300
301
    /**
302
     * Process store control
303
     *
304
     * @return string
305
     */
306
    public function procesStoreControl()
307
    {
308
        $flashMessage = null;
309
        $storeArray = $this->initStoreArray();
310
        $storeQueryConfigs = unserialize($this->settings['storeQueryConfigs'], ['allowed_classes' => false]);
311
        $storeControl = GeneralUtility::_GP('storeControl');
312
        $storeIndex = (int)$storeControl['STORE'];
313
        $saveStoreArray = 0;
314
        $writeArray = [];
315
        $msg = '';
316
        if (is_array($storeControl)) {
317
            if ($storeControl['LOAD']) {
318
                if ($storeIndex > 0) {
319
                    $writeArray = $this->loadStoreQueryConfigs($storeQueryConfigs, $storeIndex, $writeArray);
320
                    $saveStoreArray = 1;
321
                    $flashMessage = GeneralUtility::makeInstance(
322
                        FlashMessage::class,
323
                        sprintf($this->languageService->getLL('query_loaded'), $storeArray[$storeIndex])
324
                    );
325
                } elseif ($storeIndex < 0 && ExtensionManagementUtility::isLoaded('sys_action')) {
326
                    $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

326
                    $actionRecord = BackendUtility::getRecord('sys_action', /** @scrutinizer ignore-type */ abs($storeIndex));
Loading history...
327
                    if (is_array($actionRecord)) {
328
                        $dA = unserialize($actionRecord['t2_data'], ['allowed_classes' => false]);
329
                        $dbSC = [];
330
                        if (is_array($dA['qC'])) {
331
                            $dbSC[0] = $dA['qC'];
332
                        }
333
                        $writeArray = $this->loadStoreQueryConfigs($dbSC, '0', $writeArray);
334
                        $saveStoreArray = 1;
335
                        $flashMessage = GeneralUtility::makeInstance(
336
                            FlashMessage::class,
337
                            sprintf($this->languageService->getLL('query_from_action_loaded'), $actionRecord['title'])
338
                        );
339
                    }
340
                }
341
            } elseif ($storeControl['SAVE']) {
342
                if ($storeIndex < 0) {
343
                    $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\Core\Database\...ew::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

343
                    $qOK = $this->saveQueryInAction(/** @scrutinizer ignore-type */ abs($storeIndex));
Loading history...
344
                    if ($qOK) {
345
                        $flashMessage = GeneralUtility::makeInstance(
346
                            FlashMessage::class,
347
                            $this->languageService->getLL('query_saved')
348
                        );
349
                    } else {
350
                        $flashMessage = GeneralUtility::makeInstance(
351
                            FlashMessage::class,
352
                            $this->languageService->getLL('query_notsaved'),
353
                            '',
354
                            FlashMessage::ERROR
355
                        );
356
                    }
357
                } else {
358
                    if (trim($storeControl['title'])) {
359
                        if ($storeIndex > 0) {
360
                            $storeArray[$storeIndex] = $storeControl['title'];
361
                        } else {
362
                            $storeArray[] = $storeControl['title'];
363
                            end($storeArray);
364
                            $storeIndex = key($storeArray);
365
                        }
366
                        $storeQueryConfigs = $this->addToStoreQueryConfigs($storeQueryConfigs, $storeIndex);
0 ignored issues
show
Bug introduced by
It seems like $storeIndex can also be of type string; however, parameter $index of TYPO3\CMS\Core\Database\...ddToStoreQueryConfigs() 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

366
                        $storeQueryConfigs = $this->addToStoreQueryConfigs($storeQueryConfigs, /** @scrutinizer ignore-type */ $storeIndex);
Loading history...
367
                        $saveStoreArray = 1;
368
                        $flashMessage = GeneralUtility::makeInstance(
369
                            FlashMessage::class,
370
                            $this->languageService->getLL('query_saved')
371
                        );
372
                    }
373
                }
374
            } elseif ($storeControl['REMOVE']) {
375
                if ($storeIndex > 0) {
376
                    $flashMessage = GeneralUtility::makeInstance(
377
                        FlashMessage::class,
378
                        sprintf($this->languageService->getLL('query_removed'), $storeArray[$storeControl['STORE']])
379
                    );
380
                    // Removing
381
                    unset($storeArray[$storeControl['STORE']]);
382
                    $saveStoreArray = 1;
383
                }
384
            }
385
            if (!empty($flashMessage)) {
386
                $msg = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)
387
                    ->resolve()
388
                    ->render([$flashMessage]);
389
            }
390
        }
391
        if ($saveStoreArray) {
392
            // Making sure, index 0 is not set!
393
            unset($storeArray[0]);
394
            $writeArray['storeArray'] = serialize($storeArray);
395
            $writeArray['storeQueryConfigs'] =
396
                serialize($this->cleanStoreQueryConfigs($storeQueryConfigs, $storeArray));
397
            $this->settings = BackendUtility::getModuleData(
398
                $this->menuItems,
399
                $writeArray,
400
                $this->moduleName,
401
                'ses'
402
            );
403
        }
404
        return $msg;
405
    }
406
407
    /**
408
     * Query marker
409
     *
410
     * @return string
411
     */
412
    public function queryMaker()
413
    {
414
        $output = '';
415
        $this->hookArray = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
416
        $msg = $this->procesStoreControl();
417
        $userTsConfig = $this->backendUserAuthentication->getTSConfig();
418
        if (!$userTsConfig['mod.']['dbint.']['disableStoreControl']) {
419
            $output .= '<h2>Load/Save Query</h2>';
420
            $output .= '<div>' . $this->makeStoreControl() . '</div>';
421
            $output .= $msg;
422
        }
423
        // Query Maker:
424
        $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
425
        $queryGenerator->init('queryConfig', $this->settings['queryTable']);
426
        if ($this->formName) {
427
            $queryGenerator->setFormName($this->formName);
428
        }
429
        $tmpCode = $queryGenerator->makeSelectorTable($this->settings);
430
        $output .= '<div id="query"></div><h2>Make query</h2><div>' . $tmpCode . '</div>';
431
        $mQ = $this->settings['search_query_makeQuery'];
432
        // Make form elements:
433
        if ($queryGenerator->table && is_array($GLOBALS['TCA'][$queryGenerator->table])) {
434
            if ($mQ) {
435
                // Show query
436
                $queryGenerator->enablePrefix = 1;
437
                $queryString = $queryGenerator->getQuery($queryGenerator->queryConfig);
438
                $selectQueryString = $queryGenerator->getSelectQuery($queryString);
439
                $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($queryGenerator->table);
440
441
                $isConnectionMysql = strpos($connection->getServerVersion(), 'MySQL') === 0;
442
                $fullQueryString = '';
443
                try {
444
                    if ($mQ === 'explain' && $isConnectionMysql) {
445
                        // EXPLAIN is no ANSI SQL, for now this is only executed on mysql
446
                        // @todo: Move away from getSelectQuery() or model differently
447
                        $fullQueryString = 'EXPLAIN ' . $selectQueryString;
448
                        $dataRows = $connection->executeQuery('EXPLAIN ' . $selectQueryString)->fetchAll();
449
                    } elseif ($mQ === 'count') {
450
                        $queryBuilder = $connection->createQueryBuilder();
451
                        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
452
                        $queryBuilder->count('*')
453
                            ->from($queryGenerator->table)
454
                            ->where(QueryHelper::stripLogicalOperatorPrefix($queryString));
455
                        $fullQueryString = $queryBuilder->getSQL();
456
                        $dataRows = [$queryBuilder->execute()->fetchColumn(0)];
457
                    } else {
458
                        $fullQueryString = $selectQueryString;
459
                        $dataRows = $connection->executeQuery($selectQueryString)->fetchAll();
460
                    }
461
                    if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
462
                        $output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
463
                    }
464
                    $cPR = $this->getQueryResultCode($mQ, $dataRows, $queryGenerator->table);
465
                    $output .= '<h2>' . $cPR['header'] . '</h2><div>' . $cPR['content'] . '</div>';
466
                } catch (DBALException $e) {
467
                    if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
468
                        $output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
469
                    }
470
                    $out = '<p><strong>Error: <span class="text-danger">'
471
                        . $e->getMessage()
472
                        . '</span></strong></p>';
473
                    $output .= '<h2>SQL error</h2><div>' . $out . '</div>';
474
                }
475
            }
476
        }
477
        return '<div class="database-query-builder">' . $output . '</div>';
478
    }
479
480
    /**
481
     * Get query result code
482
     *
483
     * @param string $type
484
     * @param array $dataRows Rows to display
485
     * @param string $table
486
     * @return array HTML-code for "header" and "content"
487
     * @throws \TYPO3\CMS\Core\Exception
488
     */
489
    public function getQueryResultCode($type, array $dataRows, $table)
490
    {
491
        $out = '';
492
        $cPR = [];
493
        switch ($type) {
494
            case 'count':
495
                $cPR['header'] = 'Count';
496
                $cPR['content'] = '<br><strong>' . (int)$dataRows[0] . '</strong> records selected.';
497
                break;
498
            case 'all':
499
                $rowArr = [];
500
                $dataRow = null;
501
                foreach ($dataRows as $dataRow) {
502
                    $rowArr[] = $this->resultRowDisplay($dataRow, $GLOBALS['TCA'][$table], $table);
503
                }
504
                if (is_array($this->hookArray['beforeResultTable'])) {
505
                    foreach ($this->hookArray['beforeResultTable'] as $_funcRef) {
506
                        $out .= GeneralUtility::callUserFunction($_funcRef, $this->settings, $this);
507
                    }
508
                }
509
                if (!empty($rowArr)) {
510
                    $cPR['header'] = 'Result';
511
                    $out .= '<table class="table table-striped table-hover">'
512
                        . $this->resultRowTitles($dataRow, $GLOBALS['TCA'][$table]) . implode(LF, $rowArr)
513
                        . '</table>';
514
                } else {
515
                    $this->renderNoResultsFoundMessage();
516
                }
517
518
                $cPR['content'] = $out;
519
                break;
520
            case 'csv':
521
                $rowArr = [];
522
                $first = 1;
523
                foreach ($dataRows as $dataRow) {
524
                    if ($first) {
525
                        $rowArr[] = $this->csvValues(array_keys($dataRow), ',', '');
526
                        $first = 0;
527
                    }
528
                    $rowArr[] = $this->csvValues($dataRow, ',', '"', $GLOBALS['TCA'][$table], $table);
529
                }
530
                if (!empty($rowArr)) {
531
                    $cPR['header'] = 'Result';
532
                    $out .= '<textarea name="whatever" rows="20" class="text-monospace" style="width:100%">'
533
                        . htmlspecialchars(implode(LF, $rowArr))
534
                        . '</textarea>';
535
                    if (!$this->noDownloadB) {
536
                        $out .= '<br><input class="btn btn-default" type="submit" name="download_file" '
537
                            . 'value="Click to download file">';
538
                    }
539
                    // Downloads file:
540
                    // @todo: args. routing anyone?
541
                    if (GeneralUtility::_GP('download_file')) {
542
                        $filename = 'TYPO3_' . $table . '_export_' . date('dmy-Hi') . '.csv';
543
                        $mimeType = 'application/octet-stream';
544
                        header('Content-Type: ' . $mimeType);
545
                        header('Content-Disposition: attachment; filename=' . $filename);
546
                        echo implode(CRLF, $rowArr);
547
                        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...
548
                    }
549
                } else {
550
                    $this->renderNoResultsFoundMessage();
551
                }
552
                $cPR['content'] = $out;
553
                break;
554
            case 'explain':
555
            default:
556
                foreach ($dataRows as $dataRow) {
557
                    $out .= '<br />' . DebugUtility::viewArray($dataRow);
558
                }
559
                $cPR['header'] = 'Explain SQL query';
560
                $cPR['content'] = $out;
561
        }
562
        return $cPR;
563
    }
564
565
    /**
566
     * CSV values
567
     *
568
     * @param array $row
569
     * @param string $delim
570
     * @param string $quote
571
     * @param array $conf
572
     * @param string $table
573
     * @return string A single line of CSV
574
     */
575
    public function csvValues($row, $delim = ',', $quote = '"', $conf = [], $table = '')
576
    {
577
        $valueArray = $row;
578
        if ($this->settings['search_result_labels'] && $table) {
579
            foreach ($valueArray as $key => $val) {
580
                $valueArray[$key] = $this->getProcessedValueExtra($table, $key, $val, $conf, ';');
581
            }
582
        }
583
        return CsvUtility::csvValues($valueArray, $delim, $quote);
584
    }
585
586
    /**
587
     * Search
588
     *
589
     * @return string
590
     */
591
    public function search()
592
    {
593
        $swords = $this->settings['sword'];
594
        $out = '';
595
        if ($swords) {
596
            foreach ($GLOBALS['TCA'] as $table => $value) {
597
                // Get fields list
598
                $conf = $GLOBALS['TCA'][$table];
599
                // Avoid querying tables with no columns
600
                if (empty($conf['columns'])) {
601
                    continue;
602
                }
603
                $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
604
                $tableColumns = $connection->getSchemaManager()->listTableColumns($table);
605
                $fieldsInDatabase = [];
606
                foreach ($tableColumns as $column) {
607
                    $fieldsInDatabase[] = $column->getName();
608
                }
609
                $fields = array_intersect(array_keys($conf['columns']), $fieldsInDatabase);
610
611
                $queryBuilder = $connection->createQueryBuilder();
612
                $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
613
                $queryBuilder->count('*')->from($table);
614
                $likes = [];
615
                $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($swords) . '%';
616
                foreach ($fields as $field) {
617
                    $likes[] = $queryBuilder->expr()->like(
618
                        $field,
619
                        $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
620
                    );
621
                }
622
                $count = $queryBuilder->orWhere(...$likes)->execute()->fetchColumn(0);
623
624
                if ($count > 0) {
625
                    $queryBuilder = $connection->createQueryBuilder();
626
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
627
                    $queryBuilder->select('uid', $conf['ctrl']['label'])
628
                        ->from($table)
629
                        ->setMaxResults(200);
630
                    $likes = [];
631
                    foreach ($fields as $field) {
632
                        $likes[] = $queryBuilder->expr()->like(
633
                            $field,
634
                            $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR)
635
                        );
636
                    }
637
                    $statement = $queryBuilder->orWhere(...$likes)->execute();
638
                    $lastRow = null;
639
                    $rowArr = [];
640
                    while ($row = $statement->fetch()) {
641
                        $rowArr[] = $this->resultRowDisplay($row, $conf, $table);
642
                        $lastRow = $row;
643
                    }
644
                    $markup = [];
645
                    $markup[] = '<div class="panel panel-default">';
646
                    $markup[] = '  <div class="panel-heading">';
647
                    $markup[] = htmlspecialchars($this->languageService->sL($conf['ctrl']['title'])) . ' (' . $count . ')';
648
                    $markup[] = '  </div>';
649
                    $markup[] = '  <table class="table table-striped table-hover">';
650
                    $markup[] = $this->resultRowTitles($lastRow, $conf);
651
                    $markup[] = implode(LF, $rowArr);
652
                    $markup[] = '  </table>';
653
                    $markup[] = '</div>';
654
655
                    $out .= implode(LF, $markup);
656
                }
657
            }
658
        }
659
        return $out;
660
    }
661
662
    /**
663
     * Result row display
664
     *
665
     * @param array $row
666
     * @param array $conf
667
     * @param string $table
668
     * @return string
669
     */
670
    public function resultRowDisplay($row, $conf, $table)
671
    {
672
        $out = '<tr>';
673
        foreach ($row as $fieldName => $fieldValue) {
674
            if (GeneralUtility::inList($this->settings['queryFields'], $fieldName)
675
                || !$this->settings['queryFields']
676
                && $fieldName !== 'pid'
677
                && $fieldName !== 'deleted'
678
            ) {
679
                if ($this->settings['search_result_labels']) {
680
                    $fVnew = $this->getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, '<br />');
681
                } else {
682
                    $fVnew = htmlspecialchars($fieldValue);
683
                }
684
                $out .= '<td>' . $fVnew . '</td>';
685
            }
686
        }
687
        $out .= '<td>';
688
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
689
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
690
691
        if (!$row['deleted']) {
692
            $out .= '<div class="btn-group" role="group">';
693
            $url = (string)$uriBuilder->buildUriFromRoute('record_edit', [
694
                'edit' => [
695
                    $table => [
696
                        $row['uid'] => 'edit'
697
                    ]
698
                ],
699
                'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
700
                    . HttpUtility::buildQueryString(['SET' => (array)GeneralUtility::_POST('SET')], '&')
701
            ]);
702
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars($url) . '">'
703
                . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render() . '</a>';
704
            $out .= '</div><div class="btn-group" role="group">';
705
            $out .= sprintf(
706
                '<a class="btn btn-default" href="#" data-dispatch-action="%s" data-dispatch-args-list="%s">%s</a>',
707
                'TYPO3.InfoWindow.showItem',
708
                htmlspecialchars($table . ',' . $row['uid']),
709
                $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render()
710
            );
711
            $out .= '</div>';
712
        } else {
713
            $out .= '<div class="btn-group" role="group">';
714
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
715
                        'cmd' => [
716
                            $table => [
717
                                $row['uid'] => [
718
                                    'undelete' => 1
719
                                ]
720
                            ]
721
                        ],
722
                        'redirect' => GeneralUtility::linkThisScript()
723
                    ])) . '" title="' . htmlspecialchars($this->languageService->getLL('undelete_only')) . '">';
724
            $out .= $this->iconFactory->getIcon('actions-edit-restore', Icon::SIZE_SMALL)->render() . '</a>';
725
            $formEngineParameters = [
726
                'edit' => [
727
                    $table => [
728
                        $row['uid'] => 'edit'
729
                    ]
730
                ],
731
                'returnUrl' => GeneralUtility::linkThisScript()
732
            ];
733
            $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $formEngineParameters);
734
            $out .= '<a class="btn btn-default" href="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute('tce_db', [
735
                    'cmd' => [
736
                        $table => [
737
                            $row['uid'] => [
738
                                'undelete' => 1
739
                            ]
740
                        ]
741
                    ],
742
                    'redirect' => $redirectUrl
743
                ])) . '" title="' . htmlspecialchars($this->languageService->getLL('undelete_and_edit')) . '">';
744
            $out .= $this->iconFactory->getIcon('actions-edit-restore-edit', Icon::SIZE_SMALL)->render() . '</a>';
745
            $out .= '</div>';
746
        }
747
        $_params = [$table => $row];
748
        if (is_array($this->hookArray['additionalButtons'])) {
749
            foreach ($this->hookArray['additionalButtons'] as $_funcRef) {
750
                $out .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
751
            }
752
        }
753
        $out .= '</td></tr>';
754
        return $out;
755
    }
756
757
    /**
758
     * Get processed value extra
759
     *
760
     * @param string $table
761
     * @param string $fieldName
762
     * @param string $fieldValue
763
     * @param array $conf Not used
764
     * @param string $splitString
765
     * @return string
766
     */
767
    public function getProcessedValueExtra($table, $fieldName, $fieldValue, $conf, $splitString)
0 ignored issues
show
Unused Code introduced by
The parameter $conf is not used and could be removed. ( Ignorable by Annotation )

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

767
    public function getProcessedValueExtra($table, $fieldName, $fieldValue, /** @scrutinizer ignore-unused */ $conf, $splitString)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
768
    {
769
        $out = '';
770
        $fields = [];
771
        // Analysing the fields in the table.
772
        if (is_array($GLOBALS['TCA'][$table])) {
773
            $fC = $GLOBALS['TCA'][$table]['columns'][$fieldName];
774
            $fields = $fC['config'];
775
            $fields['exclude'] = $fC['exclude'];
776
            if (is_array($fC) && $fC['label']) {
777
                $fields['label'] = preg_replace('/:$/', '', trim($this->languageService->sL($fC['label'])));
778
                switch ($fields['type']) {
779
                    case 'input':
780
                        if (preg_match('/int|year/i', $fields['eval'])) {
781
                            $fields['type'] = 'number';
782
                        } elseif (preg_match('/time/i', $fields['eval'])) {
783
                            $fields['type'] = 'time';
784
                        } elseif (preg_match('/date/i', $fields['eval'])) {
785
                            $fields['type'] = 'date';
786
                        } else {
787
                            $fields['type'] = 'text';
788
                        }
789
                        break;
790
                    case 'check':
791
                        if (!$fields['items']) {
792
                            $fields['type'] = 'boolean';
793
                        } else {
794
                            $fields['type'] = 'binary';
795
                        }
796
                        break;
797
                    case 'radio':
798
                        $fields['type'] = 'multiple';
799
                        break;
800
                    case 'select':
801
                        $fields['type'] = 'multiple';
802
                        if ($fields['foreign_table']) {
803
                            $fields['type'] = 'relation';
804
                        }
805
                        if ($fields['special']) {
806
                            $fields['type'] = 'text';
807
                        }
808
                        break;
809
                    case 'group':
810
                        if ($fields['internal_type'] === 'db') {
811
                            $fields['type'] = 'relation';
812
                        }
813
                        break;
814
                    case 'user':
815
                    case 'flex':
816
                    case 'passthrough':
817
                    case 'none':
818
                    case 'text':
819
                    default:
820
                        $fields['type'] = 'text';
821
                }
822
            } else {
823
                $fields['label'] = '[FIELD: ' . $fieldName . ']';
824
                switch ($fieldName) {
825
                    case 'pid':
826
                        $fields['type'] = 'relation';
827
                        $fields['allowed'] = 'pages';
828
                        break;
829
                    case 'cruser_id':
830
                        $fields['type'] = 'relation';
831
                        $fields['allowed'] = 'be_users';
832
                        break;
833
                    case 'tstamp':
834
                    case 'crdate':
835
                        $fields['type'] = 'time';
836
                        break;
837
                    default:
838
                        $fields['type'] = 'number';
839
                }
840
            }
841
        }
842
        switch ($fields['type']) {
843
            case 'date':
844
                if ($fieldValue != -1) {
845
                    $out = strftime('%d-%m-%Y', $fieldValue);
0 ignored issues
show
Bug introduced by
$fieldValue of type string is incompatible with the type integer expected by parameter $timestamp of strftime(). ( Ignorable by Annotation )

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

845
                    $out = strftime('%d-%m-%Y', /** @scrutinizer ignore-type */ $fieldValue);
Loading history...
846
                }
847
                break;
848
            case 'time':
849
                if ($fieldValue != -1) {
850
                    if ($splitString === '<br />') {
851
                        $out = strftime('%H:%M' . $splitString . '%d-%m-%Y', $fieldValue);
852
                    } else {
853
                        $out = strftime('%H:%M %d-%m-%Y', $fieldValue);
854
                    }
855
                }
856
                break;
857
            case 'multiple':
858
            case 'binary':
859
            case 'relation':
860
                $out = $this->makeValueList($fieldName, $fieldValue, $fields, $table, $splitString);
861
                break;
862
            case 'boolean':
863
                $out = $fieldValue ? 'True' : 'False';
864
                break;
865
            default:
866
                $out = htmlspecialchars($fieldValue);
867
        }
868
        return $out;
869
    }
870
871
    /**
872
     * Get tree list
873
     *
874
     * @param int $id
875
     * @param int $depth
876
     * @param int $begin
877
     * @param string $permsClause
878
     *
879
     * @return string
880
     */
881
    public function getTreeList($id, $depth, $begin = 0, $permsClause = null)
882
    {
883
        $depth = (int)$depth;
884
        $begin = (int)$begin;
885
        $id = (int)$id;
886
        if ($id < 0) {
887
            $id = abs($id);
888
        }
889
        if ($begin == 0) {
890
            $theList = $id;
891
        } else {
892
            $theList = '';
893
        }
894
        if ($id && $depth > 0) {
895
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
896
            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
897
            $statement = $queryBuilder->select('uid')
898
                ->from('pages')
899
                ->where(
900
                    $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
901
                    $queryBuilder->expr()->eq('sys_language_uid', 0),
902
                    QueryHelper::stripLogicalOperatorPrefix($permsClause)
0 ignored issues
show
Bug introduced by
It seems like $permsClause can also be of type null; however, parameter $constraint of TYPO3\CMS\Core\Database\...LogicalOperatorPrefix() does only seem to accept string, 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

902
                    QueryHelper::stripLogicalOperatorPrefix(/** @scrutinizer ignore-type */ $permsClause)
Loading history...
903
                )
904
                ->execute();
905
            while ($row = $statement->fetch()) {
906
                if ($begin <= 0) {
907
                    $theList .= ',' . $row['uid'];
908
                }
909
                if ($depth > 1) {
910
                    $theList .= $this->getTreeList($row['uid'], $depth - 1, $begin - 1, $permsClause);
911
                }
912
            }
913
        }
914
        return $theList;
915
    }
916
917
    /**
918
     * Make value list
919
     *
920
     * @param string $fieldName
921
     * @param string $fieldValue
922
     * @param array $conf
923
     * @param string $table
924
     * @param string $splitString
925
     * @return string
926
     */
927
    public function makeValueList($fieldName, $fieldValue, $conf, $table, $splitString)
928
    {
929
        $from_table_Arr = [];
930
        $fieldSetup = $conf;
931
        $out = '';
932
        if ($fieldSetup['type'] === 'multiple') {
933
            foreach ($fieldSetup['items'] as $key => $val) {
934
                if (strpos($val[0], 'LLL:') === 0) {
935
                    $value = $this->languageService->sL($val[0]);
936
                } else {
937
                    $value = $val[0];
938
                }
939
                if (GeneralUtility::inList($fieldValue, $val[1]) || $fieldValue == $val[1]) {
940
                    if ($out !== '') {
941
                        $out .= $splitString;
942
                    }
943
                    $out .= htmlspecialchars($value);
944
                }
945
            }
946
        }
947
        if ($fieldSetup['type'] === 'binary') {
948
            foreach ($fieldSetup['items'] as $Key => $val) {
949
                if (strpos($val[0], 'LLL:') === 0) {
950
                    $value = $this->languageService->sL($val[0]);
951
                } else {
952
                    $value = $val[0];
953
                }
954
                if ($out !== '') {
955
                    $out .= $splitString;
956
                }
957
                $out .= htmlspecialchars($value);
958
            }
959
        }
960
        if ($fieldSetup['type'] === 'relation') {
961
            $dontPrefixFirstTable = 0;
962
            $useTablePrefix = 0;
963
            if ($fieldSetup['items']) {
964
                foreach ($fieldSetup['items'] as $key => $val) {
965
                    if (strpos($val[0], 'LLL:') === 0) {
966
                        $value = $this->languageService->sL($val[0]);
967
                    } else {
968
                        $value = $val[0];
969
                    }
970
                    if (GeneralUtility::inList($fieldValue, $value) || $fieldValue == $value) {
971
                        if ($out !== '') {
972
                            $out .= $splitString;
973
                        }
974
                        $out .= htmlspecialchars($value);
975
                    }
976
                }
977
            }
978
            if (strpos($fieldSetup['allowed'], ',') !== false) {
979
                $from_table_Arr = explode(',', $fieldSetup['allowed']);
980
                $useTablePrefix = 1;
981
                if (!$fieldSetup['prepend_tname']) {
982
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
983
                    $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
984
                    $statement = $queryBuilder->select($fieldName)->from($table)->execute();
985
                    while ($row = $statement->fetch()) {
986
                        if (strpos($row[$fieldName], ',') !== false) {
987
                            $checkContent = explode(',', $row[$fieldName]);
988
                            foreach ($checkContent as $singleValue) {
989
                                if (strpos($singleValue, '_') === false) {
990
                                    $dontPrefixFirstTable = 1;
991
                                }
992
                            }
993
                        } else {
994
                            $singleValue = $row[$fieldName];
995
                            if ($singleValue !== '' && strpos($singleValue, '_') === false) {
996
                                $dontPrefixFirstTable = 1;
997
                            }
998
                        }
999
                    }
1000
                }
1001
            } else {
1002
                $from_table_Arr[0] = $fieldSetup['allowed'];
1003
            }
1004
            if ($fieldSetup['prepend_tname']) {
1005
                $useTablePrefix = 1;
1006
            }
1007
            if ($fieldSetup['foreign_table']) {
1008
                $from_table_Arr[0] = $fieldSetup['foreign_table'];
1009
            }
1010
            $counter = 0;
1011
            $useSelectLabels = 0;
1012
            $useAltSelectLabels = 0;
1013
            $tablePrefix = '';
1014
            $labelFieldSelect = [];
1015
            foreach ($from_table_Arr as $from_table) {
1016
                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...
1017
                    $tablePrefix = $from_table . '_';
1018
                }
1019
                $counter = 1;
1020
                if (is_array($GLOBALS['TCA'][$from_table])) {
1021
                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
1022
                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
1023
                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
1024
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'];
1025
                        foreach ($items as $labelArray) {
1026
                            if (strpos($labelArray[0], 'LLL:') === 0) {
1027
                                $labelFieldSelect[$labelArray[1]] = $this->languageService->sL($labelArray[0]);
1028
                            } else {
1029
                                $labelFieldSelect[$labelArray[1]] = $labelArray[0];
1030
                            }
1031
                        }
1032
                        $useSelectLabels = 1;
1033
                    }
1034
                    $altLabelFieldSelect = [];
1035
                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
1036
                        $items = $GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'];
1037
                        foreach ($items as $altLabelArray) {
1038
                            if (strpos($altLabelArray[0], 'LLL:') === 0) {
1039
                                $altLabelFieldSelect[$altLabelArray[1]] = $this->languageService->sL($altLabelArray[0]);
1040
                            } else {
1041
                                $altLabelFieldSelect[$altLabelArray[1]] = $altLabelArray[0];
1042
                            }
1043
                        }
1044
                        $useAltSelectLabels = 1;
1045
                    }
1046
1047
                    if (!$this->tableArray[$from_table]) {
1048
                        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
1049
                        $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1050
                        $selectFields = ['uid', $labelField];
1051
                        if ($altLabelField) {
1052
                            $selectFields[] = $altLabelField;
1053
                        }
1054
                        $queryBuilder->select(...$selectFields)
1055
                            ->from($from_table)
1056
                            ->orderBy('uid');
1057
                        if (!$this->backendUserAuthentication->isAdmin() && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts']) {
1058
                            $webMounts = $this->backendUserAuthentication->returnWebmounts();
1059
                            $perms_clause = $this->backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
1060
                            $webMountPageTree = '';
1061
                            $webMountPageTreePrefix = '';
1062
                            foreach ($webMounts as $webMount) {
1063
                                if ($webMountPageTree) {
1064
                                    $webMountPageTreePrefix = ',';
1065
                                }
1066
                                $webMountPageTree .= $webMountPageTreePrefix
1067
                                    . $this->getTreeList($webMount, 999, $begin = 0, $perms_clause);
1068
                            }
1069
                            if ($from_table === 'pages') {
1070
                                $queryBuilder->where(
1071
                                    QueryHelper::stripLogicalOperatorPrefix($perms_clause),
1072
                                    $queryBuilder->expr()->in(
1073
                                        'uid',
1074
                                        $queryBuilder->createNamedParameter(
1075
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1076
                                            Connection::PARAM_INT_ARRAY
1077
                                        )
1078
                                    )
1079
                                );
1080
                            } else {
1081
                                $queryBuilder->where(
1082
                                    $queryBuilder->expr()->in(
1083
                                        'pid',
1084
                                        $queryBuilder->createNamedParameter(
1085
                                            GeneralUtility::intExplode(',', $webMountPageTree),
1086
                                            Connection::PARAM_INT_ARRAY
1087
                                        )
1088
                                    )
1089
                                );
1090
                            }
1091
                        }
1092
                        $statement = $queryBuilder->execute();
1093
                        $this->tableArray[$from_table] = [];
1094
                        while ($row = $statement->fetch()) {
1095
                            $this->tableArray[$from_table][] = $row;
1096
                        }
1097
                    }
1098
1099
                    foreach ($this->tableArray[$from_table] as $key => $val) {
1100
                        $this->settings['labels_noprefix'] =
1101
                            $this->settings['labels_noprefix'] == 1
1102
                                ? 'on'
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "?"; newline found
Loading history...
1103
                                : $this->settings['labels_noprefix'];
0 ignored issues
show
Coding Style introduced by
Expected 1 space before ":"; newline found
Loading history...
1104
                        $prefixString =
1105
                            $this->settings['labels_noprefix'] === 'on'
1106
                                ? ''
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "?"; newline found
Loading history...
1107
                                : ' [' . $tablePrefix . $val['uid'] . '] ';
0 ignored issues
show
Coding Style introduced by
Expected 1 space before ":"; newline found
Loading history...
1108
                        if ($out !== '') {
1109
                            $out .= $splitString;
1110
                        }
1111
                        if (GeneralUtility::inList($fieldValue, $tablePrefix . $val['uid'])
1112
                            || $fieldValue == $tablePrefix . $val['uid']) {
1113
                            if ($useSelectLabels) {
1114
                                $out .= htmlspecialchars($prefixString . $labelFieldSelect[$val[$labelField]]);
1115
                            } elseif ($val[$labelField]) {
1116
                                $out .= htmlspecialchars($prefixString . $val[$labelField]);
1117
                            } elseif ($useAltSelectLabels) {
1118
                                $out .= htmlspecialchars($prefixString . $altLabelFieldSelect[$val[$altLabelField]]);
1119
                            } else {
1120
                                $out .= htmlspecialchars($prefixString . $val[$altLabelField]);
1121
                            }
1122
                        }
1123
                    }
1124
                }
1125
            }
1126
        }
1127
        return $out;
1128
    }
1129
1130
    /**
1131
     * Render table header
1132
     *
1133
     * @param array $row Table columns
1134
     * @param array $conf Table TCA
1135
     * @return string HTML of table header
1136
     */
1137
    public function resultRowTitles($row, $conf)
1138
    {
1139
        $tableHeader = [];
1140
        // Start header row
1141
        $tableHeader[] = '<thead><tr>';
1142
        // Iterate over given columns
1143
        foreach ($row as $fieldName => $fieldValue) {
1144
            if (GeneralUtility::inList($this->settings['queryFields'], $fieldName)
1145
                || !$this->settings['queryFields']
1146
                && $fieldName !== 'pid'
1147
                && $fieldName !== 'deleted'
1148
            ) {
1149
                if ($this->settings['search_result_labels']) {
1150
                    $title = $this->languageService->sL($conf['columns'][$fieldName]['label']
1151
                        ?: $fieldName);
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "?"; newline found
Loading history...
1152
                } else {
1153
                    $title = $this->languageService->sL($fieldName);
1154
                }
1155
                $tableHeader[] = '<th>' . htmlspecialchars($title) . '</th>';
1156
            }
1157
        }
1158
        // Add empty icon column
1159
        $tableHeader[] = '<th></th>';
1160
        // Close header row
1161
        $tableHeader[] = '</tr></thead>';
1162
        return implode(LF, $tableHeader);
1163
    }
1164
1165
    /**
1166
     * CSV row titles
1167
     *
1168
     * @param array $row
1169
     * @param array $conf
1170
     * @return string
1171
     * @todo Unused?
1172
     */
1173
    public function csvRowTitles($row, $conf)
1174
    {
1175
        $out = '';
1176
        foreach ($row as $fieldName => $fieldValue) {
1177
            if (GeneralUtility::inList($this->settings['queryFields'], $fieldName)
1178
                || !$this->settings['queryFields'] && $fieldName !== 'pid') {
1179
                if ($out !== '') {
1180
                    $out .= ',';
1181
                }
1182
                if ($this->settings['search_result_labels']) {
1183
                    $out .= htmlspecialchars(
1184
                        $this->languageService->sL(
1185
                            $conf['columns'][$fieldName]['label']
1186
                            ?: $fieldName
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "?"; newline found
Loading history...
1187
                        )
1188
                    );
1189
                } else {
1190
                    $out .= htmlspecialchars($this->languageService->sL($fieldName));
1191
                }
1192
            }
1193
        }
1194
        return $out;
1195
    }
1196
1197
    /**
1198
     * Sets the current name of the input form.
1199
     *
1200
     * @param string $formName The name of the form.
1201
     */
1202
    public function setFormName($formName)
1203
    {
1204
        $this->formName = trim($formName);
1205
    }
1206
1207
    /**
1208
     * @throws \InvalidArgumentException
1209
     * @throws \TYPO3\CMS\Core\Exception
1210
     */
1211
    private function renderNoResultsFoundMessage()
1212
    {
1213
        $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, 'No rows selected!', '', FlashMessage::INFO);
1214
        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1215
        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1216
        $defaultFlashMessageQueue->enqueue($flashMessage);
1217
    }
1218
}
1219