+ */
+
+class t3lib_TCEforms_suggest {
+ public $suggestCount = 0; // count the number of ajax selectors used
+
+
+ /**
+ * Initialize an instance of t3lib_TCEforms_suggest
+ *
+ * @param t3lib_TCEforms $tceForms Reference to an TCEforms instance
+ * @return void
+ */
+ public function init(&$tceForms) {
+ $this->TCEformsObj =& $tceForms;
+ $this->backPath =& $tceForms->backPath;
+ }
+
+ /**
+ * Renders an ajax-enabled text field. Also adds required JS
+ *
+ * @param string $fieldname The fieldname in the form
+ * @param string $table The table we render this selector for
+ * @param string $field The field we render this selector for
+ * @param array $row The row which is currently edited
+ * @param array $config The TSconfig of the field
+ * @param boolean $hideSuggest Whether or not to hide the selector by default
+ * @return string The HTML code for the selector
+ */
+ public function renderSuggestSelector($fieldname, $table, $field, array $row, array $config, $hideSuggest) {
+ $this->suggestCount++;
+
+ if ($hideSuggest) {
+ $hide = ' style="display:none;"';
+ }
+
+ // TODO: move CSS to stylesheet
+ $selector = ''.$this->getLL('find_record').'
+
+ ';
+
+ $this->TCEformsObj->additionalJS_post[] = '
+ suggest.createSuggestField("'.$fieldname.'", "'.$table.'", "'.$field.'", "'.$row['uid'].'", '.$row['pid'].');
+ ';
+
+ return $selector;
+ }
+
+ /**
+ * Ajax handler for the "suggest" feature in TCEforms
+ *
+ * @param string $search The string to search for in the database
+ * @param string $table The table which is currently edited
+ * @param string $field The field which is currently edited
+ * @param integer $uid The uid of the record which is currently edited (may be NEWxxxxxx)
+ * @param integer $pid The pid of the record which is currently edited (always numeric
+ * @param string $formfield The field which is edited
+ * @return void
+ */
+ public function processAjaxRequest($params, &$ajaxObj) {
+ global $TCA, $LANG;
+
+ // instantiate the global language object here because in AJAX calls it's not available,
+ // but we need it.
+ // @see: typo3/template.php
+ require_once(PATH_typo3.'sysext/lang/lang.php');
+ $LANG = t3lib_div::makeInstance('language');
+ $LANG->init($BE_USER->uc['lang']);
+
+
+ // get parameters from $_GET/$_POST
+ $search = t3lib_div::_GP('value');
+ $table = t3lib_div::_GP('table');
+ $field = t3lib_div::_GP('field');
+ $uid = t3lib_div::_GP('uid');
+ $pageId = t3lib_div::_GP('pid');
+ $formfield = t3lib_div::_GP('formfield');
+
+
+ t3lib_div::loadTCA($table);
+
+ if (is_numeric($uid)) {
+ // already existing element, so get the TSconfig of the page itself...
+ if ($table == 'pages') {
+ $pageId = $uid;
+ // ... or the page containing the element
+ } else {
+ $row = t3lib_BEfunc::getRecord($table, $uid);
+ $pageId = $row['pid'];
+ }
+ } // New element, so use given id of parent page (i.e., don't modify it here)
+
+ $TSconfig = t3lib_BEfunc::getPagesTSconfig($pageId);
+
+ $queryTables = t3lib_div::trimExplode(',', $TCA[$table]['columns'][$field]['config']['allowed']);
+
+ $resultRows = array();
+ foreach ($queryTables as $queryTable) {
+ t3lib_div::loadTCA($queryTable);
+
+ // if the table does not exist, skip it
+ if (count($TCA[$queryTable]) == 0) continue;
+
+ // merge the configurations of different "levels" to get the working configuration for this table and
+ // field (i.e., go from the most general to the most special configuration)
+ $config = is_array($TSconfig['TCEMAIN.']['default.']['suggest.']) ?
+ $TSconfig['TCEMAIN.']['default.']['suggest.'] : array();
+ $config = t3lib_div::array_merge_recursive_overrule($config,
+ (is_array($TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.']) ?
+ $TSconfig['TCEMAIN.'][$queryTable.'.']['suggest.'] : array()));
+ // use $table instead of $queryTable here because we overlay a config for the input-field here, not for
+ // the queried table
+ $config = t3lib_div::array_merge_recursive_overrule($config,
+ (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.']) ?
+ $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'] : array()));
+ $config = t3lib_div::array_merge_recursive_overrule($config,
+ (is_array($TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.']) ?
+ $TSconfig['TCEFORM.'][$table.'.'][$field.'.']['suggest.'][$queryTable.'.'] : array()));
+
+ $class = t3lib_div::makeInstanceClassName($config['suggestClass']);
+ if (!class_exists($class)) {
+ $class = 'suggestDefaultSelector';
+ }
+ $obj = new $class($queryTable, $config);
+
+ $params = array('value' => $search);
+ $rows = $obj->queryTable($params, $this);
+
+
+ if (!$rows) {
+ continue;
+ }
+ $resultRows = t3lib_div::array_merge($resultRows, $rows);
+ unset($rows);
+ }
+
+ $listItems = array();
+ if (count($resultRows) > 0) {
+ // traverse all found records, ...
+ foreach ($resultRows as $key => $row) {
+ $rowsSort[$key] = $row['text'];
+ }
+ // ... sort them...
+ asort($rowsSort);
+ $rowsSort = array_keys($rowsSort);
+
+ // ... and put together the selector
+ $returnRows = array();
+ for ($i = 0; $i < 10 && $i < count($resultRows); $i++) {
+ $row = $resultRows[$rowsSort[$i]];
+ $returnRows[] = ''.$row['text'].'';
+ }
+ }
+ if (count($returnRows) > 0) {
+ $list = implode('', $returnRows);
+ } else {
+ $list = '';
+ }
+
+ // encode the content in utf-8, otherwise it won't be shown correctly in the backend
+ $ajaxObj->addContent(0, $LANG->csConvObj->utf8_encode('', $LANG->charSet));
+ }
+
+ /**
+ * Returns language label from locallang_core.php
+ *
+ * @param string The label key
+ * @return string The value of the label, fetched for the current backend language.
+ */
+ private function getLL($str) {
+ return $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.'.$str);
+ }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/class.t3lib_tceforms_suggest.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['classes/t3lib/class.t3lib_tceforms_suggest.php']);
+}
\ Kein Zeilenvorschub am Ende der Datei
Index: typo3/classes/class.suggestDefaultSelector.php
===================================================================
--- typo3/classes/class.suggestDefaultSelector.php (Revision 0)
+++ typo3/classes/class.suggestDefaultSelector.php (Revision 161)
@@ -0,0 +1,449 @@
+
+* All rights reserved
+*
+* This script is part of the TYPO3 project. The TYPO3 project is
+* free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* The GNU General Public License can be found at
+* http://www.gnu.org/copyleft/gpl.html.
+* A copy is found in the textfile GPL.txt and important notices to the license
+* from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+* This script is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Demo implementation of a handler class for the ajax record selector.
+ *
+ * Normally other implementations should be inherited from this one. queryTable() should not be overwritten under normal
+ * circumstances.
+ *
+ * @author Andreas Wolf
+ *
+ */
+class suggestDefaultSelector {
+ /**
+ * The name of the table to query
+ *
+ * @var string
+ */
+ protected $table = '';
+
+ /**
+ * The name of the foreign table to query (records from this table will be used for displaying instead of the ones
+ * from table)
+ *
+ * @var string
+ */
+ protected $mmForeignTable = '';
+
+ /**
+ * Holds the configuration for the table we query
+ *
+ * @var array
+ */
+ protected $tableConfig = array();
+
+ /**
+ * Counter to limit the recursions when querying the table; also needed to choose the range of records to select
+ *
+ * @var integer
+ */
+ protected $recursionCounter = 0;
+
+ /**
+ * The select-clause to use when selecting the records (is manipulated and used by different functions, so it has to
+ * be a global var)
+ *
+ * @var string
+ */
+ protected $selectClause = '';
+
+ /**
+ * Configuration for this selector from TSconfig
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * The CSS class(es) to use for rendering the selector
+ *
+ * @var string
+ */
+ protected $cssClasses = '';
+
+
+
+ /**
+ * The constructor of this class
+ *
+ * @param string $table
+ * @param array $config The configuration (TSconfig) to use for this selector
+ * @return void
+ */
+ public function __construct($table, $config) {
+ global $TCA;
+
+ $this->table = $table;
+
+ $this->config = $config;
+
+ // Get the configuration for this table
+ $this->tableConfig = $TCA[$table];
+
+ if ($config['cssClasses'] != '') {
+ $this->cssClasses = $config['cssClasses'];
+ }
+
+
+ $depth = intval($config['pidDepth']);
+
+ $pageIds = t3lib_div::trimExplode(',', $config['pid']);
+ foreach ($pageIds as $pageId) {
+ if ($pageId > 0) {
+ $pagesTemp .= $pageId.',';
+ $pageArr = $this->getAllSubpagesOfPage($pageId, $depth);
+
+ foreach ($pageArr as $pages) {
+ $pagesTemp .= $pages.',';
+ }
+ }
+ }
+ $this->pages = array_unique(t3lib_div::trimExplode(',', $pagesTemp, true));
+
+
+
+ if ($params['table'] == 'pages') {
+ $this->addWhere = ' AND '.$this->tceforms->readPerms();
+ }
+ // if table is versionized, only get the records from the Live Workspace - the overlay of WS-records is
+ // done below
+ if ($TCA[$params['table']]['ctrl']['versioningWS'] == true) {
+ $this->addWhere .= ' AND t3ver_wsid=0';
+ }
+ }
+
+ /**
+ * Queries a table for records and completely processes them
+ *
+ * Returns a two-dimensional array of almost finished records; the only need to be put into a -structure
+ *
+ * If you subclass this class, you will most likely only want to overwrite the functions called from here, but not
+ * this function itself
+ *
+ * @param array $params
+ * @param object $ref
+ * @return array The fetched and processed rows
+ */
+ public function queryTable(&$params, &$ref) {
+ global $TYPO3_DB;
+
+ $this->params = &$params;
+
+ $this->start = $this->recursionCounter * 50;
+
+ $this->prepareSelectStatement();
+
+ $rows = array();
+
+ $result = $this->selectRecords();
+ if (!$result) return false;
+
+ while ($row = $TYPO3_DB->sql_fetch_assoc($result)) {
+ // check if we already have collected 10 records
+ if (count($rows) > 10) break;
+
+ $this->manipulateRecord($row);
+
+ $this->makeWorkspaceOverlay($row);
+
+ // check if the user has access to the record
+ if (!$this->checkRecordAccess($row, $row['uid'])) {
+ continue;
+ }
+
+ $iconPath = $this->getIcon($row);
+
+ $uid = ($row['t3ver_oid'] > 0) ? $row['t3ver_oid'] : $row['uid'];
+
+ $path = $this->getRecordPath($row, $uid);
+
+ if (strlen($path) > 30) {
+ $croppedPath = $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$path,10).'...'.$GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$path,-20);
+ } else {
+ $croppedPath = $path;
+ }
+
+
+
+ $label = $this->getLabel($row);
+
+ $entry = array(
+ 'text' => ''.$label.'('.$uid.')
'.$croppedPath.'',
+ 'table' => ($this->mmForeignTable ? $this->mmForeignTable : $this->table),
+ 'path' => $path,
+ 'uid' => $uid,
+ 'icon' => $iconPath,
+ 'style' => 'background-image:url('.$iconPath.');',
+ 'class' => $this->cssClasses,
+ );
+
+ $entry = $this->renderRecord($row, $entry);
+
+
+ $rows[$table.'_'.$uid] = $entry;
+ }
+
+ // if there are less than ten records, call this function again to get more records
+ if (count($rows) < 10 && $TYPO3_DB->sql_num_rows($result) >= 50 && $recursionCounter < 10) {
+ $tmp = self::queryTable($this->params['value'], ++$recursionCounter);
+ $rows = array_merge($tmp, $rows);
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Prepare the statement for selecting the records which will be returned to the selector. May also return some
+ * other records (e.g. from a mm-table) which will be used later on to select the real records
+ *
+ * @return void
+ */
+ protected function prepareSelectStatement() {
+ global $TYPO3_DB;
+
+ $searchWholePhrase = $this->config['searchWholePhrase'];
+
+ if (isset($this->pages)) {
+ $this->selectClause = $this->tableConfig['ctrl']['label'].' LIKE \''.($searchWholePhrase ? '%' : '').$TYPO3_DB->escapeStrForLike($this->params['value'], $this->table).'%\' AND deleted=0';
+ }
+
+ $this->selectClause .= $this->getPageIdClause($this->pages) ? ' AND ('.$this->getPageIdClause($this->pages).')' : '';
+
+ // add the global clauses to the where-statement
+ $this->selectClause .= $this->addWhere;
+ }
+
+ /**
+ * Selects all subpages of one page, optionally only upto a certain level
+ *
+ * @param integer $uid
+ * @param integer $depth
+ */
+ protected function getAllSubpagesOfPage($uid, $depth = 99) {
+ global $TYPO3_DB;
+
+ $pageIds = array($uid);
+ $level = 0;
+
+ $pages = array();
+ $pages[$level] = $uid;
+
+ // check for
+ while (($depth - $level) >= 0 && $pageIds != '') {
+ ++$level;
+
+ $rows = $TYPO3_DB->exec_SELECTgetRows('uid', 'pages', '('.$this->getPageIdClause($pageIds).')', '', '', '', 'uid');
+ if (count($rows) > 0) {
+ $pageIds = array_keys($rows);
+ $pageIdsString = implode(',', $pageIds);
+
+ $pages[$level] = $pageIdsString;
+ } else {
+ break;
+ }
+ }
+
+ return $pages;
+ }
+
+ /**
+ * Manipulate a record before using it to render the selector; may be used to replace a MM-relation etc.
+ *
+ * @param array $row
+ */
+ protected function manipulateRecord(&$row) {
+ }
+
+ /**
+ * Select the recrods from the table (uses the statement prepared in $this->selectClause)
+ *
+ * @return resource The resource which holds the records
+ */
+ protected function selectRecords() {
+ global $TYPO3_DB;
+
+ $result = $TYPO3_DB->exec_SELECTquery('*', $this->table, $this->selectClause, '', '', $this->start.', 50');
+
+ if (!$result) {
+ $result = false;
+ }
+
+ return $result;
+ }
+
+ protected function checkRecordAccess($row, $uid) {
+ global $BE_USER;
+
+ $retValue = true;
+ if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
+ $row = t3lib_BEfunc::readPageAccess($uid, $BE_USER->getPagePermsClause(1));
+ if (!$row)
+ $retValue = false;
+ } else {
+ $parent = t3lib_BEfunc::readPageAccess($row['pid'], $BE_USER->getPagePermsClause(1));
+ if (!is_array($parent))
+ $retValue = false;
+ }
+
+ return $retValue;
+ }
+
+ /**
+ * Overlay the given record with its workspace-version, if any
+ *
+ * @return void
+ */
+ protected function makeWorkspaceOverlay(&$row) {
+ global $BE_USER;
+
+ // check for workspace-versions
+ if ($BE_USER->workspace != 0 && $this->tableConfig['ctrl']['versioningWS'] == true) {
+ t3lib_BEfunc::workspaceOL(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+ }
+ }
+
+ /**
+ * Return the icon for a record - just a wrapper for two functions from t3lib_iconWorks
+ *
+ * @param array $row The record to get the icon for
+ * @return string The path to the icon
+ */
+ protected function getIcon($row) {
+ $icon = t3lib_iconWorks::getIcon(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+ $icon = t3lib_iconWorks::skinImg('', $icon, '', 1);
+
+ return $icon;
+ }
+
+ /**
+ * Returns the path for a record. Is the whole path for all records except pages - for these the last part is cut
+ * off, because it contains the pagetitle itself, which would be double information
+ *
+ * The path is returned uncut, cutting has to be done by calling function.
+ *
+ * @param array $row The row
+ * @param integer $uid The records uid
+ * @return string The record-path
+ */
+ protected function getRecordPath(&$row, $uid) {
+ global $BE_USER;
+
+ if ($this->config['titleSegmentLimit']) {
+ $titleLimit = $this->config['titleSegmentLimit'];
+ } else {
+ $titleLimit = 0;
+ }
+
+ if (($this->mmForeignTable ? $this->mmForeignTable : $this->table) == 'pages') {
+ $path = t3lib_BEfunc::getRecordPath($uid, '', $titleLimit);
+
+ // for pages we only want the first (n-1) parts of the path, because the n-th part is the page itself
+ $path = substr($path, 0, strrpos($path, '/', -2)).'/';
+ } else {
+ $path = t3lib_BEfunc::getRecordPath($row['pid'], '', $titleLimit);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Returns a label for a given record; usually only a wrapper for t3lib_BEfunc::getRecordTitle
+ *
+ * @param array $row
+ * @return The label for the record
+ */
+ protected function getLabel($row) {
+ $label = t3lib_BEfunc::getRecordTitle(($this->mmForeignTable ? $this->mmForeignTable : $this->table), $row);
+ if ($label !== '') {
+ $label = t3lib_div::fixed_lgd_cs($label, 20);
+ } else {
+ $label = ''.$this->getLL('l_no_title').'';
+ }
+
+ return $label;
+ }
+
+ /**
+ * Calls a user function for rendering the page.
+ *
+ * This user function should manipulate $entry, especially $entry['text']
+ *
+ * @param array $row The row
+ * @param array $entry The entry to render
+ * @return array The rendered entry (will be put into a later on
+ */
+ protected function renderRecord($row, $entry) {
+ // call renderlet if available (normal pages etc. usually don't have one)
+ $func = $this->config['renderFunc'];
+ if ($func != '') {
+ $params = array('table' => $this->table, 'uid' => $row['uid'], 'row' => $row, 'entry' => &$entry);
+ t3lib_div::callUserFunction($func, $params, $this, '');
+ }
+
+ return $entry;
+ }
+
+ /**
+ * Returns language label from locallang_core.php
+ * Labels must be prefixed with either "l_" or "m_".
+ * The prefix "l_" maps to the prefix "labels." inside locallang_core.php
+ * The prefix "m_" maps to the prefix "mess." inside locallang_core.php
+ *
+ * @param string The label key
+ * @return string The value of the label, fetched for the current backend language.
+ */
+ function getLL($str) {
+ $content = '';
+
+ switch(substr($str,0,2)) {
+ case 'l_':
+ $content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.'.substr($str,2));
+ break;
+ case 'm_':
+ $content = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:mess.'.substr($str,2));
+ break;
+ }
+ return $content;
+ }
+
+ /**
+ * Returns a string as to use a replacement for IN (...) in an SQL where-clause. Needed for compatibility with
+ * MSSQL
+ *
+ * @param array $pageIds The pages to use for the clause
+ * @return string
+ */
+ private function getPageIdClause($pageIds) {
+ $pageArr = array();
+ foreach ($pageIds as $pageId) {
+ $pageArr[] = 'pid='.$pageId;
+ }
+
+ return implode(' OR ', $pageArr);
+ }
+}
Index: typo3/gfx/group_suggest.gif
===================================================================
Kann nicht anzeigen: Dateityp ist als binär angegeben.
svn:mime-type = application/octet-stream
Eigenschaftsänderungen: typo3/gfx/group_suggest.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Index: typo3/stylesheet.css
===================================================================
--- typo3/stylesheet.css (Revision 157)
+++ typo3/stylesheet.css (Revision 161)
@@ -646,6 +646,46 @@
+DIV.suggest_choices {
+ position:absolute;
+ width:300px;
+ background-color:white;
+ border:1px solid #888;
+ margin:0px;
+ padding:0px;
+}
+DIV.suggest_choices UL {
+ list-style-type:none;
+ margin:0px;
+ padding:0px;
+}
+DIV.suggest_choices LI.selected { background-color: #ffb !important;}
+DIV.suggest_choices LI {
+ list-style-type:none;
+ display:block;
+ margin:0;
+ padding:2px;
+ padding-left:24px;
+ height:32px;
+ cursor:pointer;
+ background-color:#EFEFF4;
+ background-repeat:no-repeat;
+ background-position:4px center;
+}
+DIV.suggest_choices LI.pages {
+ background-color:#FCC;
+}
+DIV.suggest_choices SPAN.label {
+ font-weight:bold;
+}
+DIV.suggest_choices SPAN.uid {
+ font-size:0.9em;
+ margin-left:0.3em;
+}
+DIV.suggest_choices SPAN.path {
+ font-size:0.9em;
+ margin-top:0.3em;
+}