From 26f7515de79ee8df4976b8a23abd72bcf989b2a8 Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Sat, 7 May 2016 10:30:06 +0200
Subject: [PATCH] [TASK] Hide selected items in multipleSideBySide

If a multipleSideBySide form element is not allowed to add the same
value multiple times, its selected values are hidden now and it's not
possible to add them again unless the values are removed from the
selection.

Resolves: #76071
Releases: master, 7.6
Change-Id: I99d6c609ca081f4b8cb6f8ad07f74385a23b7a5c
Reviewed-on: https://review.typo3.org/48031
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
---
 .../SelectMultipleSideBySideElement.php       |  9 +++-
 .../Resources/Public/JavaScript/FormEngine.js | 54 ++++++++++++-------
 2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Form/Element/SelectMultipleSideBySideElement.php b/typo3/sysext/backend/Classes/Form/Element/SelectMultipleSideBySideElement.php
index 8beb33bd69e7..0f24b84088e8 100644
--- a/typo3/sysext/backend/Classes/Form/Element/SelectMultipleSideBySideElement.php
+++ b/typo3/sysext/backend/Classes/Form/Element/SelectMultipleSideBySideElement.php
@@ -89,6 +89,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
             $size = 2;
         }
         $size = $config['autoSizeMax'] ? MathUtility::forceIntegerInRange(count($itemsArray) + 1, MathUtility::forceIntegerInRange($size, 1), $config['autoSizeMax']) : $size;
+        $allowMultiple = !empty($config['multiple']);
 
         $itemsToSelect = [];
         $filterTextfield = [];
@@ -97,7 +98,13 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
             // Create option tags:
             $opt = array();
             foreach ($selItems as $p) {
-                $opt[] = '<option value="' . htmlspecialchars($p[1]) . '" title="' . $p[0] . '">' . $p[0] . '</option>';
+                $disabledAttr = '';
+                $classAttr = '';
+                if (!$allowMultiple && in_array((string)$p[1], $parameterArray['itemFormElValue'], true)) {
+                    $disabledAttr = ' disabled="disabled"';
+                    $classAttr = ' class="hidden"';
+                }
+                $opt[] = '<option value="' . htmlspecialchars($p[1]) . '" title="' . $p[0] . '"' . $classAttr . $disabledAttr . '>' . $p[0] . '</option>';
             }
             // Put together the selector box:
             $selector_itemListStyle = isset($config['itemListStyle'])
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js
index b513ccde9901..d6e5117063cb 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js
@@ -80,21 +80,22 @@ define(['jquery',
 	 * or from a multi-select (two selects side-by-side)
 	 * previously known as "setFormValueFromBrowseWin"
 	 *
-	 * @param {String} fieldName formerly known as "fName" name of the field, like [tt_content][2387][header]
-	 * @param {(String|Number)} value the value to fill in (could be an integer)
-	 * @param {String} label the visible name in the selector
-	 * @param {String} title the title when hovering over it
-	 * @param {String} exclusiveValues if the select field has exclusive options that are not combine-able
+	 * @param {String} fieldName Formerly known as "fName" name of the field, like [tt_content][2387][header]
+	 * @param {(String|Number)} value The value to fill in (could be an integer)
+	 * @param {String} label The visible name in the selector
+	 * @param {String} title The title when hovering over it
+	 * @param {String} exclusiveValues If the select field has exclusive options that are not combine-able
+	 * @param {$} $optionEl The jQuery object of the selected <option> tag
 	 */
-	FormEngine.setSelectOptionFromExternalSource = setFormValueFromBrowseWin = function(fieldName, value, label, title, exclusiveValues) {
+	FormEngine.setSelectOptionFromExternalSource = setFormValueFromBrowseWin = function(fieldName, value, label, title, exclusiveValues, $optionEl) {
 		exclusiveValues = String(exclusiveValues);
 
-		var $fieldEl;
-		var $originalFieldEl = $fieldEl = FormEngine.getFieldElement(fieldName)
-				,isMultiple = false
-				,isList = false;
+		var $fieldEl,
+			$originalFieldEl = $fieldEl = FormEngine.getFieldElement(fieldName),
+			isMultiple = false,
+			isList = false;
 
-		if ($originalFieldEl.length == 0 || value === '--div--') {
+		if ($originalFieldEl.length === 0 || value === '--div--') {
 			return;
 		}
 
@@ -122,7 +123,6 @@ define(['jquery',
 		}
 
 		if (isMultiple || isList) {
-
 			// If multiple values are not allowed, clear anything that is in the control already
 			if (!isMultiple) {
 				$fieldEl.empty();
@@ -130,16 +130,18 @@ define(['jquery',
 
 			// Clear elements if exclusive values are found
 			if (exclusiveValues) {
+				var $optionSelect = $optionEl.closest('select');
 				var m = new RegExp('(^|,)' + value + '($|,)');
 				// the new value is exclusive => remove all existing values
 				if (exclusiveValues.match(m)) {
 					$fieldEl.empty();
-
-				// there is an old value and it was exclusive => it has to be removed
+					$optionSelect.find('[disabled]').removeClass('hidden').prop('disabled', false);
 				} else if ($fieldEl.children('option').length == 1) {
+					// there is an old value and it was exclusive => it has to be removed
 					m = new RegExp("(^|,)" + $fieldEl.children('option').prop('value') + "($|,)");
 					if (exclusiveValues.match(m)) {
 						$fieldEl.empty();
+						$optionSelect.find('[disabled]').removeClass('hidden').prop('disabled', false);
 					}
 				}
 			}
@@ -156,6 +158,10 @@ define(['jquery',
 						return false;
 					}
 				});
+
+				if (addNewValue) {
+					$optionEl.addClass('hidden').prop('disabled', true);
+				}
 			}
 
 			// element can be added
@@ -452,6 +458,9 @@ define(['jquery',
 				case '_list':
 					$fieldEl = $(':input.tceforms-multiselect[data-formengine-input-name="' + fieldName + '"]', $formEl);
 					break;
+				case '_avail':
+					$fieldEl = $(':input[data-relatedfieldname="' + fieldName + '"]', $formEl);
+					break;
 				case '_mul':
 				case '_hr':
 					$fieldEl = $(':input[type=hidden][data-formengine-input-name="' + fieldName + '"]', $formEl);
@@ -549,10 +558,18 @@ define(['jquery',
 	 * removes currently selected options from a select field
 	 *
 	 * @param {Object} $fieldEl a jQuery object, containing the select field
+	 * @param {Object} $availableFieldEl a jQuery object, containing all available value
 	 */
-	FormEngine.removeOption = function($fieldEl) {
+	FormEngine.removeOption = function($fieldEl, $availableFieldEl) {
+		var $selected = $fieldEl.find(':selected');
+
+		$availableFieldEl
+			.find('option[value="' + $selected.attr('value') + '"]')
+			.removeClass('hidden')
+			.prop('disabled', false);
+
 		// remove the selected options
-		$fieldEl.find(':selected').remove();
+		$selected.remove();
 	};
 
 
@@ -588,7 +605,8 @@ define(['jquery',
 				} else if ($el.hasClass('t3js-btn-moveoption-bottom')) {
 					FormEngine.moveOptionToBottom($listFieldEl);
 				} else if ($el.hasClass('t3js-btn-removeoption')) {
-					FormEngine.removeOption($listFieldEl);
+					var $availableFieldEl = FormEngine.getFieldElement(fieldName, '_avail');
+					FormEngine.removeOption($listFieldEl, $availableFieldEl);
 				}
 
 				// make sure to update the hidden field value when modifying the select value
@@ -608,7 +626,7 @@ define(['jquery',
 				// try to add each selected field to the "left" select field
 				$el.find(':selected').each(function() {
 					var $optionEl = $(this);
-					FormEngine.setSelectOptionFromExternalSource(fieldName, $optionEl.prop('value'), $optionEl.text(), $optionEl.prop('title'), exclusiveValues);
+					FormEngine.setSelectOptionFromExternalSource(fieldName, $optionEl.prop('value'), $optionEl.text(), $optionEl.prop('title'), exclusiveValues, $optionEl);
 				});
 			}
 		}).on('click', '.t3js-editform-close', function(e) {
-- 
GitLab