TYPO3 CMS  TYPO3_6-2
LiveSearch.php
Go to the documentation of this file.
1 <?php
3 
21 
28 class LiveSearch {
29 
33  const PAGE_JUMP_TABLE = 'pages';
49  private $queryString = '';
50 
54  private $startCount = 0;
55 
59  private $limitCount = 5;
60 
64  protected $userPermissions = '';
65 
69  protected $queryParser = NULL;
70 
74  public function __construct() {
75  $this->userPermissions = $GLOBALS['BE_USER']->getPagePermsClause(1);
76  $this->queryParser = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Search\\LiveSearch\\QueryParser');
77  }
78 
85  public function findPage($searchQuery) {
86  $link = '';
87  $pageId = $this->queryParser->getId($searchQuery);
88  $pageRecord = $this->findPageById($pageId);
89  if (!empty($pageRecord)) {
90  $link = $this->getEditLink(self::PAGE_JUMP_TABLE, $this->findPageById($pageId));
91  }
92  return $link;
93  }
94 
101  public function find($searchQuery) {
102  $recordArray = array();
103  $pageList = array();
104  $mounts = $GLOBALS['BE_USER']->returnWebmounts();
105  foreach ($mounts as $pageId) {
106  $pageList[] = $this->getAvailablePageIds($pageId, self::RECURSIVE_PAGE_LEVEL);
107  }
108  $pageIdList = implode(',', array_unique(explode(',', implode(',', $pageList))));
109  unset($pageList);
110  $limit = $this->startCount . ',' . $this->limitCount;
111  if ($this->queryParser->isValidCommand($searchQuery)) {
112  $this->setQueryString($this->queryParser->getSearchQueryValue($searchQuery));
113  $tableName = $this->queryParser->getTableNameFromCommand($searchQuery);
114  if ($tableName) {
115  $recordArray[] = $this->findByTable($tableName, $pageIdList, $limit);
116  }
117  } else {
118  $this->setQueryString($searchQuery);
119  $recordArray = $this->findByGlobalTableList($pageIdList);
120  }
121  return $recordArray;
122  }
123 
130  protected function findPageById($id) {
131  $pageRecord = array();
132  $row = BackendUtility::getRecord(self::PAGE_JUMP_TABLE, $id);
133  if (is_array($row)) {
134  $pageRecord = $row;
135  }
136  return $pageRecord;
137  }
138 
145  protected function findByGlobalTableList($pageIdList) {
146  $limit = $this->limitCount;
147  $getRecordArray = array();
148  foreach ($GLOBALS['TCA'] as $tableName => $value) {
149  $recordArray = $this->findByTable($tableName, $pageIdList, '0,' . $limit);
150  $recordCount = count($recordArray);
151  if ($recordCount) {
152  $limit = $limit - $recordCount;
153  $getRecordArray[] = $recordArray;
154  if ($limit <= 0) {
155  break;
156  }
157  }
158  }
159  return $getRecordArray;
160  }
161 
174  protected function findByTable($tableName, $pageIdList, $limit) {
175  $fieldsToSearchWithin = $this->extractSearchableFieldsFromTable($tableName);
176  $getRecordArray = array();
177  if (count($fieldsToSearchWithin) > 0) {
178  $pageBasedPermission = $tableName == 'pages' && $this->userPermissions ? $this->userPermissions : '1=1 ';
179  $where = 'pid IN (' . $pageIdList . ') AND ' . $pageBasedPermission . $this->makeQuerySearchByTable($tableName, $fieldsToSearchWithin);
180  $getRecordArray = $this->getRecordArray($tableName, $where, $this->makeOrderByTable($tableName), $limit);
181  }
182  return $getRecordArray;
183  }
184 
197  protected function getRecordArray($tableName, $where, $orderBy, $limit) {
198  $collect = array();
199  $isFirst = TRUE;
200  $queryParts = array(
201  'SELECT' => '*',
202  'FROM' => $tableName,
203  'WHERE' => $where,
204  'ORDERBY' => $orderBy,
205  'LIMIT' => $limit
206  );
207  $result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);
208  $dbCount = $GLOBALS['TYPO3_DB']->sql_num_rows($result);
209  while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
210  $collect[] = array(
211  'id' => $tableName . ':' . $row['uid'],
212  'pageId' => $tableName === 'pages' ? $row['uid'] : $row['pid'],
213  'recordTitle' => $isFirst ? $this->getRecordTitlePrep($this->getTitleOfCurrentRecordType($tableName), self::GROUP_TITLE_MAX_LENGTH) : '',
214  'iconHTML' => IconUtility::getSpriteIconForRecord($tableName, $row, array('title' => 'id=' . $row['uid'] . ', pid=' . $row['pid'])),
215  'title' => $this->getRecordTitlePrep(BackendUtility::getRecordTitle($tableName, $row), self::RECORD_TITLE_MAX_LENGTH),
216  'editLink' => $this->getEditLink($tableName, $row)
217  );
218  $isFirst = FALSE;
219  }
220  $GLOBALS['TYPO3_DB']->sql_free_result($result);
221  return $collect;
222  }
223 
232  protected function getEditLink($tableName, $row) {
233  $pageInfo = BackendUtility::readPageAccess($row['pid'], $this->userPermissions);
234  $calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo);
235  $editLink = '';
236  if ($tableName == 'pages') {
237  $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $row['uid']));
238  $permsEdit = $localCalcPerms & 2;
239  } else {
240  $permsEdit = $calcPerms & 16;
241  }
242  // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
243  // @todo Is there an existing function to generate this link?
244  if ($permsEdit) {
245  $returnUrl = BackendUtility::getModuleUrl('web_list', array('id' => $row['pid']));
246  $editLink = 'alt_doc.php?' . '&edit[' . $tableName . '][' . $row['uid'] . ']=edit&returnUrl=' . rawurlencode($returnUrl);
247  }
248  return $editLink;
249  }
250 
257  protected function getTitleOfCurrentRecordType($tableName) {
258  return $GLOBALS['LANG']->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
259  }
260 
270  public function getRecordTitlePrep($title, $titleLength = 0) {
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  $queryPart = '';
287  $whereParts = array();
288  // If the search string is a simple integer, assemble an equality comparison
289  if (MathUtility::canBeInterpretedAsInteger($this->queryString)) {
290  foreach ($fieldsToSearchWithin as $fieldName) {
291  if ($fieldName == 'uid' || $fieldName == 'pid' || isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) {
292  $fieldConfig = &$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
293  // Assemble the search condition only if the field is an integer, or is uid or pid
294  if ($fieldName == 'uid' || $fieldName == 'pid' || $fieldConfig['type'] == 'input' && $fieldConfig['eval'] && GeneralUtility::inList($fieldConfig['eval'], 'int')) {
295  $whereParts[] = $fieldName . '=' . $this->queryString;
296  } elseif (
297  $fieldConfig['type'] == 'text' ||
298  $fieldConfig['type'] == 'flex' ||
299  ($fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] ||
300  !preg_match('/date|time|int/', $fieldConfig['eval'])))) {
301  // Otherwise and if the field makes sense to be searched, assemble a like condition
302  $whereParts[] = $fieldName . ' LIKE \'%' . $this->queryString . '%\'';
303  }
304  }
305  }
306  } else {
307  $like = '\'%' . $GLOBALS['TYPO3_DB']->escapeStrForLike($GLOBALS['TYPO3_DB']->quoteStr($this->queryString, $tableName), $tableName) . '%\'';
308  foreach ($fieldsToSearchWithin as $fieldName) {
309  if (isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) {
310  $fieldConfig = &$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
311  // Check whether search should be case-sensitive or not
312  $format = 'LOWER(%s) LIKE LOWER(%s)';
313  if (is_array($fieldConfig['search'])) {
314  if (in_array('case', $fieldConfig['search'])) {
315  $format = '%s LIKE %s';
316  }
317  // Apply additional condition, if any
318  if ($fieldConfig['search']['andWhere']) {
319  $format = '((' . $fieldConfig['search']['andWhere'] . ') AND (' . $format . '))';
320  }
321  }
322  // Assemble the search condition only if the field makes sense to be searched
323  if ($fieldConfig['type'] == 'text' || $fieldConfig['type'] == 'flex' || $fieldConfig['type'] == 'input' && (!$fieldConfig['eval'] || !preg_match('/date|time|int/', $fieldConfig['eval']))) {
324  $whereParts[] = sprintf($format, $fieldName, $like);
325  }
326  }
327  }
328  }
329  // If at least one condition was defined, create the search query
330  if (count($whereParts) > 0) {
331  $queryPart = ' AND (' . implode(' OR ', $whereParts) . ')';
332  // And the relevant conditions for deleted and versioned records
333  $queryPart .= BackendUtility::deleteClause($tableName);
334  $queryPart .= BackendUtility::versioningPlaceholderClause($tableName);
335  $queryPart .= BackendUtility::getWorkspaceWhereClause($tableName);
336  } else {
337  $queryPart = ' AND 0 = 1';
338  }
339  return $queryPart;
340  }
341 
348  protected function makeOrderByTable($tableName) {
349  $orderBy = '';
350  if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('sortby', $GLOBALS['TCA'][$tableName]['ctrl'])) {
351  $sortBy = trim($GLOBALS['TCA'][$tableName]['ctrl']['sortby']);
352  if (!empty($sortBy)) {
353  $orderBy = 'ORDER BY ' . $sortBy;
354  }
355  } else {
356  $orderBy = $GLOBALS['TCA'][$tableName]['ctrl']['default_sortby'];
357  }
358  return $GLOBALS['TYPO3_DB']->stripOrderBy($orderBy);
359  }
360 
367  protected function extractSearchableFieldsFromTable($tableName) {
368  // Get the list of fields to search in from the TCA, if any
369  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['searchFields'])) {
370  $fieldListArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$tableName]['ctrl']['searchFields'], TRUE);
371  } else {
372  $fieldListArray = array();
373  }
374  // Add special fields
375  if ($GLOBALS['BE_USER']->isAdmin()) {
376  $fieldListArray[] = 'uid';
377  $fieldListArray[] = 'pid';
378  }
379  return $fieldListArray;
380  }
381 
388  public function getQueryString($tableName = '') {
389  return $GLOBALS['TYPO3_DB']->quoteStr($this->queryString, $tableName);
390  }
391 
398  public function setLimitCount($limitCount) {
400  if ($limit > 0) {
401  $this->limitCount = $limit;
402  }
403  }
404 
411  public function setStartCount($startCount) {
413  }
414 
422  public function setQueryString($queryString) {
423  $this->queryString = GeneralUtility::removeXSS($queryString);
424  }
425 
434  protected function getAvailablePageIds($id, $depth) {
435  $idList = '';
436  $tree = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Tree\\View\\PageTreeView');
437  $tree->init('AND ' . $this->userPermissions);
438  $tree->makeHTML = 0;
439  $tree->fieldArray = array('uid', 'php_tree_stop');
440  if ($depth) {
441  $tree->getTree($id, $depth, '');
442  }
443  $tree->ids[] = $id;
444  // add workspace pid - workspace permissions are taken into account by where clause later
445  $tree->ids[] = -1;
446  $idList = implode(',', $tree->ids);
447  return $idList;
448  }
449 
450 }
static readPageAccess($id, $perms_clause)
static getWorkspaceWhereClause($table, $workspaceId=NULL)
makeQuerySearchByTable($tableName, array $fieldsToSearchWithin)
Definition: LiveSearch.php:285
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
static getRecordTitle($table, $row, $prep=FALSE, $forceResult=TRUE)
static getSpriteIconForRecord($table, array $row, array $options=array())
static getModuleUrl($moduleName, $urlParameters=array(), $backPathOverride=FALSE, $returnAbsoluteUrl=FALSE)
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
getRecordArray($tableName, $where, $orderBy, $limit)
Definition: LiveSearch.php:197
static fixed_lgd_cs($string, $chars, $appendString='...')
static convertToPositiveInteger($theInt)
Definition: MathUtility.php:55
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static deleteClause($table, $tableAlias='')
findByTable($tableName, $pageIdList, $limit)
Definition: LiveSearch.php:174