Index: t3lib/class.t3lib_tceforms.php
===================================================================
--- t3lib/class.t3lib_tceforms.php	(Revision 1955)
+++ t3lib/class.t3lib_tceforms.php	(Arbeitskopie)
@@ -4627,6 +4627,7 @@
 
 			// add JS required for inline fields
 		if (count($this->inline->inlineData)) {
+			// print_r($this->inline->inlineData);
 			$out .=	'
 			inline.addToDataArray('.$this->inline->getJSON($this->inline->inlineData).');
 			';
Index: t3lib/jsfunc.inline.js
===================================================================
--- t3lib/jsfunc.inline.js	(Revision 1955)
+++ t3lib/jsfunc.inline.js	(Arbeitskopie)
@@ -33,7 +33,7 @@
 	prependFormFieldNames: 'data',
 	noTitleString: '[No title]',
 	data: {},
-	
+
 	addToDataArray: function(object) {
 		for (var i in object) {
 			this.data[i] = $H(this.data[i]).merge(object[i]);
@@ -43,11 +43,11 @@
 	setPrependFormFieldNames: function(value) {
 		this.prependFormFieldNames = value;
 	},
-	
+
 	setNoTitleString: function(value) {
 		this.noTitleString = value;
 	},
-	
+
 	expandCollapseRecord: function(objectId, expandSingle) {
 		var currentUid = this.parseFormElementName('none', objectId, 1);
 		var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
@@ -63,7 +63,7 @@
 
 		Element.toggle(objectId+'_fields');
 		currentState = Element.visible(objectId+'_fields') ? 1 : 0
-		
+
 		if (this.isNewRecord(objectId))
 			this.updateExpandedCollapsedStateLocally(objectId, currentState);
 		else if (currentState)
@@ -72,7 +72,7 @@
 			collapse.push(currentUid);
 
 		this.setExpandedCollapsedState(objectId, expand.join(','), collapse.join(','));
-		
+
 		return false;
 	},
 
@@ -96,16 +96,16 @@
 				}
 			}
 		}
-		
+
 		return collapse;
 	},
-	
+
 	updateExpandedCollapsedStateLocally: function(objectId, value) {
 		var ucName = 'uc'+this.parseFormElementName('parts', objectId, 3, 2);
 		var ucFormObj = document.getElementsByName(ucName);
 		if (ucFormObj.length) ucFormObj[0].value = value;
 	},
-	
+
 	createNewRecord: function(objectId,prevRecordUid) {
 		if (this.isBelowMax(objectId)) this.makeAjaxCall('createNewRecord', objectId+(prevRecordUid ? '['+prevRecordUid+']' : ''));
 		else alert('There are no more relations possible at this moment!');
@@ -113,10 +113,9 @@
 	},
 
 	setExpandedCollapsedState: function(objectId, expand, collapse) {
-		// alert(objectId+': '+expand+', '+collapse);
 		this.makeAjaxCall('setExpandedCollapsedState', objectId, expand, collapse);
 	},
-	
+
 	makeAjaxCall: function() {
 		if (arguments.length > 1) {
 			var params = '';
@@ -133,7 +132,7 @@
 			new Ajax.Request(url, options);
 		}
 	},
-	
+
 	processAjaxResponse: function(xhr) {
 		var json = eval('('+xhr.responseText+')');
 		for (var i in json.scriptCall) eval(json.scriptCall[i]);
@@ -142,7 +141,8 @@
 	showAjaxFailure: function(xhr) {
 		alert('Error: '+xhr.status+"\n"+xhr.statusText);
 	},
-		
+
+		// foreign_selector: used by selector box (type='select')
 	importNewRecord: function(objectId) {
 		var selector = $(objectId+'_selector');
 		if (selector.selectedIndex != -1) {
@@ -151,44 +151,85 @@
 		}
 		return false;
 	},
-	
+
+		// foreign_selector: used by element browser (type='group/db')
+	importElement: function(objectId, table, uid, type) {
+		if (this.checkUniqueUsed(objectId, uid, table)) {
+			return {
+				error: true,
+				close: false,
+				message: 'There is already a relation to the selected element!',
+			};
+		} else {
+			window.setTimeout(function() { inline.makeAjaxCall('createNewRecord', objectId, uid); }, 10);
+			return {error: false, close: true};
+		}
+	},
+
+		// Checks if a record was used and should be unique:
+	checkUniqueUsed: function(objectId, uid, table) {
+		if (this.data.unique && this.data.unique[objectId]) {
+			var unique = this.data.unique[objectId];
+			var values = $H(unique.used).values();
+			
+				// for select: only the uid is stored
+			if (unique['type'] == 'select') {
+				if (values.indexOf(uid) != -1) return true;
+				
+				// for group/db: table and uid is stored in a assoc array
+			} else if (unique.type == 'groupdb') {
+				for (var i=values.length-1; i>=0; i--) {
+						// if the pair table:uid is already used:
+					if (values[i].table==table && values[i].uid==uid) return true;
+				}
+			}
+		}
+		return false;
+	},
+
 		// this function is applied to a newly inserted record by AJAX
 		// it removes the used select items, that should be unique
 	setUnique: function(objectId, recordUid, selectedValue) {
 		if (this.data.unique && this.data.unique[objectId]) {
 			var unique = this.data.unique[objectId];
 
-				// remove used items from each select-field of the child records
-			if (!(unique.selector && unique.max == -1)) {
-				var elName = this.parseFormElementName('full', objectId, 1)+'['+recordUid+']['+unique.field+']';
-				var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 1);
-
-				var fieldObj = document.getElementsByName(elName);
-				var values = $H(unique.used).values();
-				
-				if (fieldObj.length) {
-						// remove all before used items from the new select-item
-					for (var i=0; i<values.length; i++) this.removeSelectOption(fieldObj[0], values[i]);
-						// set the selected item automatically to the first of the remaining items
-					selectedValue = fieldObj[0].options[0].value;
-					fieldObj[0].options[0].selected = true;
-					this.updateUnique(fieldObj[0], objectId, formName, recordUid);
-					this.handleChangedField(fieldObj[0], objectId+'['+recordUid+']');
-					if (typeof this.data.unique[objectId]['used'].length != 'undefined')
-						this.data.unique[objectId]['used'] = {};
-					this.data.unique[objectId]['used'][recordUid] = selectedValue;
+			if (unique.type == 'select') {
+					// remove used items from each select-field of the child records
+				if (!(unique.selector && unique.max == -1)) {
+					var elName = this.parseFormElementName('full', objectId, 1)+'['+recordUid+']['+unique.field+']';
+					var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 1);
+	
+					var fieldObj = document.getElementsByName(elName);
+					var values = $H(unique.used).values();
+					
+					if (fieldObj.length) {
+							// remove all before used items from the new select-item
+						for (var i=0; i<values.length; i++) this.removeSelectOption(fieldObj[0], values[i]);
+							// set the selected item automatically to the first of the remaining items
+						selectedValue = fieldObj[0].options[0].value;
+						fieldObj[0].options[0].selected = true;
+						this.updateUnique(fieldObj[0], objectId, formName, recordUid);
+						this.handleChangedField(fieldObj[0], objectId+'['+recordUid+']');
+						if (typeof this.data.unique[objectId].used.length != 'undefined') {
+							this.data.unique[objectId].used = {};
+						}
+						this.data.unique[objectId].used[recordUid] = selectedValue;
+					}
 				}
+			} else if (unique.type == 'groupdb') {
+					// add the new record to the used items:
+				this.data.unique[objectId].used[recordUid] = {'table':unique.elTable, 'uid':selectedValue};
 			}
-			
+
 				// remove used items from a selector-box
-			if (unique.selector && selectedValue) {
+			if (unique.selector == 'select' && selectedValue) {
 				var selector = $(objectId+'_selector');
 				this.removeSelectOption(selector, selectedValue);
 				this.data.unique[objectId]['used'][recordUid] = selectedValue;
 			}
 		}
 	},
-	
+
 	domAddNewRecord: function(method, insertObject, objectPrefix, htmlData) {
 		if (this.isBelowMax(objectPrefix)) {
 			if (method == 'bottom')
@@ -197,7 +238,7 @@
 				new Insertion.After(insertObject, htmlData);
 		}
 	},
-	
+
 	changeSorting: function(objectId, direction) {
 		var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectId, 3, 2);
 		var objectPrefix = this.parseFormElementName('full', objectId, 0, 1);
@@ -236,7 +277,7 @@
 		
 		return false;
 	},
-	
+
 	dragAndDropSorting: function(element) {
 		var objectId = element.getAttribute('id').replace(/_records$/, '');
 		var objectName = inline.prependFormFieldNames+inline.parseFormElementName('parts', objectId, 3);
@@ -263,7 +304,7 @@
 			}
 		}
 	},
-	
+
 	createDragAndDropSorting: function(objectId) {
 		Sortable.create(
 			objectId,
@@ -277,16 +318,16 @@
 			}
 		);
 	},
-	
+
 	destroyDragAndDropSorting: function(objectId) {
 		Sortable.destroy(objectId);
 	},
-	
+
 	redrawSortingButtons: function(objectPrefix, records) {
 		var i;
 		var headerObj;
 		var sortingObj = new Array();
-		
+
 			// if no records were passed, fetch them from form field
 		if (typeof records == 'undefined') {
 			records = new Array();
@@ -297,23 +338,23 @@
 		
 		for (i=0; i<records.length; i++) {
 			if (!records[i].length) continue;
-			
+
 			headerObj = $(objectPrefix+'['+records[i]+']_header');
 			sortingObj[0] = headerObj.getElementsByClassName('sortingUp');
 			sortingObj[1] = headerObj.getElementsByClassName('sortingDown');
-			
+
 			if (sortingObj[0].length)
 				sortingObj[0][0].style.visibility = i == 0 ? 'hidden' : 'visible';
 			if (sortingObj[1].length)
 				sortingObj[1][0].style.visibility = i == records.length-1 ? 'hidden' : 'visible';
 		}
 	},
-	
+
 	memorizeAddRecord: function(objectPrefix, newUid, afterUid, selectedValue) {
 		if (this.isBelowMax(objectPrefix)) {
 			var objectName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
 			var formObj = document.getElementsByName(objectName);
-	
+
 			if (formObj.length) {
 				var records = new Array();
 				if (formObj[0].value.length) records = formObj[0].value.split(',');
@@ -330,22 +371,23 @@
 				}
 				formObj[0].value = records.join(',');
 			}
-	
+
 			this.redrawSortingButtons(objectPrefix, records);
-			
+
 			if (this.data.unique && this.data.unique[objectPrefix]) {
 				var unique = this.data.unique[objectPrefix];
 				this.setUnique(objectPrefix, newUid, selectedValue);
 			}
 		}
-		
+
 			// if we reached the maximum off possible records after this action, hide the new buttons
-		if (!this.isBelowMax(objectPrefix))
+		if (!this.isBelowMax(objectPrefix)) {
 			this.hideElementsWithClassName('inlineNewButton',  this.parseFormElementName('full', objectPrefix, 0 , 1));
+		}
 
 		if (TBE_EDITOR) TBE_EDITOR.fieldChanged_fName(objectName, formObj);
 	},
-	
+
 	memorizeRemoveRecord: function(objectName, removeUid) {
 		var formObj = document.getElementsByName(objectName);
 		if (formObj.length) {
@@ -360,18 +402,18 @@
 		}
 		return false;
 	},
-	
+
 	updateUnique: function(srcElement, objectPrefix, formName, recordUid) {
 		if (this.data.unique && this.data.unique[objectPrefix]) {
 			var unique = this.data.unique[objectPrefix];
 			var oldValue = unique.used[recordUid];
 
-			if (unique.selector) {
+			if (unique.selector == 'select') {
 				var selector = $(objectPrefix+'_selector');
 				this.removeSelectOption(selector, srcElement.value);
 				if (typeof oldValue != 'undefined') this.readdSelectOption(selector, oldValue, unique);
 			}
-			
+
 			if (!(unique.selector && unique.max == -1)) {
 				var formObj = document.getElementsByName(formName);
 				if (unique && formObj.length) {
@@ -389,37 +431,41 @@
 			}
 		}
 	},
-	
+
 	revertUnique: function(objectPrefix, elName, recordUid) {
 		var unique = this.data.unique[objectPrefix];
 		var fieldObj = document.getElementsByName(elName+'['+unique.field+']');
 
-		if (fieldObj.length) {
-			delete(this.data.unique[objectPrefix].used[recordUid])
-			
-			if (unique.selector) {
-				if (!isNaN(fieldObj[0].value)) {
-					var selector = $(objectPrefix+'_selector');
-					this.readdSelectOption(selector, fieldObj[0].value, unique);
+		if (unique.type == 'select') {
+			if (fieldObj.length) {
+				delete(this.data.unique[objectPrefix].used[recordUid])
+				
+				if (unique.selector == 'select') {
+					if (!isNaN(fieldObj[0].value)) {
+						var selector = $(objectPrefix+'_selector');
+						this.readdSelectOption(selector, fieldObj[0].value, unique);
+					}
 				}
-			}
-			
-			if (!(unique.selector && unique.max == -1)) {
-				var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
-				var formObj = document.getElementsByName(formName);
-				if (formObj.length) {
-					var records = formObj[0].value.split(',');
-					var recordObj;
-						// walk through all inline records on that level and get the select field
-					for (var i=0; i<records.length; i++) {
-						recordObj = document.getElementsByName(this.prependFormFieldNames+'['+unique.table+']['+records[i]+']['+unique.field+']');
-						if (recordObj.length) this.readdSelectOption(recordObj[0], fieldObj[0].value, unique);
+				
+				if (!(unique.selector && unique.max == -1)) {
+					var formName = this.prependFormFieldNames+this.parseFormElementName('parts', objectPrefix, 3, 1);
+					var formObj = document.getElementsByName(formName);
+					if (formObj.length) {
+						var records = formObj[0].value.split(',');
+						var recordObj;
+							// walk through all inline records on that level and get the select field
+						for (var i=0; i<records.length; i++) {
+							recordObj = document.getElementsByName(this.prependFormFieldNames+'['+unique.table+']['+records[i]+']['+unique.field+']');
+							if (recordObj.length) this.readdSelectOption(recordObj[0], fieldObj[0].value, unique);
+						}
 					}
 				}
 			}
+		} else if (unique.type == 'groupdb') {
+			delete(this.data.unique[objectPrefix].used[recordUid])
 		}
 	},
-	
+
 	enableDisableRecord: function(objectId) {
 		var elName = this.parseFormElementName('full', objectId, 2);
 		var imageObj = $(objectId+'_disabled');
@@ -435,8 +481,9 @@
 		
 		return false;
 	},
-	
+
 	deleteRecord: function(objectId) {
+		var i, j, inlineRecords, records, childObjectId, childTable;
 		var objectPrefix = this.parseFormElementName('full', objectId, 0 , 1);
 		var elName = this.parseFormElementName('full', objectId, 2);
 		var shortName = this.parseFormElementName('parts', objectId, 2);
@@ -455,8 +502,20 @@
 			new Effect.Fade(objectId+'_div');
 		}
 
-			// remove from TBE_EDITOR (required fields, required range, etc.):
+			// Remove from TBE_EDITOR (required fields, required range, etc.):
 		if (TBE_EDITOR && TBE_EDITOR.removeElement) {
+			inlineRecords = document.getElementsByClassName('inlineRecord', objectId+'_div');
+				// Remove nested child records from TBE_EDITOR required/range checks:
+			for (i=inlineRecords.length-1; i>=0; i--) {
+				if (inlineRecords[i].value.length) {
+					records = inlineRecords[i].value.split(',');
+					childObjectId = this.data.map[inlineRecords[i].name];
+					childTable = this.data.config[childObjectId].table;
+					for (j=records.length-1; j>=0; j--) {
+						TBE_EDITOR.removeElement(this.prependFormFieldNames+'['+childTable+']['+records[j]+']');
+					}
+				}
+			}
 			TBE_EDITOR.removeElement(this.prependFormFieldNames+shortName);
 		}
 
Index: t3lib/class.t3lib_tceforms_inline.php
===================================================================
--- t3lib/class.t3lib_tceforms_inline.php	(Revision 1955)
+++ t3lib/class.t3lib_tceforms_inline.php	(Arbeitskopie)
@@ -139,6 +139,7 @@
 			// Init:
 		$config = $PA['fieldConf']['config'];
 		$foreign_table = $config['foreign_table'];
+		t3lib_div::loadTCA($foreign_table);
 
 		$minitems = t3lib_div::intInRange($config['minitems'],0);
 		$maxitems = t3lib_div::intInRange($config['maxitems'],0);
@@ -192,15 +193,20 @@
 
 			// if relations are required to be unique, get the uids that have already been used on the foreign side of the relation
 		if ($config['foreign_unique']) {
-			$uniqueIds = $this->getUniqueIds($recordList, $config);
+				// If unique *and* selector, the should both be the same - get config:
+			$selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_unique']);
+				// Get the used unique ids:
+			$uniqueIds = $this->getUniqueIds($recordList, $config, $selConfig['type']=='groupdb');
 			$possibleRecords = $this->getPossibleRecords($table,$field,$row,$config,'foreign_unique');
-			$uniqueMax = $config['appearance']['useCombination'] ? -1 : count($possibleRecords);
+			$uniqueMax = $config['appearance']['useCombination'] || $possibleRecords === false ? -1	: count($possibleRecords);
 			$this->inlineData['unique'][$nameObject.'['.$foreign_table.']'] = array(
 				'max' => $uniqueMax,
 				'used' => $uniqueIds,
+				'type' => $selConfig['type'],
 				'table' => $config['foreign_table'],
+				'elTable' => $selConfig['table'], // element/record table (one step down in hierarchy)
 				'field' => $config['foreign_unique'],
-				'selector' => $config['foreign_selector'] ? true : false,
+				'selector' => $selConfig['PA'] && $selConfig['type'] ? $selConfig['type'] : false,
 				'possible' => $this->getPossibleRecordsFlat($possibleRecords),
 			);
 		}
@@ -224,7 +230,7 @@
 			$config['inline']['inlineNewButtonStyle'] = 'display: none;';
 		}
 			// add the "Create new record" link before all child records
-		if ($config['appearance']['newRecordLinkPosition'] != 'bottom') {
+		if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'top'))) {
 			$item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
 		}
 
@@ -239,7 +245,7 @@
 		$item .= '</div>';
 
 			// add the "Create new record" link after all child records
-		if ($config['appearance']['newRecordLinkPosition'] != 'top') {
+		if (in_array($config['appearance']['newRecordLinkPosition'], array('both', 'bottom'))) {
 			$item .= $this->getNewRecordLink($nameObject.'['.$foreign_table.']', $config);
 		}
 
@@ -282,10 +288,15 @@
 		$foreign_table = $config['foreign_table'];
 		$foreign_field = $config['foreign_field'];
 		$foreign_selector = $config['foreign_selector'];
+		$selConfig = $this->getPossibleRecordsSelectorConfig($config, $foreign_selector);
 
-			// record comes from storage (e.g. database)
+			// Send a mapping information to the browser via JSON:
+			// e.g. data[<curTable>][<curId>][<curField>] => data[<pid>][<parentTable>][<parentId>][<parentField>][<curTable>][<curId>][<curField>]
+		$this->inlineData['map'][$this->inlineNames['form']] = $this->inlineNames['object'];
+
+			// Set this variable if we handle a brand new unsaved record:
 		$isNewRecord = t3lib_div::testInt($rec['uid']) ? false : true;
-			// if there is a selector field, normalize it
+			// If there is a selector field, normalize it:
 		if ($foreign_selector) {
 			$rec[$foreign_selector] = $this->normalizeUid($rec[$foreign_selector]);
 		}
@@ -294,7 +305,7 @@
 
 		if(!$hasAccess) return false;
 
-			// get the current prependObjectId
+			// Get the current naming scheme for DOM name/id attributes:
 		$nameObject = $this->inlineNames['object'];
 		$appendFormFieldNames = '['.$foreign_table.']['.$rec['uid'].']';
 		$formFieldNames = $nameObject.$appendFormFieldNames;
@@ -320,6 +331,11 @@
 				// set additional field for processing for saving
 			$fields .= '<input type="hidden" name="'.$this->prependCmdFieldNames.$appendFormFieldNames.'[delete]" value="1" disabled="disabled" />';
 		}
+
+		if ($foreign_selector && $selConfig['type'] == 'groupdb') {
+			$fields .= '<input type="hidden" name="'.$this->prependFormFieldNames.$appendFormFieldNames.'['.$foreign_selector.']" value="'.$rec[$foreign_selector].'" />';
+		}
+		
 			// if this record should be shown collapsed
 		if (!$isExpanded) $appearanceStyleFields = ' style="display: none;"';
 
@@ -368,7 +384,17 @@
 			// render the special alternative title
 		} elseif ($hasForeignLabel || $hasSymmetricLabel) {
 			$titleCol = $hasForeignLabel ? $config['foreign_label'] : $config['symmetric_label'];
-			$recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
+			$foreignConfig = $this->getPossibleRecordsSelectorConfig($config, $titleCol);
+				// Render title for everything else than group/db:
+			if ($foreignConfig['type'] != 'groupdb') {
+				$recTitle = t3lib_BEfunc::getProcessedValueExtra($foreign_table, $titleCol, $rec[$titleCol], 0, 0, false);
+				// Render title for group/db:
+			} else {
+					// $recTitle would be something like: "tx_table_123",
+				$itemParts = $this->rightExplode('_', $rec[$titleCol], 2);
+				$recTemp = t3lib_befunc::getRecordWSOL($itemParts[0], $itemParts[1]);
+				$recTitle = t3lib_BEfunc::getRecordTitle($itemParts[0], $recTemp, true);
+			}
 			$recTitle = t3lib_BEfunc::getRecordTitlePrep($recTitle);
 			if (!strcmp(trim($recTitle),'')) {
 				$recTitle = t3lib_BEfunc::getNoRecordTitle(true);
@@ -510,7 +536,7 @@
 				($isPagesTable && ($localCalcPerms&4)) || (!$isPagesTable && ($calcPerms&16))
 				)	{
 				$onClick = "inline.deleteRecord('".$nameObjectFtId."');";
-				$cells[]='<a href="#" onclick="'.htmlspecialchars('if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning').t3lib_BEfunc::referenceCount($foreign_table,$rec['uid'],' (There are %s reference(s) to this record!)')).')) {	'.$onClick.' } return false;').'">'.
+				$cells[]='<a href="#" onclick="'.htmlspecialchars('if (confirm('.$GLOBALS['LANG']->JScharCode($GLOBALS['LANG']->getLL('deleteWarning')).')) {	'.$onClick.' } return false;').'">'.
 						'<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="" />'.
 						'</a>';
 			}
@@ -583,8 +609,60 @@
 
 		return $out;
 	}
+	
+	
+	/**
+	 * Determine the configuration and the type of a record selector.
+	 *
+	 * @param	array		$conf: TCA configuration of the parent(!) field
+	 * @return	array		Associative array with the keys 'PA' and 'type', both are false if the selector was not valid.
+	 */
+	function getPossibleRecordsSelectorConfig($conf, $field = '') {
+		$foreign_table = $conf['foreign_table'];
+		$foreign_selector = $conf['foreign_selector'];
 
+		$PA = false;
+		$type = false;
+		$table = false;
+		
+		if ($field) {
+			$PA = array();
+			$PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$field];
+			$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
+			$PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$field);
+			$config = $PA['fieldConf']['config'];
+				// Determine type of Selector:
+			$type = $this->getPossibleRecordsSelectorType($config);
+				// Return table on this level:
+			$table = $type == 'select' ? $config['foreign_table'] : $config['allowed'];
+		}
+		
+		return array(
+			'PA' => $PA,
+			'type' => $type,
+			'table' => $table
+		);
+	}
+	
+
 	/**
+	 * Determine the type of a record selector, e.g. select or group/db.
+	 *
+	 * @param	array		$config: TCE configuration of the selector
+	 * @return	mixed		The type of the selector, 'select' or 'groupdb' - false not valid
+	 */
+	function getPossibleRecordsSelectorType($config) {
+		$type = false;
+		if ($config['type'] == 'select') {
+			$type = 'select';
+		} elseif ($config['type'] == 'group' && $config['internal_type'] == 'db') {
+			$type = 'groupdb';
+		}
+		return $type;
+	}
+	
+	
+	/**
 	 * Get a selector as used for the select type, to select from all available
 	 * records and to create a relation to the embedding record (e.g. like MM).
 	 *
@@ -597,6 +675,33 @@
 		$foreign_table = $conf['foreign_table'];
 		$foreign_selector = $conf['foreign_selector'];
 
+		$selConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_selector);
+		$config = $selConfig['PA']['fieldConf']['config'];
+
+		if ($selConfig['type'] == 'select') {
+			$item = $this->renderPossibleRecordsSelectorTypeSelect($selItems, $conf, $selConfig['PA'], $uniqueIds);
+		} elseif ($selConfig['type'] == 'groupdb') {
+			$item = $this->renderPossibleRecordsSelectorTypeGroupDB($conf, $selConfig['PA']);
+		}
+
+		return $item;
+	}
+
+	
+	/**
+	 * Get a selector as used for the select type, to select from all available
+	 * records and to create a relation to the embedding record (e.g. like MM).
+	 *
+	 * @param	array		$selItems: Array of all possible records
+	 * @param	array		$conf: TCA configuration of the parent(!) field
+	 * @param	array		$PA: An array with additional configuration options
+	 * @param	array		$uniqueIds: The uids that have already been used and should be unique
+	 * @return	string		A HTML <select> box with all possible records
+	 */
+	function renderPossibleRecordsSelectorTypeSelect($selItems, $conf, &$PA, $uniqueIds=array()) {
+		$foreign_table = $conf['foreign_table'];
+		$foreign_selector = $conf['foreign_selector'];
+
 		$PA = array();
 		$PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_selector];
 		$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
@@ -621,14 +726,14 @@
 
 				// Put together the selector box:
 			$selector_itemListStyle = isset($config['itemListStyle']) ? ' style="'.htmlspecialchars($config['itemListStyle']).'"' : ' style="'.$this->fObj->defaultMultipleSelectorStyle.'"';
-			$size = intval($config['size']);
-			$size = $config['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$config['autoSizeMax']) : $size;
-			$sOnChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
-			$itemsToSelect = '
+			$size = intval($conf['size']);
+			$size = $conf['autoSizeMax'] ? t3lib_div::intInRange(count($itemArray)+1,t3lib_div::intInRange($size,1),$conf['autoSizeMax']) : $size;
+			$onChange = "return inline.importNewRecord('".$this->inlineNames['object']."[".$conf['foreign_table']."]')";
+			$item = '
 				<select id="'.$this->inlineNames['object'].'['.$conf['foreign_table'].']_selector"'.
 							$this->fObj->insertDefStyle('select').
 							($size ? ' size="'.$size.'"' : '').
-							' onchange="'.htmlspecialchars($sOnChange).'"'.
+							' onchange="'.htmlspecialchars($onChange).'"'.
 							$PA['onFocus'].
 							$selector_itemListStyle.
 							($conf['foreign_unique'] ? ' isunique="isunique"' : '').'>
@@ -641,18 +746,43 @@
 				// there is only one record item in the select-box, that is selected by default
 				// the selector-box creates a new relation on using a onChange event (see some line above)
 			$createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
-			$itemsToSelect .=
-				'<a href="#" onclick="'.htmlspecialchars($sOnChange).'" align="abstop">'.
-					'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/edit2.gif','width="11" height="12"').' title="'.$createNewRelationText.'" alt="" /> '.$createNewRelationText.
+			$item .=
+				'<a href="#" onclick="'.htmlspecialchars($onChange).'" align="abstop">'.
+					'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/edit2.gif','width="11" height="12"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
 				'</a>';
 				// wrap the selector and add a spacer to the bottom
-			$itemsToSelect = '<div style="margin-bottom: 20px;">'.$itemsToSelect.'</div>';
+			$item = '<div style="margin-bottom: 20px;">'.$item.'</div>';
 		}
 
-		return $itemsToSelect;
+		return $item;
 	}
 
+	
+	/**
+	 * Generate a link that ipens an element browser in a new window.
+	 *
+	 * @param	array		$conf: TCA configuration of the parent(!) field
+	 * @param	array		$PA: An array with additional configuration options
+	 * @return	string		A HTML link that opens an element browser in a new window
+	 */
+	function renderPossibleRecordsSelectorTypeGroupDB($conf, &$PA) {
+		$foreign_table = $conf['foreign_table'];
 
+		$config = $PA['fieldConf']['config'];
+		$allowed = $config['allowed'];
+		$objectPrefix = $this->inlineNames['object'].'['.$foreign_table.']';
+
+		$createNewRelationText = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:cm.createNewRelation',1);
+		$onClick = "setFormValueOpenBrowser('db','".($objectPrefix.'|||'.$allowed.'|inline.importElement')."'); return false;";
+		$item =
+			'<a href="#" onclick="'.htmlspecialchars($onClick).'">'.
+				'<img'.t3lib_iconWorks::skinImg($this->backPath,'gfx/insert3.gif','width="14" height="14"').' align="absmiddle" '.t3lib_BEfunc::titleAltAttrib($createNewRelationText).' /> '.$createNewRelationText.
+			'</a>';
+			
+		return $item;
+	}
+	
+	
 	/**
 	 * Creates a link/button to create new records
 	 *
@@ -717,7 +847,7 @@
 		$parent = $this->getStructureLevel(-1);
 			// get TCA 'config' of the parent table
 		$config = $parent['config'];
-
+		
 			// dynamically create a new record using t3lib_transferData
 		if (!$foreignUid || !t3lib_div::testInt($foreignUid) || $config['foreign_selector']) {
 			$record = $this->getNewRecord($this->inlineFirstPid, $current['table']);
@@ -731,7 +861,10 @@
 			// this intermediate table holds a field, which is responsible for the foreign_selector, so
 			// we have to set this field to the uid we get - or if none, to a new uid
 		if ($config['foreign_selector'] && $foreignUid) {
-			$record[$config['foreign_selector']] = $foreignUid;
+			$selConfig = $this->getPossibleRecordsSelectorConfig($config, $config['foreign_selector']);
+				// For a selector of type group/db, prepend the tablename (<tablename>_<uid>):
+			$record[$config['foreign_selector']] = $selConfig['type'] != 'groupdb' ? '' : $selConfig['table'].'_';
+			$record[$config['foreign_selector']] .= $foreignUid;
 		}
 
 			// the HTML-object-id's prefix of the dynamically created record
@@ -880,7 +1013,7 @@
 	 * @param	array		The record data array where the value(s) for the field can be found
 	 * @param	array		An array with additional configuration options.
 	 * @param	string		$checkForConfField: For which field in the foreign_table the possible records should be fetched
-	 * @return	array		Array of possible record items
+	 * @return	mixed		Array of possible record items; false if type is "group/db", then everything could be "possible"
 	 */
 	function getPossibleRecords($table,$field,$row,$conf,$checkForConfField='foreign_selector') {
 			// ctrl configuration from TCA:
@@ -889,35 +1022,37 @@
 		$foreign_table = $conf['foreign_table'];
 		$foreign_check = $conf[$checkForConfField];
 
-		$PA = array();
-		$PA['fieldConf'] = $GLOBALS['TCA'][$foreign_table]['columns'][$foreign_check];
-		$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
-		$PA['fieldTSConfig'] = $this->fObj->setTSconfig($foreign_table,array(),$foreign_check);
+		$foreignConfig = $this->getPossibleRecordsSelectorConfig($conf, $foreign_check);
+		$PA = $foreignConfig['PA'];
 		$config = $PA['fieldConf']['config'];
-
-			// Getting the selector box items from the system
-		$selItems = $this->fObj->addSelectOptionsToItemArray($this->fObj->initItemArray($PA['fieldConf']),$PA['fieldConf'],$this->fObj->setTSconfig($table,$row),$field);
-		if ($config['itemsProcFunc']) $selItems = $this->fObj->procItems($selItems,$PA['fieldTSConfig']['itemsProcFunc.'],$config,$table,$row,$field);
-
-			// Possibly remove some items:
-		$removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
-		foreach($selItems as $tk => $p)	{
-
-				// Checking languages and authMode:
-			$languageDeny = $tcaTableCtrl['languageField'] && !strcmp($tcaTableCtrl['languageField'], $field) && !$GLOBALS['BE_USER']->checkLanguageAccess($p[1]);
-			$authModeDeny = $config['form_type']=='select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table,$field,$p[1],$config['authMode']);
-			if (in_array($p[1],$removeItems) || $languageDeny || $authModeDeny)	{
-				unset($selItems[$tk]);
-			} elseif (isset($PA['fieldTSConfig']['altLabels.'][$p[1]])) {
-				$selItems[$tk][0]=$this->fObj->sL($PA['fieldTSConfig']['altLabels.'][$p[1]]);
-			}
-
-				// Removing doktypes with no access:
-			if ($table.'.'.$field == 'pages.doktype')	{
-				if (!($GLOBALS['BE_USER']->isAdmin() || t3lib_div::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'],$p[1])))	{
+		
+		if ($foreignConfig['type'] == 'select') {
+				// Getting the selector box items from the system
+			$selItems = $this->fObj->addSelectOptionsToItemArray($this->fObj->initItemArray($PA['fieldConf']),$PA['fieldConf'],$this->fObj->setTSconfig($table,$row),$field);
+			if ($config['itemsProcFunc']) $selItems = $this->fObj->procItems($selItems,$PA['fieldTSConfig']['itemsProcFunc.'],$config,$table,$row,$field);
+	
+				// Possibly remove some items:
+			$removeItems = t3lib_div::trimExplode(',',$PA['fieldTSConfig']['removeItems'],1);
+			foreach($selItems as $tk => $p)	{
+	
+					// Checking languages and authMode:
+				$languageDeny = $tcaTableCtrl['languageField'] && !strcmp($tcaTableCtrl['languageField'], $field) && !$GLOBALS['BE_USER']->checkLanguageAccess($p[1]);
+				$authModeDeny = $config['form_type']=='select' && $config['authMode'] && !$GLOBALS['BE_USER']->checkAuthMode($table,$field,$p[1],$config['authMode']);
+				if (in_array($p[1],$removeItems) || $languageDeny || $authModeDeny)	{
 					unset($selItems[$tk]);
+				} elseif (isset($PA['fieldTSConfig']['altLabels.'][$p[1]])) {
+					$selItems[$tk][0]=$this->fObj->sL($PA['fieldTSConfig']['altLabels.'][$p[1]]);
 				}
+	
+					// Removing doktypes with no access:
+				if ($table.'.'.$field == 'pages.doktype')	{
+					if (!($GLOBALS['BE_USER']->isAdmin() || t3lib_div::inList($GLOBALS['BE_USER']->groupData['pagetypes_select'],$p[1])))	{
+						unset($selItems[$tk]);
+					}
+				}
 			}
+		} else {
+			$selItems = false;
 		}
 
 		return $selItems;
@@ -928,13 +1063,27 @@
 	 *
 	 * @param	array		$records: All inline records on this level
 	 * @param	array		$conf: The TCA field configuration of the inline field to be rendered
+	 * @param	boolean		$splitValue: for usage with group/db, values come like "tx_table_123|Title%20abc", but we need "tx_table" and "123"
 	 * @return	array		The uids, that have been used already and should be used unique
 	 */
-	function getUniqueIds($records, $conf=array()) {
+	function getUniqueIds($records, $conf=array(), $splitValue=false) {
 		$uniqueIds = array();
 
-		if ($conf['foreign_unique'] && count($records))
-			foreach ($records as $rec) $uniqueIds[$rec['uid']] = $rec[$conf['foreign_unique']];
+		if ($conf['foreign_unique'] && count($records)) {
+			foreach ($records as $rec) {
+				$value = $rec[$conf['foreign_unique']];
+					// Split the value and extract the table and uid:
+				if ($splitValue) {
+					$valueParts = t3lib_div::trimExplode('|', $value);
+					$itemParts = explode('_', $valueParts[0]);
+					$value = array(
+						'uid' => array_pop($itemParts),
+						'table' => implode('_', $itemParts)
+					);
+				}
+				$uniqueIds[$rec['uid']] = $value;
+			}
+		}
 
 		return $uniqueIds;
 	}
@@ -1154,12 +1303,13 @@
 		$foreign_table = $config['foreign_table'];
 
 			// an inline field must have a foreign_table, if not, stop all further inline actions for this field
-		if (!$foreign_table || !is_array($GLOBALS['TCA'][$foreign_table]))
+		if (!$foreign_table || !is_array($GLOBALS['TCA'][$foreign_table])) {
 			return false;
+		}
 
 		if (!is_array($config['appearance']))
 			$config['appearance'] = array();
-		if (!in_array($config['appearance']['newRecordLinkPosition'], array('top', 'bottom', 'both')))
+		if (!in_array($config['appearance']['newRecordLinkPosition'], array('top', 'bottom', 'both', 'none')))
 			$config['appearance']['newRecordLinkPosition'] = 'top';
 
 		return true;
@@ -1427,12 +1577,14 @@
 	 * the value of the flat array is the label of the record.
 	 *
 	 * @param	array		$possibleRecords: The possibleRecords array (for select fields)
-	 * @return	array		A flat array with key=uid, value=label
+	 * @return	mixed		A flat array with key=uid, value=label; if $possibleRecords isn't an array, false is returned.
 	 */
 	function getPossibleRecordsFlat($possibleRecords) {
-		$flat = array();
-		if (is_array($possibleRecords))
+		$flat = false;
+		if (is_array($possibleRecords)) {
+			$flat = array();
 			foreach ($possibleRecords as $record) $flat[$record[1]] = $record[0];
+		}
 		return $flat;
 	}
 
@@ -1459,7 +1611,7 @@
 								'foreign_table' => $table,
 								'%OR' => array(
 									'%AND' => array(
-										'appearance' => array('useCombination' => 1),
+#										'appearance' => array('useCombination' => 1),
 										'foreign_selector' => $field,
 									),
 									'MM' => $config['MM']
@@ -1479,11 +1631,16 @@
 				// get the parent record from structure stack
 			$level = $this->getStructureLevel(-1);
 
-				// if we have symmetric fields, check on which side we are and hide fields, that are set automatically
+				// If this field is of type 'select', add an additinal criterium:
+				// (using a selector for select types doesn't matter, but useCombination does)
+			if ($config['type'] == 'select') {
+				$searchArray['%OR']['config'][0]['%AND']['%OR']['%AND']['appearance'] = array('useCombination' => 1);
+			}
+				// If we have symmetric fields, check on which side we are and hide fields, that are set automatically:
 			if (t3lib_loadDBGroup::isOnSymmetricSide($level['uid'], $level['config'], $row)) {
 				$searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_field'] = $field;
 				$searchArray['%OR']['config'][0]['%AND']['%OR']['symmetric_sortby'] = $field;
-				// hide fields, that are set automatically
+				// Hide fields, that are set automatically:
 			} else {
 				$searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_field'] = $field;
 				$searchArray['%OR']['config'][0]['%AND']['%OR']['foreign_sortby'] = $field;
@@ -1569,5 +1726,34 @@
 		$margin = $this->inlineStyles['margin-right'];
 		return $margin;
 	}
+	
+	
+	/**
+	 * Works like explode, but the $limit counts from the end of a string.
+	 *
+	 * @param	string		$delim
+	 * @param	string		$string
+	 * @param	integer		$limit
+	 * @return	array		The right exploded array
+	 */
+	function rightExplode($delim, $string, $limit = null) {
+		$result = array();
+		$tokens =  explode($delim, $string);
+
+		if ($limit > 0) {
+			if ($limit > count($tokens)) {
+				$limit = count($tokens);
+			}
+			for ($i=1; $i<$limit; $i++) {
+				array_unshift($result, array_pop($tokens));
+			}
+			if (count($tokens)) {
+				array_unshift($result, implode($delim, $tokens));
+			}
+			return $result;
+		} else {
+			return $tokens;
+		}
+	}
  }
 ?>
\ Kein Zeilenvorschub am Ende der Datei
Index: typo3/class.browse_links.php
===================================================================
--- typo3/class.browse_links.php	(Revision 1955)
+++ typo3/class.browse_links.php	(Arbeitskopie)
@@ -974,7 +974,17 @@
 				}
 			}
 			function insertElement(table, uid, type, filename,fp,filetype,imagefile,action, close)	{	//
-				if (1=='.($pArr[0]&&!$pArr[1]&&!$pArr[2] ? 1 : 0).')	{
+					// Call user defined function:
+				if (1=='.($pArr[0]&&$pArr[4] ? 1 : 0).') {
+					if (parent.window.opener) {
+						var res = parent.window.opener.'.$pArr[4].'("'.addslashes($pArr[0]).'",table,uid,type);
+						if (res.error && res.message) alert(res.message);
+						if (res.close) focusOpenerAndClose(close);
+					} else {
+						alert("Error - reference to main window is not set properly!");
+						parent.close();
+					}
+				} else if (1=='.($pArr[0]&&!$pArr[1]&&!$pArr[2] ? 1 : 0).')	{
 					addElement(filename,table+"_"+uid,fp,close);
 				} else {
 					if (setReferences())	{
@@ -982,25 +992,25 @@
 					} else {
 						alert("Error - reference to main window is not set properly!");
 					}
-					if (close)	{
-						parent.window.opener.focus();
-						parent.close();
-					}
+					focusOpenerAndClose(close);
 				}
 				return false;
 			}
 			function addElement(elName,elValue,altElValue,close)	{	//
 				if (parent.window.opener && parent.window.opener.setFormValueFromBrowseWin)	{
 					parent.window.opener.setFormValueFromBrowseWin("'.$pArr[0].'",altElValue?altElValue:elValue,elName);
-					if (close)	{
-						parent.window.opener.focus();
-						parent.close();
-					}
+					focusOpenerAndClose(close);
 				} else {
 					alert("Error - reference to main window is not set properly!");
 					parent.close();
 				}
 			}
+			function focusOpenerAndClose(close)	{	//
+				if (close)	{
+					parent.window.opener.focus();
+					parent.close();
+				}
+			}
 		';
 
 			// Finally, add the accumulated JavaScript to the template object:
@@ -1378,7 +1388,7 @@
 	function main_db()	{
 
 			// Starting content:
-		$content=$this->doc->startPage('TBE file selector');
+		$content=$this->doc->startPage('TBE record selector');
 
 			// Init variable:
 		$pArr = explode('|',$this->bparams);
