TYPO3 API  SVNRelease
class.t3lib_tceforms_suggest_defaultreceiver.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2007-2011 Andreas Wolf <andreas.wolf@ikt-werk.de>
00006  *  All rights reserved
00007  *
00008  *  This script is part of the TYPO3 project. The TYPO3 project is
00009  *  free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  The GNU General Public License can be found at
00015  *  http://www.gnu.org/copyleft/gpl.html.
00016  *  A copy is found in the textfile GPL.txt and important notices to the license
00017  *  from the author is found in LICENSE.txt distributed with these scripts.
00018  *
00019  *
00020  *  This script is distributed in the hope that it will be useful,
00021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  *  GNU General Public License for more details.
00024  *
00025  *  This copyright notice MUST APPEAR in all copies of the script!
00026  ***************************************************************/
00027 
00028 /**
00029  * Default implementation of a handler class for an ajax record selector.
00030  *
00031  * Normally other implementations should be inherited from this one.
00032  * queryTable() should not be overwritten under normal circumstances.
00033  *
00034  * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
00035  * @author Benjamin Mack <benni@typo3.org>
00036  *
00037  */
00038 class t3lib_TCEforms_Suggest_DefaultReceiver {
00039     /**
00040      * The name of the table to query
00041      *
00042      * @var string
00043      */
00044     protected $table = '';
00045 
00046     /**
00047      * The name of the foreign table to query (records from this table will be used for displaying instead of the ones
00048      * from $table)
00049      *
00050      * @var string
00051      */
00052     protected $mmForeignTable = '';
00053 
00054     /**
00055      * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
00056      * be a global var)
00057      *
00058      * @var string
00059      */
00060     protected $selectClause = '';
00061 
00062     /**
00063      * The statement by which records will be ordered
00064      *
00065      * @var string
00066      */
00067     protected $orderByStatement = '';
00068 
00069     /**
00070      * Additional WHERE clause to be appended to the SQL
00071      *
00072      * @var string
00073      */
00074     protected $addWhere = '';
00075 
00076     /**
00077      * Configuration for this selector from TSconfig
00078      *
00079      * @var array
00080      */
00081     protected $config = array();
00082 
00083     /**
00084      * The list of pages that are allowed to perform the search for records on
00085      *
00086      * @var array of PIDs
00087      */
00088     protected $allowedPages = array();
00089 
00090     /**
00091      * The maximum number of items to select.
00092      *
00093      * @var integer
00094      */
00095     protected $maxItems = 10;
00096 
00097 
00098     /**
00099      * The constructor of this class
00100      *
00101      * @param string $table The table to query
00102      * @param array $config The configuration (TCA overlayed with TSconfig) to use for this selector
00103      * @return void
00104      */
00105     public function __construct($table, $config) {
00106         $this->table = $table;
00107         $this->config = $config;
00108 
00109             // get a list of all the pages that should be looked on
00110         if (isset($config['pidList'])) {
00111             $allowedPages = $pageIds = t3lib_div::trimExplode(',', $config['pidList']);
00112             $depth = intval($config['pidDepth']);
00113             foreach ($pageIds as $pageId) {
00114                 if ($pageId > 0) {
00115                     $allowedPages = t3lib_div::array_merge_recursive_overrule($allowedPages, $this->getAllSubpagesOfPage($pageId, $depth));
00116                 }
00117             }
00118             $this->allowedPages = array_unique($allowedPages);
00119         }
00120 
00121         if (isset($config['maxItemsInResultList'])) {
00122             $this->maxItems = $config['maxItemsInResultList'];
00123         }
00124 
00125         if ($this->table == 'pages') {
00126             $this->addWhere = ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1);
00127         }
00128 
00129             // if table is versionized, only get the records from the Live Workspace
00130             // the overlay itself of WS-records is done below
00131         if ($GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] == TRUE) {
00132             $this->addWhere .= ' AND t3ver_wsid = 0';
00133         }
00134 
00135         if (isset($config['addWhere'])) {
00136             $this->addWhere .= ' ' . $config['addWhere'];
00137         }
00138     }
00139 
00140     /**
00141      * Queries a table for records and completely processes them
00142      *
00143      * Returns a two-dimensional array of almost finished records; the only need to be put into a <li>-structure
00144      *
00145      * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not
00146      * this function itself
00147      *
00148      * @param array $params
00149      * @param object $ref The parent object
00150      * @return mixed array of rows or false if nothing found
00151      */
00152     public function queryTable(&$params, $recursionCounter = 0) {
00153         $rows = array();
00154 
00155         $this->params = &$params;
00156         $start = $recursionCounter * 50;
00157 
00158         $this->prepareSelectStatement();
00159         $this->prepareOrderByStatement();
00160         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00161             '*',
00162             $this->table,
00163             $this->selectClause,
00164             '',
00165             $this->orderByStatement,
00166             $start . ', 50');
00167 
00168         $allRowsCount = $GLOBALS['TYPO3_DB']->sql_num_rows($res);
00169 
00170         if ($allRowsCount) {
00171             while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
00172 
00173                     // check if we already have collected the maximum number of records
00174                 if (count($rows) > $this->maxItems) {
00175                     break;
00176                 }
00177 
00178                 $this->manipulateRecord($row);
00179                 $this->makeWorkspaceOverlay($row);
00180 
00181                     // check if the user has access to the record
00182                 if (!$this->checkRecordAccess($row, $row['uid'])) {
00183                     continue;
00184                 }
00185 
00186                 $iconPath = $this->getIcon($row);
00187                 $uid = ($row['t3ver_oid'] > 0 ? $row['t3ver_oid'] : $row['uid']);
00188 
00189                 $path = $this->getRecordPath($row, $uid);
00190                 if (strlen($path) > 30) {
00191                     $croppedPath = '<abbr title="' . htmlspecialchars($path) . '">' .
00192                                    htmlspecialchars($GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $path, 10) .
00193                                                     '...' . $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $path, -20)
00194                                    ) . '</abbr>';
00195                 } else {
00196                     $croppedPath = htmlspecialchars($path);
00197                 }
00198 
00199                 $label = $this->getLabel($row);
00200                 $entry = array(
00201                     'text' => '<span class="suggest-label">' . $label . '</span><span class="suggest-uid">[' . $uid . ']</span><br />
00202                                 <span class="suggest-path">' . $croppedPath . '</span>',
00203                     'table' => ($this->mmForeignTable ? $this->mmForeignTable : $this->table),
00204                     'label' => $label,
00205                     'path' => $path,
00206                     'uid' => $uid,
00207                     'icon' => $iconPath,
00208                     'style' => 'background-image:url(' . $iconPath . ');',
00209                     'class' => (isset($this->config['cssClass']) ? $this->config['cssClass'] : ''),
00210                 );
00211 
00212                 $rows[$this->table . '_' . $uid] = $this->renderRecord($row, $entry);
00213             }
00214 
00215             $GLOBALS['TYPO3_DB']->sql_free_result($res);
00216 
00217                 // if there are less records than we need, call this function again to get more records
00218             if (count($rows) < $this->maxItems &&
00219                 $allRowsCount >= 50 && $recursionCounter < $this->maxItems) {
00220                 $tmp = self::queryTable($params, ++$recursionCounter);
00221                 $rows = array_merge($tmp, $rows);
00222             }
00223         }
00224         return $rows;
00225     }
00226 
00227     /**
00228      * Prepare the statement for selecting the records which will be returned to the selector. May also return some
00229      * other records (e.g. from a mm-table) which will be used later on to select the real records
00230      *
00231      * @return void
00232      */
00233     protected function prepareSelectStatement() {
00234         $searchWholePhrase = $this->config['searchWholePhrase'];
00235 
00236         $searchString = $this->params['value'];
00237         $searchUid = intval($searchString);
00238         if (strlen($searchString)) {
00239             $searchString = $GLOBALS['TYPO3_DB']->quoteStr($searchString, $this->table);
00240             $likeCondition = ' LIKE \'' . ($searchWholePhrase ? '%' : '') .
00241                              $GLOBALS['TYPO3_DB']->escapeStrForLike($searchString, $this->table) . '%\'';
00242 
00243                 // Search in all fields given by label or label_alt
00244             $selectFieldsList = $GLOBALS['TCA'][$this->table]['ctrl']['label'] . ',' . $GLOBALS['TCA'][$this->table]['ctrl']['label_alt'];
00245             $selectFields = t3lib_div::trimExplode(',', $selectFieldsList, TRUE);
00246 
00247             $selectParts = array();
00248             foreach ($selectFields as $field) {
00249                 $selectParts[] = $field . $likeCondition;
00250             }
00251             $this->selectClause = '(' . implode(' OR ', $selectParts) . ')';
00252 
00253             if ($searchUid > 0 && $searchUid == $searchString) {
00254                 $this->selectClause = '(' . $this->selectClause . ' OR uid = ' . $searchUid . ')';
00255             }
00256         }
00257         if (isset($GLOBALS['TCA'][$this->table]['ctrl']['delete'])) {
00258             $this->selectClause .= ' AND ' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] . ' = 0';
00259         }
00260 
00261         if (count($this->allowedPages)) {
00262             $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($this->allowedPages);
00263             if (count($pidList)) {
00264                 $this->selectClause .= ' AND pid IN (' . implode(', ', $pidList) . ') ';
00265             }
00266         }
00267 
00268             // add an additional search condition comment
00269         if (isset($this->config['searchCondition']) && strlen($this->config['searchCondition']) > 0) {
00270             $this->selectClause .= ' AND ' . $this->config['searchCondition'];
00271         }
00272 
00273             // add the global clauses to the where-statement
00274         $this->selectClause .= $this->addWhere;
00275     }
00276 
00277     /**
00278      * Selects all subpages of one page, optionally only upto a certain level
00279      *
00280      * @param integer $uid The uid of the page
00281      * @param integer $depth The depth to select upto. Defaults to 99
00282      * @return array of page IDs
00283      */
00284     protected function getAllSubpagesOfPage($uid, $depth = 99) {
00285         $pageIds = array($uid);
00286         $level = 0;
00287 
00288         $pages = array($uid);
00289 
00290             // fetch all
00291         while (($depth - $level) > 0 && !empty($pageIds)) {
00292             ++$level;
00293 
00294             $pidList = $GLOBALS['TYPO3_DB']->cleanIntArray($pageIds);
00295             $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'pid IN (' . implode(', ', $pidList) . ')', '', '', '', 'uid');
00296             if (count($rows) > 0) {
00297                 $pageIds = array_keys($rows);
00298 
00299                 $pages = array_merge($pages, $pageIds);
00300             } else {
00301                 break;
00302             }
00303         }
00304 
00305         return $pages;
00306     }
00307 
00308     /**
00309      * Prepares the clause by which the result elements are sorted. See description of ORDER BY in
00310      * SQL standard for reference.
00311      *
00312      * @return void
00313      */
00314     protected function prepareOrderByStatement() {
00315         if ($GLOBALS['TCA'][$this->table]['ctrl']['label']) {
00316             $this->orderByStatement = $GLOBALS['TCA'][$this->table]['ctrl']['label'];
00317         }
00318     }
00319 
00320     /**
00321      * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
00322      *
00323      * @param array $row
00324      */
00325     protected function manipulateRecord(&$row) {
00326     }
00327 
00328     /**
00329      *  Selects whether the logged in Backend User is allowed to read a specific record
00330      */
00331     protected function checkRecordAccess($row, $uid) {
00332         $retValue = TRUE;
00333         $table = ($this->mmForeignTable ? $this->mmForeignTable : $this->table);
00334         if ($table == 'pages') {
00335             if (!t3lib_BEfunc::readPageAccess($uid, $GLOBALS['BE_USER']->getPagePermsClause(1))) {
00336                 $retValue = FALSE;
00337             }
00338         } else {
00339             if (!is_array(t3lib_BEfunc::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1)))) {
00340                 $retValue = FALSE;
00341             }
00342         }
00343         return $retValue;
00344     }
00345 
00346     /**
00347      * Overlay the given record with its workspace-version, if any
00348      *
00349      * @param array The record to get the workspace version for
00350      * @return void (passed by reference)
00351      */
00352     protected function makeWorkspaceOverlay(&$row) {
00353             // check for workspace-versions
00354         if ($GLOBALS['BE_USER']->workspace != 0 && $GLOBALS['TCA'][$this->table]['ctrl']['versioningWS'] == TRUE) {
00355             t3lib_BEfunc::workspaceOL(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
00356         }
00357     }
00358 
00359     /**
00360      * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
00361      *
00362      * @param array $row The record to get the icon for
00363      * @return string The path to the icon
00364      */
00365     protected function getIcon($row) {
00366         $icon = t3lib_iconWorks::getIcon(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
00367         return t3lib_iconWorks::skinImg('', $icon, '', 1);
00368     }
00369 
00370     /**
00371      * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut
00372      * off, because it contains the pagetitle itself, which would be double information
00373      *
00374      * The path is returned uncut, cutting has to be done by calling function.
00375      *
00376      * @param  array    $row The row
00377      * @param  array    $record The record
00378      * @return string The record-path
00379      */
00380     protected function getRecordPath(&$row, $uid) {
00381         $titleLimit = max($this->config['maxPathTitleLength'], 0);
00382 
00383         if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
00384             $path = t3lib_BEfunc::getRecordPath($uid, '', $titleLimit);
00385                 // for pages we only want the first (n-1) parts of the path, because the n-th part is the page itself
00386             $path = substr($path, 0, strrpos($path, '/', -2)) . '/';
00387         } else {
00388             $path = t3lib_BEfunc::getRecordPath($row['pid'], '', $titleLimit);
00389         }
00390 
00391         return $path;
00392     }
00393 
00394     /**
00395      * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
00396      *
00397      * @param array $row The record to get the label for
00398      * @return string The label
00399      */
00400     protected function getLabel($row) {
00401         return t3lib_BEfunc::getRecordTitle(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row, TRUE);
00402     }
00403 
00404     /**
00405      * Calls a user function for rendering the page.
00406      *
00407      * This user function should manipulate $entry, especially $entry['text'].
00408      *
00409      * @param array $row The row
00410      * @param array $entry The entry to render
00411      * @return array The rendered entry (will be put into a <li> later on
00412      */
00413     protected function renderRecord($row, $entry) {
00414             // call renderlet if available (normal pages etc. usually don't have one)
00415         if ($this->config['renderFunc'] != '') {
00416             $params = array(
00417                 'table' => $this->table,
00418                 'uid' => $row['uid'],
00419                 'row' => $row,
00420                 'entry' => &$entry
00421             );
00422             t3lib_div::callUserFunction($this->config['renderFunc'], $params, $this, '');
00423         }
00424 
00425         return $entry;
00426     }
00427 }
00428 
00429 ?>