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