2 /***************************************************************
5 * (c) 2009-2010 Michael Klapper <michael.klapper@aoemedia.de>
6 * (c) 2010 Jeff Segars <jeff@webempoweredchurch.org>
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
30 * Class for handling backend live search.
32 * @author Michael Klapper <michael.klapper@aoemedia.de>
33 * @author Jeff Segars <jeff@webempoweredchurch.org>
37 class t3lib_search_livesearch
{
42 const PAGE_JUMP_TABLE
= 'pages';
47 const RECURSIVE_PAGE_LEVEL
= 99;
52 const GROUP_TITLE_MAX_LENGTH
= 15;
57 const RECORD_TITLE_MAX_LENGTH
= 37;
62 private $queryString = '';
67 private $startCount = 0;
72 private $limitCount = 5;
77 protected $userPermissions = '';
80 * @var t3lib_search_livesearch_queryParser
82 protected $queryParser = NULL
;
85 * Initialize access settings.
89 public function __construct() {
90 $this->userPermissions
= $GLOBALS['BE_USER']->getPagePermsClause(1);
91 $this->queryParser
= t3lib_div
::makeInstance('t3lib_search_livesearch_queryParser');
95 * Find records from database based on the given $searchQuery.
97 * @param string $searchQuery
98 * @return string Edit link to an page record if exists. Otherwise an empty string will returned
100 public function findPage($searchQuery) {
102 $pageId = $this->queryParser
->getId($searchQuery);
103 $pageRecord = $this->findPageById($pageId);
105 if (!empty($pageRecord)) {
106 $link = $this->getEditLink(self
::PAGE_JUMP_TABLE
, $this->findPageById($pageId));
113 * Find records from database based on the given $searchQuery.
115 * @param string $searchQuery
116 * @return array Result list of database search.
118 public function find($searchQuery) {
119 $recordArray = array();
120 $pageIdList = $this->getAvailablePageIds(
121 implode(',', $GLOBALS['BE_USER']->returnWebmounts()),
122 self
::RECURSIVE_PAGE_LEVEL
124 $limit = $this->startCount
. ',' . $this->limitCount
;
126 if ($this->queryParser
->isValidCommand($searchQuery)) {
127 $this->setQueryString($this->queryParser
->getSearchQueryValue($searchQuery));
128 $tableName = $this->queryParser
->getTableNameFromCommand($searchQuery);
130 $recordArray[] = $this->findByTable($tableName, $pageIdList, $limit);
133 $this->setQueryString($searchQuery);
134 $recordArray = $this->findByGlobalTableList($pageIdList, $limit);
137 // @todo Need to make sure we don't return too many records. How do we handle this when querying across multiple tables?
138 $recordArray = array_slice($recordArray, 0, $this->limitCount
);
144 * Retrieve the page record from given $id.
149 protected function findPageById($id) {
150 $pageRecord = array();
151 $row = t3lib_BEfunc
::getRecord(self
::PAGE_JUMP_TABLE
, $id);
153 if (is_array($row)) {
161 * Find records from all registered TCA table & column values.
163 * @param string $pageIdList Comma seperated list of page IDs
164 * @param string $limit MySql Limit notation
165 * @return array Records found in the database matching the searchQuery
167 protected function findByGlobalTableList($pageIdList, $limit) {
168 $getRecordArray = array();
169 foreach ($GLOBALS['TCA'] as $tableName => $value) {
170 $getRecordArray[] = $this->findByTable($tableName, $pageIdList, $limit);
173 return $getRecordArray;
177 * Find records by given table name.
179 * @param string $tableName Database table name
180 * @param string $pageIdList Comma seperated list of page IDs
181 * @param string $limit MySql Limit notation
182 * @return array Records found in the database matching the searchQuery
184 * @see getRecordArray()
185 * @see makeOrderByTable()
186 * @see makeQuerySearchByTable()
187 * @see extractSearchableFieldsFromTable()
189 protected function findByTable($tableName, $pageIdList, $limit) {
190 $fieldsToSearchWithin = $this->extractSearchableFieldsFromTable($tableName);
192 $getRecordArray = array();
193 if (count($fieldsToSearchWithin) > 0) {
194 $pageBasedPermission = ($tableName == 'pages' && $this->userPermissions
) ?
$this->userPermissions
: '1=1 ';
195 $where = 'pid IN (' . $pageIdList . ') AND ' . $pageBasedPermission . $this->makeQuerySearchByTable($tableName, $fieldsToSearchWithin);
196 $orderBy = $this->makeOrderByTable($tableName);
197 $getRecordArray = $this->getRecordArray(
200 $this->makeOrderByTable($tableName),
205 return $getRecordArray;
209 * Process the Database operation to get the search result.
211 * @param string $tableName Database table name
212 * @param string $where
213 * @param string $orderBy
214 * @param string $limit MySql Limit notation
217 * @see t3lib_db::exec_SELECT_queryArray()
218 * @see t3lib_db::sql_num_rows()
219 * @see t3lib_db::sql_fetch_assoc()
220 * @see t3lib_iconWorks::getSpriteIconForRecord()
221 * @see getTitleFromCurrentRow()
224 protected function getRecordArray($tableName, $where, $orderBy, $limit) {
229 'FROM' => $tableName,
231 'ORDERBY' => $orderBy,
234 $result = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);
235 $dbCount = $GLOBALS['TYPO3_DB']->sql_num_rows($result);
237 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
239 'id' => $tableName . ':' . $row['uid'],
240 'recordTitle' => ($isFirst) ?
$this->getRecordTitlePrep($this->getTitleOfCurrentRecordType($tableName), self
::GROUP_TITLE_MAX_LENGTH
) : '',
241 'iconHTML' => t3lib_iconWorks
::getSpriteIconForRecord($tableName, $row),
242 'title' => $this->getRecordTitlePrep($this->getTitleFromCurrentRow($tableName, $row), self
::RECORD_TITLE_MAX_LENGTH
),
243 'editLink' => $this->getEditLink($tableName, $row),
252 * Build a backend edit link based on given record.
254 * @param string $tableName Record table name
255 * @param array $row Current record row from database.
256 * @return string Link to open an edit window for record.
258 * @see t3lib_BEfunc::readPageAccess()
260 protected function getEditLink($tableName, $row) {
261 $pageInfo = t3lib_BEfunc
::readPageAccess($row['pid'], $this->userPermissions
);
262 $calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo);
265 if ($tableName == 'pages') {
266 $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc
::getRecord('pages', $row['uid']));
267 $permsEdit = $localCalcPerms & 2;
269 $permsEdit = $calcPerms & 16;
272 // "Edit" link: ( Only if permissions to edit the page-record of the content of the parent page ($this->id)
273 // @todo Is there an existing function to generate this link?
275 $editLink = 'alt_doc.php?' . '&edit[' . $tableName . '][' . $row['uid'] . ']=edit';
282 * Retrieve the record name
284 * @param string $tableName Record table name
287 protected function getTitleOfCurrentRecordType($tableName) {
288 return $GLOBALS['LANG']->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
292 * Crops a title string to a limited lenght and if it really was cropped, wrap it in a <span title="...">|</span>,
293 * which offers a tooltip with the original title when moving mouse over it.
295 * @param string $title: The title string to be cropped
296 * @param integer $titleLength: Crop title after this length - if not set, BE_USER->uc['titleLen'] is used
297 * @return string The processed title string, wrapped in <span title="...">|</span> if cropped
299 public function getRecordTitlePrep($title, $titleLength = 0) {
300 // If $titleLength is not a valid positive integer, use BE_USER->uc['titleLen']:
301 if (!$titleLength ||
!t3lib_div
::testInt($titleLength) ||
$titleLength < 0) {
302 $titleLength = $GLOBALS['BE_USER']->uc
['titleLen'];
305 return htmlspecialchars(t3lib_div
::fixed_lgd_cs($title, $titleLength));
310 * Retrieve the column name which contains the title value
312 * @param string $tableName Record table name
313 * @param array $row Current record row from database.
316 * @todo Use the backend function to get the calculated label instead.
318 protected function getTitleFromCurrentRow($tableName, $row) {
319 $titleColumnName = $GLOBALS['TCA'][$tableName]['ctrl']['label'];
320 return $row[$titleColumnName];
324 * Build the MySql where clause by table.
326 * @param string $tableName Record table name
327 * @param array $fieldsToSearchWithin User right based visible fields where we can search within.
330 protected function makeQuerySearchByTable($tableName, $fieldsToSearchWithin) {
332 $queryLikeStatement = ' LIKE \'%' . $this->getQueryString($tableName) . '%\'';
333 $queryPart = ' AND (' . implode($queryLikeStatement . ' OR ', $fieldsToSearchWithin) . $queryLikeStatement . ')';
334 $queryPart .= t3lib_BEfunc
::deleteClause($tableName);
335 $queryPart .= t3lib_BEfunc
::versioningPlaceholderClause($tableName);
341 * Build the MySql ORDER BY statement.
344 * @param string $tableName Record table name
346 * @see t3lib_db::stripOrderBy()
348 protected function makeOrderByTable($tableName) {
351 if (is_array($GLOBALS['TCA'][$tableName]['ctrl']) && array_key_exists('sortby', $GLOBALS['TCA'][$tableName]['ctrl'])) {
352 $orderBy = 'ORDER BY ' . $GLOBALS['TCA'][$tableName]['ctrl']['sortby'];
354 $orderBy = $GLOBALS['TCA'][$tableName]['ctrl']['default_sortby'];
357 return $GLOBALS['TYPO3_DB']->stripOrderBy($orderBy);
361 * Get all fields from given table where we can search for.
363 * @param string $tableName
366 protected function extractSearchableFieldsFromTable($tableName) {
367 $fieldListArray = array();
369 // Traverse configured columns and add them to field array, if available for user.
370 foreach ((array) $GLOBALS['TCA'][$tableName]['columns'] as $fieldName => $fieldValue) {
373 (!$fieldValue['exclude'] ||
$GLOBALS['BE_USER']->check('non_exclude_fields', $tableName . ':' . $fieldName)) // does current user have access to the field
375 ($fieldValue['config']['type'] != 'passthrough') // field type is not searchable
377 (!preg_match('/date|time|int/', $fieldValue['config']['eval'])) // field can't be of type date, time, int
380 ($fieldValue['config']['type'] == 'text')
382 ($fieldValue['config']['type'] == 'input')
385 $fieldListArray[] = $fieldName;
389 // Add special fields:
390 if ($GLOBALS['BE_USER']->isAdmin()) {
391 $fieldListArray[] = 'uid';
392 $fieldListArray[] = 'pid';
395 return $fieldListArray;
399 * Safely retrieve the queryString.
401 * @param string $tableName
403 * @see t3lib_db::quoteStr()
405 public function getQueryString($tableName = '') {
406 return $GLOBALS['TYPO3_DB']->quoteStr($this->queryString
, $tableName);
410 * Setter for limit value.
412 * @param integer $limitCount
415 public function setLimitCount($limitCount) {
416 $limit = t3lib_div
::intval_positive($limitCount);
418 $this->limitCount
= $limit;
423 * Setter for start count value.
425 * @param integer $startCount
428 public function setStartCount($startCount) {
429 $this->startCount
= t3lib_div
::intval_positive($startCount);
433 * Setter for the search query string.
435 * @param string $queryString
437 * @see t3lib_div::removeXSS()
439 public function setQueryString($queryString) {
440 $this->queryString
= t3lib_div
::removeXSS($queryString);
444 * Creates an instance of t3lib_pageTree which will select a page tree to
445 * $depth and return the object. In that object we will find the ids of the tree.
447 * @param integer Page id.
448 * @param integer Depth to go down.
450 * @return string coma separated list of uids
452 protected function getAvailablePageIds($id, $depth) {
454 $tree = t3lib_div
::makeInstance('t3lib_pageTree');
455 $tree->init('AND ' . $this->userPermissions
);
457 $tree->fieldArray
= array('uid', 'php_tree_stop');
459 $tree->getTree($id, $depth, '');
462 $idList = implode(',', $tree->ids
);