[ Index ]
 

Code source de Typo3 4.1.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/t3lib/ -> class.t3lib_tceforms_inline.php (source)

   1  <?php
   2  /***************************************************************
   3  *  Copyright notice
   4  *
   5  *  (c) 2006 Oliver Hader <oh@inpublica.de>
   6  *  All rights reserved
   7  *
   8  *  This script is part of the TYPO3 project. The TYPO3 project is
   9  *  free software; you can redistribute it and/or modify
  10  *  it under the terms of the GNU General Public License as published by
  11  *  the Free Software Foundation; either version 2 of the License, or
  12  *  (at your option) any later version.
  13  *
  14  *  The GNU General Public License can be found at
  15  *  http://www.gnu.org/copyleft/gpl.html.
  16  *  A copy is found in the textfile GPL.txt and important notices to the license
  17  *  from the author is found in LICENSE.txt distributed with these scripts.
  18  *
  19  *
  20  *  This script is distributed in the hope that it will be useful,
  21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23  *  GNU General Public License for more details.
  24  *
  25  *  This copyright notice MUST APPEAR in all copies of the script!
  26  ***************************************************************/
  27  /**
  28   * The Inline-Relational-Record-Editing (IRRE) functions as part of the TCEforms.
  29   *
  30   * $Id: class.t3lib_tceforms_inline.php 2482 2007-09-03 08:48:42Z ohader $
  31   *
  32   * @author    Oliver Hader <oh@inpublica.de>
  33   */
  34  /**
  35   * [CLASS/FUNCTION INDEX of SCRIPT]
  36   *
  37   *
  38   *
  39   *   88: class t3lib_TCEforms_inline
  40   *  109:     function init(&$tceForms)
  41   *  127:     function getSingleField_typeInline($table,$field,$row,&$PA)
  42   *
  43   *              SECTION: Regular rendering of forms, fields, etc.
  44   *  263:     function renderForeignRecord($parentUid, $rec, $config = array())
  45   *  319:     function renderForeignRecordHeader($parentUid, $foreign_table,$rec,$config = array())
  46   *  375:     function renderForeignRecordHeaderControl($table,$row,$config = array())
  47   *  506:     function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array())
  48   *  560:     function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds=array())
  49   *  627:     function addJavaScript()
  50   *  643:     function addJavaScriptSortable($objectId)
  51   *
  52   *              SECTION: Handling of AJAX calls
  53   *  665:     function createNewRecord($domObjectId, $foreignUid = 0)
  54   *  755:     function getJSON($jsonArray)
  55   *  770:     function getNewRecordLink($objectPrefix, $conf = array())
  56   *
  57   *              SECTION: Get data from database and handle relations
  58   *  807:     function getRelatedRecords($table,$field,$row,&$PA,$config)
  59   *  839:     function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector')
  60   *  885:     function getUniqueIds($records, $conf=array())
  61   *  905:     function getRecord($pid, $table, $uid, $cmd='')
  62   *  929:     function getNewRecord($pid, $table)
  63   *
  64   *              SECTION: Structure stack for handling inline objects/levels
  65   *  951:     function pushStructure($table, $uid, $field = '', $config = array())
  66   *  967:     function popStructure()
  67   *  984:     function updateStructureNames()
  68   * 1000:     function getStructureItemName($levelData)
  69   * 1015:     function getStructureLevel($level)
  70   * 1032:     function getStructurePath($structureDepth = -1)
  71   * 1057:     function parseStructureString($string, $loadConfig = false)
  72   *
  73   *              SECTION: Helper functions
  74   * 1098:     function checkConfiguration(&$config)
  75   * 1123:     function checkAccess($cmd, $table, $theUid)
  76   * 1185:     function compareStructureConfiguration($compare)
  77   * 1199:     function normalizeUid($string)
  78   * 1213:     function wrapFormsSection($section, $styleAttrs = array(), $tableAttrs = array())
  79   * 1242:     function isInlineChildAndLabelField($table, $field)
  80   * 1258:     function getStructureDepth()
  81   * 1295:     function arrayCompareComplex($subjectArray, $searchArray, $type = '')
  82   * 1349:     function isAssociativeArray($object)
  83   * 1364:     function getPossibleRecordsFlat($possibleRecords)
  84   * 1383:     function skipField($table, $field, $row, $config)
  85   *
  86   * TOTAL FUNCTIONS: 35
  87   * (This index is automatically created/updated by the extension "extdeveval")
  88   *
  89   */
  90  class t3lib_TCEforms_inline {
  91      var $fObj;                                // Reference to the calling TCEforms instance
  92      var $backPath;                            // Reference to $fObj->backPath
  93  
  94      var $isAjaxCall = false;                // Indicates if a field is rendered upon an AJAX call
  95      var $inlineStructure = array();            // the structure/hierarchy where working in, e.g. cascading inline tables
  96      var $inlineFirstPid;                    // the first call of an inline type appeared on this page (pid of record)
  97      var $inlineNames = array();                // keys: form, object -> hold the name/id for each of them
  98      var $inlineData = array();                // inline data array used for JSON output
  99      var $inlineView = array();                // expanded/collapsed states for the current BE user
 100      var $inlineCount = 0;                    // count the number of inline types used
 101      var $inlineStyles = array();
 102  
 103      var $prependNaming = 'data';            // how the $this->fObj->prependFormFieldNames should be set ('data' is default)
 104      var $prependFormFieldNames;                // reference to $this->fObj->prependFormFieldNames
 105      var $prependCmdFieldNames;                // reference to $this->fObj->prependCmdFieldNames
 106  
 107  
 108      /**
 109       * Intialize an instance of t3lib_TCEforms_inline
 110       *
 111       * @param    object        $tceForms: Reference to an TCEforms instance
 112       * @return    void
 113       */
 114  	function init(&$tceForms) {
 115          $this->fObj =& $tceForms;
 116          $this->backPath =& $tceForms->backPath;
 117          $this->prependFormFieldNames =& $this->fObj->prependFormFieldNames;
 118          $this->prependCmdFieldNames =& $this->fObj->prependCmdFieldNames;
 119          $this->inlineStyles['margin-right'] = '5';
 120      }
 121  
 122  
 123      /**
 124       * Generation of TCEform elements of the type "inline"
 125       * This will render inline-relational-record sets. Relations.
 126       *
 127       * @param    string        $table: The table name of the record
 128       * @param    string        $field: The field name which this element is supposed to edit
 129       * @param    array        $row: The record data array where the value(s) for the field can be found
 130       * @param    array        $PA: An array with additional configuration options.
 131       * @return    string        The HTML code for the TCEform field
 132       */
 133  	function getSingleField_typeInline($table,$field,$row,&$PA) {
 134              // check the TCA configuration - if false is returned, something was wrong
 135          if ($this->checkConfiguration($PA['fieldConf']['config']) === false) return false;
 136  
 137              // count the number of processed inline elements
 138          $this->inlineCount++;
 139  
 140              // Init:
 141          $config = $PA['fieldConf']['config'];
 142          $foreign_table = $config['foreign_table'];
 143          t3lib_div::loadTCA($foreign_table);
 144  
 145          $minitems = t3lib_div::intInRange($config['minitems'],0);
 146          $maxitems = t3lib_div::intInRange($config['maxitems'],0);
 147          if (!$maxitems)    $maxitems=100000;
 148  
 149              // Register the required number of elements:
 150          $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
 151  
 152              // remember the page id (pid of record) where inline editing started first
 153              // we need that pid for ajax calls, so that they would know where the action takes place on the page structure
 154          if (!isset($this->inlineFirstPid)) {
 155                  // if this record is not new, try to fetch the inlineView states
 156                  // @TODO: Add checking/cleaning for unused tables, records, etc. to save space in uc-field
 157              if (t3lib_div::testInt($row['uid'])) {
 158                  $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
 159                  $this->inlineView = $inlineView[$table][$row['uid']];
 160              }
 161                  // If the parent is a page, use the uid(!) of the (new?) page as pid for the child records:
 162              if ($table == 'pages') {
 163                  $this->inlineFirstPid = $row['uid'];
 164                  // If pid is negative, fetch the previous record and take its pid:
 165              } elseif ($row['pid'] < 0) {
 166                  $prevRec = t3lib_BEfunc::getRecord($table, abs($row['pid']));
 167                  $this->inlineFirstPid = $prevRec['pid'];
 168                  // Take the pid as it is:
 169              } else {
 170                  $this->inlineFirstPid = $row['pid'];
 171              }
 172          }
 173              // add the current inline job to the structure stack
 174          $this->pushStructure($table, $row['uid'], $field, $config);
 175              // e.g. inline[<table>][<uid>][<field>]
 176          $nameForm = $this->inlineNames['form'];
 177              // e.g. inline[<pid>][<table1>][<uid1>][<field1>][<table2>][<uid2>][<field2>]
 178          $nameObject = $this->inlineNames['object'];
 179              // get the records related to this inline record
 180          $recordList = $this->getRelatedRecords($table,$field,$row,$PA,$config);
 181              // set the first and last record to the config array
 182          $config['inline']['first'] = $recordList[0]['uid'];
 183          $config['inline']['last'] = $recordList[count($recordList)-1]['uid'];
 184  
 185              // Tell the browser what we have (using JSON later):
 186          $top = $this->getStructureLevel(0);
 187          $this->inlineData['config'][$nameObject] = array('table' => $foreign_table);
 188          $this->inlineData['config'][$nameObject.'['.$foreign_table.']'] = array(
 189              'min' => $minitems,
 190              'max' => $maxitems,
 191              'sortable' => $config['appearance']['useSortable'],
 192              'top' => array(
 193                  'table' => $top['table'],
 194                  'uid'    => $top['uid'],
 195              ),
 196          );
 197              // Set a hint for nested IRRE and tab elements:
 198          $this->inlineData['nested'][$nameObject] = $this->fObj->getDynNestedStack(false, $this->isAjaxCall);
 199  
 200              // if relations are required to be unique, get the uids that have already been used on the foreign side of the relation
 201          if ($config['foreign_unique']) {
 202                  // If uniqueness *and* selector are set, they should point to the same field - so, get the configuration of one:
 203              $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']);
 204                  // Get the used unique ids:
 205              $uniqueIds = $this->getUniqueIds($recordList, $config, $selConfig['type']=='groupdb');
 206              $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config,'foreign_unique');
 207              $uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1    : count($possibleRecords);
 208              $this->inlineData['unique'][$nameObject.'['.$foreign_table.']'] = array(
 209                  'max' => $uniqueMax,
 210                  'used' => $uniqueIds,
 211                  'type' => $selConfig['type'],
 212                  'table' => $config['foreign_table'],
 213                  'elTable' => $selConfig['table'], // element/record table (one step down in hierarchy)
 214                  'field' => $config['foreign_unique'],
 215                  'selector' => $selConfig['selector'],
 216                  'possible' => $this->getPossibleRecordsFlat($possibleRecords),
 217              );
 218          }
 219  
 220              // if it's required to select from possible child records (reusable children), add a selector box
 221          if ($config['foreign_selector']) {
 222                  // if not already set by the foreign_unique, set the possibleRecords here and the uniqueIds to an empty array
 223              if (!$config['foreign_unique']) {
 224                  $possibleRecords = $this->getPossibleRecords($table,$field,$row,$config);
 225                  $uniqueIds = array();
 226              }
 227              $selectorBox = $this->renderPossibleRecordsSelector($possibleRecords,$config,$uniqueIds);
 228              $item .= $selectorBox;
 229          }
 230  
 231              // wrap all inline fields of a record with a <div> (like a container)
 232          $item .= '<div id="'.$nameObject.'">';
 233  
 234              // define how to show the "Create new record" link - if there are more than maxitems, hide it
 235          if (count($recordList) >= $maxitems || ($uniqueMax > 0 && count($recordList) >= $uniqueMax)) {
 236              $config['inline']['inlineNewButtonStyle'] = 'display: none;';
 237          }
 238              // add the "Create new record" link before all child records
 239          if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'top'))) {
 240              $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
 241          }
 242  
 243          $item .= '<div id="'.$nameObject.'_records">';
 244          $relationList = array();
 245          if (count($recordList)) {
 246              foreach ($recordList as $rec) {
 247                  $item .= $this->renderForeignRecord($row['uid'],$rec,$config);
 248                  $relationList[] = $rec['uid'];
 249              }
 250          }
 251          $item .= '</div>';
 252  
 253              // add the "Create new record" link after all child records
 254          if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'bottom'))) {
 255              $item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
 256          }
 257  
 258              // add Drag&Drop functions for sorting to TCEforms::$additionalJS_post
 259          if (count($relationList) > 1 && $config['appearance']['useSortable'])
 260              $this->addJavaScriptSortable($nameObject.'_records');
 261              // publish the uids of the child records in the given order to the browser
 262          $item .= '<input type="hidden" name="'.$nameForm.'" value="'.implode(',', $relationList).'" class="inlineRecord" />';
 263              // close the wrap for all inline fields (container)
 264          $item .= '</div>';
 265  
 266              // on finishing this section, remove the last item from the structure stack
 267          $this->popStructure();
 268  
 269              // if this was the first call to the inline type, restore the values
 270          if (!$this->getStructureDepth()) {
 271              unset($this->inlineFirstPid);
 272          }
 273  
 274          return $item;
 275      }
 276  
 277  
 278      /*******************************************************
 279       *
 280       * Regular rendering of forms, fields, etc.
 281       *
 282       *******************************************************/
 283  
 284  
 285      /**
 286       * Render the form-fields of a related (foreign) record.
 287       *
 288       * @param    string        $parentUid: The uid of the parent (embedding) record (uid or NEW...)
 289       * @param    array        $rec: The table record of the child/embedded table (normaly post-processed by t3lib_transferData)
 290       * @param    array        $config: content of $PA['fieldConf']['config']
 291       * @return    string        The HTML code for this "foreign record"
 292       */
 293  	function renderForeignRecord($parentUid, $rec, $config = array()) {
 294          $foreign_table = $config['foreign_table'];
 295          $foreign_field = $config['foreign_field'];
 296          $foreign_selector = $config['foreign_selector'];
 297  
 298              // Send a mapping information to the browser via JSON:
 299              // e.g. data[<curTable>][<curId>][<curField>] => data[<pid>][<parentTable>][<parentId>][<parentField>][<curTable>][<curId>][<curField>]
 300          $this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
 301  
 302              // Set this variable if we handle a brand new unsaved record:
 303          $isNewRecord = t3lib_div::testInt($rec['uid']) ? false : true;
 304              // If there is a selector field, normalize it:
 305          if ($foreign_selector) {
 306              $rec[$foreign_selector] = $this->normalizeUid($rec[$foreign_selector]);
 307          }
 308  
 309          $hasAccess = $this->checkAccess($isNewRecord?'new':'edit', $foreign_table, $rec['uid']);
 310  
 311          if(!$hasAccess) return false;
 312  
 313              // Get the current naming scheme for DOM name/id attributes:
 314          $nameObject = $this->inlineNames['object'];
 315          $appendFormFieldNames = '['.$foreign_table.']['.$rec['uid'].']';
 316          $formFieldNames = $nameObject.$appendFormFieldNames;
 317              // Put the current level also to the dynNestedStack of TCEforms:
 318          $this->fObj->pushToDynNestedStack('inline', $this->inlineNames['object'].$appendFormFieldNames);
 319  
 320          $header = $this->renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config);
 321          $combination = $this->renderCombinationTable($rec, $appendFormFieldNames, $config);
 322          $fields = $this->fObj->getMainFields($foreign_table,$rec);
 323          $fields = $this->wrapFormsSection($fields);
 324  
 325          if ($isNewRecord) {
 326                  // show this record expanded or collapsed
 327              $isExpanded = is_array($config['appearance']) && $config['appearance']['collapseAll'] ? 1 : 0;
 328                  // get the top parent table
 329              $top = $this->getStructureLevel(0);
 330              $ucFieldName = 'uc['.$top['table'].']['.$top['uid'].']'.$appendFormFieldNames;
 331                  // set additional fields for processing for saving
 332              $fields .= '<input type="hidden" name="'.$this->prependFormFieldNames.$appendFormFieldNames.'[pid]" value="'.$rec['pid'].'"/>';
 333              $fields .= '<input type="hidden" name="'.$ucFieldName.'" value="'.$isExpanded.'" />';
 334  
 335          } else {
 336                  // show this record expanded or collapsed
 337              $isExpanded = $this->getExpandedCollapsedState($foreign_table, $rec['uid']);
 338                  // set additional field for processing for saving
 339              $fields .= '<input type="hidden" name="'.$this->prependCmdFieldNames.$appendFormFieldNames.'[delete]" value="1" disabled="disabled" />';
 340          }
 341  
 342              // if this record should be shown collapsed
 343          if (!$isExpanded) $appearanceStyleFields = ' style="display: none;"';
 344  
 345              // set the record container with data for output
 346          $out = '<div id="'.$formFieldNames.'_header">'.$header.'</div>';
 347          $out .= '<div id="'.$formFieldNames.'_fields"'.$appearanceStyleFields.'>'.$fields.$combination.'</div>';
 348              // wrap the header, fields and combination part of a child record with a div container
 349          $out = '<div id="'.$formFieldNames.'_div" class="inlineDiv'.($isNewRecord ? ' inlineIsNewRecord' : '').'">' . $out . '</div>';
 350  
 351              // Remove the current level also from the dynNestedStack of TCEforms:
 352          $this->fObj->popFromDynNestedStack();
 353  
 354          return $out;
 355      }
 356  
 357  
 358      /**
 359       * Renders the HTML header for a foreign record, such as the title, toggle-function, drag'n'drop, etc.
 360       * Later on the command-icons are inserted here.
 361       *
 362       * @param    string        $parentUid: The uid of the parent (embedding) record (uid or NEW...)
 363       * @param    string        $foreign_table: The foreign_table we create a header for
 364       * @param    array        $rec: The current record of that foreign_table
 365       * @param    array        $config: content of $PA['fieldConf']['config']
 366       * @return    string        The HTML code of the header
 367       */
 368  	function renderForeignRecordHeader($parentUid, $foreign_table, $rec, $config = array()) {
 369              // Init:
 370          $formFieldNames = $this->inlineNames['object'].'['.$foreign_table.']['.$rec['uid'].']';
 371          $expandSingle = $config['appearance']['expandSingle'] ? 1 : 0;
 372          $onClick = "return inline.expandCollapseRecord('".htmlspecialchars($formFieldNames)."', $expandSingle)";
 373  
 374              // Pre-Processing:
 375          $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
 376          $hasForeignLabel = !$isOnSymmetricSide && $config['foreign_label'] ? true : false;
 377          $hasSymmetricLabel = $isOnSymmetricSide && $config['symmetric_label'] ? true : false;
 378              // Get the record title/label for a record:
 379              // render using a self-defined user function
 380          if ($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc']) {
 381              $params = array(
 382                  'table' => $foreign_table,
 383                  'row'    => $rec,
 384                  'title'    => '',
 385                  'isOnSymmetricSide' => $isOnSymmetricSide
 386              );
 387              $null = null;    // callUserFunction requires a third parameter, but we don't want to give $this as reference!
 388              t3lib_div::callUserFunction($GLOBALS['TCA'][$foreign_table]['ctrl']['label_userFunc'], $params, $null);
 389              $recTitle = $params['title'];
 390              // render the special alternative title
 391          } elseif ($hasForeignLabel || $hasSymmetricLabel) {
 392              $titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
 393              $foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol);
 394                  // Render title for everything else than group/db:
 395              if ($foreignConfig['type'] != 'groupdb') {
 396                  $recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
 397                  // Render title for group/db:
 398              } else {
 399                      // $recTitle could be something like: "tx_table_123|...",
 400                  $valueParts = t3lib_div::trimExplode('|', $rec[$titleCol]);
 401                  $itemParts = t3lib_div::revExplode('_', $valueParts[0], 2);
 402                  $recTemp = t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]);
 403                  $recTitle = t3lib_BEfunc::getRecordTitle($itemParts[0], $recTemp, true);
 404              }
 405              $recTitle = t3lib_BEfunc::getRecordTitlePrep($recTitle);
 406              if (!strcmp(trim($recTitle),'')) {
 407                  $recTitle = t3lib_BEfunc::getNoRecordTitle(true);
 408              }
 409              // render the standard
 410          } else {
 411              $recTitle = t3lib_BEfunc::getRecordTitle($foreign_table, $rec, true);
 412          }
 413  
 414          $altText = t3lib_BEfunc::getRecordIconAltText($rec, $foreign_table);
 415          $iconImg =
 416              '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.t3lib_iconWorks::getIconImage(
 417                  $foreign_table, $rec, $this->backPath,
 418                  'title="'.htmlspecialchars($altText).'" class="absmiddle"'
 419              ).'</a>';
 420  
 421          $label =
 422              '<a href="#" onclick="'.htmlspecialchars($onClick).'" style="display: block;">'.
 423                  '<span id="'.$formFieldNames.'_label">'.$recTitle.'</span>'.
 424              '</a>';
 425  
 426          $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config);
 427  
 428              // @TODO: Check the table wrapping and the CSS definitions
 429          $header =
 430              '<table cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-right: '.$this->inlineStyles['margin-right'].'px;"'.
 431              ($this->fObj->borderStyle[2] ? ' background="'.htmlspecialchars($this->backPath.$this->fObj->borderStyle[2]).'"':'').
 432              ($this->fObj->borderStyle[3] ? ' class="'.htmlspecialchars($this->fObj->borderStyle[3]).'"':'').'>' .
 433              '<tr class="class-main12"><td width="18">'.$iconImg.'</td><td align="left"><b>'.$label.'</b></td><td align="right">'.$ctrl.'</td></tr></table>';
 434  
 435          return $header;
 436      }
 437  
 438  
 439      /**
 440       * Render the control-icons for a record header (create new, sorting, delete, disable/enable).
 441       * Most of the parts are copy&paste from class.db_list_extra.inc and modified for the JavaScript calls here
 442       *
 443       * @param    string        $parentUid: The uid of the parent (embedding) record (uid or NEW...)
 444       * @param    string        $foreign_table: The table (foreign_table) we create control-icons for
 445       * @param    array        $rec: The current record of that foreign_table
 446       * @param    array        $config: (modified) TCA configuration of the field
 447       * @return    string        The HTML code with the control-icons
 448       */
 449  	function renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config = array()) {
 450              // Initialize:
 451          $cells=array();
 452          $isNewItem = substr($rec['uid'], 0, 3) == 'NEW';
 453  
 454          $tcaTableCtrl =& $GLOBALS['TCA'][$foreign_table]['ctrl'];
 455          $tcaTableCols =& $GLOBALS['TCA'][$foreign_table]['columns'];
 456  
 457          $isPagesTable = $foreign_table == 'pages' ? true : false;
 458          $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $config, $rec);
 459          $enableManualSorting = $tcaTableCtrl['sortby'] || $config['MM'] || (!$isOnSymmetricSide && $config['foreign_sortby']) || ($isOnSymmetricSide && $config['symmetric_sortby']) ? true : false;
 460  
 461          $nameObjectFt = $this->inlineNames['object'].'['.$foreign_table.']';
 462          $nameObjectFtId = $nameObjectFt.'['.$rec['uid'].']';
 463  
 464          $calcPerms = $GLOBALS['BE_USER']->calcPerms(
 465              t3lib_BEfunc::readPageAccess($rec['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1))
 466          );
 467  
 468              // If the listed table is 'pages' we have to request the permission settings for each page:
 469          if ($isPagesTable)    {
 470              $localCalcPerms = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages',$rec['uid']));
 471          }
 472  
 473              // This expresses the edit permissions for this particular element:
 474          $permsEdit = ($isPagesTable && ($localCalcPerms&2)) || (!$isPagesTable && ($calcPerms&16));
 475  
 476              // "Info": (All records)
 477          if (!$isNewItem)
 478              $cells[]='<a href="#" onclick="'.htmlspecialchars('top.launchView(\''.$foreign_table.'\', \''.$rec['uid'].'\'); return false;').'">'.
 479                  '<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="" />'.
 480                  '</a>';
 481  
 482              // If the table is NOT a read-only table, then show these links:
 483          if (!$tcaTableCtrl['readOnly'])    {
 484  
 485                  // "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):
 486              if ($enableManualSorting || $tcaTableCtrl['useColumnsForDefaultValues'])    {
 487                  if (
 488                      (!$isPagesTable && ($calcPerms&16)) ||     // For NON-pages, must have permission to edit content on this parent page
 489                      ($isPagesTable && ($calcPerms&8))        // For pages, must have permission to create new pages here.
 490                      )    {
 491                      $onClick = "return inline.createNewRecord('".$nameObjectFt."','".$rec['uid']."')";
 492                      if ($config['inline']['inlineNewButtonStyle']) {
 493                          $style = ' style="'.$config['inline']['inlineNewButtonStyle'].'"';
 494                      }
 495                      $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="inlineNewButton"'.$style.'>'.
 496                              '<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="" />'.
 497                              '</a>';
 498                  }
 499              }
 500  
 501                  // Drag&Drop Sorting: Sortable handler for script.aculo.us
 502              if ($permsEdit && $enableManualSorting && $config['appearance']['useSortable'])    {
 503                  $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" />';
 504              }
 505  
 506                  // "Up/Down" links
 507              if ($permsEdit && $enableManualSorting)    {
 508                  $onClick = "return inline.changeSorting('".$nameObjectFtId."', '1')";    // Up
 509                  $style = $config['inline']['first'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
 510                  $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingUp" '.$style.'>'.
 511                          '<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="" />'.
 512                          '</a>';
 513  
 514                  $onClick = "return inline.changeSorting('".$nameObjectFtId."', '-1')";    // Down
 515                  $style = $config['inline']['last'] == $rec['uid'] ? 'style="visibility: hidden;"' : '';
 516                  $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'" class="sortingDown" '.$style.'>'.
 517                          '<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="" />'.
 518                          '</a>';
 519              }
 520  
 521                  // "Hide/Unhide" links:
 522              $hiddenField = $tcaTableCtrl['enablecolumns']['disabled'];
 523              if ($permsEdit && $hiddenField && $tcaTableCols[$hiddenField] && (!$tcaTableCols[$hiddenField]['exclude'] || $GLOBALS['BE_USER']->check('non_exclude_fields',$foreign_table.':'.$hiddenField)))    {
 524                  $onClick = "return inline.enableDisableRecord('".$nameObjectFtId."')";
 525                  if ($rec[$hiddenField])    {
 526                      $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
 527                              '<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" />'.
 528                              '</a>';
 529                  } else {
 530                      $cells[]='<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
 531                              '<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" />'.
 532                              '</a>';
 533                  }
 534              }
 535  
 536                  // "Delete" link:
 537              if (
 538                  ($isPagesTable && ($localCalcPerms&4)) || (!$isPagesTable && ($calcPerms&16))
 539                  )    {
 540                  $onClick = "inline.deleteRecord('".$nameObjectFtId."');";
 541                  $cells[]='<a href="#" onclick="'.htmlspecialchars('if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')).')) {    '.$onClick.' } return false;').'">'.
 542                          '<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="" />'.
 543                          '</a>';
 544              }
 545          }
 546  
 547              // If the record is edit-locked    by another user, we will show a little warning sign:
 548          if ($lockInfo=t3lib_BEfunc::isRecordLocked($foreign_table,$rec['uid']))    {
 549              $cells[]='<a href="#" onclick="'.htmlspecialchars('alert('.$GLOBALS['LANG']->JScharCode($lockInfo['msg']).');return false;').'">'.
 550                      '<img'.t3lib_iconWorks::skinImg('','gfx/recordlock_warning3.gif','width="17" height="12"').' title="'.htmlspecialchars($lockInfo['msg']).'" alt="" />'.
 551                      '</a>';
 552          }
 553  
 554              // Compile items into a DIV-element:
 555          return '
 556                                              <!-- CONTROL PANEL: '.$foreign_table.':'.$rec['uid'].' -->
 557                                              <div class="typo3-DBctrl">'.implode('',$cells).'</div>';
 558      }
 559  
 560  
 561      /**
 562       * Render a table with TCEforms, that occurs on a intermediate table but should be editable directly,
 563       * so two tables are combined (the intermediate table with attributes and the sub-embedded table).
 564       * -> This is a direct embedding over two levels!
 565       *
 566       * @param    array        $rec: The table record of the child/embedded table (normaly post-processed by t3lib_transferData)
 567       * @param    string        $appendFormFieldNames: The [<table>][<uid>] of the parent record (the intermediate table)
 568       * @param    array        $config: content of $PA['fieldConf']['config']
 569       * @return    string        A HTML string with <table> tag around.
 570       */
 571  	function renderCombinationTable(&$rec, $appendFormFieldNames, $config = array()) {
 572          $foreign_table = $config['foreign_table'];
 573          $foreign_selector = $config['foreign_selector'];
 574  
 575          if ($foreign_selector && $config['appearance']['useCombination']) {
 576              $comboConfig = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector]['config'];
 577              $comboRecord = array();
 578  
 579                  // record does already exist, so load it
 580              if (t3lib_div::testInt($rec[$foreign_selector])) {
 581                  $comboRecord = $this->getRecord(
 582                      $this->inlineFirstPid,
 583                      $comboConfig['foreign_table'],
 584                      $rec[$foreign_selector]
 585                  );
 586                  $isNewRecord = false;
 587                  // it's a new record, so get some default data
 588              } else {
 589                  $comboRecord = $this->getNewRecord(
 590                      $this->inlineFirstPid,
 591                      $comboConfig['foreign_table']
 592                  );
 593                  $isNewRecord = true;
 594              }
 595  
 596                  // get the TCEforms interpretation of the TCA of the child table
 597              $out = $this->fObj->getMainFields($comboConfig['foreign_table'], $comboRecord);
 598              $out = $this->wrapFormsSection($out, array(), array('class' => 'wrapperAttention'));
 599  
 600                  // if this is a new record, add a pid value to store this record and the pointer value for the intermediate table
 601              if ($isNewRecord) {
 602                  $comboFormFieldName = $this->prependFormFieldNames.'['.$comboConfig['foreign_table'].']['.$comboRecord['uid'].'][pid]';
 603                  $out .= '<input type="hidden" name="'.$comboFormFieldName.'" value="'.$this->inlineFirstPid.'"/>';
 604              }
 605  
 606                  // if the foreign_selector field is also responsible for uniqueness, tell the browser the uid of the "other" side of the relation
 607              if ($isNewRecord || $config['foreign_unique'] == $foreign_selector) {
 608                  $parentFormFieldName = $this->prependFormFieldNames.$appendFormFieldNames.'['.$foreign_selector.']';
 609                  $out .= '<input type="hidden" name="'.$parentFormFieldName.'" value="'.$comboRecord['uid'].'" />';
 610              }
 611          }
 612  
 613          return $out;
 614      }
 615  
 616  
 617      /**
 618       * Get a selector as used for the select type, to select from all available
 619       * records and to create a relation to the embedding record (e.g. like MM).
 620       *
 621       * @param    array        $selItems: Array of all possible records
 622       * @param    array        $conf: TCA configuration of the parent(!) field
 623       * @param    array        $uniqueIds: The uids that have already been used and should be unique
 624       * @return    string        A HTML <select> box with all possible records
 625       */
 626  	function renderPossibleRecordsSelector($selItems, $conf, $uniqueIds=array()) {
 627          $foreign_table = $conf['foreign_table'];
 628          $foreign_selector = $conf['foreign_selector'];
 629  
 630          $selConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_selector);
 631          $config = $selConfig['PA']['fieldConf']['config'];
 632  
 633          if ($selConfig['type'] == 'select') {
 634              $item = $this->renderPossibleRecordsSelectorTypeSelect($selItems, $conf, $selConfig['PA'], $uniqueIds);
 635          } elseif ($selConfig['type'] == 'groupdb') {
 636              $item = $this->renderPossibleRecordsSelectorTypeGroupDB($conf, $selConfig['PA']);
 637          }
 638  
 639          return $item;
 640      }
 641  
 642  
 643      /**
 644       * Get a selector as used for the select type, to select from all available
 645       * records and to create a relation to the embedding record (e.g. like MM).
 646       *
 647       * @param    array        $selItems: Array of all possible records
 648       * @param    array        $conf: TCA configuration of the parent(!) field
 649       * @param    array        $PA: An array with additional configuration options
 650       * @param    array        $uniqueIds: The uids that have already been used and should be unique
 651       * @return    string        A HTML <select> box with all possible records
 652       */
 653  	function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds=array()) {
 654          $foreign_table = $conf['foreign_table'];
 655          $foreign_selector = $conf['foreign_selector'];
 656  
 657          $PA = array();
 658          $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
 659          $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
 660          $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$foreign_selector);
 661          $config = $PA['fieldConf']['config'];
 662  
 663          if(!$disabled) {
 664                  // Create option tags:
 665              $opt = array();
 666              $styleAttrValue = '';
 667              foreach($selItems as $p)    {
 668                  if ($config['iconsInOptionTags'])    {
 669                      $styleAttrValue = $this->fObj->optionTagStyle($p[2]);
 670                  }
 671                  if (!in_array($p[1], $uniqueIds)) {
 672                      $opt[]= '<option value="'.htmlspecialchars($p[1]).'"'.
 673                                      ' style="'.(in_array($p[1], $uniqueIds) ? '' : '').
 674                                      ($styleAttrValue ? ' style="'.htmlspecialchars($styleAttrValue) : '').'">'.
 675                                      htmlspecialchars($p[0]).'</option>';
 676                  }
 677              }
 678  
 679                  // Put together the selector box:
 680              $selector_itemListStyle = isset($config['itemListStyle']) ? ' style="'.htmlspecialchars($config['itemListStyle']).'"' : ' style="'.$this->fObj->defaultMultipleSelectorStyle.'"';
 681              $size = intval($conf['size']);
 682              $size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$conf['autoSizeMax']) : $size;
 683              $onChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
 684              $item = '
 685                  <select id="'.$this->inlineNames['object'].'['.$conf['foreign_table'].']_selector"'.
 686                              $this->fObj->insertDefStyle('select').
 687                              ($size ? ' size="'.$size.'"' : '').
 688                              ' onchange="'.htmlspecialchars($onChange).'"'.
 689                              $PA['onFocus'].
 690                              $selector_itemListStyle.
 691                              ($conf['foreign_unique'] ? ' isunique="isunique"' : '').'>
 692                      '.implode('
 693                      ',$opt).'
 694                  </select>';
 695  
 696                  // add a "Create new relation" link for adding new relations
 697                  // this is neccessary, if the size of the selector is "1" or if
 698                  // there is only one record item in the select-box, that is selected by default
 699                  // the selector-box creates a new relation on using a onChange event (see some line above)
 700              $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
 701              $item .=
 702                  '<a href="#" onclick="'.htmlspecialchars($onChange).'" align="abstop">'.
 703                      '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/edit2.gif','width="11" height="12"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
 704                  '</a>';
 705                  // wrap the selector and add a spacer to the bottom
 706              $item = '<div style="margin-bottom: 20px;">'.$item.'</div>';
 707          }
 708  
 709          return $item;
 710      }
 711  
 712  
 713      /**
 714       * Generate a link that opens an element browser in a new window.
 715       * For group/db there is no way o use a "selector" like a <select>|</select>-box.
 716       *
 717       * @param    array        $conf: TCA configuration of the parent(!) field
 718       * @param    array        $PA: An array with additional configuration options
 719       * @return    string        A HTML link that opens an element browser in a new window
 720       */
 721  	function renderPossibleRecordsSelectorTypeGroupDB($conf, &$PA) {
 722          $foreign_table = $conf['foreign_table'];
 723  
 724          $config = $PA['fieldConf']['config'];
 725          $allowed = $config['allowed'];
 726          $objectPrefix = $this->inlineNames['object'].'['.$foreign_table.']';
 727  
 728          $createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
 729          $onClick = "setFormValueOpenBrowser('db','".('|||'.$allowed.'|'.$objectPrefix.'|inline.checkUniqueElement||inline.importElement')."'); return false;";
 730          $item =
 731              '<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
 732                  '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/insert3.gif','width="14" height="14"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
 733              '</a>';
 734  
 735          return $item;
 736      }
 737  
 738  
 739      /**
 740       * Creates a link/button to create new records
 741       *
 742       * @param    string        $objectPrefix: The "path" to the child record to create (e.g. '[parten_table][parent_uid][parent_field][child_table]')
 743       * @param    array        $conf: TCA configuration of the parent(!) field
 744       * @return    string        The HTML code for the new record link
 745       */
 746  	function getNewRecordLink($objectPrefix, $conf = array()) {
 747          if ($conf['inline']['inlineNewButtonStyle']) $style = ' style="'.$conf['inline']['inlineNewButtonStyle'].'"';
 748  
 749          $onClick = "return inline.createNewRecord('$objectPrefix')";
 750          $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createnew',1);
 751  
 752          if ($conf['appearance']['newRecordLinkAddTitle'])
 753              $tableTitle .= ' '.$GLOBALS['LANG']->sL($GLOBALS['TCA'][$conf['foreign_table']]['ctrl']['title'],1);
 754  
 755          $out = '
 756                  <div class="typo3-newRecordLink">
 757                      <a href="#" onClick="'.$onClick.'" class="inlineNewButton"'.$style.' title="'.$title.$tableTitle.'">'.
 758                      '<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/new_el.gif','width="11" height="12"').' alt="'.$title.$tableTitle.'" />'.
 759                      $title.t3lib_div::fixed_lgd_cs($tableTitle, $this->fObj->titleLen).
 760                      '</a>
 761                  </div>';
 762          return $out;
 763      }
 764  
 765  
 766      /**
 767       * Add Sortable functionality using script.acolo.us "Sortable".
 768       *
 769       * @param    string        $objectId: The container id of the object - elements inside will be sortable
 770       * @return    void
 771       */
 772  	function addJavaScriptSortable($objectId) {
 773          $this->fObj->additionalJS_post[] = '
 774              inline.createDragAndDropSorting("'.$objectId.'");
 775          ';
 776      }
 777  
 778  
 779      /*******************************************************
 780       *
 781       * Handling of AJAX calls
 782       *
 783       *******************************************************/
 784  
 785  
 786      /**
 787       * Initialize environment for AJAX calls
 788       *
 789       * @param    string        $method: Name of the method to be called
 790       * @param    array        $arguments: Arguments to be delivered to the method
 791       * @return    void
 792       */
 793  	function initForAJAX($method, &$arguments) {
 794              // Set t3lib_TCEforms::$RTEcounter to the given value:
 795          if ($method == 'createNewRecord') {
 796              $this->fObj->RTEcounter = intval(array_shift($arguments));
 797          }
 798      }
 799  
 800  
 801      /**
 802       * Handle AJAX calls to show a new inline-record of the given table.
 803       * Normally this method is never called from inside TYPO3. Always from outside by AJAX.
 804       *
 805       * @param    string        $domObjectId: The calling object in hierarchy, that requested a new record.
 806       * @param    string        $foreignUid: If set, the new record should be inserted after that one.
 807       * @return    string        A JSON string
 808       */
 809  	function createNewRecord($domObjectId, $foreignUid = 0) {
 810          $this->isAjaxCall = true;
 811              // parse the DOM identifier (string), add the levels to the structure stack (array) and load the TCA config
 812          $this->parseStructureString($domObjectId, true);
 813              // the current table - for this table we should add/import records
 814          $current = $this->inlineStructure['unstable'];
 815              // the parent table - this table embeds the current table
 816          $parent = $this->getStructureLevel(-1);
 817              // get TCA 'config' of the parent table
 818          $config = $parent['config'];
 819  
 820              // Put the current level also to the dynNestedStack of TCEforms:
 821          $this->fObj->pushToDynNestedStack('inline', $this->inlineNames['object']);
 822  
 823              // dynamically create a new record using t3lib_transferData
 824          if (!$foreignUid || !t3lib_div::testInt($foreignUid) || $config['foreign_selector']) {
 825              $record = $this->getNewRecord($this->inlineFirstPid, $current['table']);
 826  
 827              // dynamically import an existing record (this could be a call from a select box)
 828          } else {
 829              $record = $this->getRecord($this->inlineFirstPid, $current['table'], $foreignUid);
 830          }
 831  
 832              // now there is a foreign_selector, so there is a new record on the intermediate table, but
 833              // this intermediate table holds a field, which is responsible for the foreign_selector, so
 834              // we have to set this field to the uid we get - or if none, to a new uid
 835          if ($config['foreign_selector'] && $foreignUid) {
 836              $selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_selector']);
 837                  // For a selector of type group/db, prepend the tablename (<tablename>_<uid>):
 838              $record[$config['foreign_selector']] = $selConfig['type'] != 'groupdb' ? '' : $selConfig['table'].'_';
 839              $record[$config['foreign_selector']] .= $foreignUid;
 840          }
 841  
 842              // the HTML-object-id's prefix of the dynamically created record
 843          $objectPrefix = $this->inlineNames['object'].'['.$current['table'].']';
 844          $objectId = $objectPrefix.'['.$record['uid'].']';
 845  
 846              // render the foreign record that should passed back to browser
 847          $item = $this->renderForeignRecord($parent['uid'], $record, $config);
 848          if($item === false) {
 849              $jsonArray = array(
 850                  'data'    => 'Access denied',
 851                  'scriptCall' => array(
 852                      "alert('Access denied');",
 853                  )
 854              );
 855              return $this->getJSON($jsonArray);
 856          }
 857  
 858              // Encode TCEforms AJAX response with utf-8:
 859          $item = $GLOBALS['LANG']->csConvObj->utf8_encode($item, $GLOBALS['LANG']->charSet);
 860  
 861          if (!$current['uid']) {
 862              $jsonArray = array(
 863                  'data'    => $item,
 864                  'scriptCall' => array(
 865                      "inline.domAddNewRecord('bottom','".$this->inlineNames['object']."_records','$objectPrefix',json.data);",
 866                      "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."',null,'$foreignUid');"
 867                  )
 868              );
 869  
 870              // append the HTML data after an existing record in the container
 871          } else {
 872              $jsonArray = array(
 873                  'data'    => $item,
 874                  'scriptCall' => array(
 875                      "inline.domAddNewRecord('after','".$domObjectId.'_div'."','$objectPrefix',json.data);",
 876                      "inline.memorizeAddRecord('$objectPrefix','".$record['uid']."','".$current['uid']."','$foreignUid');"
 877                  )
 878              );
 879          }
 880  
 881              // add the JavaScript data that would have been added at the bottom of a regular TCEforms calls
 882          $jsonArray['scriptCall'][] = $this->fObj->JSbottom($this->fObj->formName, true);
 883              // if script.aculo.us Sortable is used, update the Observer to know the the record
 884          if ($config['appearance']['useSortable'])
 885              $jsonArray['scriptCall'][] = "inline.createDragAndDropSorting('".$this->inlineNames['object']."_records');";
 886              // if TCEforms has some JavaScript code to be executed, just do it
 887          if ($this->fObj->extJSCODE)
 888              $jsonArray['scriptCall'][] = $this->fObj->extJSCODE;
 889              // tell the browser to scroll to the newly created record
 890          $jsonArray['scriptCall'][] = "Element.scrollTo('".$objectId."_div');";
 891              // fade out and fade in the new record in the browser view to catch the user's eye
 892          $jsonArray['scriptCall'][] = "inline.fadeOutFadeIn('".$objectId."_div');";
 893  
 894              // Remove the current level also from the dynNestedStack of TCEforms:
 895          $this->fObj->popFromDynNestedStack();
 896  
 897              // return the JSON string
 898          return $this->getJSON($jsonArray);
 899      }
 900  
 901  
 902      /**
 903       * Save the expanded/collapsed state of a child record in the BE_USER->uc.
 904       *
 905       * @param    string        $domObjectId: The calling object in hierarchy, that requested a new record.
 906       * @param    string        $expand: Whether this record is expanded.
 907       * @param    string        $collapse: Whether this record is collapsed.
 908       * @return    void
 909       */
 910  	function setExpandedCollapsedState($domObjectId, $expand, $collapse) {
 911              // parse the DOM identifier (string), add the levels to the structure stack (array), but don't load TCA config
 912          $this->parseStructureString($domObjectId, false);
 913              // the current table - for this table we should add/import records
 914          $current = $this->inlineStructure['unstable'];
 915              // the top parent table - this table embeds the current table
 916          $top = $this->getStructureLevel(0);
 917  
 918              // only do some action if the top record and the current record were saved before
 919          if (t3lib_div::testInt($top['uid'])) {
 920              $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
 921              $inlineViewCurrent =& $inlineView[$top['table']][$top['uid']];
 922  
 923              $expandUids = t3lib_div::trimExplode(',', $expand);
 924              $collapseUids = t3lib_div::trimExplode(',', $collapse);
 925  
 926                  // set records to be expanded
 927              foreach ($expandUids as $uid) {
 928                  $inlineViewCurrent[$current['table']][] = $uid;
 929              }
 930                  // set records to be collapsed
 931              foreach ($collapseUids as $uid) {
 932                  $inlineViewCurrent[$current['table']] = $this->removeFromArray($uid, $inlineViewCurrent[$current['table']]);
 933              }
 934  
 935                  // save states back to database
 936              if (is_array($inlineViewCurrent[$current['table']])) {
 937                  $GLOBALS['BE_USER']->uc['inlineView'] = serialize($inlineView);
 938                  $GLOBALS['BE_USER']->writeUC();
 939              }
 940          }
 941      }
 942  
 943  
 944      /*******************************************************
 945       *
 946       * Get data from database and handle relations
 947       *
 948       *******************************************************/
 949  
 950  
 951      /**
 952       * Get the related records of the embedding item, this could be 1:n, m:n.
 953       *
 954       * @param    string        $table: The table name of the record
 955       * @param    string        $field: The field name which this element is supposed to edit
 956       * @param    array        $row: The record data array where the value(s) for the field can be found
 957       * @param    array        $PA: An array with additional configuration options.
 958       * @param    array        $config: (Redundant) content of $PA['fieldConf']['config'] (for convenience)
 959       * @return    array        The records related to the parent item
 960       */
 961  	function getRelatedRecords($table,$field,$row,&$PA,$config) {
 962          $records = array();
 963  
 964              // Creating the label for the "No Matching Value" entry.
 965          $nMV_label = isset($PA['fieldTSConfig']['noMatchingValue_label']) ? $this->fObj->sL($PA['fieldTSConfig']['noMatchingValue_label']) : '[ '.$this->fObj->getLL('l_noMatchingValue').' ]';
 966  
 967              // Register the required number of elements:
 968          # $this->fObj->requiredElements[$PA['itemFormElName']] = array($minitems,$maxitems,'imgName'=>$table.'_'.$row['uid'].'_'.$field);
 969  
 970              // Perform modification of the selected items array:
 971          $itemArray = t3lib_div::trimExplode(',',$PA['itemFormElValue'],1);
 972          foreach($itemArray as $tk => $tv) {
 973              $tvP = explode('|',$tv,2);
 974                  // get the records for this uid using t3lib_transferdata
 975              $records[] = $this->getRecord($row['pid'], $config['foreign_table'], $tvP[0]);
 976          }
 977  
 978          return $records;
 979      }
 980  
 981  
 982      /**
 983       * Get possible records.
 984       * Copied from TCEform and modified.
 985       *
 986       * @param    string        The table name of the record
 987       * @param    string        The field name which this element is supposed to edit
 988       * @param    array        The record data array where the value(s) for the field can be found
 989       * @param    array        An array with additional configuration options.
 990       * @param    string        $checkForConfField: For which field in the foreign_table the possible records should be fetched
 991       * @return    mixed        Array of possible record items; false if type is "group/db", then everything could be "possible"
 992       */
 993  	function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector') {
 994              // ctrl configuration from TCA:
 995          $tcaTableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
 996              // Field configuration from TCA:
 997          $foreign_table = $conf['foreign_table'];
 998          $foreign_check = $conf[$checkForConfField];
 999  
1000          $foreignConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_check);
1001          $PA = $foreignConfig['PA'];
1002          $config = $PA['fieldConf']['config'];
1003          
1004          if ($foreignConfig['type'] == 'select') {
1005                  // Getting the selector box items from the system
1006              $selItems = $this->fObj->addSelectOptionsToItemArray($this->fObj->initItemArray($PA['fieldConf']),$PA['fieldConf'],$this->fObj->setTSconfig($table,$row),$field);
1007              if ($config['itemsProcFunc']) $selItems = $this->fObj->procItems($selItems,$PA['fieldTSConfig']['itemsProcFunc.'],$config,$table,$row,$field);
1008      
1009                  // Possibly remove some items:
1010              $removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
1011              foreach($selItems as $tk => $p)    {
1012      
1013                      // Checking languages and authMode:
1014                  $languageDeny = $tcaTableCtrl['languageField'] && !strcmp($tcaTableCtrl['languageField'], $field) && !$GLOBALS['BE_USER']->checkLanguageAccess($p[1]);
1015                  $authModeDeny = $config['form_type']=='select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table,$field,$p[1],$config['authMode']);
1016                  if (in_array($p[1],$removeItems) || $languageDeny || $authModeDeny)    {
1017                      unset($selItems[$tk]);
1018                  } elseif (isset($PA['fieldTSConfig']['altLabels.'][$p[1]])) {
1019                      $selItems[$tk][0]=$this->fObj->sL($PA['fieldTSConfig']['altLabels.'][$p[1]]);
1020                  }
1021      
1022                      // Removing doktypes with no access:
1023                  if ($table.'.'.$field == 'pages.doktype')    {
1024                      if (!($GLOBALS['BE_USER']->isAdmin() || t3lib_div::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'],$p[1])))    {
1025                          unset($selItems[$tk]);
1026                      }
1027                  }
1028              }
1029          } else {
1030              $selItems = false;
1031          }
1032  
1033          return $selItems;
1034      }
1035  
1036      /**
1037       * Gets the uids of a select/selector that should be unique an have already been used.
1038       *
1039       * @param    array        $records: All inline records on this level
1040       * @param    array        $conf: The TCA field configuration of the inline field to be rendered
1041       * @param    boolean        $splitValue: for usage with group/db, values come like "tx_table_123|Title%20abc", but we need "tx_table" and "123"
1042       * @return    array        The uids, that have been used already and should be used unique
1043       */
1044  	function getUniqueIds($records, $conf=array(), $splitValue=false) {
1045          $uniqueIds = array();
1046  
1047          if ($conf['foreign_unique'] && count($records)) {
1048              foreach ($records as $rec) {
1049                  $value = $rec[$conf['foreign_unique']];
1050                      // Split the value and extract the table and uid:
1051                  if ($splitValue) {
1052                      $valueParts = t3lib_div::trimExplode('|', $value);
1053                      $itemParts = explode('_', $valueParts[0]);
1054                      $value = array(
1055                          'uid' => array_pop($itemParts),
1056                          'table' => implode('_', $itemParts)
1057                      );
1058                  }
1059                  $uniqueIds[$rec['uid']] = $value;
1060              }
1061          }
1062  
1063          return $uniqueIds;
1064      }
1065  
1066  
1067      /**
1068       * Get a single record row for a TCA table from the database.
1069       * t3lib_transferData is used for "upgrading" the values, especially the relations.
1070       *
1071       * @param    integer        $pid: The pid of the page the record should be stored (only relevant for NEW records)
1072       * @param    string        $table: The table to fetch data from (= foreign_table)
1073       * @param    string        $uid: The uid of the record to fetch, or the pid if a new record should be created
1074       * @param    string        $cmd: The command to perform, empty or 'new'
1075       * @return    array        A record row from the database post-processed by t3lib_transferData
1076       */
1077  	function getRecord($pid, $table, $uid, $cmd='') {
1078          $trData = t3lib_div::makeInstance('t3lib_transferData');
1079          $trData->addRawData = TRUE;
1080          # $trData->defVals = $this->defVals;
1081          $trData->lockRecords=1;
1082          $trData->disableRTE = $GLOBALS['SOBE']->MOD_SETTINGS['disableRTE'];
1083              // if a new record should be created
1084          $trData->fetchRecord($table, $uid, ($cmd === 'new' ? 'new' : ''));
1085          reset($trData->regTableItems_data);
1086          $rec = current($trData->regTableItems_data);
1087          $rec['uid'] = $cmd == 'new' ? uniqid('NEW') : $uid;
1088          if ($cmd=='new') $rec['pid'] = $pid;
1089  
1090          return $rec;
1091      }
1092  
1093  
1094      /**
1095       * Wrapper. Calls getRecord in case of a new record should be created.
1096       *
1097       * @param    integer        $pid: The pid of the page the record should be stored (only relevant for NEW records)
1098       * @param    string        $table: The table to fetch data from (= foreign_table)
1099       * @return    array        A record row from the database post-processed by t3lib_transferData
1100       */
1101  	function getNewRecord($pid, $table) {
1102          return $this->getRecord($pid, $table, $pid, 'new');
1103      }
1104  
1105  
1106      /*******************************************************
1107       *
1108       * Structure stack for handling inline objects/levels
1109       *
1110       *******************************************************/
1111  
1112  
1113      /**
1114       * Add a new level on top of the structure stack. Other functions can access the
1115       * stack and determine, if there's possibly a endless loop.
1116       *
1117       * @param    string        $table: The table name of the record
1118       * @param    string        $uid: The uid of the record that embeds the inline data
1119       * @param    string        $field: The field name which this element is supposed to edit
1120       * @param    array        $config: The TCA-configuration of the inline field
1121       * @return    void
1122       */
1123  	function pushStructure($table, $uid, $field = '', $config = array()) {
1124          $this->inlineStructure['stable'][] = array(
1125              'table'    => $table,
1126              'uid' => $uid,
1127              'field' => $field,
1128              'config' => $config,
1129          );
1130          $this->updateStructureNames();
1131      }
1132  
1133  
1134      /**
1135       * Remove the item on top of the structure stack and return it.
1136       *
1137       * @return    array        The top item of the structure stack - array(<table>,<uid>,<field>,<config>)
1138       */
1139  	function popStructure() {
1140          if (count($this->inlineStructure['stable'])) {
1141              $popItem = array_pop($this->inlineStructure['stable']);
1142              $this->updateStructureNames();
1143          }
1144          return $popItem;
1145      }
1146  
1147  
1148      /**
1149       * For common use of DOM object-ids and form field names of a several inline-level,
1150       * these names/identifiers are preprocessed and set to $this->inlineNames.
1151       * This function is automatically called if a level is pushed to or removed from the
1152       * inline structure stack.
1153       *
1154       * @return    void
1155       */
1156  	function updateStructureNames() {
1157          $current = $this->getStructureLevel(-1);
1158              // if there are still more inline levels available
1159          if ($current !== false) {
1160              $lastItemName = $this->getStructureItemName($current);
1161              $this->inlineNames = array(
1162                  'form' => $this->prependFormFieldNames.$lastItemName,
1163                  'object' => $this->prependNaming.'['.$this->inlineFirstPid.']'.$this->getStructurePath(),
1164              );
1165              // if there are no more inline levels available
1166          } else {
1167              $this->inlineNames = array();
1168          }
1169      }
1170  
1171  
1172      /**
1173       * Create a name/id for usage in HTML output of a level of the structure stack.
1174       *
1175       * @param    array        $levelData: Array of a level of the structure stack (containing the keys table, uid and field)
1176       * @return    string        The name/id of that level, to be used for HTML output
1177       */
1178  	function getStructureItemName($levelData) {
1179          if (is_array($levelData)) {
1180              $name =    '['.$levelData['table'].']' .
1181                      '['.$levelData['uid'].']' .
1182                      (isset($levelData['field']) ? '['.$levelData['field'].']' : '');
1183          }
1184          return $name;
1185      }
1186  
1187  
1188      /**
1189       * Get a level from the stack and return the data.
1190       * If the $level value is negative, this function works top-down,
1191       * if the $level value is positive, this function works bottom-up.
1192       *
1193       * @param    integer        $level: Which level to return
1194       * @return    array        The item of the stack at the requested level
1195       */
1196  	function getStructureLevel($level) {
1197          $inlineStructureCount = count($this->inlineStructure['stable']);
1198          if ($level < 0) $level = $inlineStructureCount+$level;
1199          if ($level >= 0 && $level < $inlineStructureCount)
1200              return $this->inlineStructure['stable'][$level];
1201          else
1202              return false;
1203      }
1204  
1205  
1206      /**
1207       * Get the identifiers of a given depth of level, from the top of the stack to the bottom.
1208       * An identifier consists looks like [<table>][<uid>][<field>].
1209       *
1210       * @param    integer        $structureDepth: How much levels to output, beginning from the top of the stack
1211       * @return    string        The path of identifiers
1212       */
1213  	function getStructurePath($structureDepth = -1) {
1214          $structureCount = count($this->inlineStructure['stable']);
1215          if ($structureDepth < 0 || $structureDepth > $structureCount) $structureDepth = $structureCount;
1216  
1217          for ($i = 1; $i <= $structureDepth; $i++) {
1218              $current = $this->getStructureLevel(-$i);
1219              $string = $this->getStructureItemName($current).$string;
1220          }
1221  
1222          return $string;
1223      }
1224  
1225  
1226      /**
1227       * Convert the DOM object-id of an inline container to an array.
1228       * The object-id could look like 'data[inline][tx_mmftest_company][1][employees]'.
1229       * The result is written to $this->inlineStructure.
1230       * There are two keys:
1231       *  - 'stable': Containing full qualified identifiers (table, uid and field)
1232       *  - 'unstable': Containting partly filled data (e.g. only table and possibly field)
1233       *
1234       * @param    string        $domObjectId: The DOM object-id
1235       * @param    boolean        $loadConfig: Load the TCA configuration for that level
1236       * @return    void
1237       */
1238  	function parseStructureString($string, $loadConfig = false) {
1239          $unstable = array();
1240          $vector = array('table', 'uid', 'field');
1241          $pattern = '/^'.$this->prependNaming.'\[(.+?)\]\[(.+)\]$/';
1242          if (preg_match($pattern, $string, $match)) {
1243              $this->inlineFirstPid = $match[1];
1244              $parts = explode('][', $match[2]);
1245              $partsCnt = count($parts);
1246              for ($i = 0; $i < $partsCnt; $i++) {
1247                  if ($i > 0 && $i % 3 == 0) {
1248                          // load the TCA configuration of the table field and store it in the stack
1249                      if ($loadConfig) {
1250                          t3lib_div::loadTCA($unstable['table']);
1251                          $unstable['config'] = $GLOBALS['TCA'][$unstable['table']]['columns'][$unstable['field']]['config'];
1252                              // Fetch TSconfig:
1253                          $TSconfig = $this->fObj->setTSconfig(
1254                              $unstable['table'],
1255                              array('uid' => $unstable['uid'], 'pid' => $this->inlineFirstPid),
1256                              $unstable['field']
1257                          );
1258                              // Override TCA field config by TSconfig:
1259                          if (!$TSconfig['disabled']) {
1260                              $unstable['config'] = $this->fObj->overrideFieldConf($unstable['config'], $TSconfig);
1261                          }
1262                      }
1263                      $this->inlineStructure['stable'][] = $unstable;
1264                      $unstable = array();
1265                  }
1266                  $unstable[$vector[$i % 3]] = $parts[$i];
1267              }
1268              $this->updateStructureNames();
1269              if (count($unstable)) $this->inlineStructure['unstable'] = $unstable;
1270          }
1271      }
1272  
1273  
1274      /*******************************************************
1275       *
1276       * Helper functions
1277       *
1278       *******************************************************/
1279  
1280  
1281      /**
1282       * Does some checks on the TCA configuration of the inline field to render.
1283       *
1284       * @param    array        $config: Reference to the TCA field configuration
1285       * @return    boolean        If critical configuration errors were found, false is returned
1286       */
1287  	function checkConfiguration(&$config) {
1288          $foreign_table = $config['foreign_table'];
1289  
1290              // An inline field must have a foreign_table, if not, stop all further inline actions for this field:
1291          if (!$foreign_table || !is_array($GLOBALS['TCA'][$foreign_table])) {
1292              return false;
1293          }
1294              // Init appearance if not set:
1295          if (!is_array($config['appearance'])) {
1296              $config['appearance'] = array();
1297          }
1298              // Set the position/appearance of the "Create new record" link:
1299          if ($config['foreign_selector'] && !$config['appearance']['useCombination']) {
1300              $config['appearance']['newRecordLinkPosition'] = 'none';
1301          } elseif (!in_array($config['appearance']['newRecordLinkPosition'], array('top', 'bottom', 'both', 'none'))) {
1302              $config['appearance']['newRecordLinkPosition'] = 'top';
1303          }
1304  
1305          return true;
1306      }
1307  
1308  
1309      /**
1310       * Checks the page access rights (Code for access check mostly taken from alt_doc.php)
1311       * as well as the table access rights of the user.
1312       *
1313       * @param    string        $cmd: The command that sould be performed ('new' or 'edit')
1314       * @param    string        $table: The table to check access for
1315       * @param    string        $theUid: The record uid of the table
1316       * @return    boolean        Returns true is the user has access, or false if not
1317       */
1318  	function checkAccess($cmd, $table, $theUid) {
1319              // 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...)
1320              // First, resetting flags.
1321          $hasAccess = 0;
1322          $deniedAccessReason = '';
1323  
1324              // If the command is to create a NEW record...:
1325          if ($cmd=='new') {
1326                  // If the pid is numerical, check if it's possible to write to this page:
1327              if (t3lib_div::testInt($this->inlineFirstPid)) {
1328                  $calcPRec = t3lib_BEfunc::getRecord('pages', $this->inlineFirstPid);
1329                  if(!is_array($calcPRec)) {
1330                      return false;
1331                  }
1332                  $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);    // Permissions for the parent page
1333                  if ($table=='pages')    {    // If pages:
1334                      $hasAccess = $CALC_PERMS&8 ? 1 : 0; // Are we allowed to create new subpages?
1335                  } else {
1336                      $hasAccess = $CALC_PERMS&16 ? 1 : 0; // Are we allowed to edit content on this page?
1337                  }
1338                  // If the pid is a NEW... value, the access will be checked on creating the page:
1339                  // (if the page with the same NEW... value could be created in TCEmain, this child record can neither)
1340              } else {
1341                  $hasAccess = 1;
1342              }
1343          } else {    // Edit:
1344              $calcPRec = t3lib_BEfunc::getRecord($table,$theUid);
1345              t3lib_BEfunc::fixVersioningPid($table,$calcPRec);
1346              if (is_array($calcPRec))    {
1347                  if ($table=='pages')    {    // If pages:
1348                      $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec);
1349                      $hasAccess = $CALC_PERMS&2 ? 1 : 0;
1350                  } else {
1351                      $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(t3lib_BEfunc::getRecord('pages',$calcPRec['pid']));    // Fetching pid-record first.
1352                      $hasAccess = $CALC_PERMS&16 ? 1 : 0;
1353                  }
1354  
1355                      // Check internals regarding access:
1356                  if ($hasAccess)    {
1357                      $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec);
1358                  }
1359              }
1360          }
1361  
1362          if(!$GLOBALS['BE_USER']->check('tables_modify', $table)) {
1363              $hasAccess = 0;
1364          }
1365  
1366          if(!$hasAccess) {
1367              $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg;
1368              if($deniedAccessReason) {
1369                  debug($deniedAccessReason);
1370              }
1371          }
1372  
1373          return $hasAccess ? true : false;
1374      }
1375  
1376  
1377      /**
1378       * Check the keys and values in the $compare array against the ['config'] part of the top level of the stack.
1379       * A boolean value is return depending on how the comparison was successful.
1380       *
1381       * @param    array        $compare: keys and values to compare to the ['config'] part of the top level of the stack
1382       * @return    boolean        Whether the comparison was successful
1383       * @see     arrayCompareComplex
1384       */
1385  	function compareStructureConfiguration($compare) {
1386          $level = $this->getStructureLevel(-1);
1387          $result = $this->arrayCompareComplex($level, $compare);
1388  
1389          return $result;
1390      }
1391  
1392  
1393      /**
1394       * Normalize a relation "uid" published by transferData, like "1|Company%201"
1395       *
1396       * @param    string        $string: A transferData reference string, containing the uid
1397       * @return    string        The normalized uid
1398       */
1399  	function normalizeUid($string) {
1400          $parts = explode('|', $string);
1401          return $parts[0];
1402      }
1403  
1404  
1405      /**
1406       * Wrap the HTML code of a section with a table tag.
1407       *
1408       * @param    string        $section: The HTML code to be wrapped
1409       * @param    array        $styleAttrs: Attributes for the style argument in the table tag
1410       * @param    array        $tableAttrs: Attributes for the table tag (like width, border, etc.)
1411       * @return    string        The wrapped HTML code
1412       */
1413  	function wrapFormsSection($section, $styleAttrs = array(), $tableAttrs = array()) {
1414          if (!$styleAttrs['margin-right']) $styleAttrs['margin-right'] = $this->inlineStyles['margin-right'].'px';
1415  
1416          foreach ($styleAttrs as $key => $value) $style .= ($style?' ':'').$key.': '.htmlspecialchars($value).'; ';
1417          if ($style) $style = ' style="'.$style.'"';
1418  
1419          if (!$tableAttrs['background'] && $this->fObj->borderStyle[2]) $tableAttrs['background'] = $this->backPath.$this->borderStyle[2];
1420          if (!$tableAttrs['cellspacing']) $tableAttrs['cellspacing'] = '0';
1421          if (!$tableAttrs['cellpadding']) $tableAttrs['cellpadding'] = '0';
1422          if (!$tableAttrs['border']) $tableAttrs['border'] = '0';
1423          if (!$tableAttrs['width']) $tableAttrs['width'] = '100%';
1424          if (!$tableAttrs['class'] && $this->borderStyle[3]) $tableAttrs['class'] = $this->borderStyle[3];
1425  
1426          foreach ($tableAttrs as $key => $value) $table .= ($table?' ':'').$key.'="'.htmlspecialchars($value).'"';
1427  
1428          $out = '<table '.$table.$style.'>'.$section.'</table>';
1429          return $out;
1430      }
1431  
1432  
1433      /**
1434       * Checks if the $table is the child of a inline type AND the $field is the label field of this table.
1435       * This function is used to dynamically update the label while editing. This has no effect on labels,
1436       * that were processed by a TCEmain-hook on saving.
1437       *
1438       * @param    string        $table: The table to check
1439       * @param    string        $field: The field on this table to check
1440       * @return    boolean        is inline child and field is responsible for the label
1441       */
1442  	function isInlineChildAndLabelField($table, $field) {
1443          $level = $this->getStructureLevel(-1);
1444          if ($level['config']['foreign_label'])
1445              $label = $level['config']['foreign_label'];
1446          else
1447              $label = $GLOBALS['TCA'][$table]['ctrl']['label'];
1448          return $level['config']['foreign_table'] === $table && $label == $field ? true : false;
1449      }
1450  
1451  
1452      /**
1453       * Get the depth of the stable structure stack.
1454       * (count($this->inlineStructure['stable'])
1455       *
1456       * @return    integer        The depth of the structure stack
1457       */
1458  	function getStructureDepth() {
1459          return count($this->inlineStructure['stable']);
1460      }
1461  
1462  
1463      /**
1464       * Handles complex comparison requests on an array.
1465       * A request could look like the following:
1466       *
1467       * $searchArray = array(
1468       *         '%AND'    => array(
1469       *             'key1'    => 'value1',
1470       *             'key2'    => 'value2',
1471       *             '%OR'    => array(
1472       *                 'subarray' => array(
1473       *                     'subkey' => 'subvalue'
1474       *                 ),
1475       *                 'key3'    => 'value3',
1476       *                 'key4'    => 'value4'
1477       *             )
1478       *         )
1479       * );
1480       *
1481       * It is possible to use the array keys '%AND.1', '%AND.2', etc. to prevent
1482       * overwriting the sub-array. It could be neccessary, if you use complex comparisons.
1483       *
1484       * The example above means, key1 *AND* key2 (and their values) have to match with
1485       * the $subjectArray and additional one *OR* key3 or key4 have to meet the same
1486       * condition.
1487       * It is also possible to compare parts of a sub-array (e.g. "subarray"), so this
1488       * function recurses down one level in that sub-array.
1489       *
1490       * @param    array        $subjectArray: The array to search in
1491       * @param    array        $searchArray: The array with keys and values to search for
1492       * @param    string        $type: Use '%AND' or '%OR' for comparision
1493       * @return    boolean        The result of the comparison
1494       */
1495  	function arrayCompareComplex($subjectArray, $searchArray, $type = '') {
1496          $localMatches = 0;
1497          $localEntries = 0;
1498  
1499          if (is_array($searchArray) && count($searchArray)) {
1500                  // if no type was passed, try to determine
1501              if (!$type) {
1502                  reset($searchArray);
1503                  $type = key($searchArray);
1504                  $searchArray = current($searchArray);
1505              }
1506  
1507                  // we use '%AND' and '%OR' in uppercase
1508              $type = strtoupper($type);
1509  
1510                  // split regular elements from sub elements
1511              foreach ($searchArray as $key => $value) {
1512                  $localEntries++;
1513  
1514                      // process a sub-group of OR-conditions
1515                  if ($key == '%OR') {
1516                      $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%OR') ? 1 : 0;
1517                      // process a sub-group of AND-conditions
1518                  } elseif ($key == '%AND') {
1519                      $localMatches += $this->arrayCompareComplex($subjectArray, $value, '%AND') ? 1 : 0;
1520                      // a part of an associative array should be compared, so step down in the array hierarchy
1521                  } elseif (is_array($value) && $this->isAssociativeArray($searchArray)) {
1522                      $localMatches += $this->arrayCompareComplex($subjectArray[$key], $value, $type) ? 1 : 0;
1523                      // it is a normal array that is only used for grouping and indexing
1524                  } elseif (is_array($value)) {
1525                      $localMatches += $this->arrayCompareComplex($subjectArray, $value, $type) ? 1 : 0;
1526                      // directly compare a value
1527                  } else {
1528                      $localMatches += isset($subjectArray[$key]) && isset($value) && $subjectArray[$key] === $value ? 1 : 0;
1529                  }
1530  
1531                      // if one or more matches are required ('OR'), return true after the first successful match
1532                  if ($type == '%OR' && $localMatches > 0) return true;
1533                      // if all matches are required ('AND') and we have no result after the first run, return false
1534                  if ($type == '%AND' && $localMatches == 0) return false;
1535              }
1536          }
1537  
1538              // return the result for '%AND' (if nothing was checked, true is returned)
1539          return $localEntries == $localMatches ? true : false;
1540      }
1541  
1542  
1543      /**
1544       * Checks whether an object is an associative array.
1545       *
1546       * @param    mixed        $object: The object to be checked
1547       * @return    boolean        Returns true, if the object is an associative array
1548       */
1549  	function isAssociativeArray($object) {
1550          return is_array($object) && count($object) && (array_keys($object) !== range(0, sizeof($object) - 1))
1551              ? true
1552              : false;
1553      }
1554  
1555  
1556      /**
1557       * Remove an element from an array.
1558       *
1559       * @param    mixed        $needle: The element to be removed.
1560       * @param    array        $haystack: The array the element should be removed from.
1561       * @param    mixed        $strict: Search elements strictly.
1562       * @return    array        The array $haystack without the $needle
1563       */
1564  	function removeFromArray($needle, $haystack, $strict=null) {
1565          $pos = array_search($needle, $haystack, $strict);
1566          if ($pos !== false) unset($haystack[$pos]);
1567          return $haystack;
1568      }
1569  
1570  
1571      /**
1572       * Makes a flat array from the $possibleRecords array.
1573       * The key of the flat array is the value of the record,
1574       * the value of the flat array is the label of the record.
1575       *
1576       * @param    array        $possibleRecords: The possibleRecords array (for select fields)
1577       * @return    mixed        A flat array with key=uid, value=label; if $possibleRecords isn't an array, false is returned.
1578       */
1579  	function getPossibleRecordsFlat($possibleRecords) {
1580          $flat = false;
1581          if (is_array($possibleRecords)) {
1582              $flat = array();
1583              foreach ($possibleRecords as $record) $flat[$record[1]] = $record[0];
1584          }
1585          return $flat;
1586      }
1587  
1588  
1589      /**
1590       * Determine the configuration and the type of a record selector.
1591       *
1592       * @param    array        $conf: TCA configuration of the parent(!) field
1593       * @return    array        Associative array with the keys 'PA' and 'type', both are false if the selector was not valid.
1594       */
1595  	function getPossibleRecordsSelectorConfig($conf, $field = '') {
1596          $foreign_table = $conf['foreign_table'];
1597          $foreign_selector = $conf['foreign_selector'];
1598  
1599          $PA = false;
1600          $type = false;
1601          $table = false;
1602          $selector = false;
1603          
1604          if ($field) {
1605              $PA = array();
1606              $PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$field];
1607              $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
1608              $PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$field);
1609              $config = $PA['fieldConf']['config'];
1610                  // Determine type of Selector:
1611              $type = $this->getPossibleRecordsSelectorType($config);
1612                  // Return table on this level:
1613              $table = $type == 'select' ? $config['foreign_table'] : $config['allowed'];
1614                  // Return type of the selector if foreign_selector is defined and points to the same field as in $field:
1615              if ($foreign_selector && $foreign_selector == $field && $type) {
1616                  $selector = $type;
1617              }
1618          }
1619          
1620          return array(
1621              'PA' => $PA,
1622              'type' => $type,
1623              'table' => $table,
1624              'selector' => $selector,
1625          );
1626      }
1627      
1628  
1629      /**
1630       * Determine the type of a record selector, e.g. select or group/db.
1631       *
1632       * @param    array        $config: TCE configuration of the selector
1633       * @return    mixed        The type of the selector, 'select' or 'groupdb' - false not valid
1634       */
1635  	function getPossibleRecordsSelectorType($config) {
1636          $type = false;
1637          if ($config['type'] == 'select') {
1638              $type = 'select';
1639          } elseif ($config['type'] == 'group' && $config['internal_type'] == 'db') {
1640              $type = 'groupdb';
1641          }
1642          return $type;
1643      }
1644      
1645      
1646      /**
1647       * Check, if a field should be skipped, that was defined to be handled as foreign_field or foreign_sortby of
1648       * the parent record of the "inline"-type - if so, we have to skip this field - the rendering is done via "inline" as hidden field
1649       *
1650       * @param    string        $table: The table name
1651       * @param    string        $field: The field name
1652       * @param    array        $row: The record row from the database
1653       * @param    array        $config: TCA configuration of the field
1654       * @return    boolean        Determines whether the field should be skipped.
1655       */
1656  	function skipField($table, $field, $row, $config) {
1657          $skipThisField = false;
1658  
1659          if ($this->getStructureDepth()) {
1660              $searchArray = array(
1661                  '%OR' => array(
1662                      'config' => array(
1663                          0 => array(
1664                              '%AND' => array(
1665                                  'foreign_table' => $table,
1666                                  '%OR' => array(
1667                                      '%AND' => array(
1668                                          'appearance' => array('useCombination' => 1),
1669                                          'foreign_selector' => $field,
1670                                      ),
1671                                      'MM' => $config['MM']
1672                                  ),
1673                              ),
1674                          ),
1675                          1 => array(
1676                              '%AND' => array(
1677                                  'foreign_table' => $config['foreign_table'],
1678                                  'foreign_selector' => $config['foreign_field'],
1679                              ),
1680                          ),
1681                      ),
1682                  ),
1683              );
1684  
1685                  // get the parent record from structure stack
1686              $level = $this->getStructureLevel(-1);
1687  
1688                  // If we have symmetric fields, check on which side we are and hide fields, that are set automatically:
1689              if (t3lib_loadDBGroup::isOnSymmetricSide($level['uid'], $level['config'], $row)) {
1690                  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $field;
1691                  $searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $field;
1692                  // Hide fields, that are set automatically:
1693              } else {
1694                  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $field;
1695                  $searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $field;
1696              }
1697  
1698              $skipThisField = $this->compareStructureConfiguration($searchArray, true);
1699          }
1700  
1701          return $skipThisField;
1702      }
1703  
1704  
1705      /**
1706       * Creates recursively a JSON literal from a mulidimensional associative array.
1707       * Uses Services_JSON (http://mike.teczno.com/JSON/doc/)
1708       *
1709       * @param    array        $jsonArray: The array (or part of) to be transformed to JSON
1710       * @return    string        If $level>0: part of JSON literal; if $level==0: whole JSON literal wrapped with <script> tags
1711       */
1712  	function getJSON($jsonArray) {
1713          if (!$GLOBALS['JSON']) {
1714              require_once(PATH_typo3.'contrib/json.php');
1715              $GLOBALS['JSON'] = t3lib_div::makeInstance('Services_JSON');
1716          }
1717          return $GLOBALS['JSON']->encode($jsonArray);
1718      }
1719  
1720  
1721      /**
1722       * Checks if a uid of a child table is in the inline view settings.
1723       *
1724       * @param    string        $table: Name of the child table
1725       * @param    integer        $uid: uid of the the child record
1726       * @return    boolean        true=expand, false=collapse
1727       */
1728  	function getExpandedCollapsedState($table, $uid) {
1729          if (is_array($this->inlineView) && is_array($this->inlineView[$table])) {
1730              if (in_array($uid, $this->inlineView[$table]) !== false) return true;
1731          }
1732          return false;
1733      }
1734  
1735  
1736      /**
1737       * Update expanded/collapsed states on new inline records if any.
1738       *
1739       * @param    array        $uc: The uc array to be processed and saved (by reference)
1740       * @param    object        $tce: Instance of TCEmain that saved data before (by reference)
1741       * @return    void
1742       */
1743  	function updateInlineView(&$uc, &$tce) {
1744          if (is_array($uc) && $uc['inlineView']) {
1745              $inlineView = unserialize($GLOBALS['BE_USER']->uc['inlineView']);
1746  
1747              foreach ($uc['inlineView'] as $topTable => $topRecords) {
1748                  foreach ($topRecords as $topUid => $childElements) {
1749                      foreach ($childElements as $childTable => $childRecords) {
1750                          $uids = array_keys($tce->substNEWwithIDs_table, $childTable);
1751                          if (count($uids)) {
1752                              foreach ($childRecords as $childUid => $state) {
1753                                  if ($state && in_array($childUid, $uids)) {
1754                                      $newChildUid = $tce->substNEWwithIDs[$childUid];
1755                                      $inlineView[$topTable][$topUid][$childTable][$newChildUid] = 1;
1756                                  }
1757                              }
1758                          }
1759                      }
1760                  }
1761              }
1762  
1763              $GLOBALS['BE_USER']->uc['inlineView'] = serialize($inlineView);
1764              $GLOBALS['BE_USER']->writeUC();
1765          }
1766      }
1767  
1768  
1769      /**
1770       * Returns the the margin in pixels, that is used for each new inline level.
1771       *
1772       * @return    integer        A pixel value for the margin of each new inline level.
1773       */
1774  	function getLevelMargin() {
1775          $margin = ($this->inlineStyles['margin-right']+1)*2;
1776          return $margin;
1777      }
1778  }
1779  
1780  
1781  if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tceforms_inline.php'])    {
1782      include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tceforms_inline.php']);
1783  }
1784  ?>


Généré le : Sun Nov 25 17:13:16 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics