/src/typo3_src-4.1.2/t3lib/class.t3lib_tceforms_inline.php

00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2006 Oliver Hader <oh@inpublica.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 ***************************************************************/
00090 class t3lib_TCEforms_inline {
00091         var $fObj;                                                              // Reference to the calling TCEforms instance
00092         var $backPath;                                                  // Reference to $fObj->backPath
00093 
00094         var $isAjaxCall = false;                                // Indicates if a field is rendered upon an AJAX call
00095         var $inlineStructure = array();                 // the structure/hierarchy where working in, e.g. cascading inline tables
00096         var $inlineFirstPid;                                    // the first call of an inline type appeared on this page (pid of record)
00097         var $inlineNames = array();                             // keys: form, object -> hold the name/id for each of them
00098         var $inlineData = array();                              // inline data array used for JSON output
00099         var $inlineView = array();                              // expanded/collapsed states for the current BE user
00100         var $inlineCount = 0;                                   // count the number of inline types used
00101         var $inlineStyles = array();
00102 
00103         var $prependNaming = 'data';                    // how the $this->fObj->prependFormFieldNames should be set ('data' is default)
00104         var $prependFormFieldNames;                             // reference to $this->fObj->prependFormFieldNames
00105         var $prependCmdFieldNames;                              // reference to $this->fObj->prependCmdFieldNames
00106 
00107 
00114         function init(&$tceForms) {
00115                 $this->fObj =& $tceForms;
00116                 $this->backPath =& $tceForms->backPath;
00117                 $this->prependFormFieldNames =& $this->fObj->prependFormFieldNames;
00118                 $this->prependCmdFieldNames =& $this->fObj->prependCmdFieldNames;
00119                 $this->inlineStyles['margin-right'] = '5';
00120         }
00121 
00122 
00133         function getSingleField_typeInline($table,$field,$row,&$PA) {
00134                         // check the TCA configuration - if false is returned, something was wrong
00135                 if ($this->checkConfiguration($PA['fieldConf']['config']) === false) return false;
00136 
00137                         // count the number of processed inline elements
00138                 $this->inlineCount++;
00139 
00140                         // Init:
00141                 $config = $PA['fieldConf']['config'];
00142                 $foreign_table = $config['foreign_table'];
00143                 t3lib_div::loadTCA($foreign_table);
00144 
00145                 $minitems = t3lib_div::intInRange($config['minitems'],0);
00146                 $maxitems = t3lib_div::intInRange($config['maxitems'],0);
00147                 if (!$maxitems) $maxitems=100000;
00148 
00149                         // Register the required number of elements:
00150                 $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
00151 
00152                         // remember the page id (pid of record) where inline editing started first
00153                         // we need that pid for ajax calls, so that they would know where the action takes place on the page structure
00154                 if (!isset($this->inlineFirstPid)) {
00155                                 // if this record is not new, try to fetch the inlineView states
00156                                 // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field
00157                         if (t3lib_div::testInt($row['uid'])) {
00158                                 $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
00159                                 $this->inlineView = $inlineView[$table][$row['uid']];
00160                         }
00161                                 // If the parent is a page, use the uid(!) of the (new?) page as pid for the child records:
00162                         if ($table == 'pages') {
00163                                 $this->inlineFirstPid = $row['uid'];
00164                                 // If pid is negative, fetch the previous record and take its pid:
00165                         } elseif ($row['pid'] < 0) {
00166                                 $prevRec = t3lib_BEfunc::getRecord($table, abs($row['pid']));
00167                                 $this->inlineFirstPid = $prevRec['pid'];
00168                                 // Take the pid as it is:
00169                         } else {
00170                                 $this->inlineFirstPid = $row['pid'];
00171                         }
00172                 }
00173                         // add the current inline job to the structure stack
00174                 $this->pushStructure($table, $row['uid'], $field, $config);
00175                         // e.g. inline[<table>][<uid>][<field>]
00176                 $nameForm = $this->inlineNames['form'];
00177                         // e.g. inline[<pid>][<table1>][<uid1>][<field1>][<table2>][<uid2>][<field2>]
00178                 $nameObject = $this->inlineNames['object'];
00179                         // get the records related to this inline record
00180                 $recordList = $this->getRelatedRecords($table,$field,$row,$PA,$config);
00181                         // set the first and last record to the config array
00182                 $config['inline']['first'] = $recordList[0]['uid'];
00183                 $config['inline']['last'] = $recordList[count($recordList)-1]['uid'];
00184 
00185                         // Tell the browser what we have (using JSON later):
00186                 $top = $this->getStructureLevel(0);
00187                 $this->inlineData['config'][$nameObject] = array('table' => $foreign_table);
00188                 $this->inlineData['config'][$nameObject.'['.$foreign_table.']'] = array(
00189                         'min' => $minitems,
00190                         'max' => $maxitems,
00191                         'sortable' => $config['appearance']['useSortable'],
00192                         'top' => array(
00193                                 'table' => $top['table'],
00194                                 'uid'   => $top['uid'],
00195                         ),
00196                 );
00197                         // Set a hint for nested IRRE and tab elements:
00198                 $this->inlineData['nested'][$nameObject] = $this->fObj->getDynNestedStack(false, $this->isAjaxCall);
00199 
00200                         // if relations are required to be unique, get the uids that have already been used on the foreign side of the relation
00201                 if ($config['foreign_unique']) {
00202                                 // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one:
00203                         $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']);
00204                                 // Get the used unique ids:
00205                         $uniqueIds = $this->getUniqueIds($recordList, $config, $selConfig['type']=='groupdb');
00206                         $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config,'foreign_unique');
00207                         $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1 : count($possibleRecords);
00208                         $this->inlineData['unique'][$nameObject.'['.$foreign_table.']'] = array(
00209                                 'max' => $uniqueMax,
00210                                 'used' => $uniqueIds,
00211                                 'type' => $selConfig['type'],
00212                                 'table' => $config['foreign_table'],
00213                                 'elTable' => $selConfig['table'], // element/record table (one step down in hierarchy)
00214                                 'field' => $config['foreign_unique'],
00215                                 'selector' => $selConfig['selector'],
00216                                 'possible' => $this->getPossibleRecordsFlat($possibleRecords),
00217                         );
00218                 }
00219 
00220                         // if it's required to select from possible child records (reusable children), add a selector box
00221                 if ($config['foreign_selector']) {
00222                                 // if not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array
00223                         if (!$config['foreign_unique']) {
00224                                 $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config);
00225                                 $uniqueIds = array();
00226                         }
00227                         $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords,$config,$uniqueIds);
00228                         $item .= $selectorBox;
00229                 }
00230 
00231                         // wrap all inline fields of a record with a <div> (like a container)
00232                 $item .= '<div id="'.$nameObject.'">';
00233 
00234                         // define how to show the "Create new record" link - if there are more than maxitems, hide it
00235                 if (count($recordList) >= $maxitems || ($uniqueMax > 0 && count($recordList) >= $uniqueMax)) {
00236                         $config['inline']['inlineNewButtonStyle'] = 'display: none;';
00237                 }
00238                         // add the "Create new record" link before all child records
00239                 if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'top'))) {
00240                         $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
00241                 }
00242 
00243                 $item .= '<div id="'.$nameObject.'_records">';
00244                 $relationList = array();
00245                 if (count($recordList)) {
00246                         foreach ($recordList as $rec) {
00247                                 $item .= $this->renderForeignRecord($row['uid'],$rec,$config);
00248                                 $relationList[] = $rec['uid'];
00249                         }
00250                 }
00251                 $item .= '</div>';
00252 
00253                         // add the "Create new record" link after all child records
00254                 if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'bottom'))) {
00255                         $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
00256                 }
00257 
00258                         // add Drag&Drop functions for sorting to TCEforms::$additionalJS_post
00259                 if (count($relationList) > 1 && $config['appearance']['useSortable'])
00260                         $this->addJavaScriptSortable($nameObject.'_records');
00261                         // publish the uids of the child records in the given order to the browser
00262                 $item .= '<input type="hidden" name="'.$nameForm.'" value="'.implode(',', $relationList).'" class="inlineRecord" />';
00263                         // close the wrap for all inline fields (container)
00264                 $item .= '</div>';
00265 
00266                         // on finishing this section, remove the last item from the structure stack
00267                 $this->popStructure();
00268 
00269                         // if this was the first call to the inline type, restore the values
00270                 if (!$this->getStructureDepth()) {
00271                         unset($this->inlineFirstPid);
00272                 }
00273 
00274                 return $item;
00275         }
00276 
00277 
00278         /*******************************************************
00279          *
00280          * Regular rendering of forms, fields, etc.
00281          *
00282          *******************************************************/
00283 
00284 
00293         function renderForeignRecord($parentUid, $rec, $config = array()) {
00294                 $foreign_table = $config['foreign_table'];
00295                 $foreign_field = $config['foreign_field'];
00296                 $foreign_selector = $config['foreign_selector'];
00297 
00298                         // Send a mapping information to the browser via JSON:
00299                         // e.g. data[<curTable>][<curId>][<curField>] => data[<pid>][<parentTable>][<parentId>][<parentField>][<curTable>][<curId>][<curField>]
00300                 $this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
00301 
00302                         // Set this variable if we handle a brand new unsaved record:
00303                 $isNewRecord = t3lib_div::testInt($rec['uid']) ? false : true;
00304                         // If there is a selector field, normalize it:
00305                 if ($foreign_selector) {
00306                         $rec[$foreign_selector] = $this->normalizeUid($rec[$foreign_selector]);
00307                 }
00308 
00309                 $hasAccess = $this->checkAccess($isNewRecord?'new':'edit', $foreign_table, $rec['uid']);
00310 
00311                 if(!$hasAccess) return false;
00312 
00313                         // Get the current naming scheme for DOM name/id attributes:
00314                 $nameObject = $this->inlineNames['object'];
00315                 $appendFormFieldNames = '['.$foreign_table.']['.$rec['uid'].']';
00316                 $formFieldNames = $nameObject.$appendFormFieldNames;
00317                         // Put the current level also to the dynNestedStack of TCEforms:
00318                 $this->fObj->pushToDynNestedStack('inline', $this->inlineNames['object'].$appendFormFieldNames);
00319 
00320                 $header = $this->renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config);
00321                 $combination = $this->renderCombinationTable($rec, $appendFormFieldNames, $config);
00322                 $fields = $this->fObj->getMainFields($foreign_table,$rec);
00323                 $fields = $this->wrapFormsSection($fields);
00324 
00325                 if ($isNewRecord) {
00326                                 // show this record expanded or collapsed
00327                         $isExpanded = is_array($config['appearance']) && $config['appearance']['collapseAll'] ? 1 : 0;
00328                                 // get the top parent table
00329                         $top = $this->getStructureLevel(0);
00330                         $ucFieldName = 'uc['.$top['table'].']['.$top['uid'].']'.$appendFormFieldNames;
00331                                 // set additional fields for processing for saving
00332                         $fields .= '<input type="hidden" name="'.$this->prependFormFieldNames.$appendFormFieldNames.'[pid]" value="'.$rec['pid'].'"/>';
00333                         $fields .= '<input type="hidden" name="'.$ucFieldName.'" value="'.$isExpanded.'" />';
00334 
00335                 } else {
00336                                 // show this record expanded or collapsed
00337                         $isExpanded = $this->getExpandedCollapsedState($foreign_table, $rec['uid']);
00338                                 // set additional field for processing for saving
00339                         $fields .= '<input type="hidden" name="'.$this->prependCmdFieldNames.$appendFormFieldNames.'[delete]" value="1" disabled="disabled" />';
00340                 }
00341 
00342                         // if this record should be shown collapsed
00343                 if (!$isExpanded) $appearanceStyleFields = ' style="display: none;"';
00344 
00345                         // set the record container with data for output
00346                 $out = '<div id="'.$formFieldNames.'_header">'.$header.'</div>';
00347                 $out .= '<div id="'.$formFieldNames.'_fields"'.$appearanceStyleFields.'>'.$fields.$combination.'</div>';
00348                         // wrap the header, fields and combination part of a child record with a div container
00349                 $out = '<div id="'.$formFieldNames.'_div"'.($isNewRecord ? ' class="inlineIsNewRecord"' : '').'>' . $out . '</div>';
00350 
00351                         // Remove the current level also from the dynNestedStack of TCEforms:
00352                 $this->fObj->popFromDynNestedStack();
00353 
00354                 return $out;
00355         }
00356 
00357 
00368         function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config = array()) {
00369                         // Init:
00370                 $formFieldNames = $this->inlineNames['object'].'['.$foreign_table.']['.$rec['uid'].']';
00371                 $expandSingle = $config['appearance']['expandSingle'] ? 1 : 0;
00372                 $onClick = "return inline.expandCollapseRecord('".htmlspecialchars($formFieldNames)."', $expandSingle)";
00373 
00374                         // Pre-Processing:
00375                 $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
00376                 $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? true : false;
00377                 $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? true : false;
00378                         // Get the record title/label for a record:
00379                         // render using a self-defined user function
00380                 if ($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc']) {
00381                         $params = array(
00382                                 'table' => $foreign_table,
00383                                 'row'   => $rec,
00384                                 'title' => '',
00385                                 'isOnSymmetricSide' => $isOnSymmetricSide
00386                         );
00387                         $null = null;   // callUserFunction requires a third parameter, but we don't want to give $this as reference!
00388                         t3lib_div::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null);
00389                         $recTitle = $params['title'];
00390                         // render the special alternative title
00391                 } elseif ($hasForeignLabel || $hasSymmetricLabel) {
00392                         $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
00393                         $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol);
00394                                 // Render title for everything else than group/db:
00395                         if ($foreignConfig['type'] != 'groupdb') {
00396                                 $recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
00397                                 // Render title for group/db:
00398                         } else {
00399                                         // $recTitle could be something like: "tx_table_123|...",
00400                                 $valueParts = t3lib_div::trimExplode('|', $rec[$titleCol]);
00401                                 $itemParts = t3lib_div::revExplode('_', $valueParts[0], 2);
00402                                 $recTemp = t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]);
00403                                 $recTitle = t3lib_BEfunc::getRecordTitle($itemParts[0], $recTemp, true);
00404                         }
00405                         $recTitle = t3lib_BEfunc::getRecordTitlePrep($recTitle);
00406                         if (!strcmp(trim($recTitle),'')) {
00407                                 $recTitle = t3lib_BEfunc::getNoRecordTitle(true);
00408                         }
00409                         // render the standard
00410                 } else {
00411                         $recTitle = t3lib_BEfunc::getRecordTitle($foreign_table, $rec, true);
00412                 }
00413 
00414                 $altText = t3lib_BEfunc::getRecordIconAltText($rec, $foreign_table);
00415                 $iconImg =
00416                         '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.t3lib_iconWorks::getIconImage(
00417                                 $foreign_table, $rec, $this->backPath,
00418                                 'title="'.htmlspecialchars($altText).'" class="absmiddle"'
00419                         ).'</a>';
00420 
00421                 $label =
00422                         '<a href="#" onclick="'.htmlspecialchars($onClick).'" style="display: block;">'.
00423                                 '<span id="'.$formFieldNames.'_label">'.$recTitle.'</span>'.
00424                         '</a>';
00425 
00426                 $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config);
00427 
00428                         // @TODO: Check the table wrapping and the CSS definitions
00429                 $header =
00430                         '<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-right: '.$this->inlineStyles['margin-right'].'px;"'.
00431                         ($this->fObj->borderStyle[2] ? ' background="'.htmlspecialchars($this->backPath.$this->fObj->borderStyle[2]).'"':'').
00432                         ($this->fObj->borderStyle[3] ? ' class="'.htmlspecialchars($this->fObj->borderStyle[3]).'"':'').'>' .
00433                         '<tr class="class-main12"><td width="18">'.$iconImg.'</td><td align="left"><b>'.$label.'</b></td><td align="right">'.$ctrl.'</td></tr></table>';
00434 
00435                 return $header;
00436         }
00437 
00438 
00449         function renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config = array()) {
00450                         // Initialize:
00451                 $cells=array();
00452                 $isNewItem = substr($rec['uid'], 0, 3) == 'NEW';
00453 
00454                 $tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl'];
00455                 $tcaTableCols =& $GLOBALS['TCA'][$foreign_table]['columns'];
00456 
00457                 $isPagesTable = $foreign_table == 'pages' ? true : false;
00458                 $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
00459                 $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || (!$isOnSymmetricSide && $config['foreign_sortby']) || ($isOnSymmetricSide && $config['symmetric_sortby']) ? true : false;
00460 
00461                 $nameObjectFt = $this->inlineNames['object'].'['.$foreign_table.']';
00462                 $nameObjectFtId = $nameObjectFt.'['.$rec['uid'].']';
00463 
00464                 $calcPerms = $GLOBALS['BE_USER']->calcPerms(
00465                         t3lib_BEfunc::readPageAccess($rec['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1))
00466                 );
00467 
00468                         // If the listed table is 'pages' we have to request the permission settings for each page:
00469                 if ($isPagesTable)      {
00470                         $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages',$rec['uid']));
00471                 }
00472 
00473                         // This expresses the edit permissions for this particular element:
00474                 $permsEdit = ($isPagesTable && ($localCalcPerms&2)) || (!$isPagesTable && ($calcPerms&16));
00475 
00476                         // "Info": (All records)
00477                 if (!$isNewItem)
00478                         $cells[]='<a href="#" onclick="'.htmlspecialchars('top.launchView(\''.$foreign_table.'\', \''.$rec['uid'].'\'); return false;').'">'.
00479                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/zoom2.gif','width="12" height="12"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:showInfo',1).'" alt="" />'.
00480                                 '</a>';
00481 
00482                         // If the table is NOT a read-only table, then show these links:
00483                 if (!$tcaTableCtrl['readOnly']) {
00484 
00485                                 // "New record after" link (ONLY if the records in the table are sorted by a "sortby"-row or if default values can depend on previous record):
00486                         if ($enableManualSorting || $tcaTableCtrl['useColumnsForDefaultValues'])        {
00487                                 if (
00488                                         (!$isPagesTable && ($calcPerms&16)) ||  // For NON-pages, must have permission to edit content on this parent page
00489                                         ($isPagesTable && ($calcPerms&8))               // For pages, must have permission to create new pages here.
00490                                         )       {
00491                                         $onClick = "return inline.createNewRecord('".$nameObjectFt."','".$rec['uid']."')";
00492                                         if ($config['inline']['inlineNewButtonStyle']) {
00493                                                 $style = ' style="'.$config['inline']['inlineNewButtonStyle'].'"';
00494                                         }
00495                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="inlineNewButton"'.$style.'>'.
00496                                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/new_'.($isPagesTable?'page':'el').'.gif','width="'.($isPagesTable?13:11).'" height="12"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:new'.($isPagesTable?'Page':'Record'),1).'" alt="" />'.
00497                                                         '</a>';
00498                                 }
00499                         }
00500 
00501                                 // Drag&Drop Sorting: Sortable handler for script.aculo.us
00502                         if ($permsEdit && $enableManualSorting && $config['appearance']['useSortable']) {
00503                                 $cells[] = '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/move.gif','width="16" height="16" hspace="2"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.move',1).'" alt="" style="cursor: move;" class="sortableHandle" />';
00504                         }
00505 
00506                                 // "Up/Down" links
00507                         if ($permsEdit && $enableManualSorting) {
00508                                 $onClick = "return inline.changeSorting('".$nameObjectFtId."', '1')";   // Up
00509                                 $style = $config['inline']['first'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
00510                                 $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingUp" '.$style.'>'.
00511                                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/button_up.gif','width="11" height="10"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:moveUp',1).'" alt="" />'.
00512                                                 '</a>';
00513 
00514                                 $onClick = "return inline.changeSorting('".$nameObjectFtId."', '-1')";  // Down
00515                                 $style = $config['inline']['last'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
00516                                 $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingDown" '.$style.'>'.
00517                                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/button_down.gif','width="11" height="10"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:moveDown',1).'" alt="" />'.
00518                                                 '</a>';
00519                         }
00520 
00521                                 // "Hide/Unhide" links:
00522                         $hiddenField = $tcaTableCtrl['enablecolumns']['disabled'];
00523                         if ($permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields',$foreign_table.':'.$hiddenField))) {
00524                                 $onClick = "return inline.enableDisableRecord('".$nameObjectFtId."')";
00525                                 if ($rec[$hiddenField]) {
00526                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00527                                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/button_unhide.gif','width="11" height="10"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:unHide'.($isPagesTable?'Page':''),1).'" alt="" id="'.$nameObjectFtId.'_disabled" />'.
00528                                                         '</a>';
00529                                 } else {
00530                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00531                                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/button_hide.gif','width="11" height="10"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:hide'.($isPagesTable?'Page':''),1).'" alt="" id="'.$nameObjectFtId.'_disabled" />'.
00532                                                         '</a>';
00533                                 }
00534                         }
00535 
00536                                 // "Delete" link:
00537                         if (
00538                                 ($isPagesTable && ($localCalcPerms&4)) || (!$isPagesTable && ($calcPerms&16))
00539                                 )       {
00540                                 $onClick = "inline.deleteRecord('".$nameObjectFtId."');";
00541                                 $cells[]='<a href="#" onclick="'.htmlspecialchars('if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')).')) {   '.$onClick.' } return false;').'">'.
00542                                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/garbage.gif','width="11" height="12"').' title="'.$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_list.xml:delete',1).'" alt="" />'.
00543                                                 '</a>';
00544                         }
00545                 }
00546 
00547                         // If the record is edit-locked by another user, we will show a little warning sign:
00548                 if ($lockInfo=t3lib_BEfunc::isRecordLocked($foreign_table,$rec['uid'])) {
00549                         $cells[]='<a href="#" onclick="'.htmlspecialchars('alert('.$GLOBALS['LANG']->JScharCode($lockInfo['msg']).');return false;').'">'.
00550                                         '<img'.t3lib_iconWorks::skinImg('','gfx/recordlock_warning3.gif','width="17" height="12"').' title="'.htmlspecialchars($lockInfo['msg']).'" alt="" />'.
00551                                         '</a>';
00552                 }
00553 
00554                         // Compile items into a DIV-element:
00555                 return '
00556                                                                                         <!-- CONTROL PANEL: '.$foreign_table.':'.$rec['uid'].' -->
00557                                                                                         <div class="typo3-DBctrl">'.implode('',$cells).'</div>';
00558         }
00559 
00560 
00571         function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array()) {
00572                 $foreign_table = $config['foreign_table'];
00573                 $foreign_selector = $config['foreign_selector'];
00574 
00575                 if ($foreign_selector && $config['appearance']['useCombination']) {
00576                         $comboConfig = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector]['config'];
00577                         $comboRecord = array();
00578 
00579                                 // record does already exist, so load it
00580                         if (t3lib_div::testInt($rec[$foreign_selector])) {
00581                                 $comboRecord = $this->getRecord(
00582                                         $this->inlineFirstPid,
00583                                         $comboConfig['foreign_table'],
00584                                         $rec[$foreign_selector]
00585                                 );
00586                                 $isNewRecord = false;
00587                                 // it's a new record, so get some default data
00588                         } else {
00589                                 $comboRecord = $this->getNewRecord(
00590                                         $this->inlineFirstPid,
00591                                         $comboConfig['foreign_table']
00592                                 );
00593                                 $isNewRecord = true;
00594                         }
00595 
00596                                 // get the TCEforms interpretation of the TCA of the child table
00597                         $out = $this->fObj->getMainFields($comboConfig['foreign_table'], $comboRecord);
00598                         $out = $this->wrapFormsSection($out, array(), array('class' => 'wrapperAttention'));
00599 
00600                                 // if this is a new record, add a pid value to store this record and the pointer value for the intermediate table
00601                         if ($isNewRecord) {
00602                                 $comboFormFieldName = $this->prependFormFieldNames.'['.$comboConfig['foreign_table'].']['.$comboRecord['uid'].'][pid]';
00603                                 $out .= '<input type="hidden" name="'.$comboFormFieldName.'" value="'.$this->inlineFirstPid.'"/>';
00604                         }
00605 
00606                                 // if the foreign_selector field is also responsible for uniqueness, tell the browser the uid of the "other" side of the relation
00607                         if ($isNewRecord || $config['foreign_unique'] == $foreign_selector) {
00608                                 $parentFormFieldName = $this->prependFormFieldNames.$appendFormFieldNames.'['.$foreign_selector.']';
00609                                 $out .= '<input type="hidden" name="'.$parentFormFieldName.'" value="'.$comboRecord['uid'].'" />';
00610                         }
00611                 }
00612 
00613                 return $out;
00614         }
00615 
00616 
00626         function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds=array()) {
00627                 $foreign_table = $conf['foreign_table'];
00628                 $foreign_selector = $conf['foreign_selector'];
00629 
00630                 $selConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_selector);
00631                 $config = $selConfig['PA']['fieldConf']['config'];
00632 
00633                 if ($selConfig['type'] == 'select') {
00634                         $item = $this->renderPossibleRecordsSelectorTypeSelect($selItems, $conf, $selConfig['PA'], $uniqueIds);
00635                 } elseif ($selConfig['type'] == 'groupdb') {
00636                         $item = $this->renderPossibleRecordsSelectorTypeGroupDB($conf, $selConfig['PA']);
00637                 }
00638 
00639                 return $item;
00640         }
00641 
00642 
00653         function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds=array()) {
00654                 $foreign_table = $conf['foreign_table'];
00655                 $foreign_selector = $conf['foreign_selector'];
00656 
00657                 $PA = array();
00658                 $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
00659                 $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ? $PA['fieldConf']['config']['form_type'] : $PA['fieldConf']['config']['type'];       // Using "form_type" locally in this script
00660                 $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$foreign_selector);
00661                 $config = $PA['fieldConf']['config'];
00662 
00663                 if(!$disabled) {
00664                                 // Create option tags:
00665                         $opt = array();
00666                         $styleAttrValue = '';
00667                         foreach($selItems as $p)        {
00668                                 if ($config['iconsInOptionTags'])       {
00669                                         $styleAttrValue = $this->fObj->optionTagStyle($p[2]);
00670                                 }
00671                                 if (!in_array($p[1], $uniqueIds)) {
00672                                         $opt[]= '<option value="'.htmlspecialchars($p[1]).'"'.
00673                                                                         ' style="'.(in_array($p[1], $uniqueIds) ? '' : '').
00674                                                                         ($styleAttrValue ? ' style="'.htmlspecialchars($styleAttrValue) : '').'">'.
00675                                                                         htmlspecialchars($p[0]).'</option>';
00676                                 }
00677                         }
00678 
00679                                 // Put together the selector box:
00680                         $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="'.htmlspecialchars($config['itemListStyle']).'"' : ' style="'.$this->fObj->defaultMultipleSelectorStyle.'"';
00681                         $size = intval($conf['size']);
00682                         $size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$conf['autoSizeMax']) : $size;
00683                         $onChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
00684                         $item = '
00685                                 <select id="'.$this->inlineNames['object'].'['.$conf['foreign_table'].']_selector"'.
00686                                                         $this->fObj->insertDefStyle('select').
00687                                                         ($size ? ' size="'.$size.'"' : '').
00688                                                         ' onchange="'.htmlspecialchars($onChange).'"'.
00689                                                         $PA['onFocus'].
00690                                                         $selector_itemListStyle.
00691                                                         ($conf['foreign_unique'] ? ' isunique="isunique"' : '').'>
00692                                         '.implode('
00693                                         ',$opt).'
00694                                 </select>';
00695 
00696                                 // add a "Create new relation" link for adding new relations
00697                                 // this is neccessary, if the size of the selector is "1" or if
00698                                 // there is only one record item in the select-box, that is selected by default
00699                                 // the selector-box creates a new relation on using a onChange event (see some line above)
00700                         $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
00701                         $item .=
00702                                 '<a href="#" onclick="'.htmlspecialchars($onChange).'" align="abstop">'.
00703                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/edit2.gif','width="11" height="12"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
00704                                 '</a>';
00705                                 // wrap the selector and add a spacer to the bottom
00706                         $item = '<div style="margin-bottom: 20px;">'.$item.'</div>';
00707                 }
00708 
00709                 return $item;
00710         }
00711 
00712 
00721         function renderPossibleRecordsSelectorTypeGroupDB($conf, &$PA) {
00722                 $foreign_table = $conf['foreign_table'];
00723 
00724                 $config = $PA['fieldConf']['config'];
00725                 $allowed = $config['allowed'];
00726                 $objectPrefix = $this->inlineNames['object'].'['.$foreign_table.']';
00727 
00728                 $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
00729                 $onClick = "setFormValueOpenBrowser('db','".('|||'.$allowed.'|'.$objectPrefix.'|inline.checkUniqueElement||inline.importElement')."'); return false;";
00730                 $item =
00731                         '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00732                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/insert3.gif','width="14" height="14"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
00733                         '</a>';
00734 
00735                 return $item;
00736         }
00737 
00738 
00746         function getNewRecordLink($objectPrefix, $conf = array()) {
00747                 if ($conf['inline']['inlineNewButtonStyle']) $style = ' style="'.$conf['inline']['inlineNewButtonStyle'].'"';
00748 
00749                 $onClick = "return inline.createNewRecord('$objectPrefix')";
00750                 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createnew',1);
00751 
00752                 if ($conf['appearance']['newRecordLinkAddTitle'])
00753                         $tableTitle .= ' '.$GLOBALS['LANG']->sL($GLOBALS['TCA'][$conf['foreign_table']]['ctrl']['title'],1);
00754 
00755                 $out = '
00756                                 <div class="typo3-newRecordLink">
00757                                         <a href="#" onClick="'.$onClick.'" class="inlineNewButton"'.$style.' title="'.$title.$tableTitle.'">'.
00758                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/new_el.gif','width="11" height="12"').' alt="'.$title.$tableTitle.'" />'.
00759                                         $title.t3lib_div::fixed_lgd_cs($tableTitle, $this->fObj->titleLen).
00760                                         '</a>
00761                                 </div>';
00762                 return $out;
00763         }
00764 
00765 
00772         function addJavaScriptSortable($objectId) {
00773                 $this->fObj->additionalJS_post[] = '
00774                         inline.createDragAndDropSorting("'.$objectId.'");
00775                 ';
00776         }
00777 
00778 
00779         /*******************************************************
00780          *
00781          * Handling of AJAX calls
00782          *
00783          *******************************************************/
00784 
00785 
00793         function initForAJAX($method, &$arguments) {
00794                         // Set t3lib_TCEforms::$RTEcounter to the given value:
00795                 if ($method == 'createNewRecord') {
00796                         $this->fObj->RTEcounter = intval(array_shift($arguments));
00797                 }
00798         }
00799 
00800 
00809         function createNewRecord($domObjectId, $foreignUid = 0) {
00810                 $this->isAjaxCall = true;
00811                         // parse the DOM identifier (string), add the levels to the structure stack (array) and load the TCA config
00812                 $this->parseStructureString($domObjectId, true);
00813                         // the current table - for this table we should add/import records
00814                 $current = $this->inlineStructure['unstable'];
00815                         // the parent table - this table embeds the current table
00816                 $parent = $this->getStructureLevel(-1);
00817                         // get TCA 'config' of the parent table
00818                 $config = $parent['config'];
00819 
00820                         // Put the current level also to the dynNestedStack of TCEforms:
00821                 $this->fObj->pushToDynNestedStack('inline', $this->inlineNames['object']);
00822 
00823                         // dynamically create a new record using t3lib_transferData
00824                 if (!$foreignUid || !t3lib_div::testInt($foreignUid) || $config['foreign_selector']) {
00825                         $record = $this->getNewRecord($this->inlineFirstPid, $current['table']);
00826 
00827                         // dynamically import an existing record (this could be a call from a select box)
00828                 } else {
00829                         $record = $this->getRecord($this->inlineFirstPid, $current['table'], $foreignUid);
00830                 }
00831 
00832                         // now there is a foreign_selector, so there is a new record on the intermediate table, but
00833                         // this intermediate table holds a field, which is responsible for the foreign_selector, so
00834                         // we have to set this field to the uid we get - or if none, to a new uid
00835                 if ($config['foreign_selector'] && $foreignUid) {
00836                         $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_selector']);
00837                                 // For a selector of type group/db, prepend the tablename (<tablename>_<uid>):
00838                         $record[$config['foreign_selector']] = $selConfig['type'] != 'groupdb' ? '' : $selConfig['table'].'_';
00839                         $record[$config['foreign_selector']] .= $foreignUid;
00840                 }
00841 
00842                         // the HTML-object-id's prefix of the dynamically created record
00843                 $objectPrefix = $this->inlineNames['object'].'['.$current['table'].']';
00844                 $objectId = $objectPrefix.'['.$record['uid'].']';
00845 
00846                         // render the foreign record that should passed back to browser
00847                 $item = $this->renderForeignRecord($parent['uid'], $record, $config);
00848                 if($item === false) {
00849                         $jsonArray = array(
00850                                 'data'  => 'Access denied',
00851                                 'scriptCall' => array(
00852                                         "alert('Access denied');",
00853                                 )
00854                         );
00855                         return $this->getJSON($jsonArray);
00856                 }
00857 
00858                         // Encode TCEforms AJAX response with utf-8:
00859                 $item = $GLOBALS['LANG']->csConvObj->utf8_encode($item, $GLOBALS['LANG']->charSet);
00860 
00861                 if (!$current['uid']) {
00862                         $jsonArray = array(
00863                                 'data'  => $item,
00864                                 'scriptCall' => array(
00865                                         "inline.domAddNewRecord('bottom','".$this->inlineNames['object']."_records','$objectPrefix',json.data);",
00866                                         "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."',null,'$foreignUid');"
00867                                 )
00868                         );
00869 
00870                         // append the HTML data after an existing record in the container
00871                 } else {
00872                         $jsonArray = array(
00873                                 'data'  => $item,
00874                                 'scriptCall' => array(
00875                                         "inline.domAddNewRecord('after','".$domObjectId.'_div'."','$objectPrefix',json.data);",
00876                                         "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."','".$current['uid']."','$foreignUid');"
00877                                 )
00878                         );
00879                 }
00880 
00881                         // add the JavaScript data that would have been added at the bottom of a regular TCEforms calls
00882                 $jsonArray['scriptCall'][] = $this->fObj->JSbottom($this->fObj->formName, true);
00883                         // if script.aculo.us Sortable is used, update the Observer to know the the record
00884                 if ($config['appearance']['useSortable'])
00885                         $jsonArray['scriptCall'][] = "inline.createDragAndDropSorting('".$this->inlineNames['object']."_records');";
00886                         // if TCEforms has some JavaScript code to be executed, just do it
00887                 if ($this->fObj->extJSCODE)
00888                         $jsonArray['scriptCall'][] = $this->fObj->extJSCODE;
00889                         // tell the browser to scroll to the newly created record
00890                 $jsonArray['scriptCall'][] = "Element.scrollTo('".$objectId."_div');";
00891                         // fade out and fade in the new record in the browser view to catch the user's eye
00892                 $jsonArray['scriptCall'][] = "inline.fadeOutFadeIn('".$objectId."_div');";
00893 
00894                         // Remove the current level also from the dynNestedStack of TCEforms:
00895                 $this->fObj->popFromDynNestedStack();
00896 
00897                         // return the JSON string
00898                 return $this->getJSON($jsonArray);
00899         }
00900 
00901 
00910         function setExpandedCollapsedState($domObjectId, $expand, $collapse) {
00911                         // parse the DOM identifier (string), add the levels to the structure stack (array), but don't load TCA config
00912                 $this->parseStructureString($domObjectId, false);
00913                         // the current table - for this table we should add/import records
00914                 $current = $this->inlineStructure['unstable'];
00915                         // the top parent table - this table embeds the current table
00916                 $top = $this->getStructureLevel(0);
00917 
00918                         // only do some action if the top record and the current record were saved before
00919                 if (t3lib_div::testInt($top['uid'])) {
00920                         $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
00921                         $inlineViewCurrent =& $inlineView[$top['table']][$top['uid']];
00922 
00923                         $expandUids = t3lib_div::trimExplode(',', $expand);
00924                         $collapseUids = t3lib_div::trimExplode(',', $collapse);
00925 
00926                                 // set records to be expanded
00927                         foreach ($expandUids as $uid) {
00928                                 $inlineViewCurrent[$current['table']][] = $uid;
00929                         }
00930                                 // set records to be collapsed
00931                         foreach ($collapseUids as $uid) {
00932                                 $inlineViewCurrent[$current['table']] = $this->removeFromArray($uid, $inlineViewCurrent[$current['table']]);
00933                         }
00934 
00935                                 // save states back to database
00936                         if (is_array($inlineViewCurrent[$current['table']])) {
00937                                 $GLOBALS['BE_USER']->uc['inlineView'] = serialize($inlineView);
00938                                 $GLOBALS['BE_USER']->writeUC();
00939                         }
00940                 }
00941         }
00942 
00943 
00944         /*******************************************************
00945          *
00946          * Get data from database and handle relations
00947          *
00948          *******************************************************/
00949 
00950 
00961         function getRelatedRecords($table,$field,$row,&$PA,$config) {
00962                 $records = array();
00963 
00964                         // Creating the label for the "No Matching Value" entry.
00965                 $nMV_label = isset($PA['fieldTSConfig']['noMatchingValue_label']) ? $this->fObj->sL($PA['fieldTSConfig']['noMatchingValue_label']) : '[ '.$this->fObj->getLL('l_noMatchingValue').' ]';
00966 
00967                         // Register the required number of elements:
00968                 # $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
00969 
00970                         // Perform modification of the selected items array:
00971                 $itemArray = t3lib_div::trimExplode(',',$PA['itemFormElValue'],1);
00972                 foreach($itemArray as $tk => $tv) {
00973                         $tvP = explode('|',$tv,2);
00974                                 // get the records for this uid using t3lib_transferdata
00975                         $records[] = $this->getRecord($row['pid'], $config['foreign_table'], $tvP[0]);
00976                 }
00977 
00978                 return $records;
00979         }
00980 
00981 
00993         function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector') {
00994                         // ctrl configuration from TCA:
00995                 $tcaTableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
00996                         // Field configuration from TCA:
00997                 $foreign_table = $conf['foreign_table'];
00998                 $foreign_check = $conf[$checkForConfField];
00999 
01000                 $foreignConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_check);
01001                 $PA = $foreignConfig['PA'];
01002                 $config = $PA['fieldConf']['config'];
01003                 
01004                 if ($foreignConfig['type'] == 'select') {
01005                                 // Getting the selector box items from the system
01006                         $selItems = $this->fObj->addSelectOptionsToItemArray($this->fObj->initItemArray($PA['fieldConf']),$PA['fieldConf'],$this->fObj->setTSconfig($table,$row),$field);
01007                         if ($config['itemsProcFunc']) $selItems = $this->fObj->procItems($selItems,$PA['fieldTSConfig']['itemsProcFunc.'],$config,$table,$row,$field);
01008         
01009                                 // Possibly remove some items:
01010                         $removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
01011                         foreach($selItems as $tk => $p) {
01012         
01013                                         // Checking languages and authMode:
01014                                 $languageDeny = $tcaTableCtrl['languageField'] && !strcmp($tcaTableCtrl['languageField'], $field) && !$GLOBALS['BE_USER']->checkLanguageAccess($p[1]);
01015                                 $authModeDeny = $config['form_type']=='select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table,$field,$p[1],$config['authMode']);
01016                                 if (in_array($p[1],$removeItems) || $languageDeny || $authModeDeny)     {
01017                                         unset($selItems[$tk]);
01018                                 } elseif (isset($PA['fieldTSConfig']['altLabels.'][$p[1]])) {
01019                                         $selItems[$tk][0]=$this->fObj->sL($PA['fieldTSConfig']['altLabels.'][$p[1]]);
01020                                 }
01021         
01022                                         // Removing doktypes with no access:
01023                                 if ($table.'.'.$field == 'pages.doktype')       {
01024                                         if (!($GLOBALS['BE_USER']->isAdmin() || t3lib_div::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'],$p[1])))   {
01025                                                 unset($selItems[$tk]);
01026                                         }
01027                                 }
01028                         }
01029                 } else {
01030                         $selItems = false;
01031                 }
01032 
01033                 return $selItems;
01034         }
01035 
01044         function getUniqueIds($records, $conf=array(), $splitValue=false) {
01045                 $uniqueIds = array();
01046 
01047                 if ($conf['foreign_unique'] && count($records)) {
01048                         foreach ($records as $rec) {
01049                                 $value = $rec[$conf['foreign_unique']];
01050                                         // Split the value and extract the table and uid:
01051                                 if ($splitValue) {
01052                                         $valueParts = t3lib_div::trimExplode('|', $value);
01053                                         $itemParts = explode('_', $valueParts[0]);
01054                                         $value = array(
01055                                                 'uid' => array_pop($itemParts),
01056                                                 'table' => implode('_', $itemParts)
01057                                         );
01058                                 }
01059                                 $uniqueIds[$rec['uid']] = $value;
01060                         }
01061                 }
01062 
01063                 return $uniqueIds;
01064         }
01065 
01066 
01077         function getRecord($pid, $table, $uid, $cmd='') {
01078                 $trData = t3lib_div::makeInstance('t3lib_transferData');
01079                 $trData->addRawData = TRUE;
01080                 # $trData->defVals = $this->defVals;
01081                 $trData->lockRecords=1;
01082                 $trData->disableRTE = $GLOBALS['SOBE']->MOD_SETTINGS['disableRTE'];
01083                         // if a new record should be created
01084                 $trData->fetchRecord($table, $uid, ($cmd === 'new' ? 'new' : ''));
01085                 reset($trData->regTableItems_data);
01086                 $rec = current($trData->regTableItems_data);
01087                 $rec['uid'] = $cmd == 'new' ? uniqid('NEW') : $uid;
01088                 if ($cmd=='new') $rec['pid'] = $pid;
01089 
01090                 return $rec;
01091         }
01092 
01093 
01101         function getNewRecord($pid, $table) {
01102                 return $this->getRecord($pid, $table, '', 'new');
01103         }
01104 
01105 
01106         /*******************************************************
01107          *
01108          * Structure stack for handling inline objects/levels
01109          *
01110          *******************************************************/
01111 
01112 
01123         function pushStructure($table, $uid, $field = '', $config = array()) {
01124                 $this->inlineStructure['stable'][] = array(
01125                         'table' => $table,
01126                         'uid' => $uid,
01127                         'field' => $field,
01128                         'config' => $config,
01129                 );
01130                 $this->updateStructureNames();
01131         }
01132 
01133 
01139         function popStructure() {
01140                 if (count($this->inlineStructure['stable'])) {
01141                         $popItem = array_pop($this->inlineStructure['stable']);
01142                         $this->updateStructureNames();
01143                 }
01144                 return $popItem;
01145         }
01146 
01147 
01156         function updateStructureNames() {
01157                 $current = $this->getStructureLevel(-1);
01158                         // if there are still more inline levels available
01159                 if ($current !== false) {
01160                         $lastItemName = $this->getStructureItemName($current);
01161                         $this->inlineNames = array(
01162                                 'form' => $this->prependFormFieldNames.$lastItemName,
01163                                 'object' => $this->prependNaming.'['.$this->inlineFirstPid.']'.$this->getStructurePath(),
01164                         );
01165                         // if there are no more inline levels available
01166                 } else {
01167                         $this->inlineNames = array();
01168                 }
01169         }
01170 
01171 
01178         function getStructureItemName($levelData) {
01179                 if (is_array($levelData)) {
01180                         $name = '['.$levelData['table'].']' .
01181                                         '['.$levelData['uid'].']' .
01182                                         (isset($levelData['field']) ? '['.$levelData['field'].']' : '');
01183                 }
01184                 return $name;
01185         }
01186 
01187 
01196         function getStructureLevel($level) {
01197                 $inlineStructureCount = count($this->inlineStructure['stable']);
01198                 if ($level < 0) $level = $inlineStructureCount+$level;
01199                 if ($level >= 0 && $level < $inlineStructureCount)
01200                         return $this->inlineStructure['stable'][$level];
01201                 else
01202                         return false;
01203         }
01204 
01205 
01213         function getStructurePath($structureDepth = -1) {
01214                 $structureCount = count($this->inlineStructure['stable']);
01215                 if ($structureDepth < 0 || $structureDepth > $structureCount) $structureDepth = $structureCount;
01216 
01217                 for ($i = 1; $i <= $structureDepth; $i++) {
01218                         $current = $this->getStructureLevel(-$i);
01219                         $string = $this->getStructureItemName($current).$string;
01220                 }
01221 
01222                 return $string;
01223         }
01224 
01225 
01238         function parseStructureString($string, $loadConfig = false) {
01239                 $unstable = array();
01240                 $vector = array('table', 'uid', 'field');
01241                 $pattern = '/^'.$this->prependNaming.'\[(.+?)\]\[(.+)\]$/';
01242                 if (preg_match($pattern, $string, $match)) {
01243                         $this->inlineFirstPid = $match[1];
01244                         $parts = explode('][', $match[2]);
01245                         $partsCnt = count($parts);
01246                         for ($i = 0; $i < $partsCnt; $i++) {
01247                                 if ($i > 0 && $i % 3 == 0) {
01248                                                 // load the TCA configuration of the table field and store it in the stack
01249                                         if ($loadConfig) {
01250                                                 t3lib_div::loadTCA($unstable['table']);
01251                                                 $unstable['config'] = $GLOBALS['TCA'][$unstable['table']]['columns'][$unstable['field']]['config'];
01252                                                         // Fetch TSconfig:
01253                                                 $TSconfig = $this->fObj->setTSconfig(
01254                                                         $unstable['table'],
01255                                                         array('uid' => $unstable['uid'], 'pid' => $this->inlineFirstPid),
01256                                                         $unstable['field']
01257                                                 );
01258                                                         // Override TCA field config by TSconfig:
01259                                                 if (!$TSconfig['disabled']) {
01260                                                         $unstable['config'] = $this->fObj->overrideFieldConf($unstable['config'], $TSconfig);
01261                                                 }
01262                                         }
01263                                         $this->inlineStructure['stable'][] = $unstable;
01264                                         $unstable = array();
01265                                 }
01266                                 $unstable[$vector[$i % 3]] = $parts[$i];
01267                         }
01268                         $this->updateStructureNames();
01269                         if (count($unstable)) $this->inlineStructure['unstable'] = $unstable;
01270                 }
01271         }
01272 
01273 
01274         /*******************************************************
01275          *
01276          * Helper functions
01277          *
01278          *******************************************************/
01279 
01280 
01287         function checkConfiguration(&$config) {
01288                 $foreign_table = $config['foreign_table'];
01289 
01290                         // An inline field must have a foreign_table, if not, stop all further inline actions for this field:
01291                 if (!$foreign_table || !is_array($GLOBALS['TCA'][$foreign_table])) {
01292                         return false;
01293                 }
01294                         // Init appearance if not set:
01295                 if (!is_array($config['appearance'])) {
<