TYPO3 CMS  TYPO3_7-6
LiveSearch.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 
23 
28 {
32  const PAGE_JUMP_TABLE = 'pages';
33 
38 
43 
48 
52  private $queryString = '';
53 
57  private $startCount = 0;
58 
62  private $limitCount = 5;
63 
67  protected $userPermissions = '';
68 
72  protected $queryParser = null;
73 
77  public function __construct()
78  {
79  $this->userPermissions = $GLOBALS['BE_USER']->getPagePermsClause(1);
80  $this->queryParser = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Search\LiveSearch\QueryParser::class);
81  }
82 
89  public function find($searchQuery)
90  {
91  $recordArray = [];
92  $pageList = [];
93  $mounts = $GLOBALS['BE_USER']->returnWebmounts();
94  foreach ($mounts as $pageId) {
95  $pageList[] = $this->getAvailablePageIds($pageId, self::RECURSIVE_PAGE_LEVEL);
96  }
97  $pageIdList = implode(',', array_unique(explode(',', implode(',', $pageList))));
98  unset($pageList);
99  $limit = $this->startCount . ',' . $this->limitCount;
100  if ($this->queryParser->isValidCommand($searchQuery)) {
101  $this->setQueryString($this->queryParser->getSearchQueryValue($searchQuery));
102  $tableName = $this->queryParser->getTableNameFromCommand($searchQuery);
103  if ($tableName) {
104  $recordArray[] = $this->findByTable($tableName, $pageIdList, $limit);
105  }
106  } else {
107  $this->setQueryString($searchQuery);
108  $recordArray = $this->findByGlobalTableList($pageIdList);
109  }
110  return $recordArray;
111  }
112 
119  protected function findPageById($id)
120  {
121  $pageRecord = [];
122  $row = BackendUtility::getRecord(self::PAGE_JUMP_TABLE, $id);
123  if (is_array($row)) {
124  $pageRecord = $row;
125  }
126  return $pageRecord;
127  }
128 
135  protected function findByGlobalTableList($pageIdList)
136  {
137  $limit = $this->limitCount;
138  $getRecordArray = [];
139  foreach ($GLOBALS['TCA'] as $tableName => $value) {
140  // if no access for the table (read or write), skip this table
141  if (!$GLOBALS['BE_USER']->check('tables_select', $tableName) && !$GLOBALS['BE_USER']->check('tables_modify', $tableName)) {
142  continue;
143  }
144  $recordArray = $this->findByTable($tableName, $pageIdList, '0,' . $limit);
145  $recordCount = count($recordArray);
146  if ($recordCount) {
147  $limit = $limit - $recordCount;
148  $getRecordArray[] = $recordArray;
149  if ($limit <= 0) {
150  break;
151  }
152  }
153  }
154  return $getRecordArray;
155  }
156 
169  protected function findByTable($tableName, $pageIdList, $limit)
170  {
171  $fieldsToSearchWithin = $this->extractSearchableFieldsFromTable($tableName);
172  $getRecordArray = [];
173  if (!empty($fieldsToSearchWithin)) {
174  $pageBasedPermission = $tableName == 'pages' && $this->userPermissions ? $this->userPermissions : '1=1 ';
175  $where = 'pid IN (' . $pageIdList . ') AND ' . $pageBasedPermission . $this->makeQuerySearchByTable($tableName, $fieldsToSearchWithin);
176  $getRecordArray = $this->getRecordArray($tableName, $where, $this->makeOrderByTable($tableName), $limit);
177  }
178  return $getRecordArray;
179  }
180 
192  protected function getRecordArray($tableName, $where, $orderBy, $limit)
193  {
194  $collect = [];
195  $queryParts = [
196  'SELECT' => '*',
197  'FROM' => $tableName,
198  'WHERE' => $where,
199  'ORDERBY' => $orderBy,
200  'LIMIT' => $limit
201  ];
202  $result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);
203  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
204  while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
205  $title = 'id=' . $row['uid'] . ', pid=' . $row['pid'];
206  $collect[] = [
207  'id' => $tableName . ':' . $row['uid'],
208  'pageId' => $tableName === 'pages' ? $row['uid'] : $row['pid'],
209  'typeLabel' => htmlspecialchars($this->getTitleOfCurrentRecordType($tableName)),
210  'iconHTML' => '<span title="' . htmlspecialchars($title) . '">' . $iconFactory->getIconForRecord($tableName, $row, Icon::SIZE_SMALL)->render() . '</span>',
211  'title' => htmlspecialchars(BackendUtility::getRecordTitle($tableName, $row)),
212  'editLink' => htmlspecialchars($this->getEditLink($tableName, $row))
213  ];
214  }
215  $GLOBALS['TYPO3_DB']->sql_free_result($result);
216  return $collect;
217  }
218 
227  protected function getEditLink($tableName, $row)
228  {
229  $pageInfo = BackendUtility::readPageAccess($row['pid'], $this->userPermissions);
230  $calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo);
231  $editLink = '';
232  if ($tableName == 'pages') {
233  $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
234  $permsEdit = $localCalcPerms & Permission::PAGE_EDIT;
235  } else {
236  $permsEdit = $calcPerms & Permission::CONTENT_EDIT;
237  }
238  // "Edit" link - Only if permissions to edit the page-record of the content of the parent page ($this->id)
239  if ($permsEdit) {
240  $returnUrl = BackendUtility::getModuleUrl('web_list', ['id' => $row['pid']]);
241  $editLink = BackendUtility::getModuleUrl('record_edit', [
242  'edit[' . $tableName . '][' . $row['uid'] . ']' => 'edit',
243  'returnUrl' => $returnUrl
244  ]);
245  }
246  return $editLink;
247  }
248 
255  protected function getTitleOfCurrentRecordType($tableName)
256  {
257  return $GLOBALS['LANG']->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
258  }
259 
269  public function getRecordTitlePrep($title, $titleLength = 0)
270  {
271  // If $titleLength is not a valid positive integer, use BE_USER->uc['titleLen']:
272  if (!$titleLength || !MathUtility::canBeInterpretedAsInteger($titleLength) || $titleLength < 0) {
273  $titleLength = $GLOBALS['BE_USER']->uc['titleLen'];
274  }
275  return htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, $titleLength));
276  }
277 
285  protected function makeQuerySearchByTable($tableName, array $fieldsToSearchWithin)
286  {
287  $queryPart = '';
288  $whereParts = [];
289  // If the search string is a simple integer, assemble an equality comparison
290  if (MathUtility::canBeInterpretedAsInteger($this->queryString)) {
291  foreach ($fieldsToSearchWithin as $fieldName) {
292  if ($fieldName == 'uid' || $fieldName == 'pid' || isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) {
293  $fieldConfig = &$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
294  // Assemble the search condition only if the field is an integer, or is uid or pid
295  if ($fieldName == 'uid' || $fieldName == 'pid' || $fieldConfig['type'] == 'input' && $fieldConfig['eval'] && GeneralUtility::inList($fieldConfig['eval'], 'int')) {
296  $whereParts[] = $fieldName . '=' . $this->queryString;
297  } elseif (
298  $fieldConfig['type'] == 'text' ||
299  $fieldConfig['type'] == 'flex' ||
300  ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] ||
301  !preg_match('/date|time|int/', $fieldConfig['eval'])))) {
302  // Otherwise and if the field makes sense to be searched, assemble a like condition
303  $whereParts[] = $fieldName . ' LIKE \'%' . $this->queryString . '%\'';
304  }
305  }
306  }
307  } else {
308  $like = '\'%' . $GLOBALS['TYPO3_DB']->escapeStrForLike($GLOBALS['TYPO3_DB']->quoteStr($this->queryString, $tableName), $tableName) . '%\'';
309  foreach ($fieldsToSearchWithin as $fieldName) {
310  if (isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) {
311  $fieldConfig = &$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
312  // Check whether search should be case-sensitive or not
313  $format = 'LOWER(%s) LIKE LOWER(%s)';
314  if (is_array($fieldConfig['search'])) {
315  if (in_array('case', $fieldConfig['search'])) {
316  $format = '%s LIKE %s';
317  }
318  // Apply additional condition, if any
319  if ($fieldConfig['search']['andWhere']) {
320  $format = '((' . $fieldConfig['search']['andWhere'] . ') AND (' . $format . '))';
321  }
322  }
323  // Assemble the search condition only if the field makes sense to be searched
324  if ($fieldConfig['type'] == 'text' || $fieldConfig['type'] == 'flex' || $fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval']))) {
325  $whereParts[] = sprintf($format, $fieldName, $like);
326  }
327  }
328  }
329  }
330  // If at least one condition was defined, create the search query
331  if (!empty($whereParts)) {
332  $queryPart = ' AND (' . implode(' OR ', $whereParts) . ')';
333  // And the relevant conditions for deleted and versioned records
334  $queryPart .= BackendUtility::deleteClause($tableName);
335  $queryPart .= BackendUtility::versioningPlaceholderClause($tableName);
336  $queryPart .= BackendUtility::getWorkspaceWhereClause($tableName);
337  } else {
338  $queryPart = ' AND 0 = 1';
339  }
340  return $queryPart;
341  }
342 
349  protected function makeOrderByTable($tableName)
350  {
351  $orderBy = '';
352  if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('sortby', $GLOBALS['TCA'][$tableName]['ctrl'])) {
353  $sortBy = trim($GLOBALS['TCA'][$tableName]['ctrl']['sortby']);
354  if (!empty($sortBy)) {
355  $orderBy = 'ORDER BY ' . $sortBy;
356  }
357  } else {
358  $orderBy = $GLOBALS['TCA'][$tableName]['ctrl']['default_sortby'];
359  }
360  return $GLOBALS['TYPO3_DB']->stripOrderBy($orderBy);
361  }
362 
369  protected function extractSearchableFieldsFromTable($tableName)
370  {
371  // Get the list of fields to search in from the TCA, if any
372  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['searchFields'])) {
373  $fieldListArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'], true);
374  } else {
375  $fieldListArray = [];
376  }
377  // Add special fields
378  if ($GLOBALS['BE_USER']->isAdmin()) {
379  $fieldListArray[] = 'uid';
380  $fieldListArray[] = 'pid';
381  }
382  return $fieldListArray;
383  }
384 
391  public function getQueryString($tableName = '')
392  {
393  return $GLOBALS['TYPO3_DB']->quoteStr($this->queryString, $tableName);
394  }
395 
402  public function setLimitCount($limitCount)
403  {
405  if ($limit > 0) {
406  $this->limitCount = $limit;
407  }
408  }
409 
416  public function setStartCount($startCount)
417  {
419  }
420 
428  public function setQueryString($queryString)
429  {
430  $this->queryString = GeneralUtility::removeXSS($queryString);
431  }
432 
441  protected function getAvailablePageIds($id, $depth)
442  {
443  $tree = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\PageTreeView::class);
444  $tree->init('AND ' . $this->userPermissions);
445  $tree->makeHTML = 0;
446  $tree->fieldArray = ['uid', 'php_tree_stop'];
447  if ($depth) {
448  $tree->getTree($id, $depth, '');
449  }
450  $tree->ids[] = $id;
451  // add workspace pid - workspace permissions are taken into account by where clause later
452  $tree->ids[] = -1;
453  $idList = implode(',', $tree->ids);
454  return $idList;
455  }
456 }
static getWorkspaceWhereClause($table, $workspaceId=null)
static readPageAccess($id, $perms_clause)
makeQuerySearchByTable($tableName, array $fieldsToSearchWithin)
Definition: LiveSearch.php:285
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
getRecordArray($tableName, $where, $orderBy, $limit)
Definition: LiveSearch.php:192
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
static fixed_lgd_cs($string, $chars, $appendString='...')
static convertToPositiveInteger($theInt)
Definition: MathUtility.php:55
static getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static deleteClause($table, $tableAlias='')
findByTable($tableName, $pageIdList, $limit)
Definition: LiveSearch.php:169