/src/typo3_src-4.2.0alpha1/t3lib/class.t3lib_tceforms_inline.php

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