Documentation TYPO3 par Ameos

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 $inlineStructure = array();                 // the structure/hierarchy where working in, e.g. cascading inline tables
00095         var $inlineFirstPid;                                    // the first call of an inline type appeared on this page (pid of record)
00096         var $inlineNames = array();                             // keys: form, object -> hold the name/id for each of them
00097         var $inlineData = array();                              // inline data array used for JSON output
00098         var $inlineView = array();                              // expanded/collapsed states for the current BE user
00099         var $inlineCount = 0;                                   // count the number of inline types used
00100         var $inlineStyles = array();
00101 
00102         var $prependNaming = 'data';                    // how the $this->fObj->prependFormFieldNames should be set ('data' is default)
00103         var $prependFormFieldNames;                             // reference to $this->fObj->prependFormFieldNames
00104         var $prependCmdFieldNames;                              // reference to $this->fObj->prependCmdFieldNames
00105 
00106 
00113         function init(&$tceForms) {
00114                 $this->fObj =& $tceForms;
00115                 $this->backPath =& $tceForms->backPath;
00116                 $this->prependFormFieldNames =& $this->fObj->prependFormFieldNames;
00117                 $this->prependCmdFieldNames =& $this->fObj->prependCmdFieldNames;
00118                 $this->inlineStyles['margin-right'] = '5';
00119         }
00120 
00121 
00132         function getSingleField_typeInline($table,$field,$row,&$PA) {
00133                         // check the TCA configuration - if false is returned, something was wrong
00134                 if ($this->checkConfiguration($PA['fieldConf']['config']) === false) return false;
00135 
00136                         // count the number of processed inline elements
00137                 $this->inlineCount++;
00138 
00139                         // Init:
00140                 $config = $PA['fieldConf']['config'];
00141                 $foreign_table = $config['foreign_table'];
00142                 t3lib_div::loadTCA($foreign_table);
00143 
00144                 $minitems = t3lib_div::intInRange($config['minitems'],0);
00145                 $maxitems = t3lib_div::intInRange($config['maxitems'],0);
00146                 if (!$maxitems) $maxitems=100000;
00147 
00148                         // Register the required number of elements:
00149                 $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
00150 
00151                         // remember the page id (pid of record) where inline editing started first
00152                         // we need that pid for ajax calls, so that they would know where the action takes place on the page structure
00153                 if (!isset($this->inlineFirstPid)) {
00154                                 // if this record is not new, try to fetch the inlineView states
00155                                 // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field
00156                         if (t3lib_div::testInt($row['uid'])) {
00157                                 $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
00158                                 $this->inlineView = $inlineView[$table][$row['uid']];
00159                         }
00160                                 // if pid is negative, fetch the previous record and take its pid
00161                         if ($row['pid'] < 0) {
00162                                 $prevRec = t3lib_BEfunc::getRecord($table, abs($row['pid']));
00163                                 $this->inlineFirstPid = $prevRec['pid'];
00164                                 // take the pid as it is
00165                         } else {
00166                                 $this->inlineFirstPid = $row['pid'];
00167                         }
00168                 }
00169                         // add the current inline job to the structure stack
00170                 $this->pushStructure($table, $row['uid'], $field, $config);
00171                         // e.g. inline[<table>][<uid>][<field>]
00172                 $nameForm = $this->inlineNames['form'];
00173                         // e.g. inline[<pid>][<table1>][<uid1>][<field1>][<table2>][<uid2>][<field2>]
00174                 $nameObject = $this->inlineNames['object'];
00175                         // get the records related to this inline record
00176                 $recordList = $this->getRelatedRecords($table,$field,$row,$PA,$config);
00177                         // set the first and last record to the config array
00178                 $config['inline']['first'] = $recordList[0]['uid'];
00179                 $config['inline']['last'] = $recordList[count($recordList)-1]['uid'];
00180 
00181                         // tell the browser what we have (using JSON later)
00182                 $top = $this->getStructureLevel(0);
00183                 $this->inlineData['config'][$nameObject] = array('table' => $foreign_table);
00184                 $this->inlineData['config'][$nameObject.'['.$foreign_table.']'] = array(
00185                         'min' => $minitems,
00186                         'max' => $maxitems,
00187                         'sortable' => $config['appearance']['useSortable'],
00188                         'top' => array(
00189                                 'table' => $top['table'],
00190                                 'uid'   => $top['uid'],
00191                         ),
00192                 );
00193 
00194                         // if relations are required to be unique, get the uids that have already been used on the foreign side of the relation
00195                 if ($config['foreign_unique']) {
00196                                 // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one:
00197                         $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']);
00198                                 // Get the used unique ids:
00199                         $uniqueIds = $this->getUniqueIds($recordList, $config, $selConfig['type']=='groupdb');
00200                         $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config,'foreign_unique');
00201                         $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1 : count($possibleRecords);
00202                         $this->inlineData['unique'][$nameObject.'['.$foreign_table.']'] = array(
00203                                 'max' => $uniqueMax,
00204                                 'used' => $uniqueIds,
00205                                 'type' => $selConfig['type'],
00206                                 'table' => $config['foreign_table'],
00207                                 'elTable' => $selConfig['table'], // element/record table (one step down in hierarchy)
00208                                 'field' => $config['foreign_unique'],
00209                                 'selector' => $selConfig['selector'],
00210                                 'possible' => $this->getPossibleRecordsFlat($possibleRecords),
00211                         );
00212                 }
00213 
00214                         // if it's required to select from possible child records (reusable children), add a selector box
00215                 if ($config['foreign_selector']) {
00216                                 // if not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array
00217                         if (!$config['foreign_unique']) {
00218                                 $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config);
00219                                 $uniqueIds = array();
00220                         }
00221                         $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords,$config,$uniqueIds);
00222                         $item .= $selectorBox;
00223                 }
00224 
00225                         // wrap all inline fields of a record with a <div> (like a container)
00226                 $item .= '<div id="'.$nameObject.'">';
00227 
00228                         // define how to show the "Create new record" link - if there are more than maxitems, hide it
00229                 if (count($recordList) >= $maxitems || ($uniqueMax > 0 && count($recordList) >= $uniqueMax)) {
00230                         $config['inline']['inlineNewButtonStyle'] = 'display: none;';
00231                 }
00232                         // add the "Create new record" link before all child records
00233                 if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'top'))) {
00234                         $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
00235                 }
00236 
00237                 $item .= '<div id="'.$nameObject.'_records">';
00238                 $relationList = array();
00239                 if (count($recordList)) {
00240                         foreach ($recordList as $rec) {
00241                                 $item .= $this->renderForeignRecord($row['uid'],$rec,$config);
00242                                 $relationList[] = $rec['uid'];
00243                         }
00244                 }
00245                 $item .= '</div>';
00246 
00247                         // add the "Create new record" link after all child records
00248                 if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'bottom'))) {
00249                         $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
00250                 }
00251 
00252                         // add Drag&Drop functions for sorting to TCEforms::$additionalJS_post
00253                 if (count($relationList) > 1 && $config['appearance']['useSortable'])
00254                         $this->addJavaScriptSortable($nameObject.'_records');
00255                         // publish the uids of the child records in the given order to the browser
00256                 $item .= '<input type="hidden" name="'.$nameForm.'" value="'.implode(',', $relationList).'" class="inlineRecord" />';
00257                         // close the wrap for all inline fields (container)
00258                 $item .= '</div>';
00259 
00260                         // on finishing this section, remove the last item from the structure stack
00261                 $this->popStructure();
00262 
00263                         // if this was the first call to the inline type, restore the values
00264                 if (!$this->getStructureDepth()) {
00265                         unset($this->inlineFirstPid);
00266                 }
00267 
00268                 return $item;
00269         }
00270 
00271 
00272         /*******************************************************
00273          *
00274          * Regular rendering of forms, fields, etc.
00275          *
00276          *******************************************************/
00277 
00278 
00287         function renderForeignRecord($parentUid, $rec, $config = array()) {
00288                 $foreign_table = $config['foreign_table'];
00289                 $foreign_field = $config['foreign_field'];
00290                 $foreign_selector = $config['foreign_selector'];
00291 
00292                         // Send a mapping information to the browser via JSON:
00293                         // e.g. data[<curTable>][<curId>][<curField>] => data[<pid>][<parentTable>][<parentId>][<parentField>][<curTable>][<curId>][<curField>]
00294                 $this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
00295 
00296                         // Set this variable if we handle a brand new unsaved record:
00297                 $isNewRecord = t3lib_div::testInt($rec['uid']) ? false : true;
00298                         // If there is a selector field, normalize it:
00299                 if ($foreign_selector) {
00300                         $rec[$foreign_selector] = $this->normalizeUid($rec[$foreign_selector]);
00301                 }
00302 
00303                 $hasAccess = $this->checkAccess($isNewRecord?'new':'edit', $foreign_table, $rec['uid']);
00304 
00305                 if(!$hasAccess) return false;
00306 
00307                         // Get the current naming scheme for DOM name/id attributes:
00308                 $nameObject = $this->inlineNames['object'];
00309                 $appendFormFieldNames = '['.$foreign_table.']['.$rec['uid'].']';
00310                 $formFieldNames = $nameObject.$appendFormFieldNames;
00311 
00312                 $header = $this->renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config);
00313                 $combination = $this->renderCombinationTable($rec, $appendFormFieldNames, $config);
00314                 $fields = $this->fObj->getMainFields($foreign_table,$rec);
00315                 $fields = $this->wrapFormsSection($fields);
00316 
00317                 if ($isNewRecord) {
00318                                 // show this record expanded or collapsed
00319                         $isExpanded = is_array($config['appearance']) && $config['appearance']['collapseAll'] ? 1 : 0;
00320                                 // get the top parent table
00321                         $top = $this->getStructureLevel(0);
00322                         $ucFieldName = 'uc['.$top['table'].']['.$top['uid'].']'.$appendFormFieldNames;
00323                                 // set additional fields for processing for saving
00324                         $fields .= '<input type="hidden" name="'.$this->prependFormFieldNames.$appendFormFieldNames.'[pid]" value="'.$rec['pid'].'"/>';
00325                         $fields .= '<input type="hidden" name="'.$ucFieldName.'" value="'.$isExpanded.'" />';
00326 
00327                 } else {
00328                                 // show this record expanded or collapsed
00329                         $isExpanded = $this->getExpandedCollapsedState($foreign_table, $rec['uid']);
00330                                 // set additional field for processing for saving
00331                         $fields .= '<input type="hidden" name="'.$this->prependCmdFieldNames.$appendFormFieldNames.'[delete]" value="1" disabled="disabled" />';
00332                 }
00333 
00334                         // if this record should be shown collapsed
00335                 if (!$isExpanded) $appearanceStyleFields = ' style="display: none;"';
00336 
00337                         // set the record container with data for output
00338                 $out = '<div id="'.$formFieldNames.'_header">'.$header.'</div>';
00339                 $out .= '<div id="'.$formFieldNames.'_fields"'.$appearanceStyleFields.'>'.$fields.$combination.'</div>';
00340                         // wrap the header, fields and combination part of a child record with a div container
00341                 $out = '<div id="'.$formFieldNames.'_div"'.($isNewRecord ? ' class="inlineIsNewRecord"' : '').'>' . $out . '</div>';
00342 
00343                 return $out;
00344         }
00345 
00346 
00357         function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config = array()) {
00358                         // Init:
00359                 $formFieldNames = $this->inlineNames['object'].'['.$foreign_table.']['.$rec['uid'].']';
00360                 $expandSingle = $config['appearance']['expandSingle'] ? 1 : 0;
00361                 $onClick = "return inline.expandCollapseRecord('".htmlspecialchars($formFieldNames)."', $expandSingle)";
00362 
00363                         // Pre-Processing:
00364                 $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
00365                 $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? true : false;
00366                 $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? true : false;
00367                         // Get the record title/label for a record:
00368                         // render using a self-defined user function
00369                 if ($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc']) {
00370                         $params = array(
00371                                 'table' => $foreign_table,
00372                                 'row'   => $rec,
00373                                 'title' => '',
00374                                 'isOnSymmetricSide' => $isOnSymmetricSide
00375                         );
00376                         $null = null;   // callUserFunction requires a third parameter, but we don't want to give $this as reference!
00377                         t3lib_div::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null);
00378                         $recTitle = $params['title'];
00379                         // render the special alternative title
00380                 } elseif ($hasForeignLabel || $hasSymmetricLabel) {
00381                         $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
00382                         $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol);
00383                                 // Render title for everything else than group/db:
00384                         if ($foreignConfig['type'] != 'groupdb') {
00385                                 $recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
00386                                 // Render title for group/db:
00387                         } else {
00388                                         // $recTitle could be something like: "tx_table_123|...",
00389                                 $valueParts = t3lib_div::trimExplode('|', $rec[$titleCol]);
00390                                 $itemParts = t3lib_div::revExplode('_', $valueParts[0], 2);
00391                                 $recTemp = t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]);
00392                                 $recTitle = t3lib_BEfunc::getRecordTitle($itemParts[0], $recTemp, true);
00393                         }
00394                         $recTitle = t3lib_BEfunc::getRecordTitlePrep($recTitle);
00395                         if (!strcmp(trim($recTitle),'')) {
00396                                 $recTitle = t3lib_BEfunc::getNoRecordTitle(true);
00397                         }
00398                         // render the standard
00399                 } else {
00400                         $recTitle = t3lib_BEfunc::getRecordTitle($foreign_table, $rec, true);
00401                 }
00402 
00403                 $altText = t3lib_BEfunc::getRecordIconAltText($rec, $foreign_table);
00404                 $iconImg =
00405                         '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.t3lib_iconWorks::getIconImage(
00406                                 $foreign_table, $rec, $this->backPath,
00407                                 'title="'.htmlspecialchars($altText).'" class="absmiddle"'
00408                         ).'</a>';
00409 
00410                 $label =
00411                         '<a href="#" onclick="'.htmlspecialchars($onClick).'" style="display: block;">'.
00412                                 '<span id="'.$formFieldNames.'_label">'.$recTitle.'</span>'.
00413                         '</a>';
00414 
00415                 $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config);
00416 
00417                         // @TODO: Check the table wrapping and the CSS definitions
00418                 $header =
00419                         '<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-right: '.$this->inlineStyles['margin-right'].'px;"'.
00420                         ($this->fObj->borderStyle[2] ? ' background="'.htmlspecialchars($this->backPath.$this->fObj->borderStyle[2]).'"':'').
00421                         ($this->fObj->borderStyle[3] ? ' class="'.htmlspecialchars($this->fObj->borderStyle[3]).'"':'').'>' .
00422                         '<tr class="class-main12"><td width="18">'.$iconImg.'</td><td align="left"><b>'.$label.'</b></td><td align="right">'.$ctrl.'</td></tr></table>';
00423 
00424                 return $header;
00425         }
00426 
00427 
00438         function renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config = array()) {
00439                         // Initialize:
00440                 $cells=array();
00441                 $isNewItem = substr($rec['uid'], 0, 3) == 'NEW';
00442 
00443                 $tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl'];
00444                 $tcaTableCols =& $GLOBALS['TCA'][$foreign_table]['columns'];
00445 
00446                 $isPagesTable = $foreign_table == 'pages' ? true : false;
00447                 $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
00448                 $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || (!$isOnSymmetricSide && $config['foreign_sortby']) || ($isOnSymmetricSide && $config['symmetric_sortby']) ? true : false;
00449 
00450                 $nameObjectFt = $this->inlineNames['object'].'['.$foreign_table.']';
00451                 $nameObjectFtId = $nameObjectFt.'['.$rec['uid'].']';
00452 
00453                 $calcPerms = $GLOBALS['BE_USER']->calcPerms(
00454                         t3lib_BEfunc::readPageAccess($rec['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1))
00455                 );
00456 
00457                         // If the listed table is 'pages' we have to request the permission settings for each page:
00458                 if ($isPagesTable)      {
00459                         $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages',$rec['uid']));
00460                 }
00461 
00462                         // This expresses the edit permissions for this particular element:
00463                 $permsEdit = ($isPagesTable && ($localCalcPerms&2)) || (!$isPagesTable && ($calcPerms&16));
00464 
00465                         // "Info": (All records)
00466                 if (!$isNewItem)
00467                         $cells[]='<a href="#" onclick="'.htmlspecialchars('top.launchView(\''.$foreign_table.'\', \''.$rec['uid'].'\'); return false;').'">'.
00468                                 '<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="" />'.
00469                                 '</a>';
00470 
00471                         // If the table is NOT a read-only table, then show these links:
00472                 if (!$tcaTableCtrl['readOnly']) {
00473 
00474                                 // "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):
00475                         if ($enableManualSorting || $tcaTableCtrl['useColumnsForDefaultValues'])        {
00476                                 if (
00477                                         (!$isPagesTable && ($calcPerms&16)) ||  // For NON-pages, must have permission to edit content on this parent page
00478                                         ($isPagesTable && ($calcPerms&8))               // For pages, must have permission to create new pages here.
00479                                         )       {
00480                                         $onClick = "return inline.createNewRecord('".$nameObjectFt."','".$rec['uid']."')";
00481                                         if ($config['inline']['inlineNewButtonStyle']) {
00482                                                 $style = ' style="'.$config['inline']['inlineNewButtonStyle'].'"';
00483                                         }
00484                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="inlineNewButton"'.$style.'>'.
00485                                                         '<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="" />'.
00486                                                         '</a>';
00487                                 }
00488                         }
00489 
00490                                 // Drag&Drop Sorting: Sortable handler for script.aculo.us
00491                         if ($permsEdit && $enableManualSorting && $config['appearance']['useSortable']) {
00492                                 $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" />';
00493                         }
00494 
00495                                 // "Up/Down" links
00496                         if ($permsEdit && $enableManualSorting) {
00497                                 $onClick = "return inline.changeSorting('".$nameObjectFtId."', '1')";   // Up
00498                                 $style = $config['inline']['first'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
00499                                 $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingUp" '.$style.'>'.
00500                                                 '<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="" />'.
00501                                                 '</a>';
00502 
00503                                 $onClick = "return inline.changeSorting('".$nameObjectFtId."', '-1')";  // Down
00504                                 $style = $config['inline']['last'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
00505                                 $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingDown" '.$style.'>'.
00506                                                 '<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="" />'.
00507                                                 '</a>';
00508                         }
00509 
00510                                 // "Hide/Unhide" links:
00511                         $hiddenField = $tcaTableCtrl['enablecolumns']['disabled'];
00512                         if ($permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields',$foreign_table.':'.$hiddenField))) {
00513                                 $onClick = "return inline.enableDisableRecord('".$nameObjectFtId."')";
00514                                 if ($rec[$hiddenField]) {
00515                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00516                                                         '<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" />'.
00517                                                         '</a>';
00518                                 } else {
00519                                         $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00520                                                         '<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" />'.
00521                                                         '</a>';
00522                                 }
00523                         }
00524 
00525                                 // "Delete" link:
00526                         if (
00527                                 ($isPagesTable && ($localCalcPerms&4)) || (!$isPagesTable && ($calcPerms&16))
00528                                 )       {
00529                                 $onClick = "inline.deleteRecord('".$nameObjectFtId."');";
00530                                 $cells[]='<a href="#" onclick="'.htmlspecialchars('if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')).')) {   '.$onClick.' } return false;').'">'.
00531                                                 '<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="" />'.
00532                                                 '</a>';
00533                         }
00534                 }
00535 
00536                         // If the record is edit-locked by another user, we will show a little warning sign:
00537                 if ($lockInfo=t3lib_BEfunc::isRecordLocked($foreign_table,$rec['uid'])) {
00538                         $cells[]='<a href="#" onclick="'.htmlspecialchars('alert('.$GLOBALS['LANG']->JScharCode($lockInfo['msg']).');return false;').'">'.
00539                                         '<img'.t3lib_iconWorks::skinImg('','gfx/recordlock_warning3.gif','width="17" height="12"').' title="'.htmlspecialchars($lockInfo['msg']).'" alt="" />'.
00540                                         '</a>';
00541                 }
00542 
00543                         // Compile items into a DIV-element:
00544                 return '
00545                                                                                         <!-- CONTROL PANEL: '.$foreign_table.':'.$rec['uid'].' -->
00546                                                                                         <div class="typo3-DBctrl">'.implode('',$cells).'</div>';
00547         }
00548 
00549 
00560         function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array()) {
00561                 $foreign_table = $config['foreign_table'];
00562                 $foreign_selector = $config['foreign_selector'];
00563 
00564                 if ($foreign_selector && $config['appearance']['useCombination']) {
00565                         $comboConfig = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector]['config'];
00566                         $comboRecord = array();
00567 
00568                                 // record does already exist, so load it
00569                         if (t3lib_div::testInt($rec[$foreign_selector])) {
00570                                 $comboRecord = $this->getRecord(
00571                                         $this->inlineFirstPid,
00572                                         $comboConfig['foreign_table'],
00573                                         $rec[$foreign_selector]
00574                                 );
00575                                 $isNewRecord = false;
00576                                 // it's a new record, so get some default data
00577                         } else {
00578                                 $comboRecord = $this->getNewRecord(
00579                                         $this->inlineFirstPid,
00580                                         $comboConfig['foreign_table']
00581                                 );
00582                                 $isNewRecord = true;
00583                         }
00584 
00585                                 // get the TCEforms interpretation of the TCA of the child table
00586                         $out = $this->fObj->getMainFields($comboConfig['foreign_table'], $comboRecord);
00587                         $out = $this->wrapFormsSection($out, array(), array('class' => 'wrapperAttention'));
00588 
00589                                 // if this is a new record, add a pid value to store this record and the pointer value for the intermediate table
00590                         if ($isNewRecord) {
00591                                 $comboFormFieldName = $this->prependFormFieldNames.'['.$comboConfig['foreign_table'].']['.$comboRecord['uid'].'][pid]';
00592                                 $out .= '<input type="hidden" name="'.$comboFormFieldName.'" value="'.$this->inlineFirstPid.'"/>';
00593                         }
00594 
00595                                 // if the foreign_selector field is also responsible for uniqueness, tell the browser the uid of the "other" side of the relation
00596                         if ($isNewRecord || $config['foreign_unique'] == $foreign_selector) {
00597                                 $parentFormFieldName = $this->prependFormFieldNames.$appendFormFieldNames.'['.$foreign_selector.']';
00598                                 $out .= '<input type="hidden" name="'.$parentFormFieldName.'" value="'.$comboRecord['uid'].'" />';
00599                         }
00600                 }
00601 
00602                 return $out;
00603         }
00604 
00605 
00615         function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds=array()) {
00616                 $foreign_table = $conf['foreign_table'];
00617                 $foreign_selector = $conf['foreign_selector'];
00618 
00619                 $selConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_selector);
00620                 $config = $selConfig['PA']['fieldConf']['config'];
00621 
00622                 if ($selConfig['type'] == 'select') {
00623                         $item = $this->renderPossibleRecordsSelectorTypeSelect($selItems, $conf, $selConfig['PA'], $uniqueIds);
00624                 } elseif ($selConfig['type'] == 'groupdb') {
00625                         $item = $this->renderPossibleRecordsSelectorTypeGroupDB($conf, $selConfig['PA']);
00626                 }
00627 
00628                 return $item;
00629         }
00630 
00631 
00642         function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds=array()) {
00643                 $foreign_table = $conf['foreign_table'];
00644                 $foreign_selector = $conf['foreign_selector'];
00645 
00646                 $PA = array();
00647                 $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
00648                 $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
00649                 $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$foreign_selector);
00650                 $config = $PA['fieldConf']['config'];
00651 
00652                 if(!$disabled) {
00653                                 // Create option tags:
00654                         $opt = array();
00655                         $styleAttrValue = '';
00656                         foreach($selItems as $p)        {
00657                                 if ($config['iconsInOptionTags'])       {
00658                                         $styleAttrValue = $this->fObj->optionTagStyle($p[2]);
00659                                 }
00660                                 if (!in_array($p[1], $uniqueIds)) {
00661                                         $opt[]= '<option value="'.htmlspecialchars($p[1]).'"'.
00662                                                                         ' style="'.(in_array($p[1], $uniqueIds) ? '' : '').
00663                                                                         ($styleAttrValue ? ' style="'.htmlspecialchars($styleAttrValue) : '').'">'.
00664                                                                         htmlspecialchars($p[0]).'</option>';
00665                                 }
00666                         }
00667 
00668                                 // Put together the selector box:
00669                         $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="'.htmlspecialchars($config['itemListStyle']).'"' : ' style="'.$this->fObj->defaultMultipleSelectorStyle.'"';
00670                         $size = intval($conf['size']);
00671                         $size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$conf['autoSizeMax']) : $size;
00672                         $onChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
00673                         $item = '
00674                                 <select id="'.$this->inlineNames['object'].'['.$conf['foreign_table'].']_selector"'.
00675                                                         $this->fObj->insertDefStyle('select').
00676                                                         ($size ? ' size="'.$size.'"' : '').
00677                                                         ' onchange="'.htmlspecialchars($onChange).'"'.
00678                                                         $PA['onFocus'].
00679                                                         $selector_itemListStyle.
00680                                                         ($conf['foreign_unique'] ? ' isunique="isunique"' : '').'>
00681                                         '.implode('
00682                                         ',$opt).'
00683                                 </select>';
00684 
00685                                 // add a "Create new relation" link for adding new relations
00686                                 // this is neccessary, if the size of the selector is "1" or if
00687                                 // there is only one record item in the select-box, that is selected by default
00688                                 // the selector-box creates a new relation on using a onChange event (see some line above)
00689                         $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
00690                         $item .=
00691                                 '<a href="#" onclick="'.htmlspecialchars($onChange).'" align="abstop">'.
00692                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/edit2.gif','width="11" height="12"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
00693                                 '</a>';
00694                                 // wrap the selector and add a spacer to the bottom
00695                         $item = '<div style="margin-bottom: 20px;">'.$item.'</div>';
00696                 }
00697 
00698                 return $item;
00699         }
00700 
00701 
00710         function renderPossibleRecordsSelectorTypeGroupDB($conf, &$PA) {
00711                 $foreign_table = $conf['foreign_table'];
00712 
00713                 $config = $PA['fieldConf']['config'];
00714                 $allowed = $config['allowed'];
00715                 $objectPrefix = $this->inlineNames['object'].'['.$foreign_table.']';
00716 
00717                 $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
00718                 $onClick = "setFormValueOpenBrowser('db','".('|||'.$allowed.'|'.$objectPrefix.'|inline.checkUniqueElement||inline.importElement')."'); return false;";
00719                 $item =
00720                         '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
00721                                 '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/insert3.gif','width="14" height="14"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
00722                         '</a>';
00723 
00724                 return $item;
00725         }
00726 
00727 
00735         function getNewRecordLink($objectPrefix, $conf = array()) {
00736                 if ($conf['inline']['inlineNewButtonStyle']) $style = ' style="'.$conf['inline']['inlineNewButtonStyle'].'"';
00737 
00738                 $onClick = "return inline.createNewRecord('$objectPrefix')";
00739                 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createnew',1);
00740 
00741                 if ($conf['appearance']['newRecordLinkAddTitle'])
00742                         $tableTitle .= ' '.$GLOBALS['LANG']->sL($GLOBALS['TCA'][$conf['foreign_table']]['ctrl']['title'],1);
00743 
00744                 $out = '
00745                                 <div class="typo3-newRecordLink">
00746                                         <a href="#" onClick="'.$onClick.'" class="inlineNewButton"'.$style.' title="'.$title.$tableTitle.'">'.
00747                                         '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/new_el.gif','width="11" height="12"').' alt="'.$title.$tableTitle.'" />'.
00748                                         $title.t3lib_div::fixed_lgd_cs($tableTitle, $this->fObj->titleLen).
00749                                         '</a>
00750                                 </div>';
00751                 return $out;
00752         }
00753 
00754 
00761         function addJavaScriptSortable($objectId) {
00762                 $this->fObj->additionalJS_post[] = '
00763                         inline.createDragAndDropSorting("'.$objectId.'");
00764                 ';
00765         }
00766 
00767 
00768         /*******************************************************
00769          *
00770          * Handling of AJAX calls
00771          *
00772          *******************************************************/
00773 
00774 
00783         function createNewRecord($domObjectId, $foreignUid = 0) {
00784                         // parse the DOM identifier (string), add the levels to the structure stack (array) and load the TCA config
00785                 $this->parseStructureString($domObjectId, true);
00786                         // the current table - for this table we should add/import records
00787                 $current = $this->inlineStructure['unstable'];
00788                         // the parent table - this table embeds the current table
00789                 $parent = $this->getStructureLevel(-1);
00790                         // get TCA 'config' of the parent table
00791                 $config = $parent['config'];
00792                 
00793                         // dynamically create a new record using t3lib_transferData
00794                 if (!$foreignUid || !t3lib_div::testInt($foreignUid) || $config['foreign_selector']) {
00795                         $record = $this->getNewRecord($this->inlineFirstPid, $current['table']);
00796 
00797                         // dynamically import an existing record (this could be a call from a select box)
00798                 } else {
00799                         $record = $this->getRecord($this->inlineFirstPid, $current['table'], $foreignUid);
00800                 }
00801 
00802                         // now there is a foreign_selector, so there is a new record on the intermediate table, but
00803                         // this intermediate table holds a field, which is responsible for the foreign_selector, so
00804                         // we have to set this field to the uid we get - or if none, to a new uid
00805                 if ($config['foreign_selector'] && $foreignUid) {
00806                         $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_selector']);
00807                                 // For a selector of type group/db, prepend the tablename (<tablename>_<uid>):
00808                         $record[$config['foreign_selector']] = $selConfig['type'] != 'groupdb' ? '' : $selConfig['table'].'_';
00809                         $record[$config['foreign_selector']] .= $foreignUid;
00810                 }
00811 
00812                         // the HTML-object-id's prefix of the dynamically created record
00813                 $objectPrefix = $this->inlineNames['object'].'['.$current['table'].']';
00814                 $objectId = $objectPrefix.'['.$record['uid'].']';
00815 
00816                         // render the foreign record that should passed back to browser
00817                 $item = $this->renderForeignRecord($parent['uid'], $record, $config);
00818                 if($item === false) {
00819                         $jsonArray = array(
00820                                 'data'  => 'Access denied',
00821                                 'scriptCall' => array(
00822                                         "alert('Access denied');",
00823                                 )
00824                         );
00825                         return $this->getJSON($jsonArray);
00826                 }
00827 
00828                         // Encode TCEforms AJAX response with utf-8:
00829                 $item = $GLOBALS['LANG']->csConvObj->utf8_encode($item, $GLOBALS['LANG']->charSet);
00830 
00831                 if (!$current['uid']) {
00832                         $jsonArray = array(
00833                                 'data'  => $item,
00834                                 'scriptCall' => array(
00835                                         "inline.domAddNewRecord('bottom','".$this->inlineNames['object']."_records','$objectPrefix',json.data);",
00836                                         "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."',null,'$foreignUid');"
00837                                 )
00838                         );
00839 
00840                         // append the HTML data after an existing record in the container
00841                 } else {
00842                         $jsonArray = array(
00843                                 'data'  => $item,
00844                                 'scriptCall' => array(
00845                                         "inline.domAddNewRecord('after','".$domObjectId.'_div'."','$objectPrefix',json.data);",
00846                                         "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."','".$current['uid']."','$foreignUid');"
00847                                 )
00848                         );
00849                 }
00850 
00851                         // add the JavaScript data that would have been added at the bottom of a regular TCEforms calls
00852                 $jsonArray['scriptCall'][] = $this->fObj->JSbottom($this->fObj->formName, true);
00853                         // if script.aculo.us Sortable is used, update the Observer to know the the record
00854                 if ($config['appearance']['useSortable'])
00855                         $jsonArray['scriptCall'][] = "inline.createDragAndDropSorting('".$this->inlineNames['object']."_records');";
00856                         // if TCEforms has some JavaScript code to be executed, just do it
00857                 if ($this->fObj->extJSCODE)
00858                         $jsonArray['scriptCall'][] = $this->fObj->extJSCODE;
00859                         // tell the browser to scroll to the newly created record
00860                 $jsonArray['scriptCall'][] = "Element.scrollTo('".$objectId."_div');";
00861                         // fade out and fade in the new record in the browser view to catch the user's eye
00862                 $jsonArray['scriptCall'][] = "inline.fadeOutFadeIn('".$objectId."_div');";
00863 
00864                         // return the JSON string
00865                 return $this->getJSON($jsonArray);
00866         }
00867 
00868 
00877         function setExpandedCollapsedState($domObjectId, $expand, $collapse) {
00878                         // parse the DOM identifier (string), add the levels to the structure stack (array), but don't load TCA config
00879                 $this->parseStructureString($domObjectId, false);
00880                         // the current table - for this table we should add/import records
00881                 $current = $this->inlineStructure['unstable'];
00882                         // the top parent table - this table embeds the current table
00883                 $top = $this->getStructureLevel(0);
00884 
00885                         // only do some action if the top record and the current record were saved before
00886                 if (t3lib_div::testInt($top['uid'])) {
00887                         $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
00888                         $inlineViewCurrent =& $inlineView[$top['table']][$top['uid']];
00889 
00890                         $expandUids = t3lib_div::trimExplode(',', $expand);
00891                         $collapseUids = t3lib_div::trimExplode(',', $collapse);
00892 
00893                                 // set records to be expanded
00894                         foreach ($expandUids as $uid) {
00895                                 $inlineViewCurrent[$current['table']][] = $uid;
00896                         }
00897                                 // set records to be collapsed
00898                         foreach ($collapseUids as $uid) {
00899                                 $inlineViewCurrent[$current['table']] = $this->removeFromArray($uid, $inlineViewCurrent[$current['table']]);
00900                         }
00901 
00902                                 // save states back to database
00903                         if (is_array($inlineViewCurrent[$current['table']])) {
00904                                 $GLOBALS['BE_USER']->uc['inlineView'] = serialize($inlineView);
00905                                 $GLOBALS['BE_USER']->writeUC();
00906                         }
00907                 }
00908         }
00909 
00910 
00911         /*******************************************************
00912          *
00913          * Get data from database and handle relations
00914          *
00915          *******************************************************/
00916 
00917 
00928         function getRelatedRecords($table,$field,$row,&$PA,$config) {
00929                 $records = array();
00930 
00931                         // Creating the label for the "No Matching Value" entry.
00932                 $nMV_label = isset($PA['fieldTSConfig']['noMatchingValue_label']) ? $this->fObj->sL($PA['fieldTSConfig']['noMatchingValue_label']) : '[ '.$this->fObj->getLL('l_noMatchingValue').' ]';
00933 
00934                         // Register the required number of elements:
00935                 # $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
00936 
00937                         // Perform modification of the selected items array:
00938                 $itemArray = t3lib_div::trimExplode(',',$PA['itemFormElValue'],1);
00939                 foreach($itemArray as $tk => $tv) {
00940                         $tvP = explode('|',$tv,2);
00941                                 // get the records for this uid using t3lib_transferdata
00942                         $records[] = $this->getRecord($row['pid'], $config['foreign_table'], $tvP[0]);
00943                 }
00944 
00945                 return $records;
00946         }
00947 
00948 
00960         function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector') {
00961                         // ctrl configuration from TCA:
00962                 $tcaTableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
00963                         // Field configuration from TCA:
00964                 $foreign_table = $conf['foreign_table'];
00965                 $foreign_check = $conf[$checkForConfField];
00966 
00967                 $foreignConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_check);
00968                 $PA = $foreignConfig['PA'];
00969                 $config = $PA['fieldConf']['config'];
00970                 
00971                 if ($foreignConfig['type'] == 'select') {
00972                                 // Getting the selector box items from the system
00973                         $selItems = $this->fObj->addSelectOptionsToItemArray($this->fObj->initItemArray($PA['fieldConf']),$PA['fieldConf'],$this->fObj->setTSconfig($table,$row),$field);
00974                         if ($config['itemsProcFunc']) $selItems = $this->fObj->procItems($selItems,$PA['fieldTSConfig']['itemsProcFunc.'],$config,$table,$row,$field);
00975         
00976                                 // Possibly remove some items:
00977                         $removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
00978                         foreach($selItems as $tk => $p) {
00979         
00980                                         // Checking languages and authMode:
00981                                 $languageDeny = $tcaTableCtrl['languageField'] && !strcmp($tcaTableCtrl['languageField'], $field) && !$GLOBALS['BE_USER']->checkLanguageAccess($p[1]);
00982                                 $authModeDeny = $config['form_type']=='select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table,$field,$p[1],$config['authMode']);
00983                                 if (in_array($p[1],$removeItems) || $languageDeny || $authModeDeny)     {
00984                                         unset($selItems[$tk]);
00985                                 } elseif (isset($PA['fieldTSConfig']['altLabels.'][$p[1]])) {
00986                                         $selItems[$tk][0]=$this->fObj->sL($PA['fieldTSConfig']['altLabels.'][$p[1]]);
00987                                 }
00988         
00989                                         // Removing doktypes with no access:
00990                                 if ($table.'.'.$field == 'pages.doktype')       {
00991                                         if (!($GLOBALS['BE_USER']->isAdmin() || t3lib_div::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'],$p[1])))   {
00992                                                 unset($selItems[$tk]);
00993                                         }
00994                                 }
00995                         }
00996                 } else {
00997                         $selItems = false;
00998                 }
00999 
01000                 return $selItems;
01001         }
01002 
01011         function getUniqueIds($records, $conf=array(), $splitValue=false) {
01012                 $uniqueIds = array();
01013 
01014                 if ($conf['foreign_unique'] && count($records)) {
01015                         foreach ($records as $rec) {
01016                                 $value = $rec[$conf['foreign_unique']];
01017                                         // Split the value and extract the table and uid:
01018                                 if ($splitValue) {
01019                                         $valueParts = t3lib_div::trimExplode('|', $value);
01020                                         $itemParts = explode('_', $valueParts[0]);
01021                                         $value = array(
01022                                                 'uid' => array_pop($itemParts),
01023                                                 'table' => implode('_', $itemParts)
01024                                         );
01025                                 }
01026                                 $uniqueIds[$rec['uid']] = $value;
01027                         }
01028                 }
01029 
01030                 return $uniqueIds;
01031         }
01032 
01033 
01044         function getRecord($pid, $table, $uid, $cmd='') {
01045                 $trData = t3lib_div::makeInstance('t3lib_transferData');
01046                 $trData->addRawData = TRUE;
01047                 # $trData->defVals = $this->defVals;
01048                 $trData->lockRecords=1;
01049                 $trData->disableRTE = $GLOBALS['SOBE']->MOD_SETTINGS['disableRTE'];
01050                         // if a new record should be created
01051                 $trData->fetchRecord($table, $uid, ($cmd === 'new' ? 'new' : ''));
01052                 reset($trData->regTableItems_data);
01053                 $rec = current($trData->regTableItems_data);
01054                 $rec['uid'] = $cmd == 'new' ? uniqid('NEW') : $uid;
01055                 if ($cmd=='new') $rec['pid'] = $pid;
01056 
01057                 return $rec;
01058         }
01059 
01060 
01068         function getNewRecord($pid, $table) {
01069                 return $this->getRecord($pid, $table, '', 'new');
01070         }
01071 
01072 
01073         /*******************************************************
01074          *
01075          * Structure stack for handling inline objects/levels
01076          *
01077          *******************************************************/
01078 
01079 
01090         function pushStructure($table, $uid, $field = '', $config = array()) {
01091                 $this->inlineStructure['stable'][] = array(
01092                         'table' => $table,
01093                         'uid' => $uid,
01094                         'field' => $field,
01095                         'config' => $config,
01096                 );
01097                 $this->updateStructureNames();
01098         }
01099 
01100 
01106         function popStructure() {
01107                 if (count($this->inlineStructure['stable'])) {
01108                         $popItem = array_pop($this->inlineStructure['stable']);
01109                         $this->updateStructureNames();
01110                 }
01111                 return $popItem;
01112         }
01113 
01114 
01123         function updateStructureNames() {
01124                 $current = $this->getStructureLevel(-1);
01125                         // if there are still more inline levels available
01126                 if ($current !== false) {
01127                         $lastItemName = $this->getStructureItemName($current);
01128                         $this->inlineNames = array(
01129                                 'form' => $this->prependFormFieldNames.$lastItemName,
01130                                 'object' => $this->prependNaming.'['.$this->inlineFirstPid.']'.$this->getStructurePath(),
01131                         );
01132                         // if there are no more inline levels available
01133                 } else {
01134                         $this->inlineNames = array();
01135                 }
01136         }
01137 
01138 
01145         function getStructureItemName($levelData) {
01146                 if (is_array($levelData)) {
01147                         $name = '['.$levelData['table'].']' .
01148                                         '['.$levelData['uid'].']' .
01149                                         (isset($levelData['field']) ? '['.$levelData['field'].']' : '');
01150                 }
01151                 return $name;
01152         }
01153 
01154 
01163         function getStructureLevel($level) {
01164                 $inlineStructureCount = count($this->inlineStructure['stable']);
01165                 if ($level < 0) $level = $inlineStructureCount+$level;
01166                 if ($level >= 0 && $level < $inlineStructureCount)
01167                         return $this->inlineStructure['stable'][$level];
01168                 else
01169                         return false;
01170         }
01171 
01172 
01180         function getStructurePath($structureDepth = -1) {
01181                 $structureCount = count($this->inlineStructure['stable']);
01182                 if ($structureDepth < 0 || $structureDepth > $structureCount) $structureDepth = $structureCount;
01183 
01184                 for ($i = 1; $i <= $structureDepth; $i++) {
01185                         $current = $this->getStructureLevel(-$i);
01186                         $string = $this->getStructureItemName($current).$string;
01187                 }
01188 
01189                 return $string;
01190         }
01191 
01192 
01205         function parseStructureString($string, $loadConfig = false) {
01206                 $unstable = array();
01207                 $vector = array('table', 'uid', 'field');
01208                 $pattern = '/^'.$this->prependNaming.'\[(.+?)\]\[(.+)\]$/';
01209                 if (preg_match($pattern, $string, $match)) {
01210                         $this->inlineFirstPid = $match[1];
01211                         $parts = explode('][', $match[2]);
01212                         $partsCnt = count($parts);
01213                         for ($i = 0; $i < $partsCnt; $i++) {
01214                                 if ($i > 0 && $i % 3 == 0) {
01215                                                 // load the TCA configuration of the table field and store it in the stack
01216                                         if ($loadConfig) {
01217                                                 t3lib_div::loadTCA($unstable['table']);
01218                                                 $unstable['config'] = $GLOBALS['TCA'][$unstable['table']]['columns'][$unstable['field']]['config'];
01219                                                         // Fetch TSconfig:
01220                                                 $TSconfig = $this->fObj->setTSconfig(
01221                                                         $unstable['table'],
01222                                                         array('uid' => $unstable['uid'], 'pid' => $this->inlineFirstPid),
01223                                                         $unstable['field']
01224                                                 );
01225                                                         // Override TCA field config by TSconfig:
01226                                                 if (!$TSconfig['disabled']) {
01227                                                         $unstable['config'] = $this->fObj->overrideFieldConf($unstable['config'], $TSconfig);
01228                                                 }
01229                                         }
01230                                         $this->inlineStructure['stable'][] = $unstable;
01231                                         $unstable = array();
01232                                 }
01233                                 $unstable[$vector[$i % 3]] = $parts[$i];
01234                         }
01235                         $this->updateStructureNames();
01236                         if (count($unstable)) $this->inlineStructure['unstable'] = $unstable;
01237                 }
01238         }
01239 
01240 
01241         /*******************************************************
01242          *
01243          * Helper functions
01244          *
01245          *******************************************************/
01246 
01247 
01254         function checkConfiguration(&$config) {
01255                 $foreign_table = $config['foreign_table'];
01256 
01257                         // An inline field must have a foreign_table, if not, stop all further inline actions for this field:
01258                 if (!$foreign_table || !is_array($GLOBALS['TCA'][$foreign_table])) {
01259                         return false;
01260                 }
01261                         // Init appearance if not set:
01262                 if (!is_array($config['appearance'])) {
01263                         $config['appearance'] = array();
01264                 }
01265                         // Set the position/appearance of the "Create new record" link:
01266                 if ($config['foreign_selector'] && !$config['appearance']['useCombination']) {
01267                         $config['appearance']['newRecordLinkPosition'] = 'none';
01268                 } elseif (!in_array($config['appearance']['newRecordLinkPosition'], array('top', 'bottom', 'both', 'none'))) {
01269                         $config['appearance']['newRecordLinkPosition'] = 'top';
01270                 }
01271 
01272                 return true;
01273         }
01274 
01275 
01285         function checkAccess($cmd, $table, $theUid) {
01286                         // Checking if the user has permissions? (Only working as a precaution, because the final permission check is always down in TCE. But it's good to notify the user on beforehand...)
01287                         // First, resetting flags.
01288                 $hasAccess = 0;
01289                 $deniedAccessReason = '';
01290 
01291                         // If the command is to create a NEW record...:
01292                 if ($cmd=='new') {
01293                         $calcPRec = t3lib_BEfunc::getRecord('pages',$this->inlineFirstPid);
01294                         if(!is_array($calcPRec)) {
01295                                 return false;
01296                         }
01297                         $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);        // Permissions for the parent page
01298                         if ($table=='pages')    {       // If pages:
01299                                 $hasAccess = $CALC_PERMS&8 ? 1 : 0; // Are we allowed to create new subpages?
01300                         } else {
01301                                 $hasAccess = $CALC_PERMS&16 ? 1 : 0; // Are we allowed to edit content on this page?
01302                         }
01303                 } else {        // Edit:
01304                         $calcPRec = t3lib_BEfunc::getRecord($table,$theUid);
01305                         t3lib_BEfunc::fixVersioningPid($table,$calcPRec);
01306                         if (is_array($calcPRec))        {
01307                                 if ($table=='pages')    {       // If pages:
01308                                         $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
01309                                         $hasAccess = $CALC_PERMS&2 ? 1 : 0;
01310                                 } else {
01311                                         $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages',$calcPRec['pid']));        // Fetching pid-record first.
01312                                         $hasAccess = $CALC_PERMS&16 ? 1 : 0;
01313                                 }
01314 
01315                                         // Check internals regarding access:
01316                                 if ($hasAccess) {
01317                                         $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec);
01318                                 }
01319                         }
01320                 }
01321 
01322                 if(!$GLOBALS['BE_USER']->check('tables_modify', $table)) {
01323                         $hasAccess = 0;
01324                 }
01325 
01326                 if(!$hasAccess) {
01327                         $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg;
01328                         if($deniedAccessReason) {
01329                                 debug($deniedAccessReason);
01330                         }
01331                 }
01332 
01333                 return $hasAccess ? true : false;
01334         }
01335 
01336 
01345         function compareStructureConfiguration($compare) {
01346                 $level = $this->getStructureLevel(-1);
01347                 $result = $this->arrayCompareComplex($level, $compare);
01348 
01349                 return $result;
01350         }
01351 
01352 
01359         function normalizeUid($string) {
01360                 $parts = explode('|', $string);
01361                 return $parts[0];
01362         }
01363 
01364 
01373         function wrapFormsSection($section, $styleAttrs = array(), $tableAttrs = array()) {
01374                 if (!$styleAttrs['margin-right']) $styleAttrs['margin-right'] = $this->inlineStyles['margin-right'].'px';
01375 
01376                 foreach ($styleAttrs as $key => $value) $style .= ($style?' ':'').$key.': '.htmlspecialchars($value).'; ';
01377                 if ($style) $style = ' style="'.$style.'"';
01378 
01379                 if (!$tableAttrs['background'] && $this->fObj->borderStyle[2]) $tableAttrs['background'] = $this->backPath.$this->borderStyle[2];
01380                 if (!$tableAttrs['cellspacing']) $tableAttrs['cellspacing'] = '0';
01381                 if (!$tableAttrs['cellpadding']) $tableAttrs['cellpadding'] = '0';
01382                 if (!$tableAttrs['border']) $tableAttrs['border'] = '0';
01383                 if (!$tableAttrs['width']) $tableAttrs['width'] = '100%';
01384                 if (!$tableAttrs['class'] && $this->borderStyle[3]) $tableAttrs['class'] = $this->borderStyle[3];
01385 
01386                 foreach ($tableAttrs as $key => $value) $table .= ($table?' ':'').$key.'="'.htmlspecialchars($value).'"';
01387 
01388                 $out = '<table '.$table.$style.'>'.$section.'</table>';
01389                 return $out;
01390         }
01391 
01392 
01402         function isInlineChildAndLabelField($table, $field) {
01403                 $level = $this->getStructureLevel(-1);
01404                 if ($level['config']['foreign_label'])
01405                         $label = $level['config']['foreign_label'];
01406                 else
01407                         $label = $GLOBALS['TCA'][$table]['ctrl']['label'];
01408                 return $level['config']['foreign_table'] === $table && $label == $field ? true : false;
01409         }
01410 
01411 
01418         function getStructureDepth() {
01419                 return count($this->inlineStructure['stable']);
01420         }
01421 
01422 
01455         function arrayCompareComplex($subjectArray, $searchArray, $type = '') {
01456                 $localMatches = 0;
01457                 $localEntries = 0;
01458 
01459                 if (is_array($searchArray) && count($searchArray)) {
01460                                 // if no type was passed, try to determine
01461                         if (!$type) {
01462                                 reset($searchArray);
01463                                 $type = key($searchArray);
01464                                 $searchArray = current($searchArray);
01465                         }
01466 
01467                                 // we use '%AND' and '%OR' in uppercase
01468                         $type = strtoupper($type);
01469 
01470                                 // split regular elements from sub elements
01471                         foreach ($searchArray as $key => $value) {
01472                                 $localEntries++;
01473 
01474                                         // process a sub-group of OR-conditions
01475                                 if ($key == '%OR') {
01476                                         $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%OR') ? 1 : 0;
01477                                         // process a sub-group of AND-conditions
01478                                 } elseif ($key == '%AND') {
01479                                         $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%AND') ? 1 : 0;
01480                                         // a part of an associative array should be compared, so step down in the array hierarchy
01481                                 } elseif (is_array($value) && $this->isAssociativeArray($searchArray)) {
01482                                         $localMatches += $this->arrayCompareComplex($subjectArray[$key], $value, $type) ? 1 : 0;
01483                                         // it is a normal array that is only used for grouping and indexing
01484                                 } elseif (is_array($value)) {
01485                                         $localMatches += $this->arrayCompareComplex($subjectArray, $value, $type) ? 1 : 0;
01486                                         // directly compare a value
01487                                 } else {
01488                                         $localMatches += isset($subjectArray[$key]) && isset($value) && $subjectArray[$key] === $value ? 1 : 0;
01489                                 }
01490 
01491                                         // if one or more matches are required ('OR'), return true after the first successful match
01492                                 if ($type == '%OR' && $localMatches > 0) return true;
01493                                         // if all matches are required ('AND') and we have no result after the first run, return false
01494                                 if ($type == '%AND' && $localMatches == 0) return false;
01495                         }
01496                 }
01497 
01498                         // return the result for '%AND' (if nothing was checked, true is returned)
01499                 return $localEntries == $localMatches ? true : false;
01500         }
01501 
01502 
01509         function isAssociativeArray($object) {
01510                 return is_array($object) && count($object) && (array_keys($object) !== range(0, sizeof($object) - 1))
01511                         ? true
01512                         : false;
01513         }
01514 
01515 
01524         function removeFromArray($needle, $haystack, $strict=null) {
01525                 $pos = array_search($needle, $haystack, $strict);
01526                 if ($pos !== false) unset($haystack[$pos]);
01527                 return $haystack;
01528         }
01529 
01530 
01539         function getPossibleRecordsFlat($possibleRecords) {
01540                 $flat = false;
01541                 if (is_array($possibleRecords)) {
01542                         $flat = array();
01543                         foreach ($possibleRecords as $record) $flat[$record[1]] = $record[0];
01544                 }
01545                 return $flat;
01546         }
01547 
01548 
01555         function getPossibleRecordsSelectorConfig($conf, $field = '') {
01556                 $foreign_table = $conf['foreign_table'];
01557                 $foreign_selector = $conf['foreign_selector'];
01558 
01559                 $PA = false;
01560                 $type = false;
01561                 $table = false;
01562                 $selector = false;
01563                 
01564                 if ($field) {
01565                         $PA = array();
01566                         $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$field];
01567                         $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
01568                         $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$field);
01569                         $config = $PA['fieldConf']['config'];
01570                                 // Determine type of Selector:
01571                         $type = $this->getPossibleRecordsSelectorType($config);
01572                                 // Return table on this level:
01573                         $table = $type == 'select' ? $config['foreign_table'] : $config['allowed'];
01574                                 // Return type of the selector if foreign_selector is defined and points to the same field as in $field:
01575                         if ($foreign_selector && $foreign_selector == $field && $type) {
01576                                 $selector = $type;
01577                         }
01578                 }
01579                 
01580                 return array(
01581                         'PA' => $PA,
01582                         'type' => $type,
01583                         'table' => $table,
01584                         'selector' => $selector,
01585                 );
01586         }
01587         
01588 
01595         function getPossibleRecordsSelectorType($config) {
01596                 $type = false;
01597                 if ($config['type'] == 'select') {
01598                         $type = 'select';
01599                 } elseif ($config['type'] == 'group' && $config['internal_type'] == 'db') {
01600                         $type = 'groupdb';
01601                 }
01602                 return $type;
01603         }
01604         
01605         
01616         function skipField($table, $field, $row, $config) {
01617                 $skipThisField = false;
01618 
01619                 if ($this->getStructureDepth()) {
01620                         $searchArray = array(
01621                                 '%OR' => array(
01622                                         'config' => array(
01623                                                 0 => array(
01624                                                         '%AND' => array(
01625                                                                 'foreign_table' => $table,
01626                                                                 '%OR' => array(
01627                                                                         '%AND' => array(
01628                                                                                 'appearance' => array('useCombination' => 1),
01629                                                                                 'foreign_selector' => $field,
01630                                                                         ),
01631                                                                         'MM' => $config['MM']
01632                                                                 ),
01633                                                         ),
01634                                                 ),
01635                                                 1 => array(
01636                                                         '%AND' => array(
01637                                                                 'foreign_table' => $config['foreign_table'],
01638                                                                 'foreign_selector' => $config['foreign_field'],
01639                                                         ),
01640                                                 ),
01641                                         ),
01642                                 ),
01643                         );
01644 
01645                                 // get the parent record from structure stack
01646                         $level = $this->getStructureLevel(-1);
01647 
01648                                 // If we have symmetric fields, check on which side we are and hide fields, that are set automatically:
01649                         if (t3lib_loadDBGroup::isOnSymmetricSide($level['uid'], $level['config'], $row)) {
01650                                 $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $field;
01651                                 $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $field;
01652                                 // Hide fields, that are set automatically:
01653                         } else {
01654                                 $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $field;
01655                                 $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $field;
01656                         }
01657 
01658                         $skipThisField = $this->compareStructureConfiguration($searchArray, true);
01659                 }
01660 
01661                 return $skipThisField;
01662         }
01663 
01664 
01672         function getJSON($jsonArray) {
01673                 if (!$GLOBALS['JSON']) {
01674                         require_once(PATH_typo3.'contrib/json.php');
01675                         $GLOBALS['JSON'] = t3lib_div::makeInstance('Services_JSON');
01676                 }
01677                 return $GLOBALS['JSON']->encode($jsonArray);
01678         }
01679 
01680 
01688         function getExpandedCollapsedState($table, $uid) {
01689                 if (is_array($this->inlineView) && is_array($this->inlineView[$table])) {
01690                         if (in_array($uid, $this->inlineView[$table]) !== false) return true;
01691                 }
01692                 return false;
01693         }
01694 
01695 
01703         function updateInlineView(&$uc, &$tce) {
01704                 if (is_array($uc) && $uc['inlineView']) {
01705                         $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
01706 
01707                         foreach ($uc['inlineView'] as $topTable => $topRecords) {
01708                                 foreach ($topRecords as $topUid => $childElements) {
01709                                         foreach ($childElements as $childTable => $childRecords) {
01710                                                 $uids = array_keys($tce->substNEWwithIDs_table, $childTable);
01711                                                 if (count($uids)) {
01712                                                         foreach ($childRecords as $childUid => $state) {
01713                                                                 if ($state && in_array($childUid, $uids)) {
01714                                                                         $newChildUid = $tce->substNEWwithIDs[$childUid];
01715                                                                         $inlineView[$topTable][$topUid][$childTable][$newChildUid] = 1;
01716                                                                 }
01717                                                         }
01718                                                 }
01719                                         }
01720                                 }
01721                         }
01722 
01723                         $GLOBALS['BE_USER']->uc['inlineView'] = serialize($inlineView);
01724                         $GLOBALS['BE_USER']->writeUC();
01725                 }
01726         }
01727 
01728 
01734         function getLevelMargin() {
01735                 $margin = $this->inlineStyles['margin-right'];
01736                 return $margin;
01737         }
01738  }
01739 ?>


Généré par Les experts TYPO3 avec  doxygen 1.4.6