diff --git a/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java b/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java
index 321c9c1bc6cbeb80cb90b41e58eaf7660f8d8290..13b219ef8cb1a95cff216256306908389ed2860e 100644
--- a/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java
+++ b/src/main/java/org/olat/core/dispatcher/mapper/MapperDispatcher.java
@@ -31,13 +31,13 @@ import java.io.IOException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.logging.log4j.Logger;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.dispatcher.Dispatcher;
 import org.olat.core.dispatcher.DispatcherModule;
 import org.olat.core.gui.media.MediaResource;
 import org.olat.core.gui.media.ServletUtil;
-import org.apache.logging.log4j.Logger;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.UserSession;
 import org.olat.core.util.session.UserSessionManager;
@@ -91,20 +91,21 @@ public class MapperDispatcher implements Dispatcher {
 		// e.g. non-cacheable: 	23423
 		// e.g. cacheable: 		my.mapper.path
 		UserSession usess = CoreSpringFactory.getImpl(UserSessionManager.class).getUserSession(hreq);
-		Mapper m = CoreSpringFactory.getImpl(MapperService.class).getMapperById(usess, smappath);
+		MapperService mapperService = CoreSpringFactory.getImpl(MapperService.class);
+		Mapper m = mapperService.getMapperById(usess, smappath);
 		if (m == null) {
 			//an anonymous mapper?
-			m = CoreSpringFactory.getImpl(MapperService.class).getMapperById(null, smappath);
+			m = mapperService.getMapperById(null, smappath);
 			if(m == null) {
 				log.warn("Call to mapped resource, but mapper does not exist for path::" + smappath);
-				DispatcherModule.sendNotFound(pathInfo, hres);
+				hres.setStatus(HttpServletResponse.SC_NOT_FOUND);
 				return;
 			}
 		}
 		String mod = slashPos > 0 ? subInfo.substring(slashPos) : "";
 		if (mod.indexOf("..") != -1) {
 			log.warn("Illegal mapper path::" + mod + " contains '..'");
-			DispatcherModule.sendForbidden(pathInfo, hres);
+			hres.setStatus(HttpServletResponse.SC_FORBIDDEN);
 			return;
 		}
 		// /bla/blu.html
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
index dd27e14b5acc654693f9aa3103b625bbc45a86c4..9517f550ea39638828152d5490e3dac7ded2c995 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/Form.java
@@ -437,14 +437,21 @@ public class Form {
 	}
 	
 	private final void submit(UserRequest ureq, Event validationOkEvent) {	
+		boolean isValid = validate(ureq);
+		formWrapperComponent.fireValidation(ureq, isValid, validationOkEvent);
+		isValidAndSubmitted = isValid;
+		hasAlreadyFired = true;
+	}
+	
+	public boolean validate(UserRequest ureq) {
 		ValidatingFormComponentVisitor vfcv = new ValidatingFormComponentVisitor();
 		FormComponentTraverser ct = new FormComponentTraverser(vfcv, formLayout, false);
 		ct.visitAll(ureq);
 		// validate all form elements and gather validation status
 		ValidationStatus[] status = vfcv.getStatus();
-		//
+		
 		boolean isValid = status == null || status.length == 0;
-		// let the businesslogic validate this is implemented by the outside listener
+		// let the business logic validate this is implemented by the outside listener
 		
 		for (Iterator<FormBasicController> iterator = formListeners.iterator(); iterator.hasNext();) {
 			FormBasicController fbc = iterator.next();
@@ -452,10 +459,7 @@ public class Form {
 			//let further validate even if one fails.
 			isValid = fbc.validateFormLogic(ureq) && isValid;
 		}
-
-		formWrapperComponent.fireValidation(ureq, isValid, validationOkEvent);
-		isValidAndSubmitted = isValid;
-		hasAlreadyFired = true;
+		return isValid;
 	}
 	
 	
diff --git a/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java b/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
index 98b7357b861ea57b71162c06ea68d7eeafe954ca..643ad5575db65d23239f21a82cd690b3d8abeec3 100644
--- a/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/AssessmentModeCoordinationServiceImpl.java
@@ -249,6 +249,13 @@ public class AssessmentModeCoordinationServiceImpl implements AssessmentModeCoor
 				mode = ensureStatusOfMode(mode, Status.none);
 				sendEvent(AssessmentModeNotificationEvent.BEFORE, mode,
 						assessmentModeManager.getAssessedIdentityKeys(mode));
+			} else if(mode.getStatus() == Status.followup && mode.isManualBeginEnd() && !forceStatus) {
+				// close manual assessment mode in the follow-up time but started before the begin date
+				if(mode.getEndWithFollowupTime().compareTo(now) < 0) {
+					mode = ensureStatusOfMode(mode, Status.end);
+					sendEvent(AssessmentModeNotificationEvent.END, mode,
+							assessmentModeManager.getAssessedIdentityKeys(mode));
+				}
 			}
 		} else if(mode.getBeginWithLeadTime().compareTo(now) <= 0 && mode.getBegin().compareTo(now) > 0
 				&& mode.getBeginWithLeadTime().compareTo(mode.getBegin()) != 0) {
diff --git a/src/main/java/org/olat/course/editor/MultiSPController.java b/src/main/java/org/olat/course/editor/MultiSPController.java
index a442b06916fcfca918e623c6a752ec65102e07a1..d53da1dd07c0edd2b2bbc9c213b7c5fed93afc80 100644
--- a/src/main/java/org/olat/course/editor/MultiSPController.java
+++ b/src/main/java/org/olat/course/editor/MultiSPController.java
@@ -21,6 +21,8 @@
 package org.olat.course.editor;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -130,7 +132,10 @@ public class MultiSPController extends FormBasicController {
 
 		if(item instanceof VFSContainer) {
 			VFSContainer container = (VFSContainer)item;
-			for(VFSItem subItem:container.getItems(new MultiSPVFSItemFilter())) {	
+			List<VFSItem> subItems = container.getItems(new MultiSPVFSItemFilter());
+			Collections.sort(subItems, new VFSItemNameComparator());
+			
+			for(VFSItem subItem:subItems) {	
 				MultipleSelectionElement sel = initTreeRec(level + 1, subItem, layoutcont);
 				node.getChildren().add(sel);
 			}
@@ -233,7 +238,8 @@ public class MultiSPController extends FormBasicController {
 		}
 		
 		//recurse
-		for(MultipleSelectionElement childElement:node.getChildren()) {
+		List<MultipleSelectionElement> childElements = node.getChildren();
+		for(MultipleSelectionElement childElement:childElements) {
 			create(childElement, course, parentNode);
 		}
 	}
@@ -336,11 +342,29 @@ public class MultiSPController extends FormBasicController {
 		}
 	}
 	
-	public class MultiSPVFSItemFilter implements VFSItemFilter {
+	public static class MultiSPVFSItemFilter implements VFSItemFilter {
 		@Override
 		public boolean accept(VFSItem vfsItem) {
 			String name = vfsItem.getName();
 			return !name.startsWith(".");
 		}
 	}
+	
+	public static class VFSItemNameComparator implements Comparator<VFSItem> {
+
+		@Override
+		public int compare(VFSItem o1, VFSItem o2) {
+			if(o1 == null && o2 == null) return 0;
+			if(o1 == null) return -1;
+			if(o2 == null) return 1;
+			
+			String n1 = o1.getName();
+			String n2 = o2.getName();
+			
+			if(n1 == null && n2 == null) return 0;
+			if(n1 == null) return -1;
+			if(n2 == null) return 1;
+			return n1.compareToIgnoreCase(n2);
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html
index 63838760be8157be3bccdf4d6d63a3ff8ecee719..f530557c0403f7e5f0e8f44840fd8b9928605647 100644
--- a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html
+++ b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html
@@ -518,6 +518,14 @@
 								jEl.css('height', jEl.attr('height') + "px");
 							}
 						});
+						jQuery('#collapse_${itemResult.interactionResults.solutionFormItem.name} div.o_oo_hotcontainer>div').each(function(index, el) {
+							var jEl = jQuery(el);
+							if(jEl.css('height') == '0px') {
+								var containerEl = jEl.closest("div.o_oo_hotcontainer");
+								jEl.css('height', containerEl.css('height'));
+								jEl.css('background-size', containerEl.css('width') + " " + containerEl.css('height'));
+							}
+						});
 					})
 				});
 				/* ]]> */
diff --git a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java
index 2a01c6473aa63a92773395cbc47eb9ae03b5d9e0..dae813353e24884f18c451fb20e097d5e05468b8 100644
--- a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java
+++ b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java
@@ -181,13 +181,17 @@ public class RubricStatisticImpl implements RubricStatistic {
 		Double mean = sumAverage.getAverage();
 		if (mean == null) return null;
 		
-		List<Double> scaledValues = getScaledValues(stepCounts);
-		if (scaledValues.size() < 2) return null;
-		
 		double temp = 0;
-		for(double a: scaledValues)
-			temp += (a-mean)*(a-mean);
-		return temp/(scaledValues.size() - 1);
+		int size = 0;
+		for (int step = 1; step <= rubric.getSteps(); step++) {
+			double value = rubric.getScaleType().getStepValue(rubric.getSteps(), step, rubric.getWeight());
+			int count = stepCounts.get(step - 1).intValue();
+			for (int i = 0; i < count; i++) {
+				temp += (value-mean)*(value-mean);
+				size++;
+			}
+		}
+		return size > 2? temp/(size - 1): null;
 	}
 
 	private Double getStdDev(List<Long> stepCounts) {
@@ -197,21 +201,6 @@ public class RubricStatisticImpl implements RubricStatistic {
 		return Math.sqrt(getVariance(stepCounts));
 	}
 	
-	private List<Double> getScaledValues(List<Long> stepCounts) {
-		List<Double> scaledValues = new ArrayList<>();
-		for (int step = 1; step <= rubric.getSteps(); step++) {
-			Long count = stepCounts.get(step - 1);
-			if (count != null) {
-				double stepValue = rubric.getScaleType().getStepValue(rubric.getSteps(), step, rubric.getWeight());
-				for (int i = 0; i < count; i++) {
-					double scaledValue = stepValue * step;
-					scaledValues.add(Double.valueOf(scaledValue));
-				}
-			}
-		}
-		return scaledValues;
-	}
-
 	private void calculateTotalStatistics() {
 		Long numberOfNoResponses = countedNoResponses.stream().mapToLong(CalculatedLong::getValue).sum();
 		List<Long> totalStepCounts = getTotalStepCounts();
diff --git a/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java b/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java
index 6e35c328746b9380d7a656ba3594419e5455b63c..1c80773559172f913543d3804c6c704d150c4baa 100644
--- a/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java
+++ b/src/main/java/org/olat/modules/forms/ui/EvaluationFormExecutionController.java
@@ -92,8 +92,6 @@ public class EvaluationFormExecutionController extends FormBasicController imple
 	private boolean readOnly;
 	private boolean showDoneButton;
 
-	private boolean immediateSave = false;
-
 	private EvaluationFormSession session;
 	private final EvaluationFormResponses responses;
 	
@@ -268,11 +266,9 @@ public class EvaluationFormExecutionController extends FormBasicController imple
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if (saveLink == source) {
-			immediateSave = true;
-			mainForm.submit(ureq);
-		} else if (doneLink == source) {
-			immediateSave = false;
-			mainForm.submit(ureq);
+			if(mainForm.validate(ureq)) {
+				doSaveResponses();
+			}
 		}
 		super.formInnerEvent(ureq, source, event);
 	}
@@ -290,7 +286,7 @@ public class EvaluationFormExecutionController extends FormBasicController imple
 	@Override
 	protected void formOK(UserRequest ureq) {
 		boolean responsesSaved = doSaveResponses();
-		if (!immediateSave && responsesSaved) {
+		if (responsesSaved) {
 			doConfirmDone(ureq);
 		}
 	}
@@ -340,7 +336,7 @@ public class EvaluationFormExecutionController extends FormBasicController imple
 
 		List<ValidationMessage> messages = new ArrayList<>();
 		validate(ureq, messages);
-		if (messages.size() > 0) {
+		if (!messages.isEmpty()) {
 			for (ValidationMessage message : messages) {
 				sb.append("<p class='o_warning'>").append(message.getMessage()).append("</p>");
 			}
diff --git a/src/main/java/org/olat/modules/forms/ui/TextInputController.java b/src/main/java/org/olat/modules/forms/ui/TextInputController.java
index a01405357f9667098c6a52267a67ffcba348a104..b289e631eda83f30dcf4ee7e860cbdb6bf947402 100644
--- a/src/main/java/org/olat/modules/forms/ui/TextInputController.java
+++ b/src/main/java/org/olat/modules/forms/ui/TextInputController.java
@@ -103,7 +103,7 @@ public class TextInputController extends FormBasicController implements Evaluati
 	
 	@Override
 	protected boolean validateFormLogic(UserRequest ureq) {
-		boolean allOk = true;
+		boolean allOk = super.validateFormLogic(ureq);
 		
 		if (textInput.isNumeric()) {
 			String val = singleRowEl.getValue();
@@ -117,7 +117,7 @@ public class TextInputController extends FormBasicController implements Evaluati
 			}
 		}
 		
-		return allOk & super.validateFormLogic(ureq);
+		return allOk;
 	}
 
 	@Override
diff --git a/src/main/webapp/static/js/jquery/openolat/jquery.contenteditor.js b/src/main/webapp/static/js/jquery/openolat/jquery.contenteditor.js
index ad7ac0d8491cf8ada4e26a7b8228b27393123496..f709154f000ac02d6585e30214e875e3c0052a47 100644
--- a/src/main/webapp/static/js/jquery/openolat/jquery.contenteditor.js
+++ b/src/main/webapp/static/js/jquery/openolat/jquery.contenteditor.js
@@ -83,7 +83,8 @@
 						|| jQuery(e.target).closest(".mce-container").length > 0
 						|| jQuery(e.target).closest(".mce-widget").length > 0
 						|| jQuery(e.target).closest(".o_layered_panel .popover").length > 0
-						|| jQuery(e.target).closest(".o_layered_panel .modal-dialog").length > 0;
+						|| jQuery(e.target).closest(".o_layered_panel .modal-dialog").length > 0
+						|| e.target.nodeName == 'BODY';
 					if(!edited && !excludedEls) {
 						o_XHREvent(componentUrl, false, false, 'cid', 'close_edit_fragment');
 					}
diff --git a/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectProviderTest.java b/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectProviderTest.java
index 1146fb3195d0430939782fc3ce9c5e7e3817138b..29eb13166745594c2d1fc268cd8e16c3c1327c6c 100644
--- a/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectProviderTest.java
+++ b/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectProviderTest.java
@@ -21,6 +21,7 @@ package org.olat.modules.adobeconnect.manager;
 
 import java.util.Calendar;
 import java.util.Date;
+import java.util.TimeZone;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -38,9 +39,10 @@ public class AdobeConnectProviderTest {
 		String dateStr = "2019-04-18T12:28:28.587+02:00";
 		Date date = AbstractAdobeConnectProvider.parseIsoDate(dateStr);
 		Assert.assertNotNull(date);
-		
+
 		Calendar cal = Calendar.getInstance();
 		cal.setTime(date);
+		cal.setTimeZone(TimeZone.getTimeZone("Europe/Zurich"));
 		Assert.assertEquals(12, cal.get(Calendar.HOUR_OF_DAY));
 	}