diff --git a/pom.xml b/pom.xml
index 27650051f79df9160ad57a9e0a86dc5ae7caf934..3cce29adf56fe11a1057e34e732b742942715190 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,7 +60,7 @@
 	<properties>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<targetJdk>1.8</targetJdk>
-		<org.springframework.version>5.2.6.RELEASE</org.springframework.version>
+		<org.springframework.version>5.2.7.RELEASE</org.springframework.version>
 		<org.hibernate.version>5.4.15.Final</org.hibernate.version>
 		<apache.cxf>3.3.6</apache.cxf>
 		<apache.pdfbox>2.0.19</apache.pdfbox>
diff --git a/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java b/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java
index cee9175bfc66c36a4183ccc3a2e3f555ebf524a4..a4e5e673bfa8d6c416dd830bead25810574a6df7 100644
--- a/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java
+++ b/src/main/java/org/olat/core/commons/services/text/impl/nutch/NGramProfile.java
@@ -472,7 +472,7 @@ public class NGramProfile {
         f = new File(profilename + "." + FILE_EXTENSION);
         FileOutputStream fos = new FileOutputStream(f);
         newProfile.save(fos);
-        System.out.println("new profile " + profilename + "." + FILE_EXTENSION + " was created.");
+        //System.out.println("new profile " + profilename + "." + FILE_EXTENSION + " was created.");
         break;
 
       case SIMILARITY:
@@ -486,7 +486,7 @@ public class NGramProfile {
         fis = new FileInputStream(f);
         NGramProfile newProfile2 = NGramProfile.create(filename2, fis, encoding);
         newProfile2.normalize();
-        System.out.println("Similarity is " + newProfile.getSimilarity(newProfile2));
+        //System.out.println("Similarity is " + newProfile.getSimilarity(newProfile2));
         break;
 
       case SCORE:
@@ -500,7 +500,7 @@ public class NGramProfile {
                                                 DEFAULT_MIN_NGRAM_LENGTH,
                                                 DEFAULT_MAX_NGRAM_LENGTH);
         compare.load(fis);
-        System.out.println("Score is " + compare.getSimilarity(newProfile));
+        //System.out.println("Score is " + compare.getSimilarity(newProfile));
         break;
 
       }
diff --git a/src/main/java/org/olat/core/gui/media/ServletUtil.java b/src/main/java/org/olat/core/gui/media/ServletUtil.java
index d47bee277b1b54b9a0a8f4a7b8c35776aeeec532..06c3648272778d5aec29425c7e09c52b43161650 100644
--- a/src/main/java/org/olat/core/gui/media/ServletUtil.java
+++ b/src/main/java/org/olat/core/gui/media/ServletUtil.java
@@ -259,7 +259,6 @@ public class ServletUtil {
 		try(InputStream istream = (resourceInputStream instanceof BufferedInputStream)
 				? resourceInputStream : new BufferedInputStream(resourceInputStream, bufferSize)) {
 			stats.incrementConcurrentStreamCounter();
-			Thread.sleep(5000);
 			exception = copyRange(istream, ostream, range.start, range.end, bufferSize);
 		} catch(IOException e) {
 			handleIOException("Deliver range of data", e);
diff --git a/src/main/java/org/olat/core/util/filter/impl/XMLValidEntityFilter.java b/src/main/java/org/olat/core/util/filter/impl/XMLValidEntityFilter.java
index 6779846c33fb926875f692dad17e22c69a6904f1..b712f9e0c9985a8d9cf3c952b33179542e6757a1 100644
--- a/src/main/java/org/olat/core/util/filter/impl/XMLValidEntityFilter.java
+++ b/src/main/java/org/olat/core/util/filter/impl/XMLValidEntityFilter.java
@@ -80,7 +80,6 @@ public class XMLValidEntityFilter implements Filter {
 								writer.append('&').append(entityContent).append(';');
 							} else if (entityValue < 0x20) {
 								//skip them
-								System.out.println();
 							} else {
 								writer.append('&').append(entityContent).append(';');
 							}
diff --git a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
index 2ddd8393db1d80163eda9937fc8c45a024830e8f..e9b5a99cc7fe770bf678978d0b4842850f6d437b 100644
--- a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
+++ b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
@@ -104,11 +104,6 @@ class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {
 		//add bookmarked courses
 		if(webDAVModule.isEnableLearnersBookmarksCourse()) {
 			List<RepositoryEntry> bookmarkedEntries = repositoryManager.getLearningResourcesAsBookmarkedMember(getIdentity(), identityEnv.getRoles(), "CourseModule", 0, -1);
-			for(RepositoryEntry bookmarkedEntry:bookmarkedEntries) {
-				System.out.println(bookmarkedEntry.getDisplayname());
-			}
-			
-			
 			appendCourses(bookmarkedEntries, containers, terms, noTermContainer, namingAndGrouping, false);
 		}
 
diff --git a/src/main/java/org/olat/course/assessment/ui/mode/AssessmentModeEditController.java b/src/main/java/org/olat/course/assessment/ui/mode/AssessmentModeEditController.java
index e24ed82a68fd41df6c4202d49fd2f101a6782e3a..db50cdba29e8357a02c067bc736136b9715ed21d 100644
--- a/src/main/java/org/olat/course/assessment/ui/mode/AssessmentModeEditController.java
+++ b/src/main/java/org/olat/course/assessment/ui/mode/AssessmentModeEditController.java
@@ -393,7 +393,8 @@ public class AssessmentModeEditController extends FormBasicController {
 		safeExamBrowserEl.addActionListener(FormEvent.ONCHANGE);
 		safeExamBrowserEl.setEnabled(status != Status.end);
 		String key = assessmentMode.getSafeExamBrowserKey();
-		safeExamBrowserKeyEl = uifactory.addTextAreaElement("safeexamkey", "mode.safeexambrowser.key", 4096, 6, 60, false, false, key, formLayout);
+		safeExamBrowserKeyEl = uifactory.addTextAreaElement("safeexamkey", "mode.safeexambrowser.key", 16000, 6, 60, false, false, key, formLayout);
+		safeExamBrowserKeyEl.setMaxLength(16000);
 		safeExamBrowserKeyEl.setVisible(assessmentMode.isSafeExamBrowser());
 		safeExamBrowserKeyEl.setEnabled(status != Status.end);
 		String hint = assessmentMode.getSafeExamBrowserHint();
@@ -571,6 +572,9 @@ public class AssessmentModeEditController extends FormBasicController {
 			if(!StringHelper.containsNonWhitespace(value)) {
 				safeExamBrowserKeyEl.setErrorKey("form.legende.mandatory", null);
 				allOk &= false;
+			} else if(value.length() > safeExamBrowserKeyEl.getMaxLength()) {
+				safeExamBrowserKeyEl.setErrorKey("form.error.toolong", new String[] { Integer.toString(safeExamBrowserKeyEl.getMaxLength()) } );
+				allOk &= false;
 			}
 		}
 		
diff --git a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java
index dbfd954edd7bf696c616b7715405f7dd309a3616..7931aa8acd2621a731405d33c3edd06db125372b 100644
--- a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java
+++ b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewIdentitiesController.java
@@ -20,6 +20,7 @@
 package org.olat.course.member.wizard;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -200,7 +201,7 @@ public class ImportMemberOverviewIdentitiesController extends StepFormBasicContr
 			}
 		}
 		// make a lowercase copy of identList for processing username and email
-		List<String> identListLowercase = new ArrayList<>(identList.size());
+		Collection<String> identListLowercase = new HashSet<>(identList.size());
 		for (String ident:identList) {
 			identListLowercase.add(ident.toLowerCase());
 		}
diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
index 2b5b709fc845462aa38b8983f6f58c5eab74f3c6..49e82d9ae1da79513deebef78b992a5d049e19a2 100644
--- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
@@ -140,6 +140,8 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements QT
 
 	private static final int CURRENT_CONFIG_VERSION = 2;
 
+	private transient RepositoryEntry cachedReferenceRepositoryEntry;
+
 	public IQTESTCourseNode() {
 		this(null);
 	}
@@ -424,12 +426,27 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements QT
 		sd.setActivateableViewIdentifier(pane);
 		status.add(sd);
 	}
+	
+	/**
+	 * @return A cached instance of the reference repository entry. May be not suitable
+	 * 		to insert an assessment entry.
+	 */
+	public RepositoryEntry getCachedReferencedRepositoryEntry() {
+		RepositoryEntry cachedEntry = cachedReferenceRepositoryEntry;
+		if(IQEditController.matchIQReference(cachedEntry, getModuleConfiguration())) {
+			return cachedEntry;
+		}
+		// The method updates the cache
+		return getReferencedRepositoryEntry();
+	}
 
 	@Override
 	public RepositoryEntry getReferencedRepositoryEntry() {
 		// ",false" because we do not want to be strict, but just indicate whether
 		// the reference still exists or not
-		return IQEditController.getIQReference(getModuleConfiguration(), false);
+		RepositoryEntry entry = IQEditController.getIQReference(getModuleConfiguration(), false);
+		cachedReferenceRepositoryEntry = entry;
+		return entry;
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/cl/manager/CheckboxManagerImpl.java b/src/main/java/org/olat/course/nodes/cl/manager/CheckboxManagerImpl.java
index 6f520f8e5ad2fcfb7e304adfbf0872d77c0f8ed3..ce06632f4b2efe698a1913fc18e40dff671d8af0 100644
--- a/src/main/java/org/olat/course/nodes/cl/manager/CheckboxManagerImpl.java
+++ b/src/main/java/org/olat/course/nodes/cl/manager/CheckboxManagerImpl.java
@@ -228,10 +228,6 @@ public class CheckboxManagerImpl implements CheckboxManager {
 				}
 			}
 		}
-		
-		for(DBCheckbox dbCheckbox:uuids.values()) {
-			System.out.println("Remove them??? " + dbCheckbox.getCheckboxId());
-		}
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java b/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java
index 7d6693993c3b66f199b6f56086c26cc06ebe3c31..40dbee1311b28275b17d36f8d4af7706253d815d 100644
--- a/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java
+++ b/src/main/java/org/olat/course/nodes/en/ENEditGroupAreaFormController.java
@@ -187,12 +187,13 @@ class ENEditGroupAreaFormController extends FormBasicController implements Gener
 		List<EnrollmentRow> enrollmentRows = enrollmentManager.getEnrollments(getIdentity(), groupKeys, null, 256);
 		Map<Long,EnrollmentRow> enrollmentMap = enrollmentRows.stream().collect(Collectors.toMap(EnrollmentRow::getKey, g -> g, (u, v) -> u));
 
-		easyGroupTableRows = new ArrayList<ENEditGroupTableContentRow>();
+		easyGroupTableRows = new ArrayList<>();
 		for (Long groupKey : groupKeys) {
 			BusinessGroup group = groupMap.get(groupKey);
-			EnrollmentRow enrollment = enrollmentMap.get(groupKey);
-			
-			easyGroupTableRows.add(new ENEditGroupTableContentRow(group, enrollment));
+			if(group != null) {
+				EnrollmentRow enrollment = enrollmentMap.get(groupKey);
+				easyGroupTableRows.add(new ENEditGroupTableContentRow(group, enrollment));
+			}
 		}
 
 		easyGroupTableModel.setObjects(easyGroupTableRows);
diff --git a/src/main/java/org/olat/course/nodes/iq/IQEditController.java b/src/main/java/org/olat/course/nodes/iq/IQEditController.java
index 5ccffae1ab861376fe2d6b877b64a839dd6ede2e..f8377a910fc7330f5eb4b966731f7c94eb039959 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQEditController.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQEditController.java
@@ -380,6 +380,11 @@ public class IQEditController extends ActivateableTabbableDefaultController impl
 		moduleConfiguration.set(CONFIG_KEY_REPOSITORY_SOFTKEY, re.getSoftkey());
 	}
 	
+	public static boolean matchIQReference(RepositoryEntry re, ModuleConfiguration moduleConfiguration) {
+		String repoSoftkey = (String)moduleConfiguration.get(CONFIG_KEY_REPOSITORY_SOFTKEY);
+		return repoSoftkey != null && re != null && repoSoftkey.equals(re.getSoftkey());
+	}
+	
 	/**
 	 * Remove the reference to the repository entry.
 	 * 
diff --git a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java
index 620b487e457d42760ba9fdfc1069a445d66918d4..001840a25e0cd60aa70f167e7e45aae53da02a40 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQTESTAssessmentConfig.java
@@ -78,7 +78,7 @@ public class IQTESTAssessmentConfig implements AssessmentConfig {
 				|| IQEditController.CONFIG_VALUE_QTI1.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI))) {
 			maxScore = (Float) config.get(IQEditController.CONFIG_KEY_MAXSCORE);
 		} else {
-			RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry();
+			RepositoryEntry testEntry = courseNode.getCachedReferencedRepositoryEntry();
 			if (testEntry != null) {
 				if(QTIResourceTypeModule.isQtiWorks(testEntry.getOlatResource())) {
 					AssessmentTest assessmentTest = courseNode.loadAssessmentTest(testEntry);
@@ -107,7 +107,7 @@ public class IQTESTAssessmentConfig implements AssessmentConfig {
 				|| IQEditController.CONFIG_VALUE_QTI1.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI))) {
 			minScore = (Float) config.get(IQEditController.CONFIG_KEY_MINSCORE);
 		} else {
-			RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry();
+			RepositoryEntry testEntry = courseNode.getCachedReferencedRepositoryEntry();
 			if (testEntry != null) {
 				if(QTIResourceTypeModule.isQtiWorks(testEntry.getOlatResource())) {
 					AssessmentTest assessmentTest = courseNode.loadAssessmentTest(testEntry);
@@ -135,7 +135,7 @@ public class IQTESTAssessmentConfig implements AssessmentConfig {
 				|| IQEditController.CONFIG_VALUE_QTI1.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI))) {
 			mode = Mode.setByNode;
 		} else {
-			RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry();
+			RepositoryEntry testEntry = courseNode.getCachedReferencedRepositoryEntry();
 			if (testEntry != null) {
 				if(QTIResourceTypeModule.isQtiWorks(testEntry.getOlatResource())) {
 					AssessmentTest assessmentTest = courseNode.loadAssessmentTest(testEntry);
@@ -168,7 +168,7 @@ public class IQTESTAssessmentConfig implements AssessmentConfig {
 				|| IQEditController.CONFIG_VALUE_QTI1.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI))) {
 			cutValue = (Float) config.get(IQEditController.CONFIG_KEY_CUTVALUE);
 		} else {
-			RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry();
+			RepositoryEntry testEntry = courseNode.getCachedReferencedRepositoryEntry();
 			if (testEntry != null) {
 				if(QTIResourceTypeModule.isQtiWorks(testEntry.getOlatResource())) {
 					AssessmentTest assessmentTest = courseNode.loadAssessmentTest(testEntry);
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index a5c6ba23d8de5405fddbd9ec81e176cfc23c65c2..2cdea6e1e4e9305ed61cbdbd1512157a4f1665f8 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -95,6 +95,17 @@ public interface QTI21Service {
 	
 	public URI createAssessmentTestUri(File resourceDirectory);
 	
+	/**
+	 * Ensure the assessment test is cached and not expired by
+	 * the max. idle configuration. The goal is to maintain the
+	 * object in cache despite it to be strong reference by the
+	 * run controller.
+	 * 
+	 * @param resourceDirectory The directory where is the package
+	 */
+	public void touchCachedResolveAssessmentTest(File resourceDirectory);
+	
+	
 	/**
 	 * Load the assessmentTest based on the imsmanifest.xml found in the resource
 	 * directory. Return null if the imsmanifest.xml is not found. The assessmentTest
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
index cb01875501c63407cfc2fb5d4f762ddeed1f3e24..01e479399abc4d5851361dc9a41f5bf6ecaf2aa3 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -329,6 +329,14 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia
 		return testSessionDao.hasActiveTestSession(testEntry);
 	}
 
+	@Override
+	public void touchCachedResolveAssessmentTest(File resourceDirectory) {
+		URI assessmentObjectSystemId = createAssessmentTestUri(resourceDirectory);
+		if(assessmentObjectSystemId != null) {
+			assessmentTestsCache.get(resourceDirectory);
+        }
+	}
+
 	@Override
 	public ResolvedAssessmentTest loadAndResolveAssessmentTest(File resourceDirectory, boolean replace, boolean debugInfo) {
         URI assessmentObjectSystemId = createAssessmentTestUri(resourceDirectory);
@@ -416,7 +424,7 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia
 	public URI createAssessmentTestUri(final File resourceDirectory) {
 		final String key = resourceDirectory.getAbsolutePath();
 		try {
-			return resourceToTestURI.computeIfAbsent(key, (directoryAbsolutPath) -> {
+			return resourceToTestURI.computeIfAbsent(key, directoryAbsolutPath -> {
 				File manifestPath = new File(resourceDirectory, "imsmanifest.xml");
 				QTI21ContentPackage	cp = new QTI21ContentPackage(manifestPath.toPath());
 				try {
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java
index 9be6d7e22710ff34a1235785f3d04674f11bc0de..287fa9f055f49b5227d31d883dcec645c17cfa4f 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentHtmlBuilder.java
@@ -68,6 +68,7 @@ import uk.ac.ed.ph.jqtiplus.xmlutils.xslt.XsltSerializationOptions;
 public class AssessmentHtmlBuilder {
 	
 	private static final Logger log = Tracing.createLoggerFor(AssessmentHtmlBuilder.class);
+	private static final String SPACE = " ";
 	
 	private final QtiSerializer qtiSerializer;
 	
@@ -373,6 +374,8 @@ public class AssessmentHtmlBuilder {
 			attributes.addAttribute("data-oo-movie", ooData);
 
 			super.startElement("", "object", "object", attributes);
+			// ensure the tag is written <object> </object> and <object />
+			super.characters(SPACE.toCharArray(), 0, SPACE.length());
 			super.endElement("", "object", "object");
 		}
 	}
diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
index 125c03a5f2d11cf4a609897845e3c3dde838fa6f..d0aea891e5c466122ca8bd123c3fe2e1e612811a 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
@@ -178,6 +178,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 	private DialogBoxController confirmSuspendDialog;
 	
 	private CandidateEvent lastEvent;
+	private Date touchTimestamp;
 	private Date currentRequestTimestamp;
 	private AssessmentTestSession candidateSession;
 	private ResolvedAssessmentTest resolvedAssessmentTest;
@@ -255,6 +256,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 		FileResourceManager frm = FileResourceManager.getInstance();
 		fUnzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource());
 		resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(fUnzippedDirRoot, false, false);
+		touchTimestamp = ureq.getRequestTimestamp();
 		if(resolvedAssessmentTest == null || resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful() == null) {
 			mainVC = createVelocityContainer("error");
 		} else {
@@ -630,6 +632,28 @@ public class AssessmentTestDisplayController extends BasicController implements
 		return sessionDeleted;
 	}
 	
+	/**
+	 * This method maintains the assessment test in cache during
+	 * a test session. This controller doesn't need the cache, the
+	 * test is strong referenced but at the end of the test, score
+	 * evaluation of the course, rendering of the assessment result
+	 * can need the cache again. For very large tests, it can be a
+	 * performance issue.
+	 * 
+	 * @param ureq The user request
+	 */
+	private void touchResolvedAssessmentTest(UserRequest ureq) {
+		try {
+			Date timestamp = ureq.getRequestTimestamp();
+			if(touchTimestamp == null || (timestamp.getTime() > touchTimestamp.getTime() + 300000l)) {
+				touchTimestamp = timestamp;
+				qtiService.touchCachedResolveAssessmentTest(fUnzippedDirRoot);
+			}
+		} catch (Exception e) {
+			logError("", e);
+		}
+	}
+	
 	private boolean timeLimitBarrier(UserRequest ureq) {
 		Long assessmentTestMaxTimeLimits = getAssessmentTestMaxTimeLimit();
 		if(assessmentTestMaxTimeLimits != null) {
@@ -796,6 +820,8 @@ public class AssessmentTestDisplayController extends BasicController implements
 				restartTest(ureq);
 				break;
 		}
+		
+		touchResolvedAssessmentTest(ureq);
 	}
 	
 	private void restartTest(UserRequest ureq) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java
index 8ce18b1a999d7865690b21ce437ea84134d1442c..cdbc7b2893eb26199a26d6a79a79ecfaa28b9fe7 100644
--- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java
+++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java
@@ -519,7 +519,6 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent
 				renderTable(renderer, sb, component, resolvedAssessmentItem, itemSessionState, (Table)block, ubu, translator);
 				break;
 			case Object.QTI_CLASS_NAME:
-				System.out.println("1");
 				break;
 			default: {
 				renderStartHtmlTag(sb, component, resolvedAssessmentItem, block, null);
diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties
index 873c859a3f3178aff50ebd627af75421214e6be3..43c51cd1dec89a085c3e327777083cffcc0e9685 100644
--- a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties
@@ -11,7 +11,7 @@ adobeconnect.module.enabled.for.groups=Gruppen
 adobeconnect.module.provider=Provider
 adobeconnect.title=Adobe Connect
 check=Serververbindung testen
-confirm.delete.meeting=Wollen Sie wirklich den Meeting "{0}" l\u00F6schen?
+confirm.delete.meeting=Wollen Sie wirklich das Meeting "{0}" l\u00F6schen?
 confirm.delete.meeting.title=Meeting "{0}" l\u00F6schen
 connection.failed=Login fehlgeschlagen.
 connection.successful=Login erfolgreich\!
@@ -87,6 +87,6 @@ option.single.meeting=Verteilte Meetings
 option.single.meeting.single=Erstellt nur ein Meeting Raum pro Kursbaustein oder Gruppe
 option.single.meeting.perdate=Erstellt ein Meeting Raum pro Datum
 table.header.permanent=Dauernd
-warning.no.access=Sie k\u00F6nnen noch nicht den Meeting beitreten.
+warning.no.access=Sie k\u00F6nnen noch nicht dem Meeting beitreten.
 warning.no.meeting=Das Meeting wurde gel\u00F6scht.
 warning.not.registered.shared.documents=Nur die Personen die sich an den Meeting angemeldet haben d\u00FCrfen die Dokumenten ansehen.
diff --git a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties
index db52545f0ec195ab112ea9b467a23fcb490b8430..3133da49f45ec6f216c809bb3d0b0ead53893cc1 100644
--- a/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/bigbluebutton/ui/_i18n/LocalStrings_de.properties
@@ -166,7 +166,7 @@ view=Ansehen
 view.template=Raumvorlage "{0}"
 warning.at.least.one.meeting=Sie m\u00FCssen mindestens ein Meeting w\u00E4hlen.
 warning.meeting.started=Sie k\u00F6nnen ein gestartetes Meeting nicht mehr bearbeiten.
-warning.no.access=Sie k\u00F6nnen noch nicht den Meeting beitreten.
+warning.no.access=Sie k\u00F6nnen noch nicht dem Meeting beitreten.
 warning.no.meeting=Das Meeting wurde gel\u00F6scht.
 warning.template.in.use=Die Raumvorlage kann nicht gel\u00F6scht werden da sie nocht benutzt wird. L\u00F6schschen Sie zuerst die entsprechenden Online-Termine oder deaktivieren Sie die Raumvorlage.
 wizard.dates.title=Datum
diff --git a/src/main/java/org/olat/modules/ceditor/ui/HTMLRawEditorController.java b/src/main/java/org/olat/modules/ceditor/ui/HTMLRawEditorController.java
index ca38556f14baf711c562f6a41a110fdbb44cd494..30aa40be3ebc94f14321000827560248c8cbc801 100644
--- a/src/main/java/org/olat/modules/ceditor/ui/HTMLRawEditorController.java
+++ b/src/main/java/org/olat/modules/ceditor/ui/HTMLRawEditorController.java
@@ -107,7 +107,7 @@ public class HTMLRawEditorController extends FormBasicController implements Page
 
 	@Override
 	public List<Link> getOptionLinks() {
-		List<Link> links = new ArrayList<>(2);
+		List<Link> links = new ArrayList<>(5);
 		links.add(column4Link);
 		links.add(column3Link);
 		links.add(column2Link);
diff --git a/src/main/java/org/olat/modules/ceditor/ui/ImageRunController.java b/src/main/java/org/olat/modules/ceditor/ui/ImageRunController.java
index dd8414ca4665c1d49783c70d7525738f52b95c44..257560519ef8717672f950506d70f07f1c12f3a6 100644
--- a/src/main/java/org/olat/modules/ceditor/ui/ImageRunController.java
+++ b/src/main/java/org/olat/modules/ceditor/ui/ImageRunController.java
@@ -26,7 +26,6 @@ import org.olat.core.commons.services.image.Size;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.image.ImageComponent;
-import org.olat.core.gui.components.panel.StackedPanel;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
@@ -104,8 +103,7 @@ public class ImageRunController extends BasicController implements PageRunElemen
 		}
 		
 		mainVC.setDomReplacementWrapperRequired(false);
-		StackedPanel mainPanel = putInitialPanel(mainVC);
-		mainPanel.setDomReplaceable(false);
+		putInitialPanel(mainVC);
 	}
 	
 	public void updateImageSettings(ImageSettings settings, DublinCoreMetadata meta) {
diff --git a/src/main/java/org/olat/modules/ceditor/ui/PageEditorV2Controller.java b/src/main/java/org/olat/modules/ceditor/ui/PageEditorV2Controller.java
index 0f644a53f705b4c8472a2d3637f893fe92feb251..cd0cb07a3a87e7a6c8524544d5c3274ed80c4c95 100644
--- a/src/main/java/org/olat/modules/ceditor/ui/PageEditorV2Controller.java
+++ b/src/main/java/org/olat/modules/ceditor/ui/PageEditorV2Controller.java
@@ -63,6 +63,7 @@ import org.olat.modules.ceditor.ui.event.MoveDownElementEvent;
 import org.olat.modules.ceditor.ui.event.MoveUpElementEvent;
 import org.olat.modules.ceditor.ui.event.OpenAddElementEvent;
 import org.olat.modules.ceditor.ui.event.PositionEnum;
+import org.olat.modules.ceditor.ui.event.SaveElementEvent;
 
 /**
  * 
@@ -215,6 +216,8 @@ public class PageEditorV2Controller extends BasicController {
 			doDrop(ureq, (DropToEditorEvent)event);
 		} else if(event instanceof DropToPageElementEvent) {
 			doDrop(ureq, (DropToPageElementEvent)event);
+		} else if(event instanceof SaveElementEvent) {
+			fireEvent(ureq, Event.CHANGED_EVENT);
 		}
 	}
 	
@@ -228,6 +231,8 @@ public class PageEditorV2Controller extends BasicController {
 			}
 			return true;
 		}, editorCmp, false).visitAll(ureq);
+
+		fireEvent(ureq, Event.CHANGED_EVENT);
 	}
 	
 	private void doCloseEditionEvent(UserRequest ureq, String elementId) {
diff --git a/src/main/java/org/olat/modules/ceditor/ui/component/ContentEditorFragmentComponent.java b/src/main/java/org/olat/modules/ceditor/ui/component/ContentEditorFragmentComponent.java
index de2c1e07473ac8bd45525c76a1daf776f7f4574b..337d99de699f35b79578949d560c336c55b26f26 100644
--- a/src/main/java/org/olat/modules/ceditor/ui/component/ContentEditorFragmentComponent.java
+++ b/src/main/java/org/olat/modules/ceditor/ui/component/ContentEditorFragmentComponent.java
@@ -46,6 +46,7 @@ import org.olat.modules.ceditor.ui.event.MoveDownElementEvent;
 import org.olat.modules.ceditor.ui.event.MoveUpElementEvent;
 import org.olat.modules.ceditor.ui.event.OpenAddElementEvent;
 import org.olat.modules.ceditor.ui.event.PositionEnum;
+import org.olat.modules.ceditor.ui.event.SaveElementEvent;
 
 /**
  * 
@@ -147,6 +148,7 @@ public class ContentEditorFragmentComponent extends FormBaseComponentImpl implem
 					break;
 				case "save_element":
 					doCloseEditFragment();
+					fireEvent(ureq, new SaveElementEvent(this));
 					break;
 				case "delete_element":
 					fireEvent(ureq, new DeleteElementEvent(this));
@@ -209,6 +211,7 @@ public class ContentEditorFragmentComponent extends FormBaseComponentImpl implem
 			((PageElementEditorController)editorPart).setEditMode(false);
 		}
 		setDirty(true);
+		
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/ceditor/ui/event/SaveElementEvent.java b/src/main/java/org/olat/modules/ceditor/ui/event/SaveElementEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0bae30834a09a828f5887c52352e56a6324e881
--- /dev/null
+++ b/src/main/java/org/olat/modules/ceditor/ui/event/SaveElementEvent.java
@@ -0,0 +1,48 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.ceditor.ui.event;
+
+import org.olat.core.gui.control.Event;
+import org.olat.modules.ceditor.ui.component.ContentEditorFragment;
+
+/**
+ * 
+ * Initial date: 24 juin 2020<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SaveElementEvent extends Event {
+
+	private static final long serialVersionUID = 3300706246686745496L;
+
+	public static final String SAVE_ELEMENT = "ce-save-element";
+	
+	private final ContentEditorFragment component;
+	
+	public SaveElementEvent(ContentEditorFragment component) {
+		super(SAVE_ELEMENT);
+		this.component = component;
+	}
+	
+	public ContentEditorFragment getComponent() {
+		return component;
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
index f3459050968be28ff654e1112edf675673cc2f1d..46f5889befb4af3726e60ff9267fee01e200a328 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_de.properties
@@ -166,6 +166,7 @@ type.lectures.enabled.enabled=Ein
 type.lectures.enabled.disabled=Aus
 type.lectures.enabled.inherited=Vererbt aus Typ ({0})
 unoverride.member=\u00DCbergehen anhalten
+user.notfound=$org.olat.group.ui.main\:user.notfound
 warning.atleastone.member=Sie m\u00FCssen mindestens ein Mitglied w\u00E4hlen.
 warning.atleastone.resource=Sie m\u00FCssen mindestens ein Kurs w\u00E4hlen.
 warning.curriculum.element.type.deleted=Dieser Typ ist nicht mehr verf\u00FCgbar.
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
index e2f3c8abcd7fbb9b00e14998f4d01ea8f08598b6..8d8b2ba8da8b9671e0363fe782a1c9dd573cbc02 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_en.properties
@@ -166,6 +166,7 @@ type.lectures.enabled.enabled=On
 type.lectures.enabled.inherited=Inherited from type ({0})
 type.lectures.enabled.on=On
 unoverride.member=Stop override
+user.notfound=$org.olat.group.ui.main\:user.notfound
 warning.atleastone.member=You need to choose at least one member.
 warning.atleastone.resource=You need to choose at least one course.
 warning.curriculum.element.type.deleted=This type is no longer available.
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_fr.properties
index 2d1fd10e619a9854b61604a2257f923ffc21eda2..b6a98417bae29b3f411c30d733fc512f1ba8f3ea 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_fr.properties
@@ -166,6 +166,7 @@ type.lectures.enabled.enabled=activ\u00E9
 type.lectures.enabled.inherited=h\u00E9rit\u00E9 du type (activ\u00E9)
 type.lectures.enabled.on=Activ\u00E9
 unoverride.member=Retour en gestion ext\u00E9rieur
+user.notfound=$org.olat.group.ui.main\:user.notfound
 warning.atleastone.member=Vous devez choisir au moins une personne.
 warning.atleastone.resource=Vous devez choisir au moins un cours.
 warning.curriculum.element.type.deleted=Le type n'est plus disponible.
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_it.properties
index 27a59be09e006d2a87ff612fe8bed14359c81e13..efa760041eb3fc7c1b7e91b858e37f9c5d7fa4bb 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_it.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_it.properties
@@ -1,4 +1,5 @@
 #Wed Feb 01 13:37:33 CET 2017
 override.member=Scavalcare gestione esterna
 unoverride.member=Annullare scavalcamento
+user.notfound=$org.olat.group.ui.main\:user.notfound
 zoom=<i class='o_icon o_icon-fw o_icon_enlarge'> </i>
diff --git a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_pt_BR.properties
index 51568d96c62d8a11833c2b98d0e2237d12d12665..a43567dec889db7d0ab530dc554c7037afd258e3 100644
--- a/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_pt_BR.properties
+++ b/src/main/java/org/olat/modules/curriculum/ui/_i18n/LocalStrings_pt_BR.properties
@@ -166,6 +166,7 @@ type.lectures.enabled.enabled=Lig
 type.lectures.enabled.inherited=Herdado do tipo ({0})
 type.lectures.enabled.on=Lig
 unoverride.member=Interromper altera\u00E7\u00E3o/sobreposi\u00E7\u00E3o
+user.notfound=$org.olat.group.ui.main\:user.notfound
 warning.atleastone.member=Voc\u00EA precisa escolher pelo menos um membro.
 warning.atleastone.resource=Voc\u00EA precisa escolher pelo menos um curso.
 warning.curriculum.element.type.deleted=Este tipo n\u00E3o est\u00E1 mais dispon\u00EDvel.
diff --git a/src/main/java/org/olat/modules/docpool/webdav/DocumentPoolWebDAVMergeSource.java b/src/main/java/org/olat/modules/docpool/webdav/DocumentPoolWebDAVMergeSource.java
index 815710df72776c6da1fbcc266194cf41598641de..b6a137ed7567b6b6b36094f2469e9040c7d28750 100644
--- a/src/main/java/org/olat/modules/docpool/webdav/DocumentPoolWebDAVMergeSource.java
+++ b/src/main/java/org/olat/modules/docpool/webdav/DocumentPoolWebDAVMergeSource.java
@@ -129,7 +129,6 @@ class DocumentPoolWebDAVMergeSource extends WebDAVMergeSource {
 		VFSContainer documents = taxonomyService.getDocumentsLibrary(taxonomy);
 		SubscriptionContext subscriptionCtx = notificationsHandler.getTaxonomyDocumentsLibrarySubscriptionContext();
 		TaxonomyVFSSecurityCallback secCallback = new TaxonomyVFSSecurityCallback(taxonomyNode, subscriptionCtx);
-		System.out.println(taxonomyNode.isCanWrite());
 		documents.setLocalSecurityCallback(secCallback);
 		return new NamedContainerImpl(taxonomyNode.getTitle(), documents);
 	}
diff --git a/src/main/java/org/olat/modules/forms/ui/RubricEditorController.java b/src/main/java/org/olat/modules/forms/ui/RubricEditorController.java
index d333eabcdaeee3a917c9d1d59dbebb62131d15b4..8deaafaa705422bf5b92f1b61ebc2ad47e0470e4 100644
--- a/src/main/java/org/olat/modules/forms/ui/RubricEditorController.java
+++ b/src/main/java/org/olat/modules/forms/ui/RubricEditorController.java
@@ -238,14 +238,14 @@ public class RubricEditorController extends FormBasicController implements PageE
 		String insufficientLowerBound = rubric.getLowerBoundInsufficient() != null
 				? String.valueOf(rubric.getLowerBoundInsufficient())
 				: null;
-		lowerBoundInsufficientEl = uifactory.addTextElement("rubric.lower.bound.insufficient", 4,
+		lowerBoundInsufficientEl = uifactory.addTextElement("rubric.lower.bound.insufficient", null, 4,
 				insufficientLowerBound, insufficientCont);
 		lowerBoundInsufficientEl.setDomReplacementWrapperRequired(false);
 		lowerBoundInsufficientEl.setDisplaySize(4);
 		String insufficientUpperBound = rubric.getUpperBoundInsufficient() != null
 				? String.valueOf(rubric.getUpperBoundInsufficient())
 				: null;
-		upperBoundInsufficientEl = uifactory.addTextElement("rubric.upper.bound.insufficient", 4,
+		upperBoundInsufficientEl = uifactory.addTextElement("rubric.upper.bound.insufficient", null, 4,
 				insufficientUpperBound, insufficientCont);
 		upperBoundInsufficientEl.setDomReplacementWrapperRequired(false);
 		upperBoundInsufficientEl.setDisplaySize(4);
@@ -260,12 +260,12 @@ public class RubricEditorController extends FormBasicController implements PageE
 		neutralCont.setHelpTextKey("rubric.rating.help", new String[] { translate("rubric.neutral")} );
 		String neutralLowerBound = rubric.getLowerBoundNeutral() != null ? String.valueOf(rubric.getLowerBoundNeutral())
 				: null;
-		lowerBoundNeutralEl = uifactory.addTextElement("rubric.lower.bound.neutral", 4, neutralLowerBound, neutralCont);
+		lowerBoundNeutralEl = uifactory.addTextElement("rubric.lower.bound.neutral", null, 4, neutralLowerBound, neutralCont);
 		lowerBoundNeutralEl.setDomReplacementWrapperRequired(false);
 		lowerBoundNeutralEl.setDisplaySize(4);
 		String neutralUpperBound = rubric.getUpperBoundNeutral() != null ? String.valueOf(rubric.getUpperBoundNeutral())
 				: null;
-		upperBoundNeutralEl = uifactory.addTextElement("rubric.upper.bound.neutral", 4, neutralUpperBound, neutralCont);
+		upperBoundNeutralEl = uifactory.addTextElement("rubric.upper.bound.neutral", null, 4, neutralUpperBound, neutralCont);
 		upperBoundNeutralEl.setDomReplacementWrapperRequired(false);
 		upperBoundNeutralEl.setDisplaySize(4);
 
@@ -280,14 +280,14 @@ public class RubricEditorController extends FormBasicController implements PageE
 		String sufficientLowerBound = rubric.getLowerBoundSufficient() != null
 				? String.valueOf(rubric.getLowerBoundSufficient())
 				: null;
-		lowerBoundSufficientEl = uifactory.addTextElement("rubric.lower.bound.sufficient", 4, sufficientLowerBound,
+		lowerBoundSufficientEl = uifactory.addTextElement("rubric.lower.bound.sufficient", null, 4, sufficientLowerBound,
 				sufficientCont);
 		lowerBoundSufficientEl.setDomReplacementWrapperRequired(false);
 		lowerBoundSufficientEl.setDisplaySize(4);
 		String sufficientUpperBound = rubric.getUpperBoundSufficient() != null
 				? String.valueOf(rubric.getUpperBoundSufficient())
 				: null;
-		upperBoundSufficientEl = uifactory.addTextElement("rubric.upper.bound.sufficient", 4, sufficientUpperBound,
+		upperBoundSufficientEl = uifactory.addTextElement("rubric.upper.bound.sufficient", null, 4, sufficientUpperBound,
 				sufficientCont);
 		upperBoundSufficientEl.setDomReplacementWrapperRequired(false);
 		upperBoundSufficientEl.setDisplaySize(4);
@@ -490,7 +490,7 @@ public class RubricEditorController extends FormBasicController implements PageE
 		
 		// weight
 		String weight = slider.getWeight() != null? slider.getWeight().toString(): "";
-		TextElement weightEl = uifactory.addTextElement("weight" + count.incrementAndGet(), 4, weight, flc);
+		TextElement weightEl = uifactory.addTextElement("weight" + count.incrementAndGet(), null, 4, weight, flc);
 		weightEl.setElementCssClass("o_slider_weight");
 		weightEl.setExampleKey("slider.weight", null);
 		weightEl.setEnabled(!restrictedEditWeight);
diff --git a/src/main/java/org/olat/registration/RegistrationController.java b/src/main/java/org/olat/registration/RegistrationController.java
index c90ec44ae5aaf21c5d964b168eb78922b61bc0ea..fa224b77d6fe6dd45ef410e2a1b96392ab44035c 100644
--- a/src/main/java/org/olat/registration/RegistrationController.java
+++ b/src/main/java/org/olat/registration/RegistrationController.java
@@ -386,7 +386,6 @@ public class RegistrationController extends BasicController implements Activatea
 			if(!htmlBody) {
 				body += SEPARATOR + translate("reg.wherefrom", whereFromAttrs);
 			}
-			System.out.println(body);
 			
 			if(sendMessage(email, translate("reg.subject"), body)) {
 				showInfo("email.sent");
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_15_0_3.java b/src/main/java/org/olat/upgrade/OLATUpgrade_15_0_3.java
index bfb54e6e64abdbab13ff0be78c98c081d964e716..a7923a0977f66649c2a132ab12ef96cc3a6709ef 100644
--- a/src/main/java/org/olat/upgrade/OLATUpgrade_15_0_3.java
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_15_0_3.java
@@ -49,9 +49,14 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 
 	private static final Logger log = Tracing.createLoggerFor(OLATUpgrade_15_0_3.class);
 	
+	private static final int BATCH_SIZE = 50000;
+	
 	private static final String VERSION = "OLAT_15.0.3";
 	private static final String ASSESSMENT_LAST_ATTEMPTS = "ASSESSMENT LAST ATTEMPTS";
 	
+	private final AtomicInteger migrationLogCounter = new AtomicInteger(0);
+	private final AtomicInteger migrationEntryCounter = new AtomicInteger(0);
+	
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -97,10 +102,35 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 		boolean allOk = true;
 		if (!uhd.getBooleanDataValue(ASSESSMENT_LAST_ATTEMPTS)) {
 			try {
-				List<LoggingObject> loggedLaunches = getLoggedLastAttempts();
-				migrateLoggedAttempts(loggedLaunches);
-				List<AssessmentEntry> entries = getEmptyLastAttempts();
-				migrateEmptyLastAttempts(entries);
+				int counter = 0;
+				List<LoggingObject> loggedLaunches;
+				do {
+					loggedLaunches = getLoggedLastAttempts(counter, BATCH_SIZE);
+					migrateLoggedAttempts(loggedLaunches);
+					counter += loggedLaunches.size();
+					log.info(Tracing.M_AUDIT, "Log launch processed: {}, total processed ({})", loggedLaunches.size(), counter);
+					dbInstance.commitAndCloseSession();
+				} while(loggedLaunches.size() == BATCH_SIZE);
+
+				log.info(Tracing.M_AUDIT, "Log launch processing successful, total processed: {}", migrationLogCounter);
+			} catch (Exception e) {
+				log.error("", e);
+				allOk = false;
+			}
+			
+			try {
+				
+				int counter = 0;
+				List<AssessmentEntry> entries;
+				do {
+					entries = getEmptyLastAttempts(counter, BATCH_SIZE);
+					migrateEmptyLastAttempts(entries);
+					counter += entries.size();
+					log.info(Tracing.M_AUDIT, "Empty last attempts processed: {}, total processed ({})", entries.size(), counter);
+					dbInstance.commitAndCloseSession();
+				} while(entries.size() == BATCH_SIZE);
+				
+				log.info("Assessment entry last attemps processing successful, total processed: {}", migrationEntryCounter);
 				log.info("Assessment last modified migrated.");
 			} catch (Exception e) {
 				log.error("", e);
@@ -112,7 +142,7 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 		return allOk;
 	}
 
-	private List<LoggingObject> getLoggedLastAttempts() {
+	private List<LoggingObject> getLoggedLastAttempts(int firstResult, int maxResults) {
 		QueryBuilder sb = new QueryBuilder();
 		sb.append("select log");
 		sb.append("  from loggingobject log");
@@ -121,29 +151,29 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 		
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), LoggingObject.class)
+				.setFirstResult(firstResult)
+				.setMaxResults(maxResults)
 				.getResultList();
 	}
 	
 	private void migrateLoggedAttempts(List<LoggingObject> loggedAttempts) {
-		log.info("Migraton of {} assessment last attemps (log table) started.", loggedAttempts.size());
-		
 		Map<Long, Identity> identityCache = new HashMap<>();
 		Map<Long, RepositoryEntry> entryCache = new HashMap<>();
-		AtomicInteger migrationCounter = new AtomicInteger(0);
+		
 		for (LoggingObject loggingObject : loggedAttempts) {
 			try {
 				migrateLastAttempt(loggingObject, identityCache, entryCache);
-				migrationCounter.incrementAndGet();
+				migrationLogCounter.incrementAndGet();
 			} catch (Exception e) {
 				log.warn("Assessment last attempt (log table) not migrated. Id={}", loggingObject.getKey());
 			}
-			if(migrationCounter.get() % 25 == 0) {
+			if(migrationLogCounter.get() % 25 == 0) {
 				dbInstance.commitAndCloseSession();
 			} else {
 				dbInstance.commit();
 			}
-			if(migrationCounter.get() % 100 == 0) {
-				log.info("Assessment: num. of last attempts (log table): {}", migrationCounter);
+			if(migrationLogCounter.get() % 100 == 0) {
+				log.info("Assessment: num. of last attempts (log table): {}", migrationLogCounter);
 			}
 		}
 	}
@@ -196,20 +226,20 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 		}
 	}
 	
-	private List<AssessmentEntry> getEmptyLastAttempts() {
+	private List<AssessmentEntry> getEmptyLastAttempts(int firstResult, int maxResults) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select data from assessmententry data");
 		sb.append(" where data.attempts > 0 and lastAttempt is null");
-
+		sb.append(" order by data.key asc");
+		
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), AssessmentEntry.class)
+				.setFirstResult(firstResult)
+				.setMaxResults(maxResults)
 				.getResultList();
 	}
 
 	private void migrateEmptyLastAttempts(List<AssessmentEntry> entries) {
-		log.info("Migraton of {} assessment last attemps (empty) started.", entries.size());
-		
-		AtomicInteger migrationCounter = new AtomicInteger(0);
 		for (AssessmentEntry assessmentEntry: entries) {
 			try {
 				if (assessmentEntry.getAttempts() != null && assessmentEntry.getAttempts().intValue() > 0 && assessmentEntry.getLastAttempt() == null) {
@@ -219,17 +249,17 @@ public class OLATUpgrade_15_0_3 extends OLATUpgrade {
 					assessmentEntry.setLastAttempt(lastAttemps);
 					assessmentService.updateAssessmentEntry(assessmentEntry);
 				}
-				migrationCounter.incrementAndGet();
+				migrationEntryCounter.incrementAndGet();
 			} catch (Exception e) {
 				log.warn("Assessment last attempt (empty) not migrated. Id={}", assessmentEntry.getKey());
 			}
-			if(migrationCounter.get() % 25 == 0) {
+			if(migrationEntryCounter.get() % 25 == 0) {
 				dbInstance.commitAndCloseSession();
 			} else {
 				dbInstance.commit();
 			}
-			if(migrationCounter.get() % 100 == 0) {
-				log.info("Assessment: num. of last attempts (empty): {}", migrationCounter);
+			if(migrationEntryCounter.get() % 100 == 0) {
+				log.info("Assessment: num. of last attempts (empty): {}", migrationEntryCounter);
 			}
 		}	
 	}
diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
index ad52d24292635fa227d77be22b4bbdf4ed0ec8e7..edd4476ac5521cd046fc0c30f275f76c7ab6d739 100644
--- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
+++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
@@ -224,6 +224,10 @@
 					<constructor-arg index="0" value="OLAT_14.2.7" />
 					<property name="alterDbStatements" value="alter_14_2_x_to_14_2_7.sql" />
 				</bean>
+				<bean id="database_upgrade_14_2_13" class="org.olat.upgrade.DatabaseUpgrade">
+					<constructor-arg index="0" value="OLAT_14.2.13" />
+					<property name="alterDbStatements" value="alter_14_2_x_to_14_2_13.sql" />
+				</bean>
 				<bean id="database_upgrade_15_pre_0" class="org.olat.upgrade.DatabaseUpgrade">
 					<constructor-arg index="0" value="OLAT_15.pre.0" />
 					<property name="alterDbStatements" value="alter_14_2_x_to_15_pre_0.sql" />
diff --git a/src/main/resources/database/mysql/alter_14_2_x_to_14_2_13.sql b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_13.sql
new file mode 100644
index 0000000000000000000000000000000000000000..35ad20bf9472a62d158c4777d44bfc6145111bb6
--- /dev/null
+++ b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_13.sql
@@ -0,0 +1,4 @@
+alter table o_as_mode_course modify a_elements text(32000);
+alter table o_as_mode_course modify a_ips text(32000);
+alter table o_as_mode_course modify a_safeexambrowserkey text(32000);
+
diff --git a/src/main/resources/database/oracle/alter_14_2_x_to_14_2_13.sql b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_13.sql
new file mode 100644
index 0000000000000000000000000000000000000000..c7d4908afd6e9737c97a08f650f9a492a4abe103
--- /dev/null
+++ b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_13.sql
@@ -0,0 +1,4 @@
+alter table o_as_mode_course modify a_elements varchar(32000);
+alter table o_as_mode_course modify a_ips varchar(32000);
+alter table o_as_mode_course modify a_safeexambrowserkey varchar(32000);
+
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index 337d43a491520dbb4a8db00162bce73b27fec42e..1079566432d571080112c0470ecca8522f102df0 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -1382,12 +1382,12 @@ create table o_as_mode_course (
    a_end_with_followuptime date not null,
    a_targetaudience varchar2(16 char),
    a_restrictaccesselements number default 0 not null,
-   a_elements varchar2(2048 char),
+   a_elements varchar2(32000 char),
    a_start_element varchar2(64 char),
    a_restrictaccessips number default 0 not null,
-   a_ips varchar2(2048 char),
+   a_ips varchar2(32000 char),
    a_safeexambrowser number default 0 not null,
-   a_safeexambrowserkey varchar2(2048 char),
+   a_safeexambrowserkey varchar2(32000 char),
    a_safeexambrowserhint clob,
    a_applysettingscoach number default 0 not null,
    fk_entry number(20) not null,
diff --git a/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_13.sql b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_13.sql
new file mode 100644
index 0000000000000000000000000000000000000000..ec9c158de681214c035670d1e96afe09c73f1027
--- /dev/null
+++ b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_13.sql
@@ -0,0 +1,4 @@
+alter table o_as_mode_course alter column a_elements type varchar(32000);
+alter table o_as_mode_course alter column a_ips type varchar(32000);
+alter table o_as_mode_course alter column a_safeexambrowserkey type varchar(32000);
+
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index 8d32312bf29687312912335a9e6b027682523ecc..83f9c985cfc37312e1a5cce137060c826dfd621a 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -1342,12 +1342,12 @@ create table o_as_mode_course (
    a_end_with_followuptime timestamp not null,
    a_targetaudience varchar(16),
    a_restrictaccesselements bool not null default false,
-   a_elements varchar(2048),
+   a_elements varchar(32000),
    a_start_element varchar(64),
    a_restrictaccessips bool not null default false,
-   a_ips varchar(2048),
+   a_ips varchar(32000),
    a_safeexambrowser bool not null default false,
-   a_safeexambrowserkey varchar(2048),
+   a_safeexambrowserkey varchar(32000),
    a_safeexambrowserhint text,
    a_applysettingscoach bool not null default false,
    fk_entry int8 not null,
diff --git a/src/main/resources/infinispan-config.xml b/src/main/resources/infinispan-config.xml
index 327897d29940caa5b3877c1d2f0758c24a67a6bd..9bffb517b65388aa3ef58123dbfa86a2df2cbb45 100644
--- a/src/main/resources/infinispan-config.xml
+++ b/src/main/resources/infinispan-config.xml
@@ -80,6 +80,24 @@
 			<expiration max-idle="180000" interval="15000" />
 		</local-cache>
 		
+		<local-cache name="QTIWorks@assessmentTests" simple-cache="true" statistics="true" statistics-available="true">
+			<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" />
+			<transaction mode="NONE" auto-commit="true" />
+			<memory>
+				<object size="200" strategy="REMOVE" />
+			</memory>
+			<expiration max-idle="900000" interval="15000" />
+		</local-cache>
+		
+		<local-cache name="QTIWorks@xsltStylesheets" simple-cache="true" statistics="true" statistics-available="true">
+			<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" />
+			<transaction mode="NONE" auto-commit="true" />
+			<memory>
+				<object size="200" strategy="REMOVE" />
+			</memory>
+			<expiration max-idle="1800000" interval="15000" />
+		</local-cache>
+		
 		<local-cache name="WebDAVManager@webdav" simple-cache="true" statistics="true" statistics-available="true">
 			<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" />
 			<transaction mode="NONE" auto-commit="true" />
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 54d55dee5009c7eddff754241b3910808cc65d4a..32fa39213a6d7d87b3ec1c76bc5735188fcb5d21 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1268,8 +1268,8 @@ ldap.learningResourceManagerRoleValue=
 # Build properties
 #####
 application.name=OpenOlat
-build.version=15.0.4
-build.identifier=openolat1504-dev
+build.version=15.0.5
+build.identifier=openolat1505-dev
 build.repo.revision=local-devel
 
 #####
diff --git a/src/test/java/org/olat/selenium/page/course/CoursePageFragment.java b/src/test/java/org/olat/selenium/page/course/CoursePageFragment.java
index d229a6416a04bbf223700dfcb385cc265c0fb43f..f696bb962562d5441ed369198096f889adf13b9b 100644
--- a/src/test/java/org/olat/selenium/page/course/CoursePageFragment.java
+++ b/src/test/java/org/olat/selenium/page/course/CoursePageFragment.java
@@ -64,8 +64,13 @@ public class CoursePageFragment {
 	}
 	
 	public static CoursePageFragment getCourse(WebDriver browser, URL deploymentUrl, CourseVO course) {
-		browser.navigate().to(deploymentUrl.toExternalForm() + "url/RepositoryEntry/" + course.getRepoEntryKey());
-		OOGraphene.waitElement(courseRun, browser);
+		browser.get(deploymentUrl.toExternalForm() + "url/RepositoryEntry/" + course.getRepoEntryKey());
+		try {
+			OOGraphene.waitElementSlowly(courseRun, 10, browser);
+		} catch (Exception e) {
+			OOGraphene.takeScreenshot("GetcourseByGet", browser);
+			throw e;
+		}
 		return new CoursePageFragment(browser);
 	}