Cleanup: Set SVN properties
[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 * Counter to limit the recursions when querying the table; also needed to choose the range of records to select
56 *
57 * @var integer
58 */
59 protected $recursionCounter = 0;
60
61 /**
62 * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
63 * be a global var)
64 *
65 * @var string
66 */
67 protected $selectClause = '';
68
69 /**
70 * The statement by which records will be ordered
71 *
72 * @var string
73 */
74 protected $orderByStatement = '';
75
76 /**
77 * Additional WHERE clause to be appended to the SQL
78 *
79 * @var string
80 */
81 protected $addWhere = '';
82
83 /**
84 * Configuration for this selector from TSconfig
85 *
86 * @var array
87 */
88 protected $config = array();
89
90 /**
91 * The list of pages that are allowed to perform the search for records on
92 *
93 * @var array of PIDs
94 */
95 protected $allowedPages = array();
96
97 /**
98 * The maximum number of items to select.
99 *
100 * @var integer
101 */
102 protected $maxItems = 10;
103
104
105 /**
106 * The constructor of this class
107 *
108 * @param string $table
109 * @param array $config The configuration (TCA overlayed with TSconfig) to use for this selector
110 * @return void
111 */
112 public function __construct($table, $config) {
113 $this->table = $table;
114 $this->config = $config;
115
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) {
121 if ($pageId > 0) {
122 $allowedPages = t3lib_div::array_merge_recursive_overrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
123 }
124 }
125 $this->allowedPages = array_unique($allowedPages);
126 }
127
128 if (isset($config['maxItemsInResultList'])) {
129 $this->maxItems = $config['maxItemsInResultList'];
130 }
131
132 if ($this->table == 'pages') {
133 $this->addWhere = ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1);
134 }
135
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';
140 }
141 }
142
143 /**
144 * Queries a table for records and completely processes them
145 *
146 * Returns a two-dimensional array of almost finished records; the only need to be put into a <li>-structure
147 *
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
150 *
151 * @param array $params
152 * @param object $ref the parent object
153 * @return mixed array of rows or false if nothing found
154 */
155 public function queryTable(&$params, $recursionCounter = 0) {
156 $rows = array();
157
158 $this->params = &$params;
159 $this->start = $this->recursionCounter * 50;
160
161 $this->prepareSelectStatement();
162 $this->prepareOrderByStatement();
163 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
164 '*',
165 $this->table,
166 $this->selectClause,
167 '',
168 $this->orderByStatement,
169 $this->start . ', 50');
170
171
172 $allRowsCount = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
173
174 if ($allRowsCount) {
175 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
176
177 // check if we already have collected the maximum number of records
178 if (count($rows) > $this->maxItems) break;
179
180 $this->manipulateRecord($row);
181 $this->makeWorkspaceOverlay($row);
182
183 // check if the user has access to the record
184 if (!$this->checkRecordAccess($row, $row['uid'])) {
185 continue;
186 }
187
188 $iconPath = $this->getIcon($row);
189 $uid = ($row['t3ver_oid'] > 0 ? $row['t3ver_oid'] : $row['uid']);
190
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);
195 }
196
197 $label = $this->getLabel($row);
198 $entry = array(
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),
202 'label' => $label,
203 'path' => $path,
204 'uid' => $uid,
205 'icon' => $iconPath,
206 'style' => 'background-image:url(' . $iconPath . ');',
207 'class' => (isset($this->config['cssClass']) ? $this->config['cssClass'] : ''),
208 );
209
210 $rows[$this->table . '_' . $uid] = $this->renderRecord($row, $entry);
211 }
212
213 $GLOBALS['TYPO3_DB']->sql_free_result($res);
214
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);
220 }
221 return $rows;
222 } else {
223 return false;
224 }
225 }
226
227 /**
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
230 *
231 * @return void
232 */
233 protected function prepareSelectStatement() {
234 $searchWholePhrase = $this->config['searchWholePhrase'];
235
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).'%\'';
241
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']);
246 }
247 $selectFields[] = $GLOBALS['TCA'][$this->table]['ctrl']['label'];
248
249 $selectParts = array();
250 foreach ($selectFields as $field) {
251 $selectParts[] = $field . $likeCondition;
252 }
253 $this->selectClause = implode(' OR ', $selectParts);
254
255 if ($searchUid > 0 && $searchUid == $searchString) {
256 $this->selectClause = '(' . $this->selectClause . ' OR uid = ' . $searchUid . ')';
257 }
258 }
259 if (isset($GLOBALS['TCA'][$this->table]['ctrl']['delete'])) {
260 $this->selectClause .= ' AND ' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] . ' = 0';
261 }
262
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) . ') ';
267 }
268 }
269
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'];
273 }
274
275 // add the global clauses to the where-statement
276 $this->selectClause .= $this->addWhere;
277 }
278
279 /**
280 * Selects all subpages of one page, optionally only upto a certain level
281 *
282 * @param integer $uid
283 * @param integer $depth
284 * @return array of page IDs
285 */
286 protected function getAllSubpagesOfPage($uid, $depth = 99) {
287 $pageIds = array($uid);
288 $level = 0;
289
290 $pages = array($uid);
291
292 // fetch all
293 while (($depth - $level) > 0 && !empty($pageIds)) {
294 ++$level;
295
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);
300
301 $pages = array_merge($pages, $pageIds);
302 } else {
303 break;
304 }
305 }
306
307 return $pages;
308 }
309
310 /**
311 * Prepares the clause by which the result elements are sorted. See description of ORDER BY in
312 * SQL standard for reference.
313 *
314 * @return void
315 */
316 protected function prepareOrderByStatement() {
317 if ($GLOBALS['TCA'][$this->table]['ctrl']['label']) {
318 $this->orderByStatement = $GLOBALS['TCA'][$this->table]['ctrl']['label'];
319 }
320 }
321
322 /**
323 * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
324 *
325 * @param array $row
326 */
327 protected function manipulateRecord(&$row) {
328 }
329
330 /**
331 * Selects whether the logged in Backend User is allowed to read a specific record
332 */
333 protected function checkRecordAccess($row, $uid) {
334 $retValue = true;
335 $table = ($this->mmForeignTable ? $this->mmForeignTable : $this->table);
336 if ($table == 'pages') {
337 if (!t3lib_BEfunc::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(1))) {
338 $retValue = false;
339 }
340 } else {
341 if (!is_array(t3lib_BEfunc::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1)))) {
342 $retValue = false;
343 }
344 }
345 return $retValue;
346 }
347
348 /**
349 * Overlay the given record with its workspace-version, if any
350 *
351 * @param array the record to get the workspace version for
352 * @return void (passed by reference)
353 */
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);
358 }
359 }
360
361 /**
362 * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
363 *
364 * @param array $row The record to get the icon for
365 * @return string The path to the icon
366 */
367 protected function getIcon($row) {
368 $icon = t3lib_iconWorks::getIcon(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
369 return t3lib_iconWorks::skinImg('', $icon, '', 1);
370 }
371
372 /**
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
375 *
376 * The path is returned uncut, cutting has to be done by calling function.
377 *
378 * @param array $row The row
379 * @param integer $uid The records uid
380 * @return string The record-path
381 */
382 protected function getRecordPath(&$row, $uid) {
383 $titleLimit = max($this->config['maxPathTitleLength'], 0);
384
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)) . '/';
389 } else {
390 $path = t3lib_BEfunc::getRecordPath($row['pid'], '', $titleLimit);
391 }
392
393 return $path;
394 }
395
396 /**
397 * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
398 *
399 * @param array $row
400 * @return The label for the record
401 */
402 protected function getLabel($row) {
403 return t3lib_BEfunc::getRecordTitle(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row, true);
404 }
405
406 /**
407 * Calls a user function for rendering the page.
408 *
409 * This user function should manipulate $entry, especially $entry['text']
410 *
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
414 */
415 protected function renderRecord($row, $entry) {
416 // call renderlet if available (normal pages etc. usually don't have one)
417 if ($this->config['renderFunc'] != '') {
418 $params = array(
419 'table' => $this->table,
420 'uid' => $row['uid'],
421 'row' => $row,
422 'entry' => &$entry
423 );
424 t3lib_div::callUserFunction($this->config['renderFunc'], $params, $this, '');
425 }
426
427 return $entry;
428 }
429 }
430
431 ?>