[!!!][CLEANUP] Move t3-table and t3-button to bootstrap
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / RecordList / AbstractDatabaseRecordList.php
1 <?php
2 namespace 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
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * Child class for rendering of Web > List (not the final class.
22 * Shared between Web>List (db_list.php) and Web>Page (sysext/cms/layout/db_layout.php)
23 *
24 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
25 * @see \TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList
26 */
27 class AbstractDatabaseRecordList extends \TYPO3\CMS\Backend\RecordList\AbstractRecordList {
28
29 /**
30 * Specify a list of tables which are the only ones allowed to be displayed.
31 *
32 * @var string
33 */
34 public $tableList = '';
35
36 /**
37 * Return URL
38 *
39 * @var string
40 */
41 public $returnUrl = '';
42
43 /**
44 * Thumbnails on records containing files (pictures)
45 *
46 * @var bool
47 */
48 public $thumbs = 0;
49
50 /**
51 * default Max items shown per table in "multi-table mode", may be overridden by tables.php
52 *
53 * @var int
54 */
55 public $itemsLimitPerTable = 20;
56
57 /**
58 * default Max items shown per table in "single-table mode", may be overridden by tables.php
59 *
60 * @var int
61 */
62 public $itemsLimitSingleTable = 100;
63
64 /**
65 * Current script name
66 *
67 * @var string
68 */
69 public $script = 'index.php';
70
71 /**
72 * Indicates if all available fields for a user should be selected or not.
73 *
74 * @var int
75 */
76 public $allFields = 0;
77
78 /**
79 * Whether to show localization view or not
80 *
81 * @var bool
82 */
83 public $localizationView = FALSE;
84
85 /**
86 * If set, csvList is outputted.
87 *
88 * @var bool
89 */
90 public $csvOutput = FALSE;
91
92 /**
93 * Field, to sort list by
94 *
95 * @var string
96 */
97 public $sortField;
98
99 /**
100 * Field, indicating to sort in reverse order.
101 *
102 * @var bool
103 */
104 public $sortRev;
105
106 /**
107 * Containing which fields to display in extended mode
108 *
109 * @var array
110 */
111 public $displayFields;
112
113 /**
114 * String, can contain the field name from a table which must have duplicate values marked.
115 *
116 * @var string
117 */
118 public $duplicateField;
119
120 /**
121 * Page id
122 *
123 * @var int
124 */
125 public $id;
126
127 /**
128 * Tablename if single-table mode
129 *
130 * @var string
131 */
132 public $table = '';
133
134 /**
135 * If TRUE, records are listed only if a specific table is selected.
136 *
137 * @var bool
138 */
139 public $listOnlyInSingleTableMode = FALSE;
140
141 /**
142 * Pointer for browsing list
143 *
144 * @var int
145 */
146 public $firstElementNumber = 0;
147
148 /**
149 * Search string
150 *
151 * @var string
152 */
153 public $searchString = '';
154
155 /**
156 * Levels to search down.
157 *
158 * @var int
159 */
160 public $searchLevels = '';
161
162 /**
163 * Number of records to show
164 *
165 * @var int
166 */
167 public $showLimit = 0;
168
169 /**
170 * Query part for either a list of ids "pid IN (1,2,3)" or a single id "pid = 123" from
171 * which to select/search etc. (when search-levels are set high). See start()
172 *
173 * @var string
174 */
175 public $pidSelect = '';
176
177 /**
178 * Page select permissions
179 *
180 * @var string
181 */
182 public $perms_clause = '';
183
184 /**
185 * Some permissions...
186 *
187 * @var int
188 */
189 public $calcPerms = 0;
190
191 /**
192 * Mode for what happens when a user clicks the title of a record.
193 *
194 * @var string
195 */
196 public $clickTitleMode = '';
197
198 /**
199 * Shared module configuration, used by localization features
200 *
201 * @var array
202 */
203 public $modSharedTSconfig = array();
204
205 /**
206 * Loaded with page record with version overlay if any.
207 *
208 * @var array
209 */
210 public $pageRecord = array();
211
212 /**
213 * Tables which should not get listed
214 *
215 * @var string
216 */
217 public $hideTables = '';
218
219 /**
220 * Tables which should not list their translations
221 *
222 * @var string
223 */
224 public $hideTranslations = '';
225
226 /**
227 * TSconfig which overwrites TCA-Settings
228 *
229 * @var array
230 */
231 public $tableTSconfigOverTCA = array();
232
233 /**
234 * Array of collapsed / uncollapsed tables in multi table view
235 *
236 * @var array
237 */
238 public $tablesCollapsed = array();
239
240 /**
241 * JavaScript code accumulation
242 *
243 * @var string
244 */
245 public $JScode = '';
246
247 /**
248 * HTML output
249 *
250 * @var string
251 */
252 public $HTMLcode = '';
253
254 /**
255 * "LIMIT " in SQL...
256 *
257 * @var int
258 */
259 public $iLimit = 0;
260
261 /**
262 * Counting the elements no matter what...
263 *
264 * @var int
265 */
266 public $eCounter = 0;
267
268 /**
269 * Set to the total number of items for a table when selecting.
270 *
271 * @var string
272 */
273 public $totalItems = '';
274
275 /**
276 * Cache for record path
277 *
278 * @var array
279 */
280 public $recPath_cache = array();
281
282 /**
283 * Fields to display for the current table
284 *
285 * @var array
286 */
287 public $setFields = array();
288
289 /**
290 * Used for tracking next/prev uids
291 *
292 * @var array
293 */
294 public $currentTable = array();
295
296 /**
297 * Used for tracking duplicate values of fields
298 *
299 * @var array
300 */
301 public $duplicateStack = array();
302
303 /**
304 * @var array Module configuration
305 */
306 public $modTSconfig;
307
308 /**
309 * Override/add urlparameters in listUrl() method
310 * @var array
311 */
312 protected $overrideUrlParameters = array();
313
314 /**
315 * Initializes the list generation
316 *
317 * @param int $id Page id for which the list is rendered. Must be >= 0
318 * @param string $table Tablename - if extended mode where only one table is listed at a time.
319 * @param int $pointer Browsing pointer.
320 * @param string $search Search word, if any
321 * @param int $levels Number of levels to search down the page tree
322 * @param int $showLimit Limit of records to be listed.
323 * @return void
324 */
325 public function start($id, $table, $pointer, $search = '', $levels = '', $showLimit = 0) {
326 // Setting internal variables:
327 // sets the parent id
328 $this->id = (int)$id;
329 if ($GLOBALS['TCA'][$table]) {
330 // Setting single table mode, if table exists:
331 $this->table = $table;
332 }
333 $this->firstElementNumber = $pointer;
334 $this->searchString = trim($search);
335 $this->searchLevels = trim($levels);
336 $this->showLimit = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($showLimit, 0, 10000);
337 // Setting GPvars:
338 $this->csvOutput = GeneralUtility::_GP('csv') ? TRUE : FALSE;
339 $this->sortField = GeneralUtility::_GP('sortField');
340 $this->sortRev = GeneralUtility::_GP('sortRev');
341 $this->displayFields = GeneralUtility::_GP('displayFields');
342 $this->duplicateField = GeneralUtility::_GP('duplicateField');
343 if (GeneralUtility::_GP('justLocalized')) {
344 $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
345 }
346 // Init dynamic vars:
347 $this->counter = 0;
348 $this->JScode = '';
349 $this->HTMLcode = '';
350 // Limits
351 if (isset($this->modTSconfig['properties']['itemsLimitPerTable'])) {
352 $this->itemsLimitPerTable = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange((int)$this->modTSconfig['properties']['itemsLimitPerTable'], 1, 10000);
353 }
354 if (isset($this->modTSconfig['properties']['itemsLimitSingleTable'])) {
355 $this->itemsLimitSingleTable = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange((int)$this->modTSconfig['properties']['itemsLimitSingleTable'], 1, 10000);
356 }
357 // Set search levels:
358 $searchLevels = (int)$this->searchLevels;
359 $this->perms_clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
360 // This will hide records from display - it has nothing todo with user rights!!
361 if ($pidList = $GLOBALS['BE_USER']->getTSConfigVal('options.hideRecords.pages')) {
362 if ($pidList = $GLOBALS['TYPO3_DB']->cleanIntList($pidList)) {
363 $this->perms_clause .= ' AND pages.uid NOT IN (' . $pidList . ')';
364 }
365 }
366 // Get configuration of collapsed tables from user uc and merge with sanitized GP vars
367 $this->tablesCollapsed = is_array($GLOBALS['BE_USER']->uc['moduleData']['list']) ? $GLOBALS['BE_USER']->uc['moduleData']['list'] : array();
368 $collapseOverride = GeneralUtility::_GP('collapse');
369 if (is_array($collapseOverride)) {
370 foreach ($collapseOverride as $collapseTable => $collapseValue) {
371 if (is_array($GLOBALS['TCA'][$collapseTable]) && ($collapseValue == 0 || $collapseValue == 1)) {
372 $this->tablesCollapsed[$collapseTable] = $collapseValue;
373 }
374 }
375 // Save modified user uc
376 $GLOBALS['BE_USER']->uc['moduleData']['list'] = $this->tablesCollapsed;
377 $GLOBALS['BE_USER']->writeUC($GLOBALS['BE_USER']->uc);
378 $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
379 if ($returnUrl !== '') {
380 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($returnUrl);
381 }
382 }
383 if ($searchLevels > 0) {
384 $allowedMounts = $this->getSearchableWebmounts($this->id, $searchLevels, $this->perms_clause);
385 $pidList = implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($allowedMounts));
386 $this->pidSelect = 'pid IN (' . $pidList . ')';
387 } elseif ($searchLevels < 0) {
388 // Search everywhere
389 $this->pidSelect = '1=1';
390 } else {
391 $this->pidSelect = 'pid=' . (int)$id;
392 }
393 // Initialize languages:
394 if ($this->localizationView) {
395 $this->initializeLanguages();
396 }
397 }
398
399 /**
400 * Traverses the table(s) to be listed and renders the output code for each:
401 * The HTML is accumulated in $this->HTMLcode
402 * Finishes off with a stopper-gif
403 *
404 * @return void
405 */
406 public function generateList() {
407 // Set page record in header
408 $this->pageRecord = BackendUtility::getRecordWSOL('pages', $this->id);
409 // Traverse the TCA table array:
410 foreach ($GLOBALS['TCA'] as $tableName => $value) {
411 // Checking if the table should be rendered:
412 // Checks that we see only permitted/requested tables:
413 if ((!$this->table || $tableName == $this->table) && (!$this->tableList || GeneralUtility::inList($this->tableList, $tableName)) && $GLOBALS['BE_USER']->check('tables_select', $tableName)) {
414 // Don't show table if hidden by TCA ctrl section
415 $hideTable = $GLOBALS['TCA'][$tableName]['ctrl']['hideTable'] ? TRUE : FALSE;
416 // Don't show table if hidden by pageTSconfig mod.web_list.hideTables
417 if (in_array($tableName, GeneralUtility::trimExplode(',', $this->hideTables))) {
418 $hideTable = TRUE;
419 }
420 // Override previous selection if table is enabled or hidden by TSconfig TCA override mod.web_list.table
421 if (isset($this->tableTSconfigOverTCA[$tableName . '.']['hideTable'])) {
422 $hideTable = $this->tableTSconfigOverTCA[$tableName . '.']['hideTable'] ? TRUE : FALSE;
423 }
424 if ($hideTable) {
425 continue;
426 }
427 // check if we are in single- or multi-table mode
428 if ($this->table) {
429 $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems']) ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems'] : $this->itemsLimitSingleTable;
430 } else {
431 // if there are no records in table continue current foreach
432 $firstRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
433 'uid',
434 $tableName,
435 $this->pidSelect . BackendUtility::deleteClause($tableName) . BackendUtility::versioningPlaceholderClause($tableName)
436 );
437 if ($firstRow === FALSE) {
438 continue;
439 }
440 $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxDBListItems']) ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxDBListItems'] : $this->itemsLimitPerTable;
441 }
442 if ($this->showLimit) {
443 $this->iLimit = $this->showLimit;
444 }
445 // Setting fields to select:
446 if ($this->allFields) {
447 $fields = $this->makeFieldList($tableName);
448 $fields[] = 'tstamp';
449 $fields[] = 'crdate';
450 $fields[] = '_PATH_';
451 $fields[] = '_CONTROL_';
452 if (is_array($this->setFields[$tableName])) {
453 $fields = array_intersect($fields, $this->setFields[$tableName]);
454 } else {
455 $fields = array();
456 }
457 } else {
458 $fields = array();
459 }
460 // Find ID to use (might be different for "versioning_followPages" tables)
461 if ((int)$this->searchLevels === 0) {
462 $this->pidSelect = 'pid=' . (int)$this->id;
463 }
464 // Finally, render the list:
465 $this->HTMLcode .= $this->getTable($tableName, $this->id, implode(',', $fields));
466 }
467 }
468 }
469
470 /**
471 * Creates the search box
472 *
473 * @param bool $formFields If TRUE, the search box is wrapped in its own form-tags
474 * @return string HTML for the search box
475 */
476 public function getSearchBox($formFields = 1) {
477 // Setting form-elements, if applicable:
478 $formElements = array('', '');
479 if ($formFields) {
480 $formElements = array('<form action="' . htmlspecialchars($this->listURL('', -1, 'firstElementNumber,search_field')) . '" method="post">', '</form>');
481 }
482 // Make level selector:
483 $opt = array();
484 $parts = explode('|', $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.enterSearchLevels'));
485 foreach ($parts as $kv => $label) {
486 $opt[] = '<option value="' . $kv . '"' . ($kv == (int)$this->searchLevels ? ' selected="selected"' : '') . '>' . htmlspecialchars($label) . '</option>';
487 }
488 $lMenu = '<select class="form-control" name="search_levels" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.search_levels', TRUE) . '" id="search_levels">' . implode('', $opt) . '</select>';
489 // Table with the search box:
490 $content = '<div class="db_list-searchbox-form db_list-searchbox-toolbar" id="db_list-searchbox-toolbar" style="display: ' . ($this->searchString == '' ? 'none' : 'block') . ';">
491 ' . $formElements[0] . '
492 <div id="typo3-dblist-search" class="container">
493 <div class="row">
494 <div class="col-xs-3 col-md-3 col-lg-3">
495 <div class="input-group">
496 <label class="pull-left" for="search_field">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.label.searchString', TRUE) . ': </label>
497 <input class="form-control" type="search" placeholder="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.enterSearchString', TRUE) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.searchString', TRUE) . '" name="search_field" id="search_field" value="' . htmlspecialchars($this->searchString) . '" />
498 </div>
499 </div>
500 <div class="col-xs-3 col-md-3 col-lg-3">
501 <div class="input-group">
502 <label class="pull-left" for="search_levels">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.label.search_levels', TRUE) . ': </label>
503 ' . $lMenu . '
504 </div>
505 </div>
506 <div class="col-xs-3 col-md-3 col-lg-3">
507 <div class="input-group">
508 <label class="pull-left" for="showLimit">' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.label.limit', TRUE) . ': </label>
509 <input class="form-control" type="number" placeholder="10" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.limit', TRUE) . '" name="showLimit" id="showLimit" value="' . htmlspecialchars(($this->showLimit ? $this->showLimit : '')) . '" />
510 </div>
511 </div>
512 <div class="col-xs-3 col-md-3 col-lg-3">
513 <div class="input-group">
514 <input type="submit" class="btn btn-default btn-block" name="search" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.search', TRUE) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.search', TRUE) . '" />
515 </div>
516 </div>
517 </div>
518 ' . $formElements[1] . '</div></div>';
519 return $content;
520 }
521
522 /******************************
523 *
524 * Various helper functions
525 *
526 ******************************/
527 /**
528 * Setting the field names to display in extended list.
529 * Sets the internal variable $this->setFields
530 *
531 * @return void
532 */
533 public function setDispFields() {
534 // Getting from session:
535 $dispFields = $GLOBALS['BE_USER']->getModuleData('list/displayFields');
536 // If fields has been inputted, then set those as the value and push it to session variable:
537 if (is_array($this->displayFields)) {
538 reset($this->displayFields);
539 $tKey = key($this->displayFields);
540 $dispFields[$tKey] = $this->displayFields[$tKey];
541 $GLOBALS['BE_USER']->pushModuleData('list/displayFields', $dispFields);
542 }
543 // Setting result:
544 $this->setFields = $dispFields;
545 }
546
547 /**
548 * Create thumbnail code for record/field
549 *
550 * @param array $row Record array
551 * @param string $table Table (record is from)
552 * @param string $field Field name for which thumbnail are to be rendered.
553 * @return string HTML for thumbnails, if any.
554 */
555 public function thumbCode($row, $table, $field) {
556 return BackendUtility::thumbCode($row, $table, $field, $this->backPath);
557 }
558
559 /**
560 * Returns the SQL-query array to select the records from a table $table with pid = $id
561 *
562 * @param string $table Table name
563 * @param int $id Page id (NOT USED! $this->pidSelect is used instead)
564 * @param string $addWhere Additional part for where clause
565 * @param string $fieldList Field list to select, * for all (for "SELECT [fieldlist] FROM ...")
566 * @return array Returns query array
567 */
568 public function makeQueryArray($table, $id, $addWhere = '', $fieldList = '*') {
569 $hookObjectsArr = array();
570 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list.inc']['makeQueryArray'])) {
571 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list.inc']['makeQueryArray'] as $classRef) {
572 $hookObjectsArr[] = GeneralUtility::getUserObj($classRef);
573 }
574 }
575 // Set ORDER BY:
576 $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ? 'ORDER BY ' . $GLOBALS['TCA'][$table]['ctrl']['sortby'] : $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
577 if ($this->sortField) {
578 if (in_array($this->sortField, $this->makeFieldList($table, 1))) {
579 $orderBy = 'ORDER BY ' . $this->sortField;
580 if ($this->sortRev) {
581 $orderBy .= ' DESC';
582 }
583 }
584 }
585 // Set LIMIT:
586 $limit = $this->iLimit ? ($this->firstElementNumber ? $this->firstElementNumber . ',' : '') . ($this->iLimit + 1) : '';
587 // Filtering on displayable pages (permissions):
588 $pC = $table == 'pages' && $this->perms_clause ? ' AND ' . $this->perms_clause : '';
589 // Adding search constraints:
590 $search = $this->makeSearchString($table, $id);
591 // Compiling query array:
592 $queryParts = array(
593 'SELECT' => $fieldList,
594 'FROM' => $table,
595 'WHERE' => $this->pidSelect . ' ' . $pC . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table) . ' ' . $addWhere . ' ' . $search,
596 'GROUPBY' => '',
597 'ORDERBY' => $GLOBALS['TYPO3_DB']->stripOrderBy($orderBy),
598 'LIMIT' => $limit
599 );
600 // Filter out records that are translated, if TSconfig mod.web_list.hideTranslations is set
601 if ((in_array($table, GeneralUtility::trimExplode(',', $this->hideTranslations)) || $this->hideTranslations === '*') && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) && $table !== 'pages_language_overlay') {
602 $queryParts['WHERE'] .= ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=0 ';
603 }
604 // Apply hook as requested in http://forge.typo3.org/issues/16634
605 foreach ($hookObjectsArr as $hookObj) {
606 if (method_exists($hookObj, 'makeQueryArray_post')) {
607 $_params = array(
608 'orderBy' => $orderBy,
609 'limit' => $limit,
610 'pC' => $pC,
611 'search' => $search
612 );
613 $hookObj->makeQueryArray_post($queryParts, $this, $table, $id, $addWhere, $fieldList, $_params);
614 }
615 }
616 // Return query:
617 return $queryParts;
618 }
619
620 /**
621 * Based on input query array (query for selecting count(*) from a table) it will select the number of records and set the value in $this->totalItems
622 *
623 * @param array $queryParts Query array
624 * @return void
625 * @see makeQueryArray()
626 */
627 public function setTotalItems($queryParts) {
628 $this->totalItems = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('*', $queryParts['FROM'], $queryParts['WHERE']);
629 }
630
631 /**
632 * Creates part of query for searching after a word ($this->searchString)
633 * fields in input table.
634 *
635 * @param string $table Table, in which the fields are being searched.
636 * @param int $currentPid Page id for the possible search limit. -1 only if called from an old XCLASS.
637 * @return string Returns part of WHERE-clause for searching, if applicable.
638 */
639 public function makeSearchString($table, $currentPid = -1) {
640 $result = '';
641 $currentPid = (int)$currentPid;
642 $tablePidField = $table == 'pages' ? 'uid' : 'pid';
643 // Make query, only if table is valid and a search string is actually defined:
644 if ($this->searchString) {
645 $result = ' AND 0=1';
646 $searchableFields = $this->getSearchFields($table);
647 if (count($searchableFields) > 0) {
648 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->searchString)) {
649 $whereParts = array(
650 'uid=' . $this->searchString
651 );
652 foreach ($searchableFields as $fieldName) {
653 if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
654 $fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
655 $condition = $fieldName . '=' . $this->searchString;
656 if ($fieldConfig['type'] == 'input' && $fieldConfig['eval'] && GeneralUtility::inList($fieldConfig['eval'], 'int')) {
657 if (is_array($fieldConfig['search']) && in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
658 $condition = '(' . $condition . ' AND ' . $tablePidField . '=' . $currentPid . ')';
659 }
660 $whereParts[] = $condition;
661 } elseif (
662 $fieldConfig['type'] == 'text' ||
663 $fieldConfig['type'] == 'flex' ||
664 ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval'])))) {
665 $condition = $fieldName . ' LIKE \'%' . $this->searchString . '%\'';
666 $whereParts[] = $condition;
667 }
668 }
669 }
670 } else {
671 $whereParts = array();
672 $like = '\'%' . $GLOBALS['TYPO3_DB']->quoteStr($GLOBALS['TYPO3_DB']->escapeStrForLike($this->searchString, $table), $table) . '%\'';
673 foreach ($searchableFields as $fieldName) {
674 if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
675 $fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
676 $format = 'LCASE(%s) LIKE LCASE(%s)';
677 if (is_array($fieldConfig['search'])) {
678 if (in_array('case', $fieldConfig['search'])) {
679 $format = '%s LIKE %s';
680 }
681 if (in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
682 $format = '(' . $format . ' AND ' . $tablePidField . '=' . $currentPid . ')';
683 }
684 if ($fieldConfig['search']['andWhere']) {
685 $format = '((' . $fieldConfig['search']['andWhere'] . ') AND (' . $format . '))';
686 }
687 }
688 if ($fieldConfig['type'] == 'text' || $fieldConfig['type'] == 'flex' || $fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval']))) {
689 $whereParts[] = sprintf($format, $fieldName, $like);
690 }
691 }
692 }
693 }
694 // If search-fields were defined (and there always are) we create the query:
695 if (count($whereParts)) {
696 $result = ' AND (' . implode(' OR ', $whereParts) . ')';
697 }
698 }
699 }
700 return $result;
701 }
702
703 /**
704 * Fetches a list of fields to use in the Backend search for the given table.
705 *
706 * @param string $tableName
707 * @return array
708 */
709 protected function getSearchFields($tableName) {
710 $fieldArray = array();
711 $fieldListWasSet = FALSE;
712 // Get fields from ctrl section of TCA first
713 if (isset($GLOBALS['TCA'][$tableName]['ctrl']['searchFields'])) {
714 $fieldArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'], TRUE);
715 $fieldListWasSet = TRUE;
716 }
717 // Call hook to add or change the list
718 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['mod_list']['getSearchFieldList'])) {
719 $hookParameters = array(
720 'tableHasSearchConfiguration' => $fieldListWasSet,
721 'tableName' => $tableName,
722 'searchFields' => &$fieldArray,
723 'searchString' => $this->searchString
724 );
725 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['mod_list']['getSearchFieldList'] as $hookFunction) {
726 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
727 }
728 }
729 return $fieldArray;
730 }
731
732 /**
733 * Returns the title (based on $code) of a table ($table) with the proper link around. For headers over tables.
734 * The link will cause the display of all extended mode or not for the table.
735 *
736 * @param string $table Table name
737 * @param string $code Table label
738 * @return string The linked table label
739 */
740 public function linkWrapTable($table, $code) {
741 if ($this->table != $table) {
742 return '<a href="' . htmlspecialchars($this->listURL('', $table, 'firstElementNumber')) . '">' . $code . '</a>';
743 } else {
744 return '<a href="' . htmlspecialchars($this->listURL('', '', 'sortField,sortRev,table,firstElementNumber')) . '">' . $code . '</a>';
745 }
746 }
747
748 /**
749 * 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...)
750 *
751 * @param string $table Table name
752 * @param int $uid Item uid
753 * @param string $code Item title (not htmlspecialchars()'ed yet)
754 * @param array $row Item row
755 * @return string The item title. Ready for HTML output (is htmlspecialchars()'ed)
756 */
757 public function linkWrapItems($table, $uid, $code, $row) {
758 $origCode = $code;
759 // If the title is blank, make a "no title" label:
760 if ((string)$code === '') {
761 $code = '<i>[' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', 1) . ']</i> - ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(
762 BackendUtility::getRecordTitle($table, $row), $GLOBALS['BE_USER']->uc['titleLen']));
763 } else {
764 $code = htmlspecialchars(GeneralUtility::fixed_lgd_cs($code, $this->fixedL), ENT_QUOTES, 'UTF-8', FALSE);
765 if ($code != htmlspecialchars($origCode)) {
766 $code = '<span title="' . htmlspecialchars($origCode, ENT_QUOTES, 'UTF-8', FALSE) . '">' . $code . '</span>';
767 }
768 }
769 switch ((string)$this->clickTitleMode) {
770 case 'edit':
771 // If the listed table is 'pages' we have to request the permission settings for each page:
772 if ($table == 'pages') {
773 $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
774 $permsEdit = $localCalcPerms & 2;
775 } else {
776 $permsEdit = $this->calcPerms & 16;
777 }
778 // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
779 if ($permsEdit) {
780 $params = '&edit[' . $table . '][' . $row['uid'] . ']=edit';
781 $code = '<a href="#" onclick="' . htmlspecialchars(
782 BackendUtility::editOnClick($params, $this->backPath, -1)) . '" title="' . $GLOBALS['LANG']->getLL('edit', TRUE) . '">' . $code . '</a>';
783 }
784 break;
785 case 'show':
786 // "Show" link (only pages and tt_content elements)
787 if ($table == 'pages' || $table == 'tt_content') {
788 $code = '<a href="#" onclick="' . htmlspecialchars(
789 BackendUtility::viewOnClick(($table == 'tt_content' ? $this->id . '#' . $row['uid'] : $row['uid']))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE) . '">' . $code . '</a>';
790 }
791 break;
792 case 'info':
793 // "Info": (All records)
794 $code = '<a href="#" onclick="' . htmlspecialchars(('top.launchView(\'' . $table . '\', \'' . $row['uid'] . '\'); return false;')) . '" title="' . $GLOBALS['LANG']->getLL('showInfo', TRUE) . '">' . $code . '</a>';
795 break;
796 default:
797 // Output the label now:
798 if ($table == 'pages') {
799 $code = '<a href="' . htmlspecialchars($this->listURL($uid, '', 'firstElementNumber')) . '" onclick="setHighlight(' . $uid . ')">' . $code . '</a>';
800 } else {
801 $code = $this->linkUrlMail($code, $origCode);
802 }
803 }
804 return $code;
805 }
806
807 /**
808 * Wrapping input code in link to URL or email if $testString is either.
809 *
810 * @param string $code code to wrap
811 * @param string $testString String which is tested for being a URL or email and which will be used for the link if so.
812 * @return string Link-Wrapped $code value, if $testString was URL or email.
813 */
814 public function linkUrlMail($code, $testString) {
815 // Check for URL:
816 $schema = parse_url($testString);
817 if ($schema['scheme'] && GeneralUtility::inList('http,https,ftp', $schema['scheme'])) {
818 return '<a href="' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
819 }
820 // Check for email:
821 if (GeneralUtility::validEmail($testString)) {
822 return '<a href="mailto:' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
823 }
824 // Return if nothing else...
825 return $code;
826 }
827
828 /**
829 * Creates the URL to this script, including all relevant GPvars
830 * Fixed GPvars are id, table, imagemode, returnUrl, search_field, search_levels and showLimit
831 * The GPvars "sortField" and "sortRev" are also included UNLESS they are found in the $exclList variable.
832 *
833 * @param string $altId Alternative id value. Enter blank string for the current id ($this->id)
834 * @param string $table Tablename to display. Enter "-1" for the current table.
835 * @param string $exclList Commalist of fields NOT to include ("sortField", "sortRev" or "firstElementNumber")
836 * @return string URL
837 */
838 public function listURL($altId = '', $table = -1, $exclList = '') {
839 $urlParameters = array();
840 if ((string)$altId !== '') {
841 $urlParameters['id'] = $altId;
842 } else {
843 $urlParameters['id'] = $this->id;
844 }
845 if ($table === -1) {
846 $urlParameters['table'] = $this->table;
847 } else {
848 $urlParameters['table'] = $table;
849 }
850 if ($this->thumbs) {
851 $urlParameters['imagemode'] = $this->thumbs;
852 }
853 if ($this->returnUrl) {
854 $urlParameters['returnUrl'] = $this->returnUrl;
855 }
856 if ((!$exclList || !GeneralUtility::inList($exclList, 'search_field')) && $this->searchString) {
857 $urlParameters['search_field'] = $this->searchString;
858 }
859 if ($this->searchLevels) {
860 $urlParameters['search_levels'] = $this->searchLevels;
861 }
862 if ($this->showLimit) {
863 $urlParameters['showLimit'] = $this->showLimit;
864 }
865 if ((!$exclList || !GeneralUtility::inList($exclList, 'firstElementNumber')) && $this->firstElementNumber) {
866 $urlParameters['pointer'] = $this->firstElementNumber;
867 }
868 if ((!$exclList || !GeneralUtility::inList($exclList, 'sortField')) && $this->sortField) {
869 $urlParameters['sortField'] = $this->sortField;
870 }
871 if ((!$exclList || !GeneralUtility::inList($exclList, 'sortRev')) && $this->sortRev) {
872 $urlParameters['sortRev'] = $this->sortRev;
873 }
874
875 $urlParameters = array_merge_recursive($urlParameters, $this->overrideUrlParameters);
876
877 return BackendUtility::getModuleUrl(GeneralUtility::_GP('M'), $urlParameters);
878 }
879
880 /**
881 * Returns "requestUri" - which is basically listURL
882 *
883 * @return string Content of ->listURL()
884 */
885 public function requestUri() {
886 return $this->listURL();
887 }
888
889 /**
890 * Makes the list of fields to select for a table
891 *
892 * @param string $table Table name
893 * @param bool $dontCheckUser If set, users access to the field (non-exclude-fields) is NOT checked.
894 * @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)
895 * @return array Array, where values are fieldnames to include in query
896 */
897 public function makeFieldList($table, $dontCheckUser = 0, $addDateFields = 0) {
898 // Init fieldlist array:
899 $fieldListArr = array();
900 // Check table:
901 if (is_array($GLOBALS['TCA'][$table]) && isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
902 if (isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
903 // Traverse configured columns and add them to field array, if available for user.
904 foreach ($GLOBALS['TCA'][$table]['columns'] as $fN => $fieldValue) {
905 if ($dontCheckUser || (!$fieldValue['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $fN)) && $fieldValue['config']['type'] != 'passthrough') {
906 $fieldListArr[] = $fN;
907 }
908 }
909
910 $fieldListArr[] = 'uid';
911 $fieldListArr[] = 'pid';
912
913 // Add date fields
914 if ($dontCheckUser || $GLOBALS['BE_USER']->isAdmin() || $addDateFields) {
915 if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
916 $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['tstamp'];
917 }
918 if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
919 $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['crdate'];
920 }
921 }
922 // Add more special fields:
923 if ($dontCheckUser || $GLOBALS['BE_USER']->isAdmin()) {
924 if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
925 $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['cruser_id'];
926 }
927 if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
928 $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
929 }
930 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('version') && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
931 $fieldListArr[] = 't3ver_id';
932 $fieldListArr[] = 't3ver_state';
933 $fieldListArr[] = 't3ver_wsid';
934 }
935 }
936 } else {
937 GeneralUtility::sysLog(sprintf('$TCA is broken for the table "%s": no required "columns" entry in $TCA.', $table), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
938 }
939 }
940 return $fieldListArr;
941 }
942
943 /**
944 * Get all allowed mount pages to be searched in.
945 *
946 * @param int $id Page id
947 * @param int $depth Depth to go down
948 * @param string $perms_clause select clause
949 * @return array
950 */
951 protected function getSearchableWebmounts($id, $depth, $perms_clause) {
952 /** @var \TYPO3\CMS\Backend\Tree\View\PageTreeView $tree */
953 $tree = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\PageTreeView::class);
954 $tree->init('AND ' . $perms_clause);
955 $tree->makeHTML = 0;
956 $tree->fieldArray = array('uid', 'php_tree_stop');
957 $idList = array();
958
959 $allowedMounts = !$GLOBALS['BE_USER']->isAdmin() && $id === 0
960 ? $GLOBALS['BE_USER']->returnWebmounts()
961 : array($id);
962
963 foreach ($allowedMounts as $allowedMount) {
964 $idList[] = $allowedMount;
965 if ($depth) {
966 $tree->getTree($allowedMount, $depth, '');
967 }
968 $idList = array_merge($idList, $tree->ids);
969 }
970
971 return $idList;
972 }
973
974 /**
975 * Redirects to TCEforms (alt_doc) if a record is just localized.
976 *
977 * @param string $justLocalized String with table, orig uid and language separated by ":
978 * @return void
979 */
980 public function localizationRedirect($justLocalized) {
981 list($table, $orig_uid, $language) = explode(':', $justLocalized);
982 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
983 $localizedRecord = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid', $table, $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$language . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$orig_uid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table));
984 if (is_array($localizedRecord)) {
985 // Create parameters and finally run the classic page module for creating a new page translation
986 $url = substr($this->listURL(), strlen($this->backPath));
987 $params = '&edit[' . $table . '][' . $localizedRecord['uid'] . ']=edit';
988 $returnUrl = '&returnUrl=' . rawurlencode($url);
989 $location = $GLOBALS['BACK_PATH'] . 'alt_doc.php?' . $params . $returnUrl;
990 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($location);
991 }
992 }
993 }
994
995 /**
996 * Set URL parameters to override or add in the listUrl() method.
997 *
998 * @param array $urlParameters
999 * @return void
1000 */
1001 public function setOverrideUrlParameters(array $urlParameters) {
1002 $this->overrideUrlParameters = $urlParameters;
1003 }
1004
1005 }