2 /***************************************************************
5 * (c) 2007-2009 Andreas Wolf <andreas.wolf@ikt-werk.de>
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
29 * Default implementation of a handler class for an ajax record selector.
31 * Normally other implementations should be inherited from this one.
32 * queryTable() should not be overwritten under normal circumstances.
34 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
35 * @author Benjamin Mack <benni@typo3.org>
38 class t3lib_TCEforms_Suggest_DefaultReceiver
{
40 * The name of the table to query
44 protected $table = '';
47 * The name of the foreign table to query (records from this table will be used for displaying instead of the ones
52 protected $mmForeignTable = '';
55 * Counter to limit the recursions when querying the table; also needed to choose the range of records to select
59 protected $recursionCounter = 0;
62 * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
67 protected $selectClause = '';
70 * The statement by which records will be ordered
74 protected $orderByStatement = '';
77 * Additional WHERE clause to be appended to the SQL
81 protected $addWhere = '';
84 * Configuration for this selector from TSconfig
88 protected $config = array();
91 * The list of pages that are allowed to perform the search for records on
95 protected $allowedPages = array();
98 * The maximum number of items to select.
102 protected $maxItems = 10;
106 * The constructor of this class
108 * @param string $table
109 * @param array $config The configuration (TCA overlayed with TSconfig) to use for this selector
112 public function __construct($table, $config) {
113 $this->table
= $table;
114 $this->config
= $config;
116 // get a list of all the pages that should be looked on
117 if (isset($config['pidList'])) {
118 $allowedPages = $pageIds = t3lib_div
::trimExplode(',', $config['pidList']);
119 $depth = intval($config['pidDepth']);
120 foreach ($pageIds as $pageId) {
122 $allowedPages = t3lib_div
::array_merge_recursive_overrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
125 $this->allowedPages
= array_unique($allowedPages);
128 if (isset($config['maxItemsInResultList'])) {
129 $this->maxItems
= $config['maxItemsInResultList'];
132 if ($this->table
== 'pages') {
133 $this->addWhere
= ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1);
136 // if table is versionized, only get the records from the Live Workspace
137 // the overlay itself of WS-records is done below
138 if ($GLOBALS['TCA'][$this->table
]['ctrl']['versioningWS'] == true
) {
139 $this->addWhere
.= ' AND t3ver_wsid = 0';
144 * Queries a table for records and completely processes them
146 * Returns a two-dimensional array of almost finished records; the only need to be put into a <li>-structure
148 * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not
149 * this function itself
151 * @param array $params
152 * @param object $ref the parent object
153 * @return mixed array of rows or false if nothing found
155 public function queryTable(&$params, $recursionCounter = 0) {
158 $this->params
= &$params;
159 $this->start
= $this->recursionCounter
* 50;
161 $this->prepareSelectStatement();
162 $this->prepareOrderByStatement();
163 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
168 $this->orderByStatement
,
169 $this->start
. ', 50');
172 $allRowsCount = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
175 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
177 // check if we already have collected the maximum number of records
178 if (count($rows) > $this->maxItems
) break;
180 $this->manipulateRecord($row);
181 $this->makeWorkspaceOverlay($row);
183 // check if the user has access to the record
184 if (!$this->checkRecordAccess($row, $row['uid'])) {
188 $iconPath = $this->getIcon($row);
189 $uid = ($row['t3ver_oid'] > 0 ?
$row['t3ver_oid'] : $row['uid']);
191 $croppedPath = $path = $this->getRecordPath($row, $uid);
192 if (strlen($croppedPath) > 30) {
193 $croppedPath = $GLOBALS['LANG']->csConvObj
->crop($GLOBALS['LANG']->charSet
, $path, 10) .
194 '...' . $GLOBALS['LANG']->csConvObj
->crop($GLOBALS['LANG']->charSet
, $path, -20);
197 $label = $this->getLabel($row);
199 'text' => '<span class="suggest-label">' . $label . '</span><span class="suggest-uid">[' . $uid . ']</span><br />
200 <span class="suggest-path">' . $croppedPath . '</span>',
201 'table' => ($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
),
206 'style' => 'background-image:url(' . $iconPath . ');',
207 'class' => (isset($this->config
['cssClass']) ?
$this->config
['cssClass'] : ''),
210 $rows[$this->table
. '_' . $uid] = $this->renderRecord($row, $entry);
213 $GLOBALS['TYPO3_DB']->sql_free_result($res);
215 // if there are less records than we need, call this function again to get more records
216 if (count($rows) < $this->maxItems
&&
217 $allRowsCount >= 50 && $recursionCounter < $this->maxItems
) {
218 $tmp = self
::queryTable($params, ++
$recursionCounter);
219 $rows = array_merge($tmp, $rows);
228 * Prepare the statement for selecting the records which will be returned to the selector. May also return some
229 * other records (e.g. from a mm-table) which will be used later on to select the real records
233 protected function prepareSelectStatement() {
234 $searchWholePhrase = $this->config
['searchWholePhrase'];
236 $searchString = $this->params
['value'];
237 $searchUid = intval($searchString);
238 if (strlen($searchString)) {
239 $likeCondition = ' LIKE \'' . ($searchWholePhrase ?
'%' : '') .
240 $GLOBALS['TYPO3_DB']->escapeStrForLike($searchString, $this->table
).'%\'';
242 $selectFields = array();
243 if (isset($GLOBALS['TCA'][$this->table
]['ctrl']['label_alt'])) {
244 // Search in all fields given by label or label_alt
245 $selectFields = t3lib_div
::trimExplode(',', $GLOBALS['TCA'][$this->table
]['ctrl']['label_alt']);
247 $selectFields[] = $GLOBALS['TCA'][$this->table
]['ctrl']['label'];
249 $selectParts = array();
250 foreach ($selectFields as $field) {
251 $selectParts[] = $field . $likeCondition;
253 $this->selectClause
= implode(' OR ', $selectParts);
255 if ($searchUid > 0 && $searchUid == $searchString) {
256 $this->selectClause
= '(' . $this->selectClause
. ' OR uid = ' . $searchUid . ')';
259 if (isset($GLOBALS['TCA'][$this->table
]['ctrl']['delete'])) {
260 $this->selectClause
.= ' AND ' . $GLOBALS['TCA'][$this->table
]['ctrl']['delete'] . ' = 0';
263 if (count($this->allowedPages
)) {
264 $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($this->allowedPages
);
265 if (count($pidList)) {
266 $this->selectClause
.= ' AND pid IN (' . implode(', ', $pidList) . ') ';
270 // add an additional search condition comment
271 if (isset($this->config
['searchCondition']) && strlen($this->config
['searchCondition']) > 0) {
272 $this->selectClause
.= ' AND ' . $this->config
['searchCondition'];
275 // add the global clauses to the where-statement
276 $this->selectClause
.= $this->addWhere
;
280 * Selects all subpages of one page, optionally only upto a certain level
282 * @param integer $uid
283 * @param integer $depth
284 * @return array of page IDs
286 protected function getAllSubpagesOfPage($uid, $depth = 99) {
287 $pageIds = array($uid);
290 $pages = array($uid);
293 while (($depth - $level) > 0 && !empty($pageIds)) {
296 $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($pageIds);
297 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'pid IN (' . implode(', ', $pidList) . ')', '', '', '', 'uid');
298 if (count($rows) > 0) {
299 $pageIds = array_keys($rows);
301 $pages = array_merge($pages, $pageIds);
311 * Prepares the clause by which the result elements are sorted. See description of ORDER BY in
312 * SQL standard for reference.
316 protected function prepareOrderByStatement() {
317 if ($GLOBALS['TCA'][$this->table
]['ctrl']['label']) {
318 $this->orderByStatement
= $GLOBALS['TCA'][$this->table
]['ctrl']['label'];
323 * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
327 protected function manipulateRecord(&$row) {
331 * Selects whether the logged in Backend User is allowed to read a specific record
333 protected function checkRecordAccess($row, $uid) {
335 $table = ($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
);
336 if ($table == 'pages') {
337 if (!t3lib_BEfunc
::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(1))) {
341 if (!is_array(t3lib_BEfunc
::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1)))) {
349 * Overlay the given record with its workspace-version, if any
351 * @param array the record to get the workspace version for
352 * @return void (passed by reference)
354 protected function makeWorkspaceOverlay(&$row) {
355 // check for workspace-versions
356 if ($GLOBALS['BE_USER']->workspace
!= 0 && $GLOBALS['TCA'][$this->table
]['ctrl']['versioningWS'] == true
) {
357 t3lib_BEfunc
::workspaceOL(($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
), $row);
362 * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
364 * @param array $row The record to get the icon for
365 * @return string The path to the icon
367 protected function getIcon($row) {
368 $icon = t3lib_iconWorks
::getIcon(($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
), $row);
369 return t3lib_iconWorks
::skinImg('', $icon, '', 1);
373 * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut
374 * off, because it contains the pagetitle itself, which would be double information
376 * The path is returned uncut, cutting has to be done by calling function.
378 * @param array $row The row
379 * @param integer $uid The records uid
380 * @return string The record-path
382 protected function getRecordPath(&$row, $uid) {
383 $titleLimit = max($this->config
['maxPathTitleLength'], 0);
385 if (($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
) == 'pages') {
386 $path = t3lib_BEfunc
::getRecordPath($uid, '', $titleLimit);
387 // for pages we only want the first (n-1) parts of the path, because the n-th part is the page itself
388 $path = substr($path, 0, strrpos($path, '/', -2)) . '/';
390 $path = t3lib_BEfunc
::getRecordPath($row['pid'], '', $titleLimit);
397 * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
400 * @return The label for the record
402 protected function getLabel($row) {
403 return t3lib_BEfunc
::getRecordTitle(($this->mmForeignTable ?
$this->mmForeignTable
: $this->table
), $row, true
);
407 * Calls a user function for rendering the page.
409 * This user function should manipulate $entry, especially $entry['text']
411 * @param array $row The row
412 * @param array $entry The entry to render
413 * @return array The rendered entry (will be put into a <li> later on
415 protected function renderRecord($row, $entry) {
416 // call renderlet if available (normal pages etc. usually don't have one)
417 if ($this->config
['renderFunc'] != '') {
419 'table' => $this->table
,
420 'uid' => $row['uid'],
424 t3lib_div
::callUserFunction($this->config
['renderFunc'], $params, $this, '');