cfb844fd7ee11d6f6cb579b927286e94dceace6f
[Packages/TYPO3.CMS.git] / t3lib / tceforms / class.t3lib_tceforms_suggest_defaultreceiver.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2007-2009 Andreas Wolf <andreas.wolf@ikt-werk.de>
6 * All rights reserved
7 *
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.
13 *
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.
18 *
19 *
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.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Default implementation of a handler class for an ajax record selector.
30 *
31 * Normally other implementations should be inherited from this one.
32 * queryTable() should not be overwritten under normal circumstances.
33 *
34 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
35 * @author Benjamin Mack <benni@typo3.org>
36 *
37 */
38 class t3lib_TCEforms_Suggest_DefaultReceiver {
39 /**
40 * The name of the table to query
41 *
42 * @var string
43 */
44 protected $table = '';
45
46 /**
47 * The name of the foreign table to query (records from this table will be used for displaying instead of the ones
48 * from $table)
49 *
50 * @var string
51 */
52 protected $mmForeignTable = '';
53
54 /**
55 * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
56 * be a global var)
57 *
58 * @var string
59 */
60 protected $selectClause = '';
61
62 /**
63 * The statement by which records will be ordered
64 *
65 * @var string
66 */
67 protected $orderByStatement = '';
68
69 /**
70 * Additional WHERE clause to be appended to the SQL
71 *
72 * @var string
73 */
74 protected $addWhere = '';
75
76 /**
77 * Configuration for this selector from TSconfig
78 *
79 * @var array
80 */
81 protected $config = array();
82
83 /**
84 * The list of pages that are allowed to perform the search for records on
85 *
86 * @var array of PIDs
87 */
88 protected $allowedPages = array();
89
90 /**
91 * The maximum number of items to select.
92 *
93 * @var integer
94 */
95 protected $maxItems = 10;
96
97
98 /**
99 * The constructor of this class
100 *
101 * @param string $table
102 * @param array $config The configuration (TCA overlayed with TSconfig) to use for this selector
103 * @return void
104 */
105 public function __construct($table, $config) {
106 $this->table = $table;
107 $this->config = $config;
108
109 // get a list of all the pages that should be looked on
110 if (isset($config['pidList'])) {
111 $allowedPages = $pageIds = t3lib_div::trimExplode(',', $config['pidList']);
112 $depth = intval($config['pidDepth']);
113 foreach ($pageIds as $pageId) {
114 if ($pageId > 0) {
115 $allowedPages = t3lib_div::array_merge_recursive_overrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
116 }
117 }
118 $this->allowedPages = array_unique($allowedPages);
119 }
120
121 if (isset($config['maxItemsInResultList'])) {
122 $this->maxItems = $config['maxItemsInResultList'];
123 }
124
125 if ($this->table == 'pages') {
126 $this->addWhere = ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1);
127 }
128
129 // if table is versionized, only get the records from the Live Workspace
130 // the overlay itself of WS-records is done below
131 if ($GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] == true) {
132 $this->addWhere .= ' AND t3ver_wsid = 0';
133 }
134 }
135
136 /**
137 * Queries a table for records and completely processes them
138 *
139 * Returns a two-dimensional array of almost finished records; the only need to be put into a <li>-structure
140 *
141 * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not
142 * this function itself
143 *
144 * @param array $params
145 * @param object $ref the parent object
146 * @return mixed array of rows or false if nothing found
147 */
148 public function queryTable(&$params, $recursionCounter = 0) {
149 $rows = array();
150
151 $this->params = &$params;
152 $start = $recursionCounter * 50;
153
154 $this->prepareSelectStatement();
155 $this->prepareOrderByStatement();
156 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
157 '*',
158 $this->table,
159 $this->selectClause,
160 '',
161 $this->orderByStatement,
162 $start . ', 50');
163
164
165 $allRowsCount = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
166
167 if ($allRowsCount) {
168 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
169
170 // check if we already have collected the maximum number of records
171 if (count($rows) > $this->maxItems) break;
172
173 $this->manipulateRecord($row);
174 $this->makeWorkspaceOverlay($row);
175
176 // check if the user has access to the record
177 if (!$this->checkRecordAccess($row, $row['uid'])) {
178 continue;
179 }
180
181 $iconPath = $this->getIcon($row);
182 $uid = ($row['t3ver_oid'] > 0 ? $row['t3ver_oid'] : $row['uid']);
183
184 $path = $this->getRecordPath($row, $uid);
185 if (strlen($path) > 30) {
186 $croppedPath = '<abbr title="' . htmlspecialchars($path) . '">' .
187 htmlspecialchars($GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $path, 10) .
188 '...' . $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $path, -20)
189 ) . '</abbr>';
190 } else {
191 $croppedPath = htmlspecialchars($path);
192 }
193
194 $label = $this->getLabel($row);
195 $entry = array(
196 'text' => '<span class="suggest-label">' . $label . '</span><span class="suggest-uid">[' . $uid . ']</span><br />
197 <span class="suggest-path">' . $croppedPath . '</span>',
198 'table' => ($this->mmForeignTable ? $this->mmForeignTable : $this->table),
199 'label' => $label,
200 'path' => $path,
201 'uid' => $uid,
202 'icon' => $iconPath,
203 'style' => 'background-image:url(' . $iconPath . ');',
204 'class' => (isset($this->config['cssClass']) ? $this->config['cssClass'] : ''),
205 );
206
207 $rows[$this->table . '_' . $uid] = $this->renderRecord($row, $entry);
208 }
209
210 $GLOBALS['TYPO3_DB']->sql_free_result($res);
211
212 // if there are less records than we need, call this function again to get more records
213 if (count($rows) < $this->maxItems &&
214 $allRowsCount >= 50 && $recursionCounter < $this->maxItems) {
215 $tmp = self::queryTable($params, ++$recursionCounter);
216 $rows = array_merge($tmp, $rows);
217 }
218 return $rows;
219 } else {
220 return false;
221 }
222 }
223
224 /**
225 * Prepare the statement for selecting the records which will be returned to the selector. May also return some
226 * other records (e.g. from a mm-table) which will be used later on to select the real records
227 *
228 * @return void
229 */
230 protected function prepareSelectStatement() {
231 $searchWholePhrase = $this->config['searchWholePhrase'];
232
233 $searchString = $this->params['value'];
234 $searchUid = intval($searchString);
235 if (strlen($searchString)) {
236 $likeCondition = ' LIKE \'' . ($searchWholePhrase ? '%' : '') .
237 $GLOBALS['TYPO3_DB']->escapeStrForLike($searchString, $this->table).'%\'';
238
239 // Search in all fields given by label or label_alt
240 $selectFieldsList = $GLOBALS['TCA'][$this->table]['ctrl']['label'] . ',' . $GLOBALS['TCA'][$this->table]['ctrl']['label_alt'];
241 $selectFields = t3lib_div::trimExplode(',', $selectFieldsList, TRUE);
242
243 $selectParts = array();
244 foreach ($selectFields as $field) {
245 $selectParts[] = $field . $likeCondition;
246 }
247 $this->selectClause = '(' . implode(' OR ', $selectParts) . ')';
248
249 if ($searchUid > 0 && $searchUid == $searchString) {
250 $this->selectClause = '(' . $this->selectClause . ' OR uid = ' . $searchUid . ')';
251 }
252 }
253 if (isset($GLOBALS['TCA'][$this->table]['ctrl']['delete'])) {
254 $this->selectClause .= ' AND ' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] . ' = 0';
255 }
256
257 if (count($this->allowedPages)) {
258 $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($this->allowedPages);
259 if (count($pidList)) {
260 $this->selectClause .= ' AND pid IN (' . implode(', ', $pidList) . ') ';
261 }
262 }
263
264 // add an additional search condition comment
265 if (isset($this->config['searchCondition']) && strlen($this->config['searchCondition']) > 0) {
266 $this->selectClause .= ' AND ' . $this->config['searchCondition'];
267 }
268
269 // add the global clauses to the where-statement
270 $this->selectClause .= $this->addWhere;
271 }
272
273 /**
274 * Selects all subpages of one page, optionally only upto a certain level
275 *
276 * @param integer $uid
277 * @param integer $depth
278 * @return array of page IDs
279 */
280 protected function getAllSubpagesOfPage($uid, $depth = 99) {
281 $pageIds = array($uid);
282 $level = 0;
283
284 $pages = array($uid);
285
286 // fetch all
287 while (($depth - $level) > 0 && !empty($pageIds)) {
288 ++$level;
289
290 $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($pageIds);
291 $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'pid IN (' . implode(', ', $pidList) . ')', '', '', '', 'uid');
292 if (count($rows) > 0) {
293 $pageIds = array_keys($rows);
294
295 $pages = array_merge($pages, $pageIds);
296 } else {
297 break;
298 }
299 }
300
301 return $pages;
302 }
303
304 /**
305 * Prepares the clause by which the result elements are sorted. See description of ORDER BY in
306 * SQL standard for reference.
307 *
308 * @return void
309 */
310 protected function prepareOrderByStatement() {
311 if ($GLOBALS['TCA'][$this->table]['ctrl']['label']) {
312 $this->orderByStatement = $GLOBALS['TCA'][$this->table]['ctrl']['label'];
313 }
314 }
315
316 /**
317 * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
318 *
319 * @param array $row
320 */
321 protected function manipulateRecord(&$row) {
322 }
323
324 /**
325 * Selects whether the logged in Backend User is allowed to read a specific record
326 */
327 protected function checkRecordAccess($row, $uid) {
328 $retValue = true;
329 $table = ($this->mmForeignTable ? $this->mmForeignTable : $this->table);
330 if ($table == 'pages') {
331 if (!t3lib_BEfunc::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(1))) {
332 $retValue = false;
333 }
334 } else {
335 if (!is_array(t3lib_BEfunc::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1)))) {
336 $retValue = false;
337 }
338 }
339 return $retValue;
340 }
341
342 /**
343 * Overlay the given record with its workspace-version, if any
344 *
345 * @param array the record to get the workspace version for
346 * @return void (passed by reference)
347 */
348 protected function makeWorkspaceOverlay(&$row) {
349 // check for workspace-versions
350 if ($GLOBALS['BE_USER']->workspace != 0 && $GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] == true) {
351 t3lib_BEfunc::workspaceOL(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
352 }
353 }
354
355 /**
356 * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
357 *
358 * @param array $row The record to get the icon for
359 * @return string The path to the icon
360 */
361 protected function getIcon($row) {
362 $icon = t3lib_iconWorks::getIcon(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
363 return t3lib_iconWorks::skinImg('', $icon, '', 1);
364 }
365
366 /**
367 * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut
368 * off, because it contains the pagetitle itself, which would be double information
369 *
370 * The path is returned uncut, cutting has to be done by calling function.
371 *
372 * @param array $row The row
373 * @param integer $uid The records uid
374 * @return string The record-path
375 */
376 protected function getRecordPath(&$row, $uid) {
377 $titleLimit = max($this->config['maxPathTitleLength'], 0);
378
379 if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
380 $path = t3lib_BEfunc::getRecordPath($uid, '', $titleLimit);
381 // for pages we only want the first (n-1) parts of the path, because the n-th part is the page itself
382 $path = substr($path, 0, strrpos($path, '/', -2)) . '/';
383 } else {
384 $path = t3lib_BEfunc::getRecordPath($row['pid'], '', $titleLimit);
385 }
386
387 return $path;
388 }
389
390 /**
391 * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
392 *
393 * @param array $row
394 * @return The label for the record
395 */
396 protected function getLabel($row) {
397 return t3lib_BEfunc::getRecordTitle(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row, true);
398 }
399
400 /**
401 * Calls a user function for rendering the page.
402 *
403 * This user function should manipulate $entry, especially $entry['text']
404 *
405 * @param array $row The row
406 * @param array $entry The entry to render
407 * @return array The rendered entry (will be put into a <li> later on
408 */
409 protected function renderRecord($row, $entry) {
410 // call renderlet if available (normal pages etc. usually don't have one)
411 if ($this->config['renderFunc'] != '') {
412 $params = array(
413 'table' => $this->table,
414 'uid' => $row['uid'],
415 'row' => $row,
416 'entry' => &$entry
417 );
418 t3lib_div::callUserFunction($this->config['renderFunc'], $params, $this, '');
419 }
420
421 return $entry;
422 }
423 }
424
425 ?>