TYPO3  7.6
AbstractDatabaseRecordList.php
Go to the documentation of this file.
1 <?php
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 
30 
37 {
38 
44  public $tableList = '';
45 
51  public $returnUrl = '';
52 
58  public $thumbs = 0;
59 
65  public $itemsLimitPerTable = 20;
66 
72  public $itemsLimitSingleTable = 100;
73 
79  public $script = 'index.php';
80 
86  public $allFields = 0;
87 
93  public $localizationView = false;
94 
100  public $csvOutput = false;
101 
107  public $sortField;
108 
114  public $sortRev;
115 
122 
129 
135  public $id;
136 
142  public $table = '';
143 
150 
157 
163  public $searchString = '';
164 
170  public $searchLevels = '';
171 
177  public $showLimit = 0;
178 
185  public $pidSelect = '';
186 
192  public $perms_clause = '';
193 
199  public $calcPerms = 0;
200 
206  public $clickTitleMode = '';
207 
213  public $modSharedTSconfig = array();
214 
220  public $pageRecord = array();
221 
227  public $hideTables = '';
228 
234  public $hideTranslations = '';
235 
241  public $tableTSconfigOverTCA = array();
242 
248  public $tablesCollapsed = array();
249 
255  public $JScode = '';
256 
262  public $HTMLcode = '';
263 
269  public $iLimit = 0;
270 
276  public $eCounter = 0;
277 
283  public $totalItems = '';
284 
290  public $recPath_cache = array();
291 
297  public $setFields = array();
298 
304  public $currentTable = array();
305 
311  public $duplicateStack = array();
312 
316  public $modTSconfig;
317 
322  protected $overrideUrlParameters = array();
323 
333  protected $tableDisplayOrder = [];
334 
346  public function start($id, $table, $pointer, $search = '', $levels = 0, $showLimit = 0)
347  {
348  $backendUser = $this->getBackendUserAuthentication();
349  $db = $this->getDatabaseConnection();
350  // Setting internal variables:
351  // sets the parent id
352  $this->id = (int)$id;
353  if ($GLOBALS['TCA'][$table]) {
354  // Setting single table mode, if table exists:
355  $this->table = $table;
356  }
357  $this->firstElementNumber = $pointer;
358  $this->searchString = trim($search);
359  $this->searchLevels = (int)$levels;
360  $this->showLimit = MathUtility::forceIntegerInRange($showLimit, 0, 10000);
361  // Setting GPvars:
362  $this->csvOutput = (bool)GeneralUtility::_GP('csv');
363  $this->sortField = GeneralUtility::_GP('sortField');
364  $this->sortRev = GeneralUtility::_GP('sortRev');
365  $this->displayFields = GeneralUtility::_GP('displayFields');
366  $this->duplicateField = GeneralUtility::_GP('duplicateField');
367  if (GeneralUtility::_GP('justLocalized')) {
368  $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
369  }
370  // Init dynamic vars:
371  $this->counter = 0;
372  $this->JScode = '';
373  $this->HTMLcode = '';
374  // Limits
375  if (isset($this->modTSconfig['properties']['itemsLimitPerTable'])) {
376  $this->itemsLimitPerTable = MathUtility::forceIntegerInRange((int)$this->modTSconfig['properties']['itemsLimitPerTable'], 1, 10000);
377  }
378  if (isset($this->modTSconfig['properties']['itemsLimitSingleTable'])) {
379  $this->itemsLimitSingleTable = MathUtility::forceIntegerInRange((int)$this->modTSconfig['properties']['itemsLimitSingleTable'], 1, 10000);
380  }
381  // Set search levels:
382  $searchLevels = $this->searchLevels;
383  $this->perms_clause = $backendUser->getPagePermsClause(1);
384  // This will hide records from display - it has nothing to do with user rights!!
385  if ($pidList = $backendUser->getTSConfigVal('options.hideRecords.pages')) {
386  if ($pidList = $db->cleanIntList($pidList)) {
387  $this->perms_clause .= ' AND pages.uid NOT IN (' . $pidList . ')';
388  }
389  }
390  // Get configuration of collapsed tables from user uc and merge with sanitized GP vars
391  $this->tablesCollapsed = is_array($backendUser->uc['moduleData']['list']) ? $backendUser->uc['moduleData']['list'] : array();
392  $collapseOverride = GeneralUtility::_GP('collapse');
393  if (is_array($collapseOverride)) {
394  foreach ($collapseOverride as $collapseTable => $collapseValue) {
395  if (is_array($GLOBALS['TCA'][$collapseTable]) && ($collapseValue == 0 || $collapseValue == 1)) {
396  $this->tablesCollapsed[$collapseTable] = $collapseValue;
397  }
398  }
399  // Save modified user uc
400  $backendUser->uc['moduleData']['list'] = $this->tablesCollapsed;
401  $backendUser->writeUC($backendUser->uc);
403  if ($returnUrl !== '') {
404  HttpUtility::redirect($returnUrl);
405  }
406  }
407  if ($searchLevels > 0) {
408  $allowedMounts = $this->getSearchableWebmounts($this->id, $searchLevels, $this->perms_clause);
409  $pidList = implode(',', $db->cleanIntArray($allowedMounts));
410  $this->pidSelect = 'pid IN (' . $pidList . ')';
411  } elseif ($searchLevels < 0) {
412  // Search everywhere
413  $this->pidSelect = '1=1';
414  } else {
415  $this->pidSelect = 'pid=' . (int)$id;
416  }
417  // Initialize languages:
418  if ($this->localizationView) {
419  $this->initializeLanguages();
420  }
421  }
422 
430  public function generateList()
431  {
432  // Set page record in header
433  $this->pageRecord = BackendUtility::getRecordWSOL('pages', $this->id);
434  $hideTablesArray = GeneralUtility::trimExplode(',', $this->hideTables);
435 
436  $backendUser = $this->getBackendUserAuthentication();
437 
438  // pre-process tables and add sorting instructions
439  $tableNames = array_flip(array_keys($GLOBALS['TCA']));
440  foreach ($tableNames as $tableName => &$config) {
441  $hideTable = false;
442 
443  // Checking if the table should be rendered:
444  // Checks that we see only permitted/requested tables:
445  if ($this->table && $tableName !== $this->table
446  || $this->tableList && !GeneralUtility::inList($this->tableList, $tableName)
447  || !$backendUser->check('tables_select', $tableName)
448  ) {
449  $hideTable = true;
450  }
451 
452  if (!$hideTable) {
453  // Don't show table if hidden by TCA ctrl section
454  // Don't show table if hidden by pageTSconfig mod.web_list.hideTables
455  $hideTable = $hideTable || !empty($GLOBALS['TCA'][$tableName]['ctrl']['hideTable']) || in_array($tableName, $hideTablesArray, true);
456  // Override previous selection if table is enabled or hidden by TSconfig TCA override mod.web_list.table
457  if (isset($this->tableTSconfigOverTCA[$tableName . '.']['hideTable'])) {
458  $hideTable = (bool)$this->tableTSconfigOverTCA[$tableName . '.']['hideTable'];
459  }
460  }
461  if ($hideTable) {
462  unset($tableNames[$tableName]);
463  } else {
464  if (isset($this->tableDisplayOrder[$tableName])) {
465  // Copy display order information
466  $tableNames[$tableName] = $this->tableDisplayOrder[$tableName];
467  } else {
468  $tableNames[$tableName] = [];
469  }
470  }
471  }
472  unset($config);
473 
474  $orderedTableNames = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($tableNames);
475 
476  $db = $this->getDatabaseConnection();
477  foreach ($orderedTableNames as $tableName => $_) {
478  // check if we are in single- or multi-table mode
479  if ($this->table) {
480  $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems']) ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxSingleDBListItems'] : $this->itemsLimitSingleTable;
481  } else {
482  // if there are no records in table continue current foreach
483  $firstRow = $db->exec_SELECTgetSingleRow(
484  'uid',
485  $tableName,
486  $this->pidSelect . BackendUtility::deleteClause($tableName) . BackendUtility::versioningPlaceholderClause($tableName)
487  );
488  if ($firstRow === false) {
489  continue;
490  }
491  $this->iLimit = isset($GLOBALS['TCA'][$tableName]['interface']['maxDBListItems']) ? (int)$GLOBALS['TCA'][$tableName]['interface']['maxDBListItems'] : $this->itemsLimitPerTable;
492  }
493  if ($this->showLimit) {
494  $this->iLimit = $this->showLimit;
495  }
496  // Setting fields to select:
497  if ($this->allFields) {
498  $fields = $this->makeFieldList($tableName);
499  $fields[] = 'tstamp';
500  $fields[] = 'crdate';
501  $fields[] = '_PATH_';
502  $fields[] = '_CONTROL_';
503  if (is_array($this->setFields[$tableName])) {
504  $fields = array_intersect($fields, $this->setFields[$tableName]);
505  } else {
506  $fields = array();
507  }
508  } else {
509  $fields = array();
510  }
511  // Find ID to use (might be different for "versioning_followPages" tables)
512  if ($this->searchLevels === 0) {
513  $this->pidSelect = 'pid=' . (int)$this->id;
514  }
515  // Finally, render the list:
516  $this->HTMLcode .= $this->getTable($tableName, $this->id, implode(',', $fields));
517  }
518  }
519 
528  public function getTable($tableName, $id, $fields = '')
529  {
530  return '';
531  }
532 
539  public function getSearchBox($formFields = true)
540  {
542  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
543  $lang = $this->getLanguageService();
544  // Setting form-elements, if applicable:
545  $formElements = array('', '');
546  if ($formFields) {
547  $formElements = array('<form action="' . htmlspecialchars($this->listURL('', -1, 'firstElementNumber,search_field')) . '" method="post">', '</form>');
548  }
549  // Make level selector:
550  $opt = array();
551  $parts = explode('|', $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.enterSearchLevels'));
552  foreach ($parts as $kv => $label) {
553  $opt[] = '<option value="' . $kv . '"' . ($kv === $this->searchLevels ? ' selected="selected"' : '') . '>' . htmlspecialchars($label) . '</option>';
554  }
555  $lMenu = '<select class="form-control" name="search_levels" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.search_levels', true) . '" id="search_levels">' . implode('', $opt) . '</select>';
556  // Table with the search box:
557  $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') . ';">
558  ' . $formElements[0] . '
559  <div id="typo3-dblist-search">
560  <div class="panel panel-default">
561  <div class="panel-body">
562  <div class="form-inline form-inline-spaced">
563  <div class="form-group">
564  <input class="form-control" type="search" placeholder="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.enterSearchString', true) . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.searchString', true) . '" name="search_field" id="search_field" value="' . htmlspecialchars($this->searchString) . '" />
565  </div>
566  <div class="form-group">
567  <label for="search_levels">' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.label.search_levels', true) . ': </label>
568  ' . $lMenu . '
569  </div>
570  <div class="form-group">
571  <label for="showLimit">' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.label.limit', true) . ': </label>
572  <input class="form-control" type="number" min="0" max="10000" placeholder="10" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.limit', true) . '" name="showLimit" id="showLimit" value="' . htmlspecialchars(($this->showLimit ? $this->showLimit : '')) . '" />
573  </div>
574  <div class="form-group">
575  <button type="submit" class="btn btn-default" name="search" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.search', true) . '">
576  ' . $iconFactory->getIcon('actions-search', Icon::SIZE_SMALL)->render() . ' ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.search', true) . '
577  </button>
578  </div>
579  </div>
580  </div>
581  </div>
582  </div>
583  ' . $formElements[1] . '</div>';
584  return $content;
585  }
586 
587  /******************************
588  *
589  * Various helper functions
590  *
591  ******************************/
598  public function setDispFields()
599  {
600  $backendUser = $this->getBackendUserAuthentication();
601  // Getting from session:
602  $dispFields = $backendUser->getModuleData('list/displayFields');
603  // If fields has been inputted, then set those as the value and push it to session variable:
604  if (is_array($this->displayFields)) {
605  reset($this->displayFields);
606  $tKey = key($this->displayFields);
607  $dispFields[$tKey] = $this->displayFields[$tKey];
608  $backendUser->pushModuleData('list/displayFields', $dispFields);
609  }
610  // Setting result:
611  $this->setFields = $dispFields;
612  }
613 
622  public function thumbCode($row, $table, $field)
623  {
624  return BackendUtility::thumbCode($row, $table, $field);
625  }
626 
636  public function makeQueryArray($table, $id, $addWhere = '', $fieldList = '*')
637  {
638  $hookObjectsArr = array();
639  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list.inc']['makeQueryArray'])) {
640  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list.inc']['makeQueryArray'] as $classRef) {
641  $hookObjectsArr[] = GeneralUtility::getUserObj($classRef);
642  }
643  }
644  // Set ORDER BY:
645  $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ? 'ORDER BY ' . $GLOBALS['TCA'][$table]['ctrl']['sortby'] : $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
646  if ($this->sortField) {
647  if (in_array($this->sortField, $this->makeFieldList($table, 1))) {
648  $orderBy = 'ORDER BY ' . $this->sortField;
649  if ($this->sortRev) {
650  $orderBy .= ' DESC';
651  }
652  }
653  }
654  // Set LIMIT:
655  $limit = $this->iLimit ? ($this->firstElementNumber ? $this->firstElementNumber . ',' : '') . ($this->iLimit + 1) : '';
656  // Filtering on displayable pages (permissions):
657  $pC = $table == 'pages' && $this->perms_clause ? ' AND ' . $this->perms_clause : '';
658  // Adding search constraints:
659  $search = $this->makeSearchString($table, $id);
660  // Compiling query array:
661  $queryParts = array(
662  'SELECT' => $fieldList,
663  'FROM' => $table,
664  'WHERE' => $this->pidSelect . ' ' . $pC . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table) . ' ' . $addWhere . ' ' . $search,
665  'GROUPBY' => '',
666  'ORDERBY' => $this->getDatabaseConnection()->stripOrderBy($orderBy),
667  'LIMIT' => $limit
668  );
669  // Filter out records that are translated, if TSconfig mod.web_list.hideTranslations is set
670  if ((in_array($table, GeneralUtility::trimExplode(',', $this->hideTranslations)) || $this->hideTranslations === '*') && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) && $table !== 'pages_language_overlay') {
671  $queryParts['WHERE'] .= ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=0 ';
672  }
673  // Apply hook as requested in http://forge.typo3.org/issues/16634
674  foreach ($hookObjectsArr as $hookObj) {
675  if (method_exists($hookObj, 'makeQueryArray_post')) {
676  $_params = array(
677  'orderBy' => $orderBy,
678  'limit' => $limit,
679  'pC' => $pC,
680  'search' => $search
681  );
682  $hookObj->makeQueryArray_post($queryParts, $this, $table, $id, $addWhere, $fieldList, $_params);
683  }
684  }
685  // Return query:
686  return $queryParts;
687  }
688 
696  public function setTotalItems($queryParts)
697  {
698  $this->totalItems = $this->getDatabaseConnection()->exec_SELECTcountRows('*', $queryParts['FROM'], $queryParts['WHERE']);
699  }
700 
709  public function makeSearchString($table, $currentPid = -1)
710  {
711  $result = '';
712  $currentPid = (int)$currentPid;
713  $tablePidField = $table === 'pages' ? 'uid' : 'pid';
714  // Make query, only if table is valid and a search string is actually defined:
715  if ($this->searchString) {
716  $result = ' AND 0=1';
717  $searchableFields = $this->getSearchFields($table);
718  if (!empty($searchableFields)) {
719  if (MathUtility::canBeInterpretedAsInteger($this->searchString)) {
720  $whereParts = array(
721  'uid=' . $this->searchString
722  );
723  foreach ($searchableFields as $fieldName) {
724  if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
725  $fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
726  $condition = $fieldName . '=' . $this->searchString;
727  if ($fieldConfig['type'] == 'input' && $fieldConfig['eval'] && GeneralUtility::inList($fieldConfig['eval'], 'int')) {
728  if (is_array($fieldConfig['search']) && in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
729  $condition = '(' . $condition . ' AND ' . $tablePidField . '=' . $currentPid . ')';
730  }
731  $whereParts[] = $condition;
732  } elseif ($fieldConfig['type'] == 'text' ||
733  $fieldConfig['type'] == 'flex' ||
734  ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval'])))) {
735  $condition = $fieldName . ' LIKE \'%' . $this->searchString . '%\'';
736  $whereParts[] = $condition;
737  }
738  }
739  }
740  } else {
741  $whereParts = array();
742  $db = $this->getDatabaseConnection();
743  $like = '\'%' . $db->quoteStr($db->escapeStrForLike($this->searchString, $table), $table) . '%\'';
744  foreach ($searchableFields as $fieldName) {
745  if (isset($GLOBALS['TCA'][$table]['columns'][$fieldName])) {
746  $fieldConfig = &$GLOBALS['TCA'][$table]['columns'][$fieldName]['config'];
747  $format = 'LOWER(%s) LIKE LOWER(%s)';
748  if (is_array($fieldConfig['search'])) {
749  if (in_array('case', $fieldConfig['search'])) {
750  $format = '%s LIKE %s';
751  }
752  if (in_array('pidonly', $fieldConfig['search']) && $currentPid > 0) {
753  $format = '(' . $format . ' AND ' . $tablePidField . '=' . $currentPid . ')';
754  }
755  if ($fieldConfig['search']['andWhere']) {
756  $format = '((' . $fieldConfig['search']['andWhere'] . ') AND (' . $format . '))';
757  }
758  }
759  if ($fieldConfig['type'] == 'text' || $fieldConfig['type'] == 'flex' || $fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval']))) {
760  $whereParts[] = sprintf($format, $fieldName, $like);
761  }
762  }
763  }
764  }
765  // If search-fields were defined (and there always are) we create the query:
766  if (!empty($whereParts)) {
767  $result = ' AND (' . implode(' OR ', $whereParts) . ')';
768  }
769  }
770  }
771  return $result;
772  }
773 
780  protected function getSearchFields($tableName)
781  {
782  $fieldArray = array();
783  $fieldListWasSet = false;
784  // Get fields from ctrl section of TCA first
785  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['searchFields'])) {
786  $fieldArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'], true);
787  $fieldListWasSet = true;
788  }
789  // Call hook to add or change the list
790  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['mod_list']['getSearchFieldList'])) {
791  $hookParameters = array(
792  'tableHasSearchConfiguration' => $fieldListWasSet,
793  'tableName' => $tableName,
794  'searchFields' => &$fieldArray,
795  'searchString' => $this->searchString
796  );
797  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['mod_list']['getSearchFieldList'] as $hookFunction) {
798  GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
799  }
800  }
801  return $fieldArray;
802  }
803 
812  public function linkWrapTable($table, $code)
813  {
814  if ($this->table !== $table) {
815  return '<a href="' . htmlspecialchars($this->listURL('', $table, 'firstElementNumber')) . '">' . $code . '</a>';
816  }
817  return '<a href="' . htmlspecialchars($this->listURL('', '', 'sortField,sortRev,table,firstElementNumber')) . '">' . $code . '</a>';
818  }
819 
829  public function linkWrapItems($table, $uid, $code, $row)
830  {
831  $lang = $this->getLanguageService();
832  $origCode = $code;
833  // If the title is blank, make a "no title" label:
834  if ((string)$code === '') {
835  $code = '<i>[' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', 1) . ']</i> - ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(
837  $this->getBackendUserAuthentication()->uc['titleLen']
838  ));
839  } else {
840  $code = htmlspecialchars(GeneralUtility::fixed_lgd_cs($code, $this->fixedL), ENT_QUOTES, 'UTF-8', false);
841  if ($code != htmlspecialchars($origCode)) {
842  $code = '<span title="' . htmlspecialchars($origCode, ENT_QUOTES, 'UTF-8', false) . '">' . $code . '</span>';
843  }
844  }
845  switch ((string)$this->clickTitleMode) {
846  case 'edit':
847  // If the listed table is 'pages' we have to request the permission settings for each page:
848  if ($table == 'pages') {
849  $localCalcPerms = $this->getBackendUserAuthentication()->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
850  $permsEdit = $localCalcPerms & Permission::PAGE_EDIT;
851  } else {
852  $permsEdit = $this->calcPerms & Permission::CONTENT_EDIT;
853  }
854  // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
855  if ($permsEdit) {
856  $params = '&edit[' . $table . '][' . $row['uid'] . ']=edit';
857  $code = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::editOnClick($params, '', -1)) . '" title="' . $lang->getLL('edit', true) . '">' . $code . '</a>';
858  }
859  break;
860  case 'show':
861  // "Show" link (only pages and tt_content elements)
862  if ($table == 'pages' || $table == 'tt_content') {
863  $code = '<a href="#" onclick="' . htmlspecialchars(
864  BackendUtility::viewOnClick(($table == 'tt_content' ? $this->id . '#' . $row['uid'] : $row['uid']))
865  ) . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', true) . '">' . $code . '</a>';
866  }
867  break;
868  case 'info':
869  // "Info": (All records)
870  $code = '<a href="#" onclick="' . htmlspecialchars(('top.launchView(\'' . $table . '\', \'' . $row['uid'] . '\'); return false;')) . '" title="' . $lang->getLL('showInfo', true) . '">' . $code . '</a>';
871  break;
872  default:
873  // Output the label now:
874  if ($table == 'pages') {
875  $code = '<a href="' . htmlspecialchars($this->listURL($uid, '', 'firstElementNumber')) . '" onclick="setHighlight(' . $uid . ')">' . $code . '</a>';
876  } else {
877  $code = $this->linkUrlMail($code, $origCode);
878  }
879  }
880  return $code;
881  }
882 
890  public function linkUrlMail($code, $testString)
891  {
892  // Check for URL:
893  $schema = parse_url($testString);
894  if ($schema['scheme'] && GeneralUtility::inList('http,https,ftp', $schema['scheme'])) {
895  return '<a href="' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
896  }
897  // Check for email:
898  if (GeneralUtility::validEmail($testString)) {
899  return '<a href="mailto:' . htmlspecialchars($testString) . '" target="_blank">' . $code . '</a>';
900  }
901  // Return if nothing else...
902  return $code;
903  }
904 
915  public function listURL($altId = '', $table = '-1', $exclList = '')
916  {
917  $urlParameters = array();
918  if ((string)$altId !== '') {
919  $urlParameters['id'] = $altId;
920  } else {
921  $urlParameters['id'] = $this->id;
922  }
923  if ($table === '-1') {
924  $urlParameters['table'] = $this->table;
925  } else {
926  $urlParameters['table'] = $table;
927  }
928  if ($this->thumbs) {
929  $urlParameters['imagemode'] = $this->thumbs;
930  }
931  if ($this->returnUrl) {
932  $urlParameters['returnUrl'] = $this->returnUrl;
933  }
934  if ((!$exclList || !GeneralUtility::inList($exclList, 'search_field')) && $this->searchString) {
935  $urlParameters['search_field'] = $this->searchString;
936  }
937  if ($this->searchLevels) {
938  $urlParameters['search_levels'] = $this->searchLevels;
939  }
940  if ($this->showLimit) {
941  $urlParameters['showLimit'] = $this->showLimit;
942  }
943  if ((!$exclList || !GeneralUtility::inList($exclList, 'firstElementNumber')) && $this->firstElementNumber) {
944  $urlParameters['pointer'] = $this->firstElementNumber;
945  }
946  if ((!$exclList || !GeneralUtility::inList($exclList, 'sortField')) && $this->sortField) {
947  $urlParameters['sortField'] = $this->sortField;
948  }
949  if ((!$exclList || !GeneralUtility::inList($exclList, 'sortRev')) && $this->sortRev) {
950  $urlParameters['sortRev'] = $this->sortRev;
951  }
952 
953  $urlParameters = array_merge_recursive($urlParameters, $this->overrideUrlParameters);
954 
955  return BackendUtility::getModuleUrl(GeneralUtility::_GP('M'), $urlParameters);
956  }
957 
963  public function requestUri()
964  {
965  return $this->listURL();
966  }
967 
976  public function makeFieldList($table, $dontCheckUser = false, $addDateFields = false)
977  {
978  $backendUser = $this->getBackendUserAuthentication();
979  // Init fieldlist array:
980  $fieldListArr = array();
981  // Check table:
982  if (is_array($GLOBALS['TCA'][$table]) && isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
983  if (isset($GLOBALS['TCA'][$table]['columns']) && is_array($GLOBALS['TCA'][$table]['columns'])) {
984  // Traverse configured columns and add them to field array, if available for user.
985  foreach ($GLOBALS['TCA'][$table]['columns'] as $fN => $fieldValue) {
986  if ($dontCheckUser || (!$fieldValue['exclude'] || $backendUser->check('non_exclude_fields', $table . ':' . $fN)) && $fieldValue['config']['type'] != 'passthrough') {
987  $fieldListArr[] = $fN;
988  }
989  }
990 
991  $fieldListArr[] = 'uid';
992  $fieldListArr[] = 'pid';
993 
994  // Add date fields
995  if ($dontCheckUser || $backendUser->isAdmin() || $addDateFields) {
996  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
997  $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['tstamp'];
998  }
999  if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
1000  $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['crdate'];
1001  }
1002  }
1003  // Add more special fields:
1004  if ($dontCheckUser || $backendUser->isAdmin()) {
1005  if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
1006  $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['cruser_id'];
1007  }
1008  if ($GLOBALS['TCA'][$table]['ctrl']['sortby']) {
1009  $fieldListArr[] = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
1010  }
1011  if (ExtensionManagementUtility::isLoaded('version') && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1012  $fieldListArr[] = 't3ver_id';
1013  $fieldListArr[] = 't3ver_state';
1014  $fieldListArr[] = 't3ver_wsid';
1015  }
1016  }
1017  } else {
1018  GeneralUtility::sysLog(sprintf('$TCA is broken for the table "%s": no required "columns" entry in $TCA.', $table), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1019  }
1020  }
1021  return $fieldListArr;
1022  }
1023 
1032  protected function getSearchableWebmounts($id, $depth, $perms_clause)
1033  {
1034  $backendUser = $this->getBackendUserAuthentication();
1036  $tree = GeneralUtility::makeInstance(PageTreeView::class);
1037  $tree->init('AND ' . $perms_clause);
1038  $tree->makeHTML = 0;
1039  $tree->fieldArray = array('uid', 'php_tree_stop');
1040  $idList = array();
1041 
1042  $allowedMounts = !$backendUser->isAdmin() && $id === 0
1043  ? $backendUser->returnWebmounts()
1044  : array($id);
1045 
1046  foreach ($allowedMounts as $allowedMount) {
1047  $idList[] = $allowedMount;
1048  if ($depth) {
1049  $tree->getTree($allowedMount, $depth, '');
1050  }
1051  $idList = array_merge($idList, $tree->ids);
1052  }
1053 
1054  return $idList;
1055  }
1056 
1063  public function localizationRedirect($justLocalized)
1064  {
1065  list($table, $orig_uid, $language) = explode(':', $justLocalized);
1066  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
1067  $localizedRecord = $this->getDatabaseConnection()->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));
1068  if (is_array($localizedRecord)) {
1069  // Create parameters and finally run the classic page module for creating a new page translation
1070  $url = $this->listURL();
1071  $editUserAccountUrl = BackendUtility::getModuleUrl(
1072  'record_edit',
1073  array(
1074  'edit[' . $table . '][' . $localizedRecord['uid'] . ']' => 'edit',
1075  'returnUrl' => $url
1076  )
1077  );
1078  HttpUtility::redirect($editUserAccountUrl);
1079  }
1080  }
1081  }
1082 
1089  public function setOverrideUrlParameters(array $urlParameters)
1090  {
1091  $this->overrideUrlParameters = $urlParameters;
1092  }
1093 
1106  public function setTableDisplayOrder(array $orderInformation)
1107  {
1108  foreach ($orderInformation as $tableName => &$configuration) {
1109  if (isset($configuration['before'])) {
1110  if (is_string($configuration['before'])) {
1111  $configuration['before'] = GeneralUtility::trimExplode(',', $configuration['before'], true);
1112  } elseif (!is_array($configuration['before'])) {
1113  throw new \UnexpectedValueException('The specified "before" order configuration for table "' . $tableName . '" is invalid.', 1436195933);
1114  }
1115  }
1116  if (isset($configuration['after'])) {
1117  if (is_string($configuration['after'])) {
1118  $configuration['after'] = GeneralUtility::trimExplode(',', $configuration['after'], true);
1119  } elseif (!is_array($configuration['after'])) {
1120  throw new \UnexpectedValueException('The specified "after" order configuration for table "' . $tableName . '" is invalid.', 1436195934);
1121  }
1122  }
1123  }
1124  $this->tableDisplayOrder = $orderInformation;
1125  }
1126 
1130  protected function getBackendUserAuthentication()
1131  {
1132  return $GLOBALS['BE_USER'];
1133  }
1134 
1138  protected function getDatabaseConnection()
1139  {
1140  return $GLOBALS['TYPO3_DB'];
1141  }
1142 }