1<?php
2namespace TYPO3\CMS\Recordlist\RecordList;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17use TYPO3\CMS\Backend\RecordList\AbstractRecordList;
18use TYPO3\CMS\Backend\Routing\UriBuilder;
19use TYPO3\CMS\Backend\Tree\View\PageTreeView;
20use TYPO3\CMS\Backend\Utility\BackendUtility;
21use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22use TYPO3\CMS\Core\Cache\CacheManager;
23use TYPO3\CMS\Core\Database\Connection;
24use TYPO3\CMS\Core\Database\ConnectionPool;
25use TYPO3\CMS\Core\Database\Query\QueryBuilder;
26use TYPO3\CMS\Core\Database\Query\QueryHelper;
27use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
28use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
29use TYPO3\CMS\Core\Imaging\Icon;
30use TYPO3\CMS\Core\Imaging\IconFactory;
31use TYPO3\CMS\Core\Log\LogManager;
32use TYPO3\CMS\Core\Service\DependencyOrderingService;
33use TYPO3\CMS\Core\Type\Bitmask\Permission;
34use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
35use TYPO3\CMS\Core\Utility\GeneralUtility;
36use TYPO3\CMS\Core\Utility\HttpUtility;
37use TYPO3\CMS\Core\Utility\MathUtility;
38
39/**
40 * Child class for rendering of Web > List (not the final class)
41 * Shared between Web>List and Web>Page
42 * @see \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
43 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
44 */
45class AbstractDatabaseRecordList extends AbstractRecordList
46{
47    /**
48     * Specify a list of tables which are the only ones allowed to be displayed.
49     *
50     * @var string
51     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
52     */
53    public $tableList = '';
54
55    /**
56     * Return URL
57     *
58     * @var string
59     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
60     */
61    public $returnUrl = '';
62
63    /**
64     * Thumbnails on records containing files (pictures)
65     *
66     * @var bool
67     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
68     */
69    public $thumbs = 0;
70
71    /**
72     * default Max items shown per table in "multi-table mode", may be overridden by tables.php
73     *
74     * @var int
75     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
76     */
77    public $itemsLimitPerTable = 20;
78
79    /**
80     * default Max items shown per table in "single-table mode", may be overridden by tables.php
81     *
82     * @var int
83     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
84     */
85    public $itemsLimitSingleTable = 100;
86
87    /**
88     * Current script name
89     *
90     * @var string
91     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
92     */
93    public $script = 'index.php';
94
95    /**
96     * Indicates if all available fields for a user should be selected or not.
97     *
98     * @var int
99     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
100     */
101    public $allFields = 0;
102
103    /**
104     * If set, csvList is outputted.
105     *
106     * @var bool
107     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
108     */
109    public $csvOutput = false;
110
111    /**
112     * Field, to sort list by
113     *
114     * @var string
115     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
116     */
117    public $sortField;
118
119    /**
120     * Field, indicating to sort in reverse order.
121     *
122     * @var bool
123     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
124     */
125    public $sortRev;
126
127    /**
128     * Containing which fields to display in extended mode
129     *
130     * @var string[]
131     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
132     */
133    public $displayFields;
134
135    /**
136     * String, can contain the field name from a table which must have duplicate values marked.
137     *
138     * @var string
139     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
140     */
141    public $duplicateField;
142
143    /**
144     * Page id
145     *
146     * @var int
147     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
148     */
149    public $id;
150
151    /**
152     * Tablename if single-table mode
153     *
154     * @var string
155     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
156     */
157    public $table = '';
158
159    /**
160     * If TRUE, records are listed only if a specific table is selected.
161     *
162     * @var bool
163     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
164     */
165    public $listOnlyInSingleTableMode = false;
166
167    /**
168     * Pointer for browsing list
169     *
170     * @var int
171     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
172     */
173    public $firstElementNumber = 0;
174
175    /**
176     * Search string
177     *
178     * @var string
179     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
180     */
181    public $searchString = '';
182
183    /**
184     * Levels to search down.
185     *
186     * @var int
187     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
188     */
189    public $searchLevels = '';
190
191    /**
192     * Number of records to show
193     *
194     * @var int
195     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
196     */
197    public $showLimit = 0;
198
199    /**
200     * Page select permissions
201     *
202     * @var string
203     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
204     */
205    public $perms_clause = '';
206
207    /**
208     * Some permissions...
209     *
210     * @var int
211     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
212     */
213    public $calcPerms = 0;
214
215    /**
216     * Mode for what happens when a user clicks the title of a record.
217     *
218     * @var string
219     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
220     */
221    public $clickTitleMode = '';
222
223    /**
224     * Shared module configuration, used by localization features
225     *
226     * @var array
227     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
228     */
229    public $modSharedTSconfig = [];
230
231    /**
232     * Loaded with page record with version overlay if any.
233     *
234     * @var string[]
235     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
236     */
237    public $pageRecord = [];
238
239    /**
240     * Tables which should not get listed
241     *
242     * @var string
243     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
244     */
245    public $hideTables = '';
246
247    /**
248     * Tables which should not list their translations
249     *
250     * @var string
251     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
252     */
253    public $hideTranslations = '';
254
255    /**
256     * TSconfig which overwrites TCA-Settings
257     *
258     * @var mixed[][]
259     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
260     */
261    public $tableTSconfigOverTCA = [];
262
263    /**
264     * Array of collapsed / uncollapsed tables in multi table view
265     *
266     * @var int[][]
267     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
268     */
269    public $tablesCollapsed = [];
270
271    /**
272     * JavaScript code accumulation
273     *
274     * @var string
275     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
276     */
277    public $JScode = '';
278
279    /**
280     * HTML output
281     *
282     * @var string
283     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
284     */
285    public $HTMLcode = '';
286
287    /**
288     * "LIMIT " in SQL...
289     *
290     * @var int
291     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
292     */
293    public $iLimit = 0;
294
295    /**
296     * Counting the elements no matter what...
297     *
298     * @var int
299     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
300     */
301    public $eCounter = 0;
302
303    /**
304     * Set to the total number of items for a table when selecting.
305     *
306     * @var string
307     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
308     */
309    public $totalItems = '';
310
311    /**
312     * Cache for record path
313     *
314     * @var mixed[]
315     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
316     */
317    public $recPath_cache = [];
318
319    /**
320     * Fields to display for the current table
321     *
322     * @var string[]
323     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
324     */
325    public $setFields = [];
326
327    /**
328     * Used for tracking next/prev uids
329     *
330     * @var int[][]
331     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
332     */
333    public $currentTable = [];
334
335    /**
336     * Used for tracking duplicate values of fields
337     *
338     * @var string[]
339     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
340     */
341    public $duplicateStack = [];
342
343    /**
344     * @var array[] Module configuration
345     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
346     */
347    public $modTSconfig;
348
349    /**
350     * Override/add urlparameters in listUrl() method
351     * @var string[]
352     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
353     */
354    protected $overrideUrlParameters = [];
355
356    /**
357     * Override the page ids taken into account by getPageIdConstraint()
358     *
359     * @var array
360     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
361     */
362    protected $overridePageIdList = [];
363
364    /**
365     * Array with before/after setting for tables
366     * Structure:
367     * 'tableName' => [
368     *    'before' => ['A', ...]
369     *    'after' => []
370     *  ]
371     * @var array[]
372     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
373     */
374    protected $tableDisplayOrder = [];
375
376    /**
377     * Initializes the list generation
378     *
379     * @param int $id Page id for which the list is rendered. Must be >= 0
380     * @param string $table Tablename - if extended mode where only one table is listed at a time.
381     * @param int $pointer Browsing pointer.
382     * @param string $search Search word, if any
383     * @param int $levels Number of levels to search down the page tree
384     * @param int $showLimit Limit of records to be listed.
385     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
386     */
387    public function start($id, $table, $pointer, $search = '', $levels = 0, $showLimit = 0)
388    {
389        $backendUser = $this->getBackendUserAuthentication();
390        // Setting internal variables:
391        // sets the parent id
392        $this->id = (int)$id;
393        if ($GLOBALS['TCA'][$table]) {
394            // Setting single table mode, if table exists:
395            $this->table = $table;
396        }
397        $this->firstElementNumber = $pointer;
398        $this->searchString = trim($search);
399        $this->searchLevels = (int)$levels;
400        $this->showLimit = MathUtility::forceIntegerInRange($showLimit, 0, 10000);
401        // Setting GPvars:
402        $this->csvOutput = (bool)GeneralUtility::_GP('csv');
403        $this->sortField = GeneralUtility::_GP('sortField');
404        $this->sortRev = GeneralUtility::_GP('sortRev');
405        $this->displayFields = GeneralUtility::_GP('displayFields');
406        $this->duplicateField = GeneralUtility::_GP('duplicateField');
407        if (GeneralUtility::_GP('justLocalized')) {
408            $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
409        }
410        // Init dynamic vars:
411        $this->counter = 0;
412        $this->JScode = '';
413        $this->HTMLcode = '';
414        // Limits
415        if (isset($this->modTSconfig['properties']['itemsLimitPerTable'])) {
416            $this->itemsLimitPerTable = MathUtility::forceIntegerInRange(
417                (int)$this->modTSconfig['properties']['itemsLimitPerTable'],
418                1,
419                10000
420            );
421        }
422        if (isset($this->modTSconfig['properties']['itemsLimitSingleTable'])) {
423            $this->itemsLimitSingleTable = MathUtility::forceIntegerInRange(
424                (int)$this->modTSconfig['properties']['itemsLimitSingleTable'],
425                1,
426                10000
427            );
428        }
429
430        // $table might be NULL at this point in the code. As the expressionBuilder
431        // is used to limit returned records based on the page permissions and the
432        // uid field of the pages it can hardcoded to work on the pages table.
433        $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
434            ->getQueryBuilderForTable('pages')
435            ->expr();
436        $permsClause = $expressionBuilder->andX($backendUser->getPagePermsClause(Permission::PAGE_SHOW));
437        // This will hide records from display - it has nothing to do with user rights!!
438        $pidList = GeneralUtility::intExplode(',', $backendUser->getTSConfig()['options.']['hideRecords.']['pages'] ?? '', true);
439        if (!empty($pidList)) {
440            $permsClause->add($expressionBuilder->notIn('pages.uid', $pidList));
441        }
442        $this->perms_clause = (string)$permsClause;
443
444        // Get configuration of collapsed tables from user uc and merge with sanitized GP vars
445        $this->tablesCollapsed = is_array($backendUser->uc['moduleData']['list'])
446            ? $backendUser->uc['moduleData']['list']
447            : [];
448        $collapseOverride = GeneralUtility::_GP('collapse');
449        if (is_array($collapseOverride)) {
450            foreach ($collapseOverride as $collapseTable => $collapseValue) {
451                if (is_array($GLOBALS['TCA'][$collapseTable]) && ($collapseValue == 0 || $collapseValue == 1)) {
452                    $this->tablesCollapsed[$collapseTable] = $collapseValue;
453                }
454            }
455            // Save modified user uc
456            $backendUser->uc['moduleData']['list'] = $this->tablesCollapsed;
457            $backendUser->writeUC($backendUser->uc);
458            $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
459            if ($returnUrl !== '') {
460                HttpUtility::redirect($returnUrl);
461            }
462        }
463        $this->initializeLanguages();
464    }
465
466    /**
467     * Traverses the table(s) to be listed and renders the output code for each:
468     * The HTML is accumulated in $this->HTMLcode
469     * Finishes off with a stopper-gif
470     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
471     */
472    public function generateList()
473    {
474        // Set page record in header
475        $this->pageRecord = BackendUtility::getRecordWSOL('pages', $this->id);
476        $hideTablesArray = GeneralUtility::trimExplode(',', $this->hideTables);
477
478        $backendUser = $this->getBackendUserAuthentication();
479
480        // pre-process tables and add sorting instructions
481        $tableNames = array_flip(array_keys($GLOBALS['TCA']));
482        foreach ($tableNames as $tableName => &$config) {
483            $hideTable = false;
484
485            // Checking if the table should be rendered:
486            // Checks that we see only permitted/requested tables:
487            if ($this->table && $tableName !== $this->table
488                || $this->tableList && !GeneralUtility::inList($this->tableList, $tableName)
489                || !$backendUser->check('tables_select', $tableName)
490            ) {
491                $hideTable = true;
492            }
493
494            if (!$hideTable) {
495                // Don't show table if hidden by TCA ctrl section
496                // Don't show table if hidden by pageTSconfig mod.web_list.hideTables
497                $hideTable = $hideTable
498                    || !empty($GLOBALS['TCA'][$tableName]['ctrl']['hideTable'])
499                    || in_array($tableName, $hideTablesArray, true)
500                    || in_array('*', $hideTablesArray, true);
501                // Override previous selection if table is enabled or hidden by TSconfig TCA override mod.web_list.table
502                if (isset($this->tableTSconfigOverTCA[$tableName . '.']['hideTable'])) {
503                    $hideTable = (bool)$this->tableTSconfigOverTCA[$tableName . '.']['hideTable'];
504                }
505            }
506            if ($hideTable) {
507                unset($tableNames[$tableName]);
508            } else {
509                if (isset($this->tableDisplayOrder[$tableName])) {
510                    // Copy display order information
511                    $tableNames[$tableName] = $this->tableDisplayOrder[$tableName];
512                } else {
513                    $tableNames[$tableName] = [];
514                }
515            }
516        }
517        unset($config);
518
519        $orderedTableNames = GeneralUtility::makeInstance(DependencyOrderingService::class)
520            ->orderByDependencies($tableNames);
521
522        foreach ($orderedTableNames as $tableName => $_) {
523            // check if we are in single- or multi-table mode
524            if ($this->table) {
525                $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems'])
526                    ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems']
527                    : $this->itemsLimitSingleTable;
528            } else {
529                // if there are no records in table continue current foreach
530                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
531                    ->getQueryBuilderForTable($tableName);
532                $queryBuilder->getRestrictions()
533                    ->removeAll()
534                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
535                    ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
536                $queryBuilder = $this->addPageIdConstraint($tableName, $queryBuilder);
537                $firstRow = $queryBuilder->select('uid')
538                    ->from($tableName)
539                    ->execute()
540                    ->fetch();
541                if (!is_array($firstRow)) {
542                    continue;
543                }
544                $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxDBListItems'])
545                    ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxDBListItems']
546                    : $this->itemsLimitPerTable;
547            }
548            if ($this->showLimit) {
549                $this->iLimit = $this->showLimit;
550            }
551            // Setting fields to select:
552            if ($this->allFields) {
553                $fields = $this->makeFieldList($tableName);
554                $fields[] = 'tstamp';
555                $fields[] = 'crdate';
556                $fields[] = '_PATH_';
557                $fields[] = '_CONTROL_';
558                if (is_array($this->setFields[$tableName])) {
559                    $fields = array_intersect($fields, $this->setFields[$tableName]);
560                } else {
561                    $fields = [];
562                }
563            } else {
564                $fields = [];
565            }
566
567            // Finally, render the list:
568            $this->HTMLcode .= $this->getTable($tableName, $this->id, implode(',', $fields));
569        }
570    }
571
572    /**
573     * To be implemented in extending classes.
574     *
575     * @param string $tableName
576     * @param int $id
577     * @param string $fields List of fields to show in the listing. Pseudo fields will be added including the record header.
578     * @return string HTML code
579     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
580     */
581    public function getTable($tableName, $id, $fields = '')
582    {
583        return '';
584    }
585
586    /**
587     * Creates the search box
588     *
589     * @param bool $formFields If TRUE, the search box is wrapped in its own form-tags
590     * @return string HTML for the search box
591     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
592     */
593    public function getSearchBox($formFields = true)
594    {
595        /** @var IconFactory $iconFactory */
596        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
597        $lang = $this->getLanguageService();
598        // Setting form-elements, if applicable:
599        $formElements = ['', ''];
600        if ($formFields) {
601            $formElements = ['<form action="' . htmlspecialchars($this->listURL('', '-1', 'firstElementNumber,search_field')) . '" method="post">', '</form>'];
602        }
603        // Make level selector:
604        $opt = [];
605
606        // "New" generation of search levels ... based on TS config
607        $config = BackendUtility::getPagesTSconfig($this->id);
608        $searchLevelsFromTSconfig = $config['mod.']['web_list.']['searchLevel.']['items.'];
609        $searchLevelItems = [];
610
611        // get translated labels for search levels from pagets
612        foreach ($searchLevelsFromTSconfig as $keySearchLevel => $labelConfigured) {
613            $label = $lang->sL('LLL:' . $labelConfigured);
614            if ($label === '') {
615                $label = $labelConfigured;
616            }
617            $searchLevelItems[$keySearchLevel] = $label;
618        }
619
620        foreach ($searchLevelItems as $kv => $label) {
621            $opt[] = '<option value="' . $kv . '"' . ($kv === $this->searchLevels ? ' selected="selected"' : '') . '>' . htmlspecialchars($label) . '</option>';
622        }
623        $lMenu = '<select class="form-control" name="search_levels" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.search_levels')) . '" id="search_levels">' . implode('', $opt) . '</select>';
624        // Table with the search box:
625        $content = '<div class="db_list-searchbox-form db_list-searchbox-toolbar module-docheader-bar module-docheader-bar-search t3js-module-docheader-bar t3js-module-docheader-bar-search" id="db_list-searchbox-toolbar" style="display: ' . ($this->searchString == '' ? 'none' : 'block') . ';">
626			' . $formElements[0] . '
627                <div id="typo3-dblist-search">
628                    <div class="panel panel-default">
629                        <div class="panel-body">
630                            <div class="row">
631                                <div class="form-group col-xs-12">
632                                    <label for="search_field">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.searchString')) . ': </label>
633									<input class="form-control" type="search" placeholder="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enterSearchString')) . '" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchString')) . '" name="search_field" id="search_field" value="' . htmlspecialchars($this->searchString) . '" />
634                                </div>
635                                <div class="form-group col-xs-12 col-sm-6">
636									<label for="search_levels">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.search_levels')) . ': </label>
637									' . $lMenu . '
638                                </div>
639                                <div class="form-group col-xs-12 col-sm-6">
640									<label for="showLimit">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.limit')) . ': </label>
641									<input class="form-control" type="number" min="0" max="10000" placeholder="10" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.limit')) . '" name="showLimit" id="showLimit" value="' . htmlspecialchars(($this->showLimit ? $this->showLimit : '')) . '" />
642                                </div>
643                                <div class="form-group col-xs-12">
644                                    <div class="form-control-wrap">
645                                        <button type="submit" class="btn btn-default" name="search" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.search')) . '">
646                                            ' . $iconFactory->getIcon('actions-search', Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.search')) . '
647                                        </button>
648                                    </div>
649                                </div>
650                            </div>
651                        </div>
652                    </div>
653                </div>
654			' . $formElements[1] . '</div>';
655        return $content;
656    }
657
658    /******************************
659     *
660     * Various helper functions
661     *
662     ******************************/
663    /**
664     * Setting the field names to display in extended list.
665     * Sets the internal variable $this->setFields
666     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
667     */
668    public function setDispFields()
669    {
670        $backendUser = $this->getBackendUserAuthentication();
671        // Getting from session:
672        $dispFields = $backendUser->getModuleData('list/displayFields');
673        // If fields has been inputted, then set those as the value and push it to session variable:
674        if (is_array($this->displayFields)) {
675            reset($this->displayFields);
676            $tKey = key($this->displayFields);
677            $dispFields[$tKey] = $this->displayFields[$tKey];
678            $backendUser->pushModuleData('list/displayFields', $dispFields);
679        }
680        // Setting result:
681        $this->setFields = $dispFields;
682    }
683
684    /**
685     * Create thumbnail code for record/field
686     *
687     * @param mixed[] $row Record array
688     * @param string $table Table (record is from)
689     * @param string $field Field name for which thumbnail are to be rendered.
690     * @return string HTML for thumbnails, if any.
691     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
692     */
693    public function thumbCode($row, $table, $field)
694    {
695        return BackendUtility::thumbCode($row, $table, $field);
696    }
697
698    /**
699     * Returns a QueryBuilder configured to select $fields from $table where the pid is restricted
700     * depending on the current searchlevel setting.
701     *
702     * @param string $table Table name
703     * @param int $pageId Page id Only used to build the search constraints, getPageIdConstraint() used for restrictions
704     * @param string[] $additionalConstraints Additional part for where clause
705     * @param string[] $fields Field list to select, * for all
706     * @return \TYPO3\CMS\Core\Database\Query\QueryBuilder
707     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
708     */
709    public function getQueryBuilder(
710        string $table,
711        int $pageId,
712        array $additionalConstraints = [],
713        array $fields = ['*']
714    ): QueryBuilder {
715        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
716            ->getQueryBuilderForTable($table);
717        $queryBuilder->getRestrictions()
718            ->removeAll()
719            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
720            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
721        $queryBuilder
722            ->select(...$fields)
723            ->from($table);
724
725        if (!empty($additionalConstraints)) {
726            $queryBuilder->andWhere(...$additionalConstraints);
727        }
728
729        $queryBuilder = $this->prepareQueryBuilder($table, $pageId, $fields, $additionalConstraints, $queryBuilder);
730
731        return $queryBuilder;
732    }
733
734    /**
735     * Return the modified QueryBuilder object ($queryBuilder) which will be
736     * used to select the records from a table $table with pid = $this->pidList
737     *
738     * @param string $table Table name
739     * @param int $pageId Page id Only used to build the search constraints, $this->pidList is used for restrictions
740     * @param string[] $fieldList List of fields to select from the table
741     * @param string[] $additionalConstraints Additional part for where clause
742     * @param QueryBuilder $queryBuilder
743     * @param bool $addSorting
744     * @return QueryBuilder
745     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
746     */
747    protected function prepareQueryBuilder(
748        string $table,
749        int $pageId,
750        array $fieldList = ['*'],
751        array $additionalConstraints = [],
752        QueryBuilder $queryBuilder,
753        bool $addSorting = true
754    ): QueryBuilder {
755        $parameters = [
756            'table' => $table,
757            'fields' => $fieldList,
758            'groupBy' => null,
759            'orderBy' => null,
760            'firstResult' => $this->firstElementNumber ?: null,
761            'maxResults' => $this->iLimit ?: null
762        ];
763
764        if ($this->iLimit > 0) {
765            $queryBuilder->setMaxResults($this->iLimit);
766        }
767
768        if ($addSorting) {
769            if ($this->sortField && in_array($this->sortField, $this->makeFieldList($table, 1))) {
770                $queryBuilder->orderBy($this->sortField, $this->sortRev ? 'DESC' : 'ASC');
771            } else {
772                $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ?: $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
773                $orderBys = QueryHelper::parseOrderBy((string)$orderBy);
774                foreach ($orderBys as $orderBy) {
775                    $queryBuilder->addOrderBy($orderBy[0], $orderBy[1]);
776                }
777            }
778        }
779
780        // Build the query constraints
781        $queryBuilder = $this->addPageIdConstraint($table, $queryBuilder);
782        $searchWhere = $this->makeSearchString($table, $pageId);
783        if (!empty($searchWhere)) {
784            $queryBuilder->andWhere($searchWhere);
785        }
786
787        // Filtering on displayable pages (permissions):
788        if ($table === 'pages' && $this->perms_clause) {
789            $queryBuilder->andWhere($this->perms_clause);
790        }
791
792        // Filter out records that are translated, if TSconfig mod.web_list.hideTranslations is set
793        if (!empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])
794            && (GeneralUtility::inList($this->hideTranslations, $table) || $this->hideTranslations === '*')
795        ) {
796            $queryBuilder->andWhere($queryBuilder->expr()->eq(
797                $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
798                0
799            ));
800        }
801
802        $hookName = DatabaseRecordList::class;
803        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$hookName]['buildQueryParameters'])) {
804            // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, the modifyQuery hook should be used instead.
805            trigger_error('The hook ($GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][' . $hookName . '][\'buildQueryParameters\']) will be removed in TYPO3 v10.0, the modifyQuery hook should be used instead.', E_USER_DEPRECATED);
806            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][$hookName]['buildQueryParameters'] as $className) {
807                $hookObject = GeneralUtility::makeInstance($className);
808                if (method_exists($hookObject, 'buildQueryParametersPostProcess')) {
809                    $hookObject->buildQueryParametersPostProcess(
810                        $parameters,
811                        $table,
812                        $pageId,
813                        $additionalConstraints,
814                        $fieldList,
815                        $this,
816                        $queryBuilder
817                    );
818                }
819            }
820        }
821
822        // array_unique / array_filter used to eliminate empty and duplicate constraints
823        // the array keys are eliminated by this as well to facilitate argument unpacking
824        // when used with the querybuilder.
825        // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
826        if (!empty($parameters['where'])) {
827            $parameters['where'] = array_unique(array_filter(array_values($parameters['where'])));
828        }
829        if (!empty($parameters['where'])) {
830            $this->logDeprecation('where');
831            $queryBuilder->where(...$parameters['where']);
832        }
833        if (!empty($parameters['orderBy'])) {
834            $this->logDeprecation('orderBy');
835            foreach ($parameters['orderBy'] as $fieldNameAndSorting) {
836                list($fieldName, $sorting) = $fieldNameAndSorting;
837                $queryBuilder->addOrderBy($fieldName, $sorting);
838            }
839        }
840        if (!empty($parameters['firstResult'])) {
841            $this->logDeprecation('firstResult');
842            $queryBuilder->setFirstResult((int)$parameters['firstResult']);
843        }
844        if (!empty($parameters['maxResults']) && $parameters['maxResults'] !== $this->iLimit) {
845            $this->logDeprecation('maxResults');
846            $queryBuilder->setMaxResults((int)$parameters['maxResults']);
847        }
848        if (!empty($parameters['groupBy'])) {
849            $this->logDeprecation('groupBy');
850            $queryBuilder->groupBy($parameters['groupBy']);
851        }
852
853        return $queryBuilder;
854    }
855
856    /**
857     * Executed a query to set $this->totalItems to the number of total
858     * items, eg. for pagination
859     *
860     * @param string $table Table name
861     * @param int $pageId Only used to build the search constraints, $this->pidList is used for restrictions
862     * @param array $constraints Additional constraints for where clause
863     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
864     */
865    public function setTotalItems(string $table, int $pageId, array $constraints)
866    {
867        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
868            ->getQueryBuilderForTable($table);
869
870        $queryBuilder->getRestrictions()
871            ->removeAll()
872            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
873            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
874        $queryBuilder
875            ->from($table);
876
877        if (!empty($constraints)) {
878            $queryBuilder->andWhere(...$constraints);
879        }
880
881        $queryBuilder = $this->prepareQueryBuilder($table, $pageId, ['*'], $constraints, $queryBuilder, false);
882        // Reset limit and offset for full count query
883        $queryBuilder->setFirstResult(0);
884        $queryBuilder->setMaxResults(1);
885
886        $this->totalItems = (int)$queryBuilder->count('*')
887            ->execute()
888            ->fetchColumn();
889    }
890
891    /**
892     * Creates part of query for searching after a word ($this->searchString)
893     * fields in input table.
894     *
895     * @param string $table Table, in which the fields are being searched.
896     * @param int $currentPid Page id for the possible search limit. -1 only if called from an old XCLASS.
897     * @return string Returns part of WHERE-clause for searching, if applicable.
898     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
899     */
900    public function makeSearchString($table, $currentPid = -1)
901    {
902        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
903        $expressionBuilder = $queryBuilder->expr();
904        $constraints = [];
905        $currentPid = (int)$currentPid;
906        $tablePidField = $table === 'pages' ? 'uid' : 'pid';
907        // Make query only if table is valid and a search string is actually defined
908        if (empty($this->searchString)) {
909            return '';
910        }
911
912        $searchableFields = $this->getSearchFields($table);
913        if (MathUtility::canBeInterpretedAsInteger($this->searchString)) {
914            $constraints[] = $expressionBuilder->eq('uid', (int)$this->searchString);
915            foreach ($searchableFields as $fieldName) {
916                if (!isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
917                    continue;
918                }
919                $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
920                $fieldType = $fieldConfig['type'];
921                $evalRules = $fieldConfig['eval'] ?: '';
922                if ($fieldType === 'input' && $evalRules && GeneralUtility::inList($evalRules, 'int')) {
923                    if (is_array($fieldConfig['search'])
924                        && in_array('pidonly', $fieldConfig['search'], true)
925                        && $currentPid > 0
926                    ) {
927                        $constraints[] = $expressionBuilder->andX(
928                            $expressionBuilder->eq($fieldName, (int)$this->searchString),
929                            $expressionBuilder->eq($tablePidField, (int)$currentPid)
930                        );
931                    }
932                } elseif ($fieldType === 'text'
933                    || $fieldType === 'flex'
934                    || ($fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules)))
935                ) {
936                    $constraints[] = $expressionBuilder->like(
937                        $fieldName,
938                        $queryBuilder->quote('%' . (int)$this->searchString . '%')
939                    );
940                }
941            }
942        } elseif (!empty($searchableFields)) {
943            $like = $queryBuilder->quote('%' . $queryBuilder->escapeLikeWildcards($this->searchString) . '%');
944            foreach ($searchableFields as $fieldName) {
945                if (!isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
946                    continue;
947                }
948                $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
949                $fieldType = $fieldConfig['type'];
950                $evalRules = $fieldConfig['eval'] ?: '';
951                $searchConstraint = $expressionBuilder->andX(
952                    $expressionBuilder->comparison(
953                        'LOWER(' . $queryBuilder->quoteIdentifier($fieldName) . ')',
954                        'LIKE',
955                        'LOWER(' . $like . ')'
956                    )
957                );
958                if (is_array($fieldConfig['search'])) {
959                    $searchConfig = $fieldConfig['search'];
960                    if (in_array('case', $searchConfig)) {
961                        // Replace case insensitive default constraint
962                        $searchConstraint = $expressionBuilder->andX($expressionBuilder->like($fieldName, $like));
963                    }
964                    if (in_array('pidonly', $searchConfig) && $currentPid > 0) {
965                        $searchConstraint->add($expressionBuilder->eq($tablePidField, (int)$currentPid));
966                    }
967                    if ($searchConfig['andWhere']) {
968                        $searchConstraint->add(
969                            QueryHelper::stripLogicalOperatorPrefix($fieldConfig['search']['andWhere'])
970                        );
971                    }
972                }
973                if ($fieldType === 'text'
974                    || $fieldType === 'flex'
975                    || $fieldType === 'input' && (!$evalRules || !preg_match('/date|time|int/', $evalRules))
976                ) {
977                    if ($searchConstraint->count() !== 0) {
978                        $constraints[] = $searchConstraint;
979                    }
980                }
981            }
982        }
983        // If no search field conditions have been built ensure no results are returned
984        if (empty($constraints)) {
985            return '0=1';
986        }
987
988        return $expressionBuilder->orX(...$constraints);
989    }
990
991    /**
992     * Fetches a list of fields to use in the Backend search for the given table.
993     *
994     * @param string $tableName
995     * @return string[]
996     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
997     */
998    protected function getSearchFields($tableName)
999    {
1000        $fieldArray = [];
1001        $fieldListWasSet = false;
1002        // Get fields from ctrl section of TCA first
1003        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['searchFields'])) {
1004            $fieldArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'], true);
1005            $fieldListWasSet = true;
1006        }
1007        // Call hook to add or change the list
1008        $hookParameters = [
1009            'tableHasSearchConfiguration' => $fieldListWasSet,
1010            'tableName' => $tableName,
1011            'searchFields' => &$fieldArray,
1012            'searchString' => $this->searchString
1013        ];
1014        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['mod_list']['getSearchFieldList'] ?? [] as $hookFunction) {
1015            GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
1016        }
1017        return $fieldArray;
1018    }
1019
1020    /**
1021     * Returns the title (based on $code) of a table ($table) with the proper link around. For headers over tables.
1022     * The link will cause the display of all extended mode or not for the table.
1023     *
1024     * @param string $table Table name
1025     * @param string $code Table label
1026     * @return string The linked table label
1027     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1028     */
1029    public function linkWrapTable($table, $code)
1030    {
1031        if ($this->table !== $table) {
1032            return '<a href="' . htmlspecialchars($this->listURL('', $table, 'firstElementNumber')) . '">' . $code . '</a>';
1033        }
1034        return '<a href="' . htmlspecialchars($this->listURL('', '', 'sortField,sortRev,table,firstElementNumber')) . '">' . $code . '</a>';
1035    }
1036
1037    /**
1038     * Returns the title (based on $code) of a record (from table $table) with the proper link around (that is for 'pages'-records a link to the level of that record...)
1039     *
1040     * @param string $table Table name
1041     * @param int $uid Item uid
1042     * @param string $code Item title (not htmlspecialchars()'ed yet)
1043     * @param mixed[] $row Item row
1044     * @return string The item title. Ready for HTML output (is htmlspecialchars()'ed)
1045     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1046     */
1047    public function linkWrapItems($table, $uid, $code, $row)
1048    {
1049        $lang = $this->getLanguageService();
1050        $origCode = $code;
1051        // If the title is blank, make a "no title" label:
1052        if ((string)$code === '') {
1053            $code = '<i>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) . ']</i> - '
1054                . htmlspecialchars(BackendUtility::getRecordTitle($table, $row));
1055        } else {
1056            $code = htmlspecialchars($code, ENT_QUOTES, 'UTF-8', false);
1057            if ($code != htmlspecialchars($origCode)) {
1058                $code = '<span title="' . htmlspecialchars($origCode, ENT_QUOTES, 'UTF-8', false) . '">' . $code . '</span>';
1059            }
1060        }
1061        switch ((string)$this->clickTitleMode) {
1062            case 'edit':
1063                // If the listed table is 'pages' we have to request the permission settings for each page:
1064                if ($table === 'pages') {
1065                    $localCalcPerms = $this->getBackendUserAuthentication()->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
1066                    $permsEdit = $localCalcPerms & Permission::PAGE_EDIT;
1067                } else {
1068                    $permsEdit = $this->calcPerms & Permission::CONTENT_EDIT;
1069                }
1070                // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
1071                if ($permsEdit) {
1072                    $params = '&edit[' . $table . '][' . $row['uid'] . ']=edit';
1073                    $code = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, '', -1)) . '" title="' . htmlspecialchars($lang->getLL('edit')) . '">' . $code . '</a>';
1074                }
1075                break;
1076            case 'show':
1077                // "Show" link (only pages and tt_content elements)
1078                if ($table === 'pages' || $table === 'tt_content') {
1079                    $code = '<a href="#" onclick="' . htmlspecialchars(
1080                        BackendUtility::viewOnClick(($table === 'tt_content' ? $this->id . '#' . $row['uid'] : $row['uid']))
1081                    ) . '" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage')) . '">' . $code . '</a>';
1082                }
1083                break;
1084            case 'info':
1085                // "Info": (All records)
1086                $code = '<a href="#" onclick="' . htmlspecialchars('top.TYPO3.InfoWindow.showItem(\'' . $table . '\', \'' . $row['uid'] . '\'); return false;') . '" title="' . htmlspecialchars($lang->getLL('showInfo')) . '">' . $code . '</a>';
1087                break;
1088            default:
1089                // Output the label now:
1090                if ($table === 'pages') {
1091                    $code = '<a href="' . htmlspecialchars($this->listURL($uid, '', 'firstElementNumber')) . '" onclick="setHighlight(' . $uid . ')">' . $code . '</a>';
1092                } else {
1093                    $code = $this->linkUrlMail($code, $origCode);
1094                }
1095        }
1096        return $code;
1097    }
1098
1099    /**
1100     * Wrapping input code in link to URL or email if $testString is either.
1101     *
1102     * @param string $code code to wrap
1103     * @param string $testString String which is tested for being a URL or email and which will be used for the link if so.
1104     * @return string Link-Wrapped $code value, if $testString was URL or email.
1105     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1106     */
1107    public function linkUrlMail($code, $testString)
1108    {
1109        // Check for URL:
1110        $scheme = parse_url($testString, PHP_URL_SCHEME);
1111        if ($scheme === 'http' || $scheme === 'https' || $scheme === 'ftp') {
1112            return '<a href="' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
1113        }
1114        // Check for email:
1115        if (GeneralUtility::validEmail($testString)) {
1116            return '<a href="mailto:' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
1117        }
1118        // Return if nothing else...
1119        return $code;
1120    }
1121
1122    /**
1123     * Creates the URL to this script, including all relevant GPvars
1124     * Fixed GPvars are id, table, imagemode, returnUrl, search_field, search_levels and showLimit
1125     * The GPvars "sortField" and "sortRev" are also included UNLESS they are found in the $exclList variable.
1126     *
1127     * @param string $altId Alternative id value. Enter blank string for the current id ($this->id)
1128     * @param string $table Table name to display. Enter "-1" for the current table.
1129     * @param string $exclList Comma separated list of fields NOT to include ("sortField", "sortRev" or "firstElementNumber")
1130     * @return string URL
1131     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1132     */
1133    public function listURL($altId = '', $table = '-1', $exclList = '')
1134    {
1135        $urlParameters = [];
1136        if ((string)$altId !== '') {
1137            $urlParameters['id'] = $altId;
1138        } else {
1139            $urlParameters['id'] = $this->id;
1140        }
1141        if ($table === '-1') {
1142            $urlParameters['table'] = $this->table;
1143        } else {
1144            $urlParameters['table'] = $table;
1145        }
1146        if ($this->thumbs) {
1147            $urlParameters['imagemode'] = $this->thumbs;
1148        }
1149        if ($this->returnUrl) {
1150            $urlParameters['returnUrl'] = $this->returnUrl;
1151        }
1152        if ((!$exclList || !GeneralUtility::inList($exclList, 'search_field')) && $this->searchString) {
1153            $urlParameters['search_field'] = $this->searchString;
1154        }
1155        if ($this->searchLevels) {
1156            $urlParameters['search_levels'] = $this->searchLevels;
1157        }
1158        if ($this->showLimit) {
1159            $urlParameters['showLimit'] = $this->showLimit;
1160        }
1161        if ((!$exclList || !GeneralUtility::inList($exclList, 'firstElementNumber')) && $this->firstElementNumber) {
1162            $urlParameters['pointer'] = $this->firstElementNumber;
1163        }
1164        if ((!$exclList || !GeneralUtility::inList($exclList, 'sortField')) && $this->sortField) {
1165            $urlParameters['sortField'] = $this->sortField;
1166        }
1167        if ((!$exclList || !GeneralUtility::inList($exclList, 'sortRev')) && $this->sortRev) {
1168            $urlParameters['sortRev'] = $this->sortRev;
1169        }
1170
1171        $urlParameters = array_merge_recursive($urlParameters, $this->overrideUrlParameters);
1172
1173        if ($routePath = GeneralUtility::_GP('route')) {
1174            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
1175            $url = (string)$uriBuilder->buildUriFromRoutePath($routePath, $urlParameters);
1176        } else {
1177            $url = GeneralUtility::getIndpEnv('SCRIPT_NAME') . HttpUtility::buildQueryString($urlParameters, '?');
1178        }
1179        return $url;
1180    }
1181
1182    /**
1183     * Returns "requestUri" - which is basically listURL
1184     * @return string Content of ->listURL()
1185     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1186     */
1187    public function requestUri()
1188    {
1189        return $this->listURL();
1190    }
1191
1192    /**
1193     * Makes the list of fields to select for a table
1194     *
1195     * @param string $table Table name
1196     * @param bool $dontCheckUser If set, users access to the field (non-exclude-fields) is NOT checked.
1197     * @param bool $addDateFields If set, also adds crdate and tstamp fields (note: they will also be added if user is admin or dontCheckUser is set)
1198     * @return string[] Array, where values are fieldnames to include in query
1199     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1200     */
1201    public function makeFieldList($table, $dontCheckUser = false, $addDateFields = false)
1202    {
1203        $backendUser = $this->getBackendUserAuthentication();
1204        // Init fieldlist array:
1205        $fieldListArr = [];
1206        // Check table:
1207        if (is_array($GLOBALS['TCA'][$table]) && isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
1208            if (isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
1209                // Traverse configured columns and add them to field array, if available for user.
1210                foreach ($GLOBALS['TCA'][$table]['columns'] as $fN => $fieldValue) {
1211                    if ($dontCheckUser || (!$fieldValue['exclude'] || $backendUser->check('non_exclude_fields', $table . ':' . $fN)) && $fieldValue['config']['type'] !== 'passthrough') {
1212                        $fieldListArr[] = $fN;
1213                    }
1214                }
1215
1216                $fieldListArr[] = 'uid';
1217                $fieldListArr[] = 'pid';
1218
1219                // Add date fields
1220                if ($dontCheckUser || $backendUser->isAdmin() || $addDateFields) {
1221                    if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
1222                        $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['tstamp'];
1223                    }
1224                    if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
1225                        $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['crdate'];
1226                    }
1227                }
1228                // Add more special fields:
1229                if ($dontCheckUser || $backendUser->isAdmin()) {
1230                    if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
1231                        $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['cruser_id'];
1232                    }
1233                    if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
1234                        $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
1235                    }
1236                    if (ExtensionManagementUtility::isLoaded('workspaces') && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1237                        $fieldListArr[] = 't3ver_id';
1238                        $fieldListArr[] = 't3ver_state';
1239                        $fieldListArr[] = 't3ver_wsid';
1240                    }
1241                }
1242            } else {
1243                GeneralUtility::makeInstance(LogManager::class)
1244                    ->getLogger(__CLASS__)
1245                    ->error('TCA is broken for the table "' . $table . '": no required "columns" entry in TCA.');
1246            }
1247        }
1248        return $fieldListArr;
1249    }
1250
1251    /**
1252     * Get all allowed mount pages to be searched in.
1253     *
1254     * @param int $id Page id
1255     * @param int $depth Depth to go down
1256     * @param string $perms_clause select clause
1257     * @return int[]
1258     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1259     */
1260    protected function getSearchableWebmounts($id, $depth, $perms_clause)
1261    {
1262        $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
1263        $cacheIdentifier = md5('pidList_' . $id . '_' . $depth . '_' . $perms_clause);
1264        $idList = $runtimeCache->get($cacheIdentifier);
1265        if ($idList) {
1266            return $idList;
1267        }
1268
1269        $backendUser = $this->getBackendUserAuthentication();
1270        /** @var PageTreeView $tree */
1271        $tree = GeneralUtility::makeInstance(PageTreeView::class);
1272        $tree->init('AND ' . $perms_clause);
1273        $tree->makeHTML = 0;
1274        $tree->fieldArray = ['uid', 'php_tree_stop'];
1275        $idList = [];
1276
1277        $allowedMounts = !$backendUser->isAdmin() && $id === 0
1278            ? $backendUser->returnWebmounts()
1279            : [$id];
1280
1281        foreach ($allowedMounts as $allowedMount) {
1282            $idList[] = $allowedMount;
1283            if ($depth) {
1284                $tree->getTree($allowedMount, $depth, '');
1285            }
1286            $idList = array_merge($idList, $tree->ids);
1287        }
1288        $runtimeCache->set($cacheIdentifier, $idList);
1289        return $idList;
1290    }
1291
1292    /**
1293     * Redirects to FormEngine if a record is just localized.
1294     *
1295     * @param string $justLocalized String with table, orig uid and language separated by ":
1296     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1297     */
1298    public function localizationRedirect($justLocalized)
1299    {
1300        list($table, $orig_uid, $language) = explode(':', $justLocalized);
1301        if ($GLOBALS['TCA'][$table]
1302            && $GLOBALS['TCA'][$table]['ctrl']['languageField']
1303            && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
1304        ) {
1305            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1306            $queryBuilder->getRestrictions()
1307                ->removeAll()
1308                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1309                ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1310
1311            $localizedRecordUid = $queryBuilder->select('uid')
1312                ->from($table)
1313                ->where(
1314                    $queryBuilder->expr()->eq(
1315                        $GLOBALS['TCA'][$table]['ctrl']['languageField'],
1316                        $queryBuilder->createNamedParameter($language, \PDO::PARAM_INT)
1317                    ),
1318                    $queryBuilder->expr()->eq(
1319                        $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
1320                        $queryBuilder->createNamedParameter($orig_uid, \PDO::PARAM_INT)
1321                    )
1322                )
1323                ->setMaxResults(1)
1324                ->execute()
1325                ->fetchColumn();
1326
1327            if ($localizedRecordUid !== false) {
1328                // Create parameters and finally run the classic page module for creating a new page translation
1329                $url = $this->listURL();
1330                /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1331                $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1332                $editUserAccountUrl = (string)$uriBuilder->buildUriFromRoute(
1333                    'record_edit',
1334                    [
1335                        'edit[' . $table . '][' . $localizedRecordUid . ']' => 'edit',
1336                        'returnUrl' => $url
1337                    ]
1338                );
1339                HttpUtility::redirect($editUserAccountUrl);
1340            }
1341        }
1342    }
1343
1344    /**
1345     * Set URL parameters to override or add in the listUrl() method.
1346     *
1347     * @param string[] $urlParameters
1348     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1349     */
1350    public function setOverrideUrlParameters(array $urlParameters)
1351    {
1352        $this->overrideUrlParameters = $urlParameters;
1353    }
1354
1355    /**
1356     * Set table display order information
1357     *
1358     * Structure of $orderInformation:
1359     *   'tableName' => [
1360     *      'before' => // comma-separated string list or array of table names
1361     *      'after' => // comma-separated string list or array of table names
1362     * ]
1363     *
1364     * @param array $orderInformation
1365     * @throws \UnexpectedValueException
1366     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1367     */
1368    public function setTableDisplayOrder(array $orderInformation)
1369    {
1370        foreach ($orderInformation as $tableName => &$configuration) {
1371            if (isset($configuration['before'])) {
1372                if (is_string($configuration['before'])) {
1373                    $configuration['before'] = GeneralUtility::trimExplode(',', $configuration['before'], true);
1374                } elseif (!is_array($configuration['before'])) {
1375                    throw new \UnexpectedValueException('The specified "before" order configuration for table "' . $tableName . '" is invalid.', 1436195933);
1376                }
1377            }
1378            if (isset($configuration['after'])) {
1379                if (is_string($configuration['after'])) {
1380                    $configuration['after'] = GeneralUtility::trimExplode(',', $configuration['after'], true);
1381                } elseif (!is_array($configuration['after'])) {
1382                    throw new \UnexpectedValueException('The specified "after" order configuration for table "' . $tableName . '" is invalid.', 1436195934);
1383                }
1384            }
1385        }
1386        $this->tableDisplayOrder = $orderInformation;
1387    }
1388
1389    /**
1390     * @return array
1391     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1392     */
1393    public function getOverridePageIdList(): array
1394    {
1395        return $this->overridePageIdList;
1396    }
1397
1398    /**
1399     * @param int[]|array $overridePageIdList
1400     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1401     */
1402    public function setOverridePageIdList(array $overridePageIdList)
1403    {
1404        $this->overridePageIdList = array_map('intval', $overridePageIdList);
1405    }
1406
1407    /**
1408     * Add conditions to the QueryBuilder object ($queryBuilder) to limit a
1409     * query to a list of page IDs based on the current search level setting.
1410     *
1411     * @param string $tableName
1412     * @param QueryBuilder $queryBuilder
1413     * @return QueryBuilder Modified QueryBuilder object
1414     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1415     */
1416    protected function addPageIdConstraint(string $tableName, QueryBuilder $queryBuilder): QueryBuilder
1417    {
1418        // Set search levels:
1419        $searchLevels = $this->searchLevels;
1420
1421        // Set search levels to 999 instead of -1 as the following methods
1422        // do not support -1 as valid value for infinite search.
1423        if ($searchLevels === -1) {
1424            $searchLevels = 999;
1425        }
1426
1427        if ($searchLevels === 0) {
1428            $queryBuilder->andWhere(
1429                $queryBuilder->expr()->eq(
1430                    $tableName . '.pid',
1431                    $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1432                )
1433            );
1434        } elseif ($searchLevels > 0) {
1435            $allowedPidList = $this->getSearchableWebmounts($this->id, $searchLevels, $this->perms_clause);
1436            $queryBuilder->andWhere(
1437                $queryBuilder->expr()->in(
1438                    $tableName . '.pid',
1439                    $queryBuilder->createNamedParameter($allowedPidList, Connection::PARAM_INT_ARRAY)
1440                )
1441            );
1442        }
1443
1444        if (!empty($this->getOverridePageIdList())) {
1445            $queryBuilder->andWhere(
1446                $queryBuilder->expr()->in(
1447                    $tableName . '.pid',
1448                    $queryBuilder->createNamedParameter($this->getOverridePageIdList(), Connection::PARAM_INT_ARRAY)
1449                )
1450            );
1451        }
1452
1453        return $queryBuilder;
1454    }
1455
1456    /**
1457     * Method used to log deprecated usage of old buildQueryParametersPostProcess hook arguments
1458     *
1459     * @param string $index
1460     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1461     */
1462    protected function logDeprecation(string $index)
1463    {
1464        trigger_error('[index: ' . $index . '] $parameters in "buildQueryParameters"-Hook will be removed in TYPO3 v10.0, use $queryBuilder instead.', E_USER_DEPRECATED);
1465    }
1466
1467    /**
1468     * @return BackendUserAuthentication
1469     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
1470     */
1471    protected function getBackendUserAuthentication()
1472    {
1473        return $GLOBALS['BE_USER'];
1474    }
1475}
1476