Added feature #4553: Enable negative timestamps
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_loaddbgroup.php
index 8b69d2f..54e8cba 100755 (executable)
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 1999-2004 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
  *
  *
  *
  *
  *
  *
- *   72: class t3lib_loadDBGroup
- *   99:     function start($itemlist,$tablelist, $MMtable='',$MMuid=0)
- *  140:     function readList($itemlist)
- *  186:     function readMM($tableName,$uid)
- *  215:     function writeMM($tableName,$uid,$prependTableName=0)
- *  251:     function getValueArray($prependTableName='')
- *  279:     function convertPosNeg($valueArray,$fTable,$nfTable)
- *  301:     function getFromDB()
- *  333:     function readyForInterface()
+ *   76: class t3lib_loadDBGroup
+ *  111:     function start($itemlist, $tablelist, $MMtable='', $MMuid=0, $currentTable='', $conf=array())
+ *  179:     function readList($itemlist)
+ *  225:     function readMM($tableName,$uid)
+ *  276:     function writeMM($tableName,$uid,$prependTableName=0)
+ *  352:     function readForeignField($uid, $conf)
+ *  435:     function writeForeignField($conf, $parentUid, $updateToUid=0)
+ *  510:     function getValueArray($prependTableName='')
+ *  538:     function convertPosNeg($valueArray,$fTable,$nfTable)
+ *  560:     function getFromDB()
+ *  595:     function readyForInterface()
+ *  621:     function countItems($returnAsArray = true)
+ *  636:     function updateRefIndex($table,$id)
  *
  *
- * TOTAL FUNCTIONS: 8
+ * TOTAL FUNCTIONS: 12
  * (This index is automatically created/updated by the extension "extdeveval")
  *
  */
  * (This index is automatically created/updated by the extension "extdeveval")
  *
  */
@@ -58,6 +62,7 @@
 
 
 
 
 
 
+require_once (PATH_t3lib.'class.t3lib_refindex.php');
 
 
 
 
 
 
@@ -83,8 +88,19 @@ class t3lib_loadDBGroup      {
        var $dbPaths=Array();
        var $firstTable = '';                           // Will contain the first table name in the $tablelist (for positive ids)
        var $secondTable = '';                          // Will contain the second table name in the $tablelist (for negative ids)
        var $dbPaths=Array();
        var $firstTable = '';                           // Will contain the first table name in the $tablelist (for positive ids)
        var $secondTable = '';                          // Will contain the second table name in the $tablelist (for negative ids)
+               // private
+       var $MM_is_foreign = 0;         // boolean - if 1, uid_local and uid_foreign are switched, and the current table is inserted as tablename - this means you display a foreign relation "from the opposite side"
+       var $MM_oppositeField = '';     // field name at the "local" side of the MM relation
+       var $MM_oppositeTable = ''; // only set if MM_is_foreign is set
+       var $MM_oppositeFieldConf = ''; // only set if MM_is_foreign is set
+       var $MM_isMultiTableRelationship = 0;   // is empty by default; if MM_is_foreign is set and there is more than one table allowed (on the "local" side), then it contains the first table (as a fallback)
+       var $currentTable;      // current table => Only needed for reverse relations
+       var $undeleteRecord;            // if a record should be undeleted (so do not use the $useDeleteClause on t3lib_BEfunc)
 
 
 
 
+       var $MM_match_fields = array(); // array of fields value pairs that should match while SELECT and will be written into MM table if $MM_insert_fields is not set
+       var $MM_insert_fields = array();        // array of fields and value pairs used for insert in MM table
+       var $MM_table_where = ''; // extra MM table where
 
 
        /**
 
 
        /**
@@ -94,9 +110,41 @@ class t3lib_loadDBGroup     {
         * @param       string          Comma list of tables, first table takes priority if no table is set for an entry in the list.
         * @param       string          Name of a MM table.
         * @param       integer         Local UID for MM lookup
         * @param       string          Comma list of tables, first table takes priority if no table is set for an entry in the list.
         * @param       string          Name of a MM table.
         * @param       integer         Local UID for MM lookup
+        * @param       string          current table name
+        * @param       integer         TCA configuration for current field
         * @return      void
         */
         * @return      void
         */
-       function start($itemlist,$tablelist, $MMtable='',$MMuid=0)      {
+       function start($itemlist, $tablelist, $MMtable='', $MMuid=0, $currentTable='', $conf=array())   {
+                       // SECTION: MM reverse relations
+               $this->MM_is_foreign = ($conf['MM_opposite_field']?1:0);
+               $this->MM_oppositeField = $conf['MM_opposite_field'];
+               $this->MM_table_where = $conf['MM_table_where'];
+               $this->MM_hasUidField = $conf['MM_hasUidField'];
+               $this->MM_match_fields = is_array($conf['MM_match_fields']) ? $conf['MM_match_fields'] : array();
+               $this->MM_insert_fields = is_array($conf['MM_insert_fields']) ? $conf['MM_insert_fields'] : $this->MM_match_fields;
+               
+               $this->currentTable = $currentTable;
+               if ($this->MM_is_foreign)       {
+                       $tmp = ($conf['type']==='group'?$conf['allowed']:$conf['foreign_table']);
+                               // normally, $conf['allowed'] can contain a list of tables, but as we are looking at a MM relation from the foreign side, it only makes sense to allow one one table in $conf['allowed']
+                       $tmp = t3lib_div::trimExplode(',', $tmp);
+                       $this->MM_oppositeTable = $tmp[0];
+                       unset($tmp);
+
+                               // only add the current table name if there is more than one allowed field
+                       t3lib_div::loadTCA($this->MM_oppositeTable);    // We must be sure this has been done at least once before accessing the "columns" part of TCA for a table.
+                       $this->MM_oppositeFieldConf = $GLOBALS['TCA'][$this->MM_oppositeTable]['columns'][$this->MM_oppositeField]['config'];
+
+                       if ($this->MM_oppositeFieldConf['allowed'])     {
+                               $oppositeFieldConf_allowed = explode(',', $this->MM_oppositeFieldConf['allowed']);
+                               if (count($oppositeFieldConf_allowed) > 1)      {
+                                       $this->MM_isMultiTableRelationship = $oppositeFieldConf_allowed[0];
+                               }
+                       }
+               }
+
+                       // SECTION:     normal MM relations
+
                        // If the table list is "*" then all tables are used in the list:
                if (!strcmp(trim($tablelist),'*'))      {
                        $tablelist = implode(',',array_keys($GLOBALS['TCA']));
                        // If the table list is "*" then all tables are used in the list:
                if (!strcmp(trim($tablelist),'*'))      {
                        $tablelist = implode(',',array_keys($GLOBALS['TCA']));
@@ -125,9 +173,16 @@ class t3lib_loadDBGroup    {
                        // Now, populate the internal itemArray and tableArray arrays:
                if ($MMtable)   {       // If MM, then call this function to do that:
                        $this->readMM($MMtable,$MMuid);
                        // Now, populate the internal itemArray and tableArray arrays:
                if ($MMtable)   {       // If MM, then call this function to do that:
                        $this->readMM($MMtable,$MMuid);
+               } elseif ($MMuid && $conf['foreign_field']) {
+                               // If not MM but foreign_field, the read the records by the foreign_field
+                       $this->readForeignField($MMuid, $conf);
                } else {
                                // If not MM, then explode the itemlist by "," and traverse the list:
                        $this->readList($itemlist);
                } else {
                                // If not MM, then explode the itemlist by "," and traverse the list:
                        $this->readList($itemlist);
+                               // do automatic default_sortby, if any
+                       if ($conf['foreign_default_sortby']) {
+                               $this->sortList($conf['foreign_default_sortby']);
+                       }
                }
        }
 
                }
        }
 
@@ -176,6 +231,38 @@ class t3lib_loadDBGroup    {
        }
 
        /**
        }
 
        /**
+        * Does a sorting on $this->itemArray depending on a default sortby field.
+        * This is only used for automatic sorting of comma separated lists.
+        * This function is only relevant for data that is stored in comma separated lists!
+        * 
+        * @param       string          $sortby: The default_sortby field/command (e.g. 'price DESC')
+        * @return      void
+        */
+       function sortList($sortby) {
+                       // sort directly without fetching addional data
+               if ($sortby == 'uid') {
+                       usort($this->itemArray, create_function('$a,$b', 'return $a["id"] < $b["id"] ? -1 : 1;'));
+                       // only useful if working on the same table
+               } elseif (count($this->tableArray) == 1) {
+                       reset($this->tableArray);
+                       $table = key($this->tableArray);
+                       $uidList = implode(',', current($this->tableArray));
+
+                       if ($uidList) {
+                               $this->itemArray = array();
+                               $this->tableArray = array();
+                               
+                               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'uid IN ('.$uidList.')', '', $sortby);
+                               while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                                       $this->itemArray[] = array('id' => $row['uid'], 'table' => $table);
+                                       $this->tableArray[$table][] = $row['uid'];
+                               }
+                               $GLOBALS['TYPO3_DB']->sql_free_result($res);
+                       }
+               }
+       }
+
+       /**
         * Reads the record tablename/id into the internal arrays itemArray and tableArray from MM records.
         * You can call this function after start if you supply no list to start()
         *
         * Reads the record tablename/id into the internal arrays itemArray and tableArray from MM records.
         * You can call this function after start if you supply no list to start()
         *
@@ -185,19 +272,50 @@ class t3lib_loadDBGroup   {
         */
        function readMM($tableName,$uid)        {
                $key=0;
         */
        function readMM($tableName,$uid)        {
                $key=0;
+               $additionalWhere = '';
+               
+               if ($this->MM_is_foreign)       {       // in case of a reverse relation
+                       $uidLocal_field = 'uid_foreign';
+                       $uidForeign_field = 'uid_local';
+                       $sorting_field = 'sorting_foreign';
+
+                       if ($this->MM_isMultiTableRelationship) {
+                               $additionalWhere .= ' AND ( tablenames="'.$this->currentTable.'"';
+                               if ($this->currentTable == $this->MM_isMultiTableRelationship)  {       // be backwards compatible! When allowing more than one table after having previously allowed only one table, this case applies.
+                                       $additionalWhere .= ' OR tablenames=""';
+                               }
+                               $additionalWhere .= ' ) ';
+                       }
+                       $theTable = $this->MM_oppositeTable;
+               } else {        // default
+                       $uidLocal_field = 'uid_local';
+                       $uidForeign_field = 'uid_foreign';
+                       $sorting_field = 'sorting';
+               }
 
 
+
+               if ($this->MM_table_where) {
+                       $additionalWhere.= "\n".str_replace('###THIS_UID###', intval($uid), $this->MM_table_where);
+               }
+               foreach ($this->MM_match_fields as $field => $value) {
+                       $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $tableName);
+               }
+               
                        // Select all MM relations:
                        // Select all MM relations:
-               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $tableName, 'uid_local='.intval($uid), '', 'sorting');
+               $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $tableName, $uidLocal_field.'='.intval($uid).$additionalWhere, '', $sorting_field);
                while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))       {
                while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))       {
-                       $theTable = $row['tablenames'] ? $row['tablenames'] : $this->firstTable;                // If tablesnames columns exists and contain a name, then this value is the table, else it's the firstTable...
-                       if (($row['uid_foreign'] || $theTable=='pages') && $theTable && isset($this->tableArray[$theTable]))    {
-                               $this->itemArray[$key]['id'] = $row['uid_foreign'];
+                       if (!$this->MM_is_foreign) {    // default
+                               $theTable = $row['tablenames'] ? $row['tablenames'] : $this->firstTable;                // If tablesnames columns exists and contain a name, then this value is the table, else it's the firstTable...
+                       }
+                       if (($row[$uidForeign_field] || $theTable=='pages') && $theTable && isset($this->tableArray[$theTable]))        {
+
+                               $this->itemArray[$key]['id'] = $row[$uidForeign_field];
                                $this->itemArray[$key]['table'] = $theTable;
                                $this->itemArray[$key]['table'] = $theTable;
-                               $this->tableArray[$theTable][]= $row['uid_foreign'];
+                               $this->tableArray[$theTable][]= $row[$uidForeign_field];
                        } elseif ($this->registerNonTableValues)        {
                        } elseif ($this->registerNonTableValues)        {
-                               $this->itemArray[$key]['id'] = $row['uid_foreign'];
+                               $this->itemArray[$key]['id'] = $row[$uidForeign_field];
                                $this->itemArray[$key]['table'] = '_NO_TABLE';
                                $this->itemArray[$key]['table'] = '_NO_TABLE';
-                               $this->nonTableArray[] = $row['uid_foreign'];
+                               $this->nonTableArray[] = $row[$uidForeign_field];
                        }
                        $key++;
                }
                        }
                        $key++;
                }
@@ -212,32 +330,343 @@ class t3lib_loadDBGroup  {
         * @param       boolean         If set, then table names will always be written.
         * @return      void
         */
         * @param       boolean         If set, then table names will always be written.
         * @return      void
         */
-       function writeMM($tableName,$uid,$prependTableName=0)   {
-
-                       // Delete all relations:
-               $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName, 'uid_local='.intval($uid));
+       function writeMM($MM_tableName,$uid,$prependTableName=0)        {
+
+               if ($this->MM_is_foreign)       {       // in case of a reverse relation
+                       $uidLocal_field = 'uid_foreign';
+                       $uidForeign_field = 'uid_local';
+                       $sorting_field = 'sorting_foreign';
+               } else {        // default
+                       $uidLocal_field = 'uid_local';
+                       $uidForeign_field = 'uid_foreign';
+                       $sorting_field = 'sorting';
+               }
 
                        // If there are tables...
                $tableC = count($this->tableArray);
                if ($tableC)    {
 
                        // If there are tables...
                $tableC = count($this->tableArray);
                if ($tableC)    {
-                       $prep = ($tableC>1||$prependTableName) ? 1 : 0;
+                       $prep = ($tableC>1||$prependTableName||$this->MM_isMultiTableRelationship) ? 1 : 0;     // boolean: does the field "tablename" need to be filled?
                        $c=0;
                        $c=0;
-                       $tName=array();
+
+                       $additionalWhere_tablenames = '';
+                       if ($this->MM_is_foreign && $prep)      {
+                               $additionalWhere_tablenames = ' AND tablenames="'.$this->currentTable.'"';
+                       }
+
+                       $additionalWhere = '';
+                               // add WHERE clause if configured
+                       if ($this->MM_table_where) {
+                               $additionalWhere.= "\n".str_replace('###THIS_UID###', intval($uid), $this->MM_table_where);
+                       }
+                               // Select, update or delete only those relations that match the configured fields
+                       foreach ($this->MM_match_fields as $field => $value) {
+                               $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $MM_tableName);
+                       }
+
+                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+                               $uidForeign_field.($prep?', tablenames':'').($this->MM_hasUidField?', uid':''), 
+                               $MM_tableName, 
+                               $uidLocal_field.'='.$uid.$additionalWhere_tablenames.$additionalWhere,
+                               '',
+                               $sorting_field
+                       );
+
+                       $oldMMs = array();
+                       $oldMMs_inclUid = array();      // This array is similar to $oldMMs but also holds the uid of the MM-records, if any (configured by MM_hasUidField). If the UID is present it will be used to update sorting and delete MM-records. This is necessary if the "multiple" feature is used for the MM relations. $oldMMs is still needed for the in_array() search used to look if an item from $this->itemArray is in $oldMMs
+                       while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
+                               if (!$this->MM_is_foreign && $prep)     {
+                                       $oldMMs[] = array($row['tablenames'], $row[$uidForeign_field]);
+                               } else {
+                                       $oldMMs[] = $row[$uidForeign_field];
+                               }
+                               $oldMMs_inclUid[] = array($row['tablenames'], $row[$uidForeign_field], $row['uid']);
+                       }
 
                                // For each item, insert it:
                        foreach($this->itemArray as $val)       {
                                $c++;
 
 
                                // For each item, insert it:
                        foreach($this->itemArray as $val)       {
                                $c++;
 
-                               $insertFields = array(
-                                       'uid_local' => $uid,
-                                       'uid_foreign' => $val['id'],
-                                       'sorting' => $c
-                               );
                                if ($prep || $val['table']=='_NO_TABLE')        {
                                if ($prep || $val['table']=='_NO_TABLE')        {
-                                       $insertFields['tablenames'] = $val['table'];
+                                       if ($this->MM_is_foreign)       {       // insert current table if needed
+                                               $tablename = $this->currentTable;
+                                       } else {
+                                               $tablename = $val['table'];
+                                       }
+                               } else {
+                                       $tablename = '';
+                               }
+
+                               if(!$this->MM_is_foreign && $prep) {
+                                       $item = array($val['table'], $val['id']);
+                               } else {
+                                       $item = $val['id'];
+                               }
+
+                               if (in_array($item, $oldMMs))   {
+                                       $oldMMs_index = array_search($item, $oldMMs);
+                                       
+                                       $whereClause = $uidLocal_field.'='.$uid.' AND '.$uidForeign_field.'='.$val['id'].
+                                                                       ($this->MM_hasUidField ? ' AND uid='.intval($oldMMs_inclUid[$oldMMs_index][2]) : '');   // In principle, selecting on the UID is all we need to do if a uid field is available since that is unique! But as long as it "doesn't hurt" we just add it to the where clause. It should all match up.
+                                       if ($tablename) {
+                                               $whereClause .= ' AND tablenames="'.$tablename.'"';
+                                       }
+                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($MM_tableName, $whereClause.$additionalWhere, array($sorting_field => $c));
+
+                                       unset($oldMMs[$oldMMs_index]);  // remove the item from the $oldMMs array so after this foreach loop only the ones that need to be deleted are in there.
+                                       unset($oldMMs_inclUid[$oldMMs_index]);  // remove the item from the $oldMMs array so after this foreach loop only the ones that need to be deleted are in there.
+                               } else {
+
+                                       $insertFields = $this->MM_insert_fields;
+                                       $insertFields[$uidLocal_field] = $uid;
+                                       $insertFields[$uidForeign_field] = $val['id'];
+                                       $insertFields[$sorting_field] = $c;
+                                       if($tablename)  {
+                                               $insertFields['tablenames'] = $tablename;
+                                       }
+
+                                       $GLOBALS['TYPO3_DB']->exec_INSERTquery($MM_tableName, $insertFields);
+                                       
+                                       if ($this->MM_is_foreign)       {
+                                               $this->updateRefIndex($val['table'], $val['id']);
+                                       }
+                               }
+                       }
+
+                               // Delete all not-used relations:
+                       if(is_array($oldMMs) && count($oldMMs) > 0) {
+                               $removeClauses = array();
+                               $updateRefIndex_records = array();
+                               foreach($oldMMs as $oldMM_key => $mmItem) {
+                                       if ($this->MM_hasUidField)      {       // If UID field is present, of course we need only use that for deleting...:
+                                               $removeClauses[] = 'uid='.intval($oldMMs_inclUid[$oldMM_key][2]);
+                                               $elDelete = $oldMMs_inclUid[$oldMM_key];
+                                       } else {
+                                               if(is_array($mmItem)) {
+                                                       $removeClauses[] = 'tablenames="'.$mmItem[0].'" AND '.$uidForeign_field.'='.$mmItem[1];
+                                               } else {
+                                                       $removeClauses[] = $uidForeign_field.'='.$mmItem;
+                                               }
+                                       }
+                                       if ($this->MM_is_foreign)       {
+                                               if(is_array($mmItem)) {
+                                                       $updateRefIndex_records[] = array($mmItem[0],$mmItem[1]);
+                                               } else {
+                                                       $updateRefIndex_records[] = array($this->firstTable,$mmItem);
+                                               }
+                                       }
+                               }
+                               $deleteAddWhere = ' AND ('.implode(' OR ', $removeClauses).')';
+                               $GLOBALS['TYPO3_DB']->exec_DELETEquery($MM_tableName, $uidLocal_field.'='.intval($uid).$deleteAddWhere.$additionalWhere_tablenames.$additionalWhere);
+                               
+                                       // Update ref index:
+                               foreach($updateRefIndex_records as $pair)       {
+                                       $this->updateRefIndex($pair[0],$pair[1]);
+                               }
+                       }
+                       
+                               // Update ref index; In tcemain it is not certain that this will happen because if only the MM field is changed the record itself is not updated and so the ref-index is not either. This could also have been fixed in updateDB in tcemain, however I decided to do it here ...
+                       $this->updateRefIndex($this->currentTable,$uid);
+               }
+       }       
+
+       /**
+        * Remaps MM table elements from one local uid to another
+        * Does NOT update the reference index for you, must be called subsequently to do that!
+        *
+        * @param       string          MM table name
+        * @param       integer         Local, current UID
+        * @param       integer         Local, new UID
+        * @param       boolean         If set, then table names will always be written.
+        * @return      void
+        */
+       function remapMM($MM_tableName,$uid,$newUid,$prependTableName=0)        {
+
+               if ($this->MM_is_foreign)       {       // in case of a reverse relation
+                       $uidLocal_field = 'uid_foreign';
+               } else {        // default
+                       $uidLocal_field = 'uid_local';
+               }
+
+                       // If there are tables...
+               $tableC = count($this->tableArray);
+               if ($tableC)    {
+                       $prep = ($tableC>1||$prependTableName||$this->MM_isMultiTableRelationship) ? 1 : 0;     // boolean: does the field "tablename" need to be filled?
+                       $c=0;
+
+                       $additionalWhere_tablenames = '';
+                       if ($this->MM_is_foreign && $prep)      {
+                               $additionalWhere_tablenames = ' AND tablenames="'.$this->currentTable.'"';
+                       }
+
+                       $additionalWhere = '';
+                               // add WHERE clause if configured
+                       if ($this->MM_table_where) {
+                               $additionalWhere.= "\n".str_replace('###THIS_UID###', intval($uid), $this->MM_table_where);
+                       }
+                               // Select, update or delete only those relations that match the configured fields
+                       foreach ($this->MM_match_fields as $field => $value) {
+                               $additionalWhere.= ' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $MM_tableName);
+                       }
+
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($MM_tableName, $uidLocal_field.'='.intval($uid).$additionalWhere_tablenames.$additionalWhere, array($uidLocal_field => $newUid));
+               }
+       }
+
+       /**
+        * Reads items from a foreign_table, that has a foreign_field (uid of the parent record) and
+        * stores the parts in the internal array itemArray and tableArray.
+        *
+        * @param       integer         $uid: The uid of the parent record (this value is also on the foreign_table in the foreign_field)
+        * @param       array           $conf: TCA configuration for current field
+        * @return      void
+        */
+       function readForeignField($uid, $conf) {
+               $key = 0;
+               $uid = intval($uid);
+               $whereClause = '';
+               $foreign_table = $conf['foreign_table'];
+               $foreign_table_field = $conf['foreign_table_field'];
+               $useDeleteClause = $this->undeleteRecord ? false : true;
+
+                       // search for $uid in foreign_field, and if we have symmetric relations, do this also on symmetric_field
+               if ($conf['symmetric_field']) {
+                       $whereClause = '('.$conf['foreign_field'].'='.$uid.' OR '.$conf['symmetric_field'].'='.$uid.')';
+               } else {
+                       $whereClause = $conf['foreign_field'].'='.$uid;
+               }
+                       // use the deleteClause (e.g. "deleted=0") on this table
+               if ($useDeleteClause) {
+                       $whereClause .= t3lib_BEfunc::deleteClause($foreign_table);
+               }
+                       // if it's requested to look for the parent uid AND the parent table,
+                       // add an additional SQL-WHERE clause
+               if ($foreign_table_field && $this->currentTable) {
+                       $whereClause .= ' AND '.$foreign_table_field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->currentTable, $foreign_table);
+               }
+               
+                       // get the correct sorting field
+               if ($conf['foreign_sortby']) {                                                                                  // specific manual sortby for data handled by this field
+                       if ($conf['symmetric_sortby'] && $conf['symmetric_field']) {
+                                       // sorting depends on, from which side of the relation we're looking at it
+                               $sortby = '
+                                       CASE
+                                               WHEN '.$conf['foreign_field'].'='.$uid.'
+                                               THEN '.$conf['foreign_sortby'].'
+                                               ELSE '.$conf['symmetric_sortby'].'
+                                       END';
+                       } else {
+                                       // regular single-side behaviour
+                               $sortby = $conf['foreign_sortby'];
+                       }
+               } elseif ($conf['foreign_default_sortby']) {                                                    // specific default sortby for data handled by this field
+                       $sortby = $conf['foreign_default_sortby'];
+               } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['sortby']) {                  // manual sortby for all table records
+                       $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['sortby'];
+               } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['default_sortby']) {  // default sortby for all table records
+                       $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['default_sortby'];
+               }
+
+                       // strip a possible "ORDER BY" in front of the $sortby value
+               $sortby = $GLOBALS['TYPO3_DB']->stripOrderBy($sortby);
+                       // get the rows from storage
+               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', $foreign_table, $whereClause, '', $sortby);
+
+               if (count($rows)) {
+                       foreach ($rows as $row) {
+                               $this->itemArray[$key]['id'] = $row['uid'];
+                               $this->itemArray[$key]['table'] = $foreign_table;
+                               $this->tableArray[$foreign_table][]= $row['uid'];
+                               $key++;
+                       }
+               }
+       }
+
+       /**
+        * Write the sorting values to a foreign_table, that has a foreign_field (uid of the parent record)
+        *
+        * @param       array           $conf: TCA configuration for current field
+        * @param       integer         $parentUid: The uid of the parent record
+        * @param       boolean         $updateToUid: Whether to update the foreign field with the $parentUid (on Copy)
+        * @param       boolean         $skipSorting: Do not update the sorting columns, this could happen for imported values
+        * @return      void
+        */
+       function writeForeignField($conf, $parentUid, $updateToUid=0, $skipSorting=false) {
+               $c = 0;
+               $foreign_table = $conf['foreign_table'];
+               $foreign_field = $conf['foreign_field'];
+               $symmetric_field = $conf['symmetric_field'];
+               $foreign_table_field = $conf['foreign_table_field'];
+
+                       // if there are table items and we have a proper $parentUid
+               if (t3lib_div::testInt($parentUid) && count($this->tableArray)) {
+                               // if updateToUid is not a positive integer, set it to '0', so it will be ignored
+                       if (!(t3lib_div::testInt($updateToUid) && $updateToUid > 0)) {
+                               $updateToUid = 0;
+                       }
+                       $fields = 'uid,'.$foreign_field.($symmetric_field ? ','.$symmetric_field : '');
+
+                               // update all items
+                       foreach ($this->itemArray as $val) {
+                               $uid = $val['id'];
+                               $table = $val['table'];
+
+                                       // fetch the current (not overwritten) relation record if we should handle symmetric relations
+                               if ($conf['symmetric_field']) {
+                                       $row = t3lib_BEfunc::getRecord($table,$uid,$fields,'',false);
+                                       $isOnSymmetricSide = t3lib_loadDBGroup::isOnSymmetricSide($parentUid, $conf, $row);
+                               }
+
+                               $updateValues = array();
+
+                                       // no update to the uid is requested, so this is the normal behaviour
+                                       // just update the fields and care about sorting
+                               if (!$updateToUid) {
+                                               // Always add the pointer to the parent uid
+                                       if ($isOnSymmetricSide) {
+                                               $updateValues[$symmetric_field] = $parentUid;
+                                       } else {
+                                               $updateValues[$foreign_field] = $parentUid;
+                                       }
+                                       
+                                               // if it is configured in TCA also to store the parent table in the child record, just do it
+                                       if ($foreign_table_field && $this->currentTable) {
+                                               $updateValues[$foreign_table_field] = $this->currentTable;
+                                       }
+
+                                               // update sorting columns if not to be skipped
+                                       if (!$skipSorting) {
+                                                       // get the correct sorting field
+                                               if ($conf['foreign_sortby']) {                                                                  // specific manual sortby for data handled by this field 
+                                                       $sortby = $conf['foreign_sortby'];
+                                               } elseif ($GLOBALS['TCA'][$foreign_table]['ctrl']['sortby']) {  // manual sortby for all table records
+                                                       $sortby = $GLOBALS['TCA'][$foreign_table]['ctrl']['sortby'];
+                                               }
+                                                       // strip a possible "ORDER BY" in front of the $sortby value
+                                               $sortby = $GLOBALS['TYPO3_DB']->stripOrderBy($sortby);
+                                               $symSortby = $conf['symmetric_sortby'];
+       
+                                                       // set the sorting on the right side, it depends on who created the relation, so what uid is in the symmetric_field
+                                               if ($isOnSymmetricSide && $symSortby) {
+                                                       $updateValues[$symSortby] = ++$c;
+                                               } elseif ($sortby) {
+                                                       $updateValues[$sortby] = ++$c;
+                                               }
+                                       }
+
+                                       // update to a foreign_field/symmetric_field pointer is requested, normally used on record copies
+                                       // only update the fields, if the old uid is found somewhere - for select fields, TCEmain is doing this already!
+                               } else {
+                                       if ($isOnSymmetricSide) {
+                                               $updateValues[$symmetric_field] = $updateToUid;
+                                       } else {
+                                               $updateValues[$foreign_field] = $updateToUid;
+                                       }
                                }
 
                                }
 
-                               $GLOBALS['TYPO3_DB']->exec_INSERTquery($tableName, $insertFields);
+                               if (count($updateValues)) {
+                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateValues);
+                                       $this->updateRefIndex($table, $uid);
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -310,6 +739,9 @@ class t3lib_loadDBGroup     {
                                                if ($GLOBALS['TCA'][$key]['ctrl']['label'])     {
                                                        $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['label'];     // Titel
                                                }
                                                if ($GLOBALS['TCA'][$key]['ctrl']['label'])     {
                                                        $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['label'];     // Titel
                                                }
+                                               if ($GLOBALS['TCA'][$key]['ctrl']['label_alt']) {
+                                                       $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['label_alt']; // Alternative Title-Fields
+                                               }
                                                if ($GLOBALS['TCA'][$key]['ctrl']['thumbnail']) {
                                                        $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['thumbnail']; // Thumbnail
                                                }
                                                if ($GLOBALS['TCA'][$key]['ctrl']['thumbnail']) {
                                                        $from.= ','.$GLOBALS['TCA'][$key]['ctrl']['thumbnail']; // Thumbnail
                                                }
@@ -342,13 +774,53 @@ class t3lib_loadDBGroup   {
                foreach($this->itemArray as $key => $val)       {
                        $theRow = $this->results[$val['table']][$val['id']];
                        if ($theRow && is_array($TCA[$val['table']]))   {
                foreach($this->itemArray as $key => $val)       {
                        $theRow = $this->results[$val['table']][$val['id']];
                        if ($theRow && is_array($TCA[$val['table']]))   {
-                               $label = t3lib_div::fixed_lgd_cs(strip_tags($theRow[$TCA[$val['table']]['ctrl']['label']]),$titleLen);
+                               $label = t3lib_div::fixed_lgd_cs(strip_tags(t3lib_BEfunc::getRecordTitle($val['table'], $theRow)),$titleLen);
                                $label = ($label)?$label:'[...]';
                                $output[]=str_replace(',','',$val['table'].'_'.$val['id'].'|'.rawurlencode($label));
                        }
                }
                return implode(',',$output);
        }
                                $label = ($label)?$label:'[...]';
                                $output[]=str_replace(',','',$val['table'].'_'.$val['id'].'|'.rawurlencode($label));
                        }
                }
                return implode(',',$output);
        }
+
+       /**
+        * Counts the items in $this->itemArray and puts this value in an array by default.
+        *
+        * @param       boolean         Whether to put the count value in an array
+        * @return      mixed           The plain count as integer or the same inside an array
+        */
+       function countItems($returnAsArray = true) {
+               $count = count($this->itemArray);
+               if ($returnAsArray) $count = array($count);
+               return $count;
+       }
+
+       /**
+        * Update Reference Index (sys_refindex) for a record
+        * Should be called any almost any update to a record which could affect references inside the record.
+        * (copied from TCEmain)
+        *
+        * @param       string          Table name
+        * @param       integer         Record UID
+        * @return      void
+        */
+       function updateRefIndex($table,$id)     {
+               $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
+               $result = $refIndexObj->updateRefIndexTable($table,$id);
+       }
+
+       /**
+        * Checks, if we're looking from the "other" side, the symmetric side, to a symmetric relation.
+        *
+        * @param       string          $parentUid: The uid of the parent record
+        * @param       array           $parentConf: The TCA configuration of the parent field embedding the child records
+        * @param       array           $childRec: The record row of the child record
+        * @return      boolean         Returns true if looking from the symmetric ("other") side to the relation.
+        */
+       function isOnSymmetricSide($parentUid, $parentConf, $childRec) {
+               return t3lib_div::testInt($childRec['uid']) && $parentConf['symmetric_field'] && $parentUid == $childRec[$parentConf['symmetric_field']]
+                       ? true
+                       : false;
+       }
 }
 
 
 }