diff --git a/src/main/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAO.java b/src/main/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAO.java
index fb1d2f52f307924698aa95a7d5baa7b160f59024..3a111dc18fbda0af780ff0c27b7d15e910743d48 100644
--- a/src/main/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAO.java
+++ b/src/main/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAO.java
@@ -110,14 +110,16 @@ public class PersistentTaskDAO {
 	}
 	
 	public PersistentTask loadTaskById(Long taskKey) {
-		PersistentTask task = dbInstance.getCurrentEntityManager()
-				.find(PersistentTask.class, taskKey);
-		return task;
+		return dbInstance.getCurrentEntityManager().find(PersistentTask.class, taskKey);
 	}
 	
-	public PersistentTask pickTaskForRun(Long taskKey) {
-		PersistentTask task = dbInstance.getCurrentEntityManager()
-				.find(PersistentTask.class, taskKey, LockModeType.PESSIMISTIC_WRITE);
+	public synchronized PersistentTask pickTaskForRun(PersistentTask task) {
+		if(task != null) {// remove it from the cache
+			dbInstance.getCurrentEntityManager().detach(task);
+		}
+
+		task = dbInstance.getCurrentEntityManager()
+				.find(PersistentTask.class, task.getKey(), LockModeType.PESSIMISTIC_WRITE);
 		if(task != null) {
 			if(TaskStatus.newTask.equals(task.getStatus())) {
 				task.setStatus(TaskStatus.inWork);
@@ -125,9 +127,15 @@ public class PersistentTaskDAO {
 				task.setExecutorBootId(WebappHelper.getBootId());
 				task = dbInstance.getCurrentEntityManager().merge(task);
 			} else if(TaskStatus.inWork.equals(task.getStatus())) {
-				task.setExecutorNode(Integer.toString(WebappHelper.getNodeId()));
-				task.setExecutorBootId(WebappHelper.getBootId());
-				task = dbInstance.getCurrentEntityManager().merge(task);
+				if(WebappHelper.getBootId().equals(task.getExecutorBootId())) {
+					// someone has already pick it
+					task = null;
+				} else {
+					// reboot of a task in work
+					task.setExecutorNode(Integer.toString(WebappHelper.getNodeId()));
+					task.setExecutorBootId(WebappHelper.getBootId());
+					task = dbInstance.getCurrentEntityManager().merge(task);
+				}
 			} else if(TaskStatus.edition.equals(task.getStatus())) {
 				task = null;
 			}
diff --git a/src/main/java/org/olat/core/commons/services/taskexecutor/model/PersistentTaskRunnable.java b/src/main/java/org/olat/core/commons/services/taskexecutor/model/PersistentTaskRunnable.java
index e773c109e6d0cb99139374a01b4e0eedf0cc472e..3340081af96c1b25ec121416befc77e6971f06d0 100644
--- a/src/main/java/org/olat/core/commons/services/taskexecutor/model/PersistentTaskRunnable.java
+++ b/src/main/java/org/olat/core/commons/services/taskexecutor/model/PersistentTaskRunnable.java
@@ -41,17 +41,20 @@ public class PersistentTaskRunnable implements Runnable {
 
 	@Override
 	public void run() {
-		PersistentTask task = null;
 		PersistentTaskDAO taskDao = CoreSpringFactory.getImpl(PersistentTaskDAO.class);
+		PersistentTask task = null;
 		try {
-			task = taskDao.pickTaskForRun(taskKey);
+			task = taskDao.loadTaskById(taskKey);
 			if(task != null) {
-				Runnable runnable = taskDao.deserializeTask(task);
-				if(runnable instanceof TaskAwareRunnable) {
-					((TaskAwareRunnable)runnable).setTask(task);
+				task = taskDao.pickTaskForRun(task);
+				if(task != null) {
+					Runnable runnable = taskDao.deserializeTask(task);
+					if(runnable instanceof TaskAwareRunnable) {
+						((TaskAwareRunnable)runnable).setTask(task);
+					}
+					runnable.run();
+					taskDao.taskDone(task);
 				}
-				runnable.run();
-				taskDao.taskDone(task);
 			}
 			DBFactory.getInstance().commitAndCloseSession();
 		} catch (Throwable e) {
diff --git a/src/main/java/org/olat/core/util/pdf/PdfDocument.java b/src/main/java/org/olat/core/util/pdf/PdfDocument.java
index 221d8dc3a32513d55269358ad9f6ac1a7510f66a..5f38d9177d6fca6e4ae2a9ea5e55aee4a3579ca3 100644
--- a/src/main/java/org/olat/core/util/pdf/PdfDocument.java
+++ b/src/main/java/org/olat/core/util/pdf/PdfDocument.java
@@ -149,6 +149,12 @@ public class PdfDocument {
         while (text.length() > 0) {
             int spaceIndex = text.indexOf(' ', lastSpace + 1);
             if (spaceIndex < 0) {
+                float size = getStringWidth(text, textFont, fontSize);
+                if (size > paragraphWidth) {
+                	String subString = text.substring(0, lastSpace);
+                	lines.add(subString.trim());
+                	text = text.substring(lastSpace).trim();
+                }
                 lines.add(text);
                 text = "";
             } else {
diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
index e4e557d4b7e02266004ddb3e29db030d6f505310..1af2eb150e8a14722c7b55f1a57f9830b0a951cf 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
+++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
@@ -79,7 +79,7 @@ public class MembersCourseNodeRunController extends BasicController {
 		RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry();
 
 		List<Identity> owners;
-		List<Identity> coaches = new ArrayList<>();
+		List<Identity> coaches;
 		List<Identity> participants = new ArrayList<>();
 
 		boolean showOwners = config.getBooleanSafe(MembersCourseNode.CONFIG_KEY_SHOWOWNER);
@@ -105,8 +105,10 @@ public class MembersCourseNodeRunController extends BasicController {
 						MembersCourseNode.CONFIG_KEY_COACHES_CUR_ELEMENT)) {
 			
 			CourseGroupManager cgm = courseEnv.getCourseGroupManager();
-			MembersHelpers.addCoaches(config, cgm, businessGroupService, coaches);
+			coaches = MembersHelpers.getCoaches(config, cgm, businessGroupService);
 			showCoaches = true;
+		} else {
+			coaches = Collections.emptyList();
 		}
 		
 		boolean showParticipants = false;
@@ -115,8 +117,10 @@ public class MembersCourseNodeRunController extends BasicController {
 						MembersCourseNode.CONFIG_KEY_PARTICIPANTS_CUR_ELEMENT)) {
 			
 			CourseGroupManager cgm = courseEnv.getCourseGroupManager();
-			MembersHelpers.addParticipants(config, cgm, businessGroupService, participants);
+			participants = MembersHelpers.getParticipants(config, cgm, businessGroupService);
 			showParticipants = true;
+		} else {
+			participants = Collections.emptyList();
 		}
 		
 		Map<Long,CurriculumMemberInfos> curriculumInfos = null;
diff --git a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
index cad47689d7d41d3a20f3d062204f283eda01c77d..a1fc4cb1d99c022f25c59c4dc36b35502eb16781 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
+++ b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
@@ -21,6 +21,7 @@ package org.olat.course.nodes.members;
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -44,17 +45,33 @@ public class MembersHelpers {
 	private MembersHelpers() {
 		// CANNOT CREATE
 	}
+	
+	private static void deduplicateList(List<Identity> list) {
+		if(list == null || list.size() < 2) return;
+		
+		Set<Identity> deduplicates = new HashSet<>();
+		for(Iterator<Identity> it=list.iterator(); it.hasNext(); ) {
+			Identity identity = it.next();
+			if(deduplicates.contains(identity)) {
+				it.remove();
+			} else {
+				deduplicates.add(identity);
+			}
+		}
+	}
 
 	// -----------------------------------------------------
 	
 	public static List<Identity> getOwners(RepositoryService repositoryService, RepositoryEntry courseRepositoryEntry) {
-		return repositoryService.getMembers(courseRepositoryEntry, RepositoryEntryRelationType.all, GroupRoles.owner.name());
+		List<Identity> owners = repositoryService.getMembers(courseRepositoryEntry, RepositoryEntryRelationType.all, GroupRoles.owner.name());
+		deduplicateList(owners);
+		return owners;
 	}
 
 	// -----------------------------------------------------
 
-	public static void addCoaches(ModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
-	
+	public static List<Identity> getCoaches(ModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs) {
+		List<Identity> list = new ArrayList<>();
 		if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_COACHES_GROUP)) {
 			String coachGroupNames = moduleConfiguration.getStringValue(MembersCourseNode.CONFIG_KEY_COACHES_GROUP);
 			List<Long> coachGroupKeys = moduleConfiguration.getList(MembersCourseNode.CONFIG_KEY_COACHES_GROUP_ID, Long.class);
@@ -84,28 +101,31 @@ public class MembersHelpers {
 		if(moduleConfiguration.anyTrue(MembersCourseNode.CONFIG_KEY_COACHES_ALL)) {
 			list.addAll(retrieveCoachesFromCourseGroups(cgm));
 		}
+		
+		deduplicateList(list);
+		return list;
 	}
 	
-	public static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
 		List<Identity> coaches = cgm.getCoachesFromAreas(areaKeys);
 		return new ArrayList<>(new HashSet<>(coaches));
 	}
 	
-	public static List<Identity> retrieveCoachesFromCurriculumElements(List<Long> elementKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveCoachesFromCurriculumElements(List<Long> elementKeys, CourseGroupManager cgm) {
 		List<Identity> coaches = cgm.getCoachesFromCurriculumElements(elementKeys);
 		return new ArrayList<>(new HashSet<>(coaches));
 	}
 
 	
-	public static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
 		return new ArrayList<>(new HashSet<>(cgm.getCoachesFromBusinessGroups(groupKeys)));
 	}
 	
-	public static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) {
+	private static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) {
 		return cgm.getCoaches();
 	}
 
-	public static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) {
+	private static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) {
 		Set<Identity> uniq = new HashSet<>();
 		uniq.addAll(cgm.getCoachesFromAreas());
 		uniq.addAll(cgm.getCoachesFromBusinessGroups());
@@ -115,7 +135,8 @@ public class MembersHelpers {
 	
 	// -----------------------------------------------------
 	
-	public static void addParticipants(ModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
+	public static List<Identity> getParticipants(ModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs) {
+		List<Identity> list = new ArrayList<>();
 
 		if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP)) {
 			String participantGroupNames = moduleConfiguration.getStringValue(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP);
@@ -147,21 +168,23 @@ public class MembersHelpers {
 		if(moduleConfiguration.anyTrue(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_ALL)) {
 			list.addAll(retrieveParticipantsFromCourseGroups(cgm));
 		}
+		deduplicateList(list);
+		return list;
 	}
 	
-	public static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
 		return cgm.getParticipantsFromAreas(areaKeys);
 	}
 	
-	public static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
 		return cgm.getParticipantsFromBusinessGroups(groupKeys);
 	}
 	
-	public static List<Identity> retrieveParticipantsFromCurriculumElements(List<Long> elementKeys, CourseGroupManager cgm) {
+	private static List<Identity> retrieveParticipantsFromCurriculumElements(List<Long> elementKeys, CourseGroupManager cgm) {
 		return cgm.getParticipantsFromCurriculumElements(elementKeys);
 	}
 	
-	public static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) {
+	private static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) {
 		return cgm.getParticipants();
 	}
 	
diff --git a/src/main/java/org/olat/course/nodes/members/MembersPeekViewController.java b/src/main/java/org/olat/course/nodes/members/MembersPeekViewController.java
index e948a2fb90e1c4c118cb54c9ec3f7a78d159b5c3..7da1838091c1785a1094998f376eee8f4ba90f44 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersPeekViewController.java
+++ b/src/main/java/org/olat/course/nodes/members/MembersPeekViewController.java
@@ -127,14 +127,18 @@ public class MembersPeekViewController extends BasicController {
 			owners = new ArrayList<>();
 		}
 		
-		List<Identity> coaches = new ArrayList<>();
+		List<Identity> coaches;
 		if(withCoaches) {
-			MembersHelpers.addCoaches(config, cgm, businessGroupService, coaches);
+			coaches = MembersHelpers.getCoaches(config, cgm, businessGroupService);
+		} else {
+			coaches = new ArrayList<>();
 		}
 		
-		List<Identity> participants = new ArrayList<>();
+		List<Identity> participants;
 		if(withParticipants) {
-			MembersHelpers.addParticipants(config, cgm, businessGroupService, participants);
+			participants = MembersHelpers.getParticipants(config, cgm, businessGroupService);
+		} else {
+			participants = new ArrayList<>();
 		}
 		
 		MembersCourseNodeConfiguration nodeConfig = (MembersCourseNodeConfiguration)CourseNodeFactory.getInstance().getCourseNodeConfiguration("cmembers");
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
index 2afe7f4343e22ca7a1e2ca9c3860a783f8c94e9f..0233ba9be4bd8a056db26849bca425ee0291e466 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
@@ -30,6 +30,9 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.vfs.VFSContainer;
@@ -51,6 +54,7 @@ import org.olat.ims.qti21.model.xml.interactions.UploadAssessmentItemBuilder;
 import org.olat.ims.qti21.ui.AssessmentItemDisplayController;
 import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
 import org.olat.ims.qti21.ui.editor.events.DetachFromPoolEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent.SelectionTarget;
 import org.olat.ims.qti21.ui.editor.interactions.ChoiceScoreController;
 import org.olat.ims.qti21.ui.editor.interactions.DrawingEditorController;
 import org.olat.ims.qti21.ui.editor.interactions.EssayEditorController;
@@ -83,7 +87,7 @@ import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentItemEditorController extends BasicController {
+public class AssessmentItemEditorController extends BasicController implements Activateable2 {
 
 	private final AssessmentItemRef itemRef;
 	private final ResolvedAssessmentItem resolvedAssessmentItem;
@@ -521,6 +525,29 @@ public class AssessmentItemEditorController extends BasicController {
 		return hottextItemBuilder;
 	}
 
+	@Override
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
+		if(entries == null || entries.isEmpty()) return;
+		
+		String type = entries.get(0).getOLATResourceable().getResourceableTypeName();
+		if(SelectionTarget.description.name().equalsIgnoreCase(type) || SelectionTarget.expert.name().equalsIgnoreCase(type)) {
+			activate(ureq, itemEditor);
+		} else if(SelectionTarget.description.name().equalsIgnoreCase(type) || SelectionTarget.score.name().equalsIgnoreCase(type)) {
+			activate(ureq, scoreEditor);
+		} else if(SelectionTarget.feedback.name().equalsIgnoreCase(type)) {
+			activate(ureq, feedbackEditor);
+		}
+	}
+	
+	private void activate(UserRequest ureq, Controller ctrl) {
+		if(ctrl == null) return;
+		
+		int index = tabbedPane.indexOfTab(ctrl.getInitialComponent());
+		if(index >= 0) {
+			tabbedPane.setSelectedPane(ureq, index);
+		}	
+	}
+
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		if(tabbedPane == source) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
index ddf06f62955c65ead81087c91170330f98c425ed..f00371e4df89901ac3ba6671bc174db7e956f7c8 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentSectionEditorController.java
@@ -20,6 +20,7 @@
 package org.olat.ims.qti21.ui.editor;
 
 import java.io.File;
+import java.util.List;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
@@ -29,10 +30,14 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.Util;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.ims.qti21.ui.AssessmentTestDisplayController;
 import org.olat.ims.qti21.ui.editor.events.AssessmentSectionEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent.SelectionTarget;
 
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
 
@@ -42,7 +47,7 @@ import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentSectionEditorController extends BasicController {
+public class AssessmentSectionEditorController extends BasicController implements Activateable2 {
 
 	private final TabbedPane tabbedPane;
 	private final VelocityContainer mainVC;
@@ -95,6 +100,18 @@ public class AssessmentSectionEditorController extends BasicController {
 		//
 	}
 
+	@Override
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
+		if(entries == null || entries.isEmpty()) return;
+		
+		String type = entries.get(0).getOLATResourceable().getResourceableTypeName();
+		if(SelectionTarget.description.name().equalsIgnoreCase(type) || SelectionTarget.score.name().equalsIgnoreCase(type)) {
+			tabbedPane.setSelectedPane(ureq, tabbedPane.indexOfTab(optionsCtrl.getInitialComponent()));
+		} else if(SelectionTarget.expert.name().equalsIgnoreCase(type)) {
+			tabbedPane.setSelectedPane(ureq, tabbedPane.indexOfTab(expertOptionsCtrl.getInitialComponent()));
+		}
+	}
+
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		//
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
index 6ed1ed876218d146a544fb04ca07213d052b31cf..b49f62f1ca5ff72711f1613eafdaf0773867b2f7 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
@@ -59,6 +59,7 @@ import org.olat.core.gui.control.VetoableCloseController;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.MainLayoutBasicController;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
 import org.olat.core.gui.control.generic.messages.MessageController;
 import org.olat.core.gui.control.generic.messages.MessageUIFactory;
 import org.olat.core.gui.control.generic.modal.DialogBoxController;
@@ -70,12 +71,16 @@ import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.gui.media.MediaResource;
 import org.olat.core.helpers.Settings;
 import org.olat.core.id.Roles;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.id.context.ContextEntry;
 import org.olat.core.logging.AssertException;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
 import org.olat.core.util.Formatter;
 import org.olat.core.util.Util;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.LockResult;
+import org.olat.core.util.resource.OresHelper;
+import org.olat.core.util.tree.TreeHelper;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.ims.qti21.AssessmentTestHelper;
@@ -114,6 +119,9 @@ import org.olat.ims.qti21.ui.editor.events.AssessmentSectionEvent;
 import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent;
 import org.olat.ims.qti21.ui.editor.events.AssessmentTestPartEvent;
 import org.olat.ims.qti21.ui.editor.events.DetachFromPoolEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent.SelectionTarget;
+import org.olat.ims.qti21.ui.editor.overview.AssessmentTestOverviewConfigurationController;
 import org.olat.imscp.xml.manifest.FileType;
 import org.olat.imscp.xml.manifest.ResourceType;
 import org.olat.modules.qpool.QuestionItemFull;
@@ -132,6 +140,7 @@ import uk.ac.ed.ph.jqtiplus.node.test.AbstractPart;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
+import uk.ac.ed.ph.jqtiplus.node.test.ControlObject;
 import uk.ac.ed.ph.jqtiplus.node.test.SectionPart;
 import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
@@ -160,6 +169,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 			newEssayLink, newUploadLink, newDrawingLink;
 	private Link importFromPoolLink, importFromTableLink, exportToPoolLink, exportToDocxLink;
 	private Link reloadInCacheLink, deleteLink, copyLink;
+	private Link configurationOverviewLink;
 	private final TooledStackedPanel toolbar;
 	private VelocityContainer mainVC;
 
@@ -169,6 +179,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 	private DialogBoxController confirmDeleteCtrl;
 	private StepsMainRunController importTableWizard;
 	private LayoutMain3ColsController columnLayoutCtr;
+	private AssessmentTestOverviewConfigurationController overviewConfigCtrl;
 	
 	private File unzippedDirRoot;
 	private VFSContainer unzippedContRoot;
@@ -352,6 +363,9 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		copyLink.setDomReplacementWrapperRequired(false);
 		changeItemTools.addComponent(copyLink);
 		
+		configurationOverviewLink = LinkFactory.createToolLink("configuration.overview", translate("configuration.overview"), this, "o_icon_description");
+		configurationOverviewLink.setDomReplacementWrapperRequired(false);
+		
 		// main layout
 		mainVC = createVelocityContainer("assessment_test_composer");
 		columnLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), menuTree, mainVC, "at" + testEntry.getKey());			
@@ -413,6 +427,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		toolbar.addTool(exportItemTools, Align.left);
 		toolbar.addTool(addItemTools, Align.left);
 		toolbar.addTool(changeItemTools, Align.left);
+		toolbar.addTool(configurationOverviewLink, Align.right);
 	}
 
 	@Override
@@ -476,6 +491,14 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 				doDelete(ureq, (TreeNode)confirmDeleteCtrl.getUserObject());
 			}
 			cleanUp();
+		} else if(overviewConfigCtrl == source) {
+			if(event instanceof SelectEvent) {
+				SelectEvent se = (SelectEvent)event;
+				if(doSelect(ureq, se.getControlObject(), se.getTarget())) {
+					cleanUp();
+				}
+			}
+			
 		} else if(cmc == source) {
 			cleanUp();
 		}
@@ -483,9 +506,11 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 	}
 	
 	private void cleanUp() {
+		removeAsListenerAndDispose(overviewConfigCtrl);
 		removeAsListenerAndDispose(confirmDeleteCtrl);
 		removeAsListenerAndDispose(selectQItemCtrl);
 		removeAsListenerAndDispose(cmc);
+		overviewConfigCtrl = null;
 		confirmDeleteCtrl = null;
 		selectQItemCtrl = null;
 		cmc = null;
@@ -551,7 +576,25 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 			doCopy(ureq);
 		} else if(reloadInCacheLink == source) {
 			doForceReloadFiles(ureq);
+		} else if(configurationOverviewLink == source) {
+			doConfigurationOverview(ureq);
+		}
+	}
+	
+	private boolean doSelect(UserRequest ureq, ControlObject<?> uobject, SelectionTarget target) {
+		TreeNode selectedNode = TreeHelper.findNodeByUserObject(uobject, menuTree.getTreeModel().getRootNode());
+		if(selectedNode != null) {
+			toolbar.popUpToController(this);
+			
+			partEditorFactory(ureq, selectedNode);
+			if(currentEditorCtrl instanceof Activateable2) {
+				List<ContextEntry> entries = BusinessControlFactory.getInstance()
+						.createCEListFromString(OresHelper.createOLATResourceableType(target.name()));
+				((Activateable2)currentEditorCtrl).activate(ureq, entries, null);
+			}
+			return currentEditorCtrl != null;
 		}
+		return false;
 	}
 	
 	private void doSelectQItem(UserRequest ureq) {
@@ -1306,6 +1349,15 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 		assessmentChanged(ureq);
 	}
 	
+	private void doConfigurationOverview(UserRequest ureq) {
+		removeAsListenerAndDispose(overviewConfigCtrl);
+		
+		overviewConfigCtrl = new AssessmentTestOverviewConfigurationController(ureq, getWindowControl(), toolbar,
+				testEntry, resolvedAssessmentTest);
+		listenTo(overviewConfigCtrl);
+		toolbar.pushController(translate("configuration.overview"), overviewConfigCtrl);
+	}
+	
 	private void doConfirmDelete(UserRequest ureq) {
 		if(confirmDeleteCtrl != null) return;
 		
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
index 482b0cbe2c367236fdaf8419dc7d1958b122ab06..c8a206b73926fee6552ca4103ce0111bea4e0675 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java
@@ -20,6 +20,7 @@
 package org.olat.ims.qti21.ui.editor;
 
 import java.io.File;
+import java.util.List;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
@@ -29,11 +30,15 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.Util;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.ims.qti21.model.xml.AssessmentTestBuilder;
 import org.olat.ims.qti21.ui.AssessmentTestDisplayController;
 import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent.SelectionTarget;
 
 import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
 import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
@@ -44,7 +49,7 @@ import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentTestEditorController extends BasicController {
+public class AssessmentTestEditorController extends BasicController implements Activateable2 {
 
 	private final TabbedPane tabbedPane;
 	private final VelocityContainer mainVC;
@@ -113,6 +118,29 @@ public class AssessmentTestEditorController extends BasicController {
 		}
 	}
 
+	@Override
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
+		if(entries == null || entries.isEmpty()) return;
+		
+		String type = entries.get(0).getOLATResourceable().getResourceableTypeName();
+		if(SelectionTarget.description.name().equalsIgnoreCase(type) || SelectionTarget.score.name().equalsIgnoreCase(type)) {
+			activate(ureq, optionsCtrl);
+		} else if(SelectionTarget.expert.name().equalsIgnoreCase(type) && testPartOptionsCtrl != null) {
+			activate(ureq, testPartOptionsCtrl);
+		} else if(SelectionTarget.feedback.name().equalsIgnoreCase(type) && testPartOptionsCtrl != null) {
+			activate(ureq, testPartOptionsCtrl);
+		}
+	}
+	
+	private void activate(UserRequest ureq, Controller ctrl) {
+		if(ctrl == null) return;
+		
+		int index = tabbedPane.indexOfTab(ctrl.getInitialComponent());
+		if(index >= 0) {
+			tabbedPane.setSelectedPane(ureq, index);
+		}	
+	}
+
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		//
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
index e956dc4cb83a794acca1d6dd54ed68176b668fd3..2ecf967b5524a1927bccb08e2a1ae987e7e91523 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
@@ -15,6 +15,10 @@ add.match.column=Kolonne hinzuf\u00FCgen
 add.match.row=Zeile hinzuf\u00FCgen
 answers=Antworten
 change.elements=Elemente \u00E4ndern
+configuration.overview=\u00DCbersicht Testkonfiguration
+configuration.option.yes=Ja
+configuration.option.no=Nein
+configuration.option.inherited=Vererbt
 convert=Konvertieren
 convert.alien=Konvertieren
 convert.to=Umwandeln in\:
@@ -139,7 +143,9 @@ form.imd.wrong.kprim=Falsch
 form.kprim=Kprim
 form.match=Match
 form.metadata=Metadaten
+form.metadata.creationDate=Erstellt
 form.metadata.description=Beschreibung
+form.metadata.initialAuthor=Erstellt durch
 form.metadata.title=Titel
 form.pool=Fragenpool
 form.score=Punkte
@@ -228,6 +234,16 @@ new.upload=Datei hochladen
 preview=Vorschau
 preview.solution=Vorschau L\u00F6sung
 rights.owners=$org.olat.modules.qpool.ui\:rights.owners
+table.header.attempts=L\u00F6sungsversuche
+table.header.comment=Kommentar erlauben
+table.header.feedback=Feedback
+table.header.identifier=Frage ID
+table.header.points=Punkte
+table.header.review=R\u00FCckblick erlauben
+table.header.skipping=\u00DCberspring erlauben
+table.header.solution=L\u00F6sung anzeigen
+table.header.title=Titel
+table.header.type=Fragetyp
 time.limit.max=Zeitbeschr\u00E4nkung
 title.add=$org.olat.ims.qti.editor\:title.add
 tools.change.copy=$org.olat.ims.qti.editor\:tools.change.copy
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
index 5cc01e68a949828a60557cc71f5e7ede51cbf18e..3e0897cca8c9bcb91f55028bf5c29f80635e2cc5 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
@@ -15,6 +15,10 @@ add.match.column=Add column
 add.match.row=Add row
 answers=Answers
 change.elements=Edition
+configuration.overview=Test configuration overview
+configuration.option.yes=Yes
+configuration.option.no=No
+configuration.option.inherited=Inherited
 convert=Convert
 convert.alien=Convert
 convert.to=Convert to\:
@@ -139,7 +143,9 @@ form.imd.wrong.kprim=False
 form.kprim=Kprim
 form.match=Match
 form.metadata=Metadata
+form.metadata.creationDate=Created
 form.metadata.description=Description
+form.metadata.initialAuthor=Creator
 form.metadata.title=Title
 form.pool=Question bank
 form.score=Score
@@ -228,6 +234,16 @@ new.upload=Upload file
 preview=Preview
 preview.solution=Preview solution
 rights.owners=$org.olat.modules.qpool.ui\:rights.owners
+table.header.attempts=Attempts
+table.header.comment=Allow comment
+table.header.identifier=Question ID
+table.header.feedback=Feedback
+table.header.points=Points
+table.header.review=Allow review
+table.header.skipping=Allow skipping
+table.header.solution=Show solution
+table.header.title=Title
+table.header.type=Type
 time.limit.max=Time limit
 title.add=$org.olat.ims.qti.editor\:title.add
 tools.change.copy=$org.olat.ims.qti.editor\:tools.change.copy
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/events/SelectEvent.java b/src/main/java/org/olat/ims/qti21/ui/editor/events/SelectEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a62ff48cbf196e9f2ba1adbcbac313b8e76dd7f7
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/events/SelectEvent.java
@@ -0,0 +1,63 @@
+/**
+ * <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.ims.qti21.ui.editor.events;
+
+import org.olat.core.gui.control.Event;
+
+import uk.ac.ed.ph.jqtiplus.node.test.ControlObject;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SelectEvent extends Event {
+
+	private static final long serialVersionUID = 1554430878603467523L;
+	public static final String SELECT = "select-control-object";
+	
+	private final SelectionTarget target;
+	private final ControlObject<?> controlObject;
+	
+	public SelectEvent(ControlObject<?> controlObject, SelectionTarget target) {
+		super(SELECT);
+		this.target = target;
+		this.controlObject = controlObject;
+	}
+	
+	public SelectionTarget getTarget() {
+		return target;
+	}
+	
+	public ControlObject<?> getControlObject() {
+		return controlObject;
+	}
+	
+	public enum SelectionTarget {
+		
+		description,
+		expert,
+		score,
+		feedback
+		
+	}
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentSectionScoreCellRenderer.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentSectionScoreCellRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..46ac4c1398de683a64f67aae4323fcf879fd94cd
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentSectionScoreCellRenderer.java
@@ -0,0 +1,59 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+import org.olat.modules.assessment.ui.ScoreCellRenderer;
+
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
+
+/**
+ * 
+ * Initial date: 16 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentSectionScoreCellRenderer implements FlexiCellRenderer {
+	
+	private final ScoreCellRenderer scoreRenderer;
+	private final StaticFlexiCellRenderer actionRenderer;
+	
+	public AssessmentSectionScoreCellRenderer(String action) {
+		scoreRenderer = new ScoreCellRenderer();
+		actionRenderer = new StaticFlexiCellRenderer(action, scoreRenderer);
+	}
+
+	@Override
+	public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source,
+			URLBuilder ubu, Translator translator) {
+		ControlObjectRow object = (ControlObjectRow)source.getFlexiTableElement().getTableDataModel().getObject(row);
+		if(object.getControlObject() instanceof AssessmentSection) {
+			scoreRenderer.render(renderer, target, cellValue, row, source, ubu, translator);
+		} else {
+			actionRenderer.render(renderer, target, cellValue, row, source, ubu, translator);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewConfigurationController.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewConfigurationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e920e555671f22b3c12e5dc7636200d1bae557c
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewConfigurationController.java
@@ -0,0 +1,321 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.DoubleAdder;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.DateChooser;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.Util;
+import org.olat.course.assessment.AssessmentHelper;
+import org.olat.ims.qti21.model.xml.QtiNodesExtractor;
+import org.olat.ims.qti21.ui.assessment.components.QuestionTypeFlexiCellRenderer;
+import org.olat.ims.qti21.ui.editor.AssessmentTestComposerController;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent;
+import org.olat.ims.qti21.ui.editor.events.SelectEvent.SelectionTarget;
+import org.olat.ims.qti21.ui.editor.overview.AssessmentTestOverviewDataModel.PartCols;
+import org.olat.repository.RepositoryEntry;
+import org.olat.user.UserManager;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
+import uk.ac.ed.ph.jqtiplus.node.test.SectionPart;
+import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
+import uk.ac.ed.ph.jqtiplus.node.test.TimeLimits;
+import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
+import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
+import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentTestOverviewConfigurationController extends FormBasicController {
+	
+	private FlexiTableElement tableEl;
+	private AssessmentTestOverviewDataModel tableModel;
+	private final TooledStackedPanel toolbar;
+	
+	private final RepositoryEntry testEntry;
+	private final ResolvedAssessmentTest resolvedAssessmentTest;
+	
+	@Autowired
+	private UserManager userManager;
+	
+	public AssessmentTestOverviewConfigurationController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbar,
+			RepositoryEntry testEntry, ResolvedAssessmentTest resolvedAssessmentTest) {
+		super(ureq, wControl, "overview", Util.createPackageTranslator(AssessmentTestComposerController.class, ureq.getLocale()));
+		this.testEntry = testEntry;
+		this.resolvedAssessmentTest = resolvedAssessmentTest;
+		this.toolbar = toolbar;
+		initForm(ureq);
+		initialPanel.setCssClass("o_edit_mode");
+		loadModel();
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		initInfosForm(formLayout);
+		initTableForm(formLayout, ureq);
+	}
+	
+	private void initInfosForm(FormItemContainer formLayout) {
+		FormLayoutContainer infosLayout = FormLayoutContainer.createDefaultFormLayout("test.infos", getTranslator());
+		formLayout.add("infos", infosLayout);
+		infosLayout.setRootForm(mainForm);
+		
+		AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful();
+		
+		String title = assessmentTest.getTitle();
+		TextElement titleEl = uifactory.addTextElement("title", "form.metadata.title", 255, title, infosLayout);
+		titleEl.setEnabled(false);
+		
+		String initalAuthor = testEntry.getInitialAuthor() == null ? "-" : testEntry.getInitialAuthor();
+		if(testEntry.getInitialAuthor() != null) {
+			initalAuthor = userManager.getUserDisplayName(initalAuthor);
+		}
+		TextElement creatorEl = uifactory.addTextElement("initialAuthor", "form.metadata.initialAuthor", 255, StringHelper.escapeHtml(initalAuthor), infosLayout);
+		creatorEl.setEnabled(false);
+		DateChooser creationEl = uifactory.addDateChooser("form.metadata.creationDate", testEntry.getCreationDate(), infosLayout);
+		creationEl.setEnabled(false);
+		Double maxScore = QtiNodesExtractor.extractMaxScore(assessmentTest);
+		String maxScoreStr = AssessmentHelper.getRoundedScore(maxScore);
+		TextElement maxScoreEl = uifactory.addTextElement("max.score", "max.score", 255, maxScoreStr, infosLayout);
+		maxScoreEl.setEnabled(false);
+		Double cutValue = QtiNodesExtractor.extractCutValue(assessmentTest);
+		String cutValueStr = AssessmentHelper.getRoundedScore(cutValue);
+		TextElement cutValueEl = uifactory.addTextElement("cut.value", "cut.value", 255, cutValueStr, infosLayout);
+		cutValueEl.setEnabled(false);
+		
+		TimeLimits timeLimits = assessmentTest.getTimeLimits();
+		
+		long maxInSeconds = -1;
+		String timeMaxHour = "";
+		String timeMaxMinute = "";
+		if(timeLimits != null && timeLimits.getMaximum() != null && timeLimits.getMaximum().longValue() > 0) {
+			maxInSeconds = timeLimits.getMaximum().longValue();
+			timeMaxHour = Long.toString(maxInSeconds / 3600);
+			timeMaxMinute = Long.toString((maxInSeconds % 3600) / 60);
+		}
+		
+		if(StringHelper.containsNonWhitespace(timeMaxHour) || StringHelper.containsNonWhitespace(timeMaxMinute)) {
+			String page = velocity_root + "/max_time_limit.html";
+			FormLayoutContainer maxTimeCont = FormLayoutContainer.createCustomFormLayout("time.limit.cont", getTranslator(), page);
+			infosLayout.add(maxTimeCont);
+			maxTimeCont.setLabel("time.limit.max", null);
+			
+			timeMaxHour = timeMaxHour.equals("0") ? "" : timeMaxHour;
+			TextElement maxTimeHourEl = uifactory.addTextElement("time.limit.hour", "time.limit.max", 4, timeMaxHour, maxTimeCont);
+			maxTimeHourEl.setDomReplacementWrapperRequired(false);
+			maxTimeHourEl.setDisplaySize(4);
+			maxTimeHourEl.setEnabled(false);
+			
+			TextElement maxTimeMinuteEl = uifactory.addTextElement("time.limit.minute", "time.limit.max", 4, timeMaxMinute, maxTimeCont);
+			maxTimeMinuteEl.setDomReplacementWrapperRequired(false);
+			maxTimeMinuteEl.setDisplaySize(4);
+			maxTimeMinuteEl.setEnabled(false);
+		}
+	}
+	
+	private void initTableForm(FormItemContainer formLayout, UserRequest ureq) {	
+		FlexiTableColumnModel tableColumnModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		
+		DefaultFlexiColumnModel titleCol = new DefaultFlexiColumnModel(PartCols.title,
+				SelectionTarget.description.name(), new HierarchicalPartCellRenderer());
+		titleCol.setAlwaysVisible(true);
+		tableColumnModel.addFlexiColumnModel(titleCol);
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.maxScore,
+				new AssessmentSectionScoreCellRenderer(SelectionTarget.score.name())));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.attempts,
+				SelectionTarget.expert.name(), new MaxAttemptsCellRenderer(getTranslator())));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.skipping,
+				new TestAndSectionCellRenderer(SelectionTarget.expert.name(), new OptionCellRenderer(getTranslator()))));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.comment,
+				new TestAndSectionCellRenderer(SelectionTarget.expert.name(), new OptionCellRenderer(getTranslator()))));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.review,
+				new TestAndSectionCellRenderer(SelectionTarget.expert.name(), new OptionCellRenderer(getTranslator()))));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PartCols.solution,
+				new TestAndSectionCellRenderer(SelectionTarget.expert.name(), new OptionCellRenderer(getTranslator()))));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PartCols.type, new QuestionTypeFlexiCellRenderer(getTranslator())));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PartCols.identifier));
+		tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PartCols.feedback, SelectionTarget.feedback.name()));
+
+		tableModel = new AssessmentTestOverviewDataModel(tableColumnModel);
+		
+		tableEl = uifactory.addTableElement(getWindowControl(), "overview", tableModel, 32000, false, getTranslator(), formLayout);
+		tableEl.setElementCssClass("o_sel_qti_configuration_overview");
+		tableEl.setExportEnabled(true);
+		tableEl.setAndLoadPersistedPreferences(ureq, "assessment-test-config-overview");
+	}
+
+	@Override
+	protected void doDispose() {
+		toolbar.setCssClass("");
+	}
+	
+	private void loadModel() {
+		List<ControlObjectRow> rows = new ArrayList<>();
+		
+		AssessmentTest test = resolvedAssessmentTest.getTestLookup().getRootNodeHolder().getRootNode();
+		rows.add(ControlObjectRow.valueOf(test));
+		
+		List<TestPart> parts = test.getTestParts();
+		if(parts.size() == 1) {
+			List<AssessmentSection> sections = parts.get(0).getAssessmentSections();
+			for(AssessmentSection section:sections) {
+				loadModel(section, rows);
+			}
+		} else {
+			for(int i=0; i<parts.size(); i++) {
+				loadModel(parts.get(i), (i+1), rows);
+			}
+		}
+
+		tableModel.setObjects(rows);
+		tableEl.reset(true, true, true);
+	}
+	
+	private void loadModel(TestPart part, int pos, List<ControlObjectRow> rows) {
+		rows.add(ControlObjectRow.valueOf(part, pos));
+		List<AssessmentSection> sections = part.getAssessmentSections();
+		for(AssessmentSection section:sections) {
+			loadModel(section, rows);
+		}
+	}
+	
+	private Double loadModel(AssessmentSection section, List<ControlObjectRow> rows) {
+		ControlObjectRow sectionRow = ControlObjectRow.valueOf(section);
+		rows.add(sectionRow);
+		
+		boolean someMaxScore = false;
+		DoubleAdder atomicMaxScore = new DoubleAdder();
+		for(SectionPart part: section.getSectionParts()) {
+			
+			Double maxScore = null;
+			if(part instanceof AssessmentItemRef) {
+				maxScore = loadModel((AssessmentItemRef)part, rows);
+			} else if(part instanceof AssessmentSection) {
+				maxScore = loadModel((AssessmentSection) part, rows);
+			}
+			
+			if(maxScore != null) {
+				someMaxScore = true;
+				atomicMaxScore.add(maxScore.doubleValue());
+			}
+		}
+		
+		if(someMaxScore) {
+			sectionRow.setMaxScore(Double.valueOf(atomicMaxScore.sum()));
+		}
+		return sectionRow.getMaxScore();
+	}
+	
+	private Double loadModel(AssessmentItemRef itemRef, List<ControlObjectRow> rows) {
+		Double maxScore = null;
+		ResolvedAssessmentItem resolvedAssessmentItem = resolvedAssessmentTest.getResolvedAssessmentItem(itemRef);
+		if(resolvedAssessmentItem == null || resolvedAssessmentItem.getItemLookup() == null
+				|| resolvedAssessmentItem.getItemLookup().getRootNodeHolder() == null) {
+			rows.add(ControlObjectRow.errorOf(itemRef));
+		} else {
+			BadResourceException ex = resolvedAssessmentItem.getItemLookup().getBadResourceException();
+			if(ex != null) {
+				rows.add(ControlObjectRow.errorOf(itemRef));
+			} else {
+				AssessmentItem assessmentItem = resolvedAssessmentItem.getItemLookup().getRootNodeHolder().getRootNode();
+				ControlObjectRow row = ControlObjectRow.valueOf(itemRef, assessmentItem);
+				maxScore = row.getMaxScore();
+				rows.add(row);
+				
+			}
+		}
+		return maxScore;
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(tableEl == source) {
+			if(event instanceof SelectionEvent) {
+				SelectionEvent se = (SelectionEvent)event;
+				if(StringHelper.containsNonWhitespace(se.getCommand())) {
+					SelectionTarget target = SelectionTarget.valueOf(se.getCommand());
+					ControlObjectRow row = tableModel.getObject(se.getIndex());
+					fireEvent(ureq, new SelectEvent(row.getControlObject(), target));
+				}
+			}
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+	
+	private static class TestAndSectionCellRenderer implements FlexiCellRenderer {
+		
+		private final FlexiCellRenderer delegate;
+		private final StaticFlexiCellRenderer actionRenderer;
+		
+		public TestAndSectionCellRenderer(String action, FlexiCellRenderer delegate) {
+			this.delegate = delegate;
+			actionRenderer = new StaticFlexiCellRenderer(action, delegate);
+		}
+
+		@Override
+		public void render(Renderer renderer, StringOutput target, Object cellValue, int row,
+				FlexiTableComponent source, URLBuilder ubu, Translator translator) {
+			ControlObjectRow object = (ControlObjectRow)source.getFlexiTableElement().getTableDataModel().getObject(row);
+			if(object.getControlObject() instanceof AssessmentItemRef) {
+				delegate.render(renderer, target, cellValue, row, source, ubu, translator);
+			} else {
+				actionRenderer.render(renderer, target, cellValue, row, source, ubu, translator);
+			}	
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewDataModel.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewDataModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e95d2ebfbd25ff0593bfd732cd97abab5b4aa3a
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/AssessmentTestOverviewDataModel.java
@@ -0,0 +1,95 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentTestOverviewDataModel extends DefaultFlexiTableDataModel<ControlObjectRow> {
+	
+	public AssessmentTestOverviewDataModel(FlexiTableColumnModel columnModel) {
+		super(columnModel);
+	}
+
+	@Override
+	public Object getValueAt(int row, int col) {
+		ControlObjectRow partRow = getObject(row);
+		switch(PartCols.values()[col]) {
+			case title: return partRow;
+			case maxScore: return partRow.getMaxScore();
+			case attempts: return partRow.getAttemptOption();
+			case skipping: return partRow.getSkipping();
+			case comment: return partRow.getComment();
+			case review: return partRow.getReview();
+			case solution: return partRow.getSolution();
+			case type: return partRow.getType();
+			case identifier: return partRow.getIdentifier();
+			case feedback: return partRow.getFeedbacks();
+			default: return "ERROR";
+		}
+	}
+
+	@Override
+	public AssessmentTestOverviewDataModel createCopyWithEmptyList() {
+		return new AssessmentTestOverviewDataModel(getTableColumnModel());
+	}
+	
+	public enum PartCols implements FlexiSortableColumnDef {
+		
+		title("table.header.title"),
+		maxScore("table.header.points"),
+		attempts("table.header.attempts"),
+		skipping("table.header.skipping"),
+		comment("table.header.comment"),
+		review("table.header.review"),
+		solution("table.header.solution"),
+		type("table.header.type"),
+		identifier("table.header.identifier"),
+		feedback("table.header.feedback");
+		
+		private final String i18nKey;
+		
+		private PartCols(String i18nKey) {
+			this.i18nKey = i18nKey;
+		}
+
+		@Override
+		public String i18nHeaderKey() {
+			return i18nKey;
+		}
+
+		@Override
+		public boolean sortable() {
+			return false;
+		}
+
+		@Override
+		public String sortKey() {
+			return name();
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/ControlObjectRow.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/ControlObjectRow.java
new file mode 100644
index 0000000000000000000000000000000000000000..52fa818358f8db798ffa8c233d84a5d7001b974f
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/ControlObjectRow.java
@@ -0,0 +1,231 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import java.util.List;
+
+import org.olat.ims.qti21.model.QTI21QuestionType;
+import org.olat.ims.qti21.model.xml.QtiNodesExtractor;
+
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.test.AbstractPart;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection;
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
+import uk.ac.ed.ph.jqtiplus.node.test.ControlObject;
+import uk.ac.ed.ph.jqtiplus.node.test.ItemSessionControl;
+import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ControlObjectRow {
+	
+	private final String title;
+	private final String iconCssClass;
+	private final ControlObject<?> part;
+	
+	private Double maxScore;
+	private Boolean feedbacks;
+	private QTI21QuestionType type;
+
+	private OptionEnum review;
+	private OptionEnum comment;
+	private OptionEnum skipping;
+	private OptionEnum solution;
+	private MaxAttemptOption attemptOption;
+	
+	public ControlObjectRow(String title, ControlObject<?> part, String iconCssClass) {
+		this.part = part;
+		this.title = title;
+		this.iconCssClass = iconCssClass;
+	}
+	
+	public static ControlObjectRow valueOf(AssessmentTest assessmentTest) {
+		ControlObjectRow row = new ControlObjectRow(assessmentTest.getTitle(), assessmentTest, "o_qtiassessment_icon");
+		row.maxScore = QtiNodesExtractor.extractMaxScore(assessmentTest);
+
+		boolean hasFeedbacks = !assessmentTest.getTestFeedbacks().isEmpty();
+		row.feedbacks = Boolean.valueOf(hasFeedbacks);
+		
+		List<TestPart> parts = assessmentTest.getTestParts();
+		if(parts.size() == 1) {
+			TestPart part = parts.get(0);
+			configuration(row, part);
+		}
+		return row;
+	}
+	
+	public static ControlObjectRow valueOf(TestPart part, int pos) {
+		ControlObjectRow row = new ControlObjectRow(pos + ". Test part", part, "o_icon-lg o_qtiassessment_icon");
+		configuration(row, part);
+		return row;
+	}
+
+	public static ControlObjectRow valueOf(AssessmentSection section) {
+		ControlObjectRow row = new ControlObjectRow(section.getTitle(), section, "o_mi_qtisection");
+		configuration(row, section);
+		return row;
+	}
+	
+	public static ControlObjectRow errorOf(AssessmentItemRef itemRef) {
+		return new ControlObjectRow("ERROR", itemRef, "o_icon_error");
+	}
+	
+	public static ControlObjectRow valueOf(AssessmentItemRef itemRef, AssessmentItem assessmentItem) {
+		String itemCssClass;
+		QTI21QuestionType type = QTI21QuestionType.getType(assessmentItem);
+		if(type != null) {
+			itemCssClass = type.getCssClass();
+		} else {
+			itemCssClass = "o_mi_qtiunkown";
+		}
+		ControlObjectRow row = new ControlObjectRow(assessmentItem.getTitle(), itemRef, itemCssClass);
+		row.maxScore = QtiNodesExtractor.extractMaxScore(assessmentItem);
+		row.type = type;
+		boolean hasFeedbacks = !assessmentItem.getModalFeedbacks().isEmpty();
+		row.feedbacks = Boolean.valueOf(hasFeedbacks);
+		configuration(row, itemRef);
+		return row;
+	}
+	
+	private static void configuration(ControlObjectRow row, AbstractPart part) {
+		attempts(row, part);
+		skipping(row, part);
+		comment(row, part);
+		review(row, part);
+		solution(row, part);
+	}
+	
+	private static void attempts(ControlObjectRow row, AbstractPart part) {
+		Integer maxAttempts = null;
+		ItemSessionControl itemSessionControl = part.getItemSessionControl();
+		if(itemSessionControl != null) {
+			maxAttempts = itemSessionControl.getMaxAttempts();
+		}
+		
+		if(maxAttempts != null) {
+			OptionEnum option = (maxAttempts.intValue() == 0 ? OptionEnum.no : OptionEnum.yes);
+			row.attemptOption = MaxAttemptOption.valueOf(option, maxAttempts);	
+		} else {
+			OptionEnum option = (part instanceof TestPart) ? OptionEnum.no : OptionEnum.inherited;
+			row.attemptOption = MaxAttemptOption.valueOf(option, Integer.valueOf(1));
+		}
+	}
+	
+	private static void skipping(ControlObjectRow row, AbstractPart part) {
+		ItemSessionControl itemSessionControl = part.getItemSessionControl();
+		if(itemSessionControl != null && itemSessionControl.getAllowSkipping() != null) {
+			row.skipping = itemSessionControl.getAllowSkipping().booleanValue() ? OptionEnum.yes : OptionEnum.no;
+		} else {
+			row.skipping = (part instanceof TestPart) ? OptionEnum.yes : OptionEnum.inherited;
+		}
+	}
+	
+	private static void comment(ControlObjectRow row, AbstractPart part) {
+		ItemSessionControl itemSessionControl = part.getItemSessionControl();
+		if(itemSessionControl != null && itemSessionControl.getAllowComment() != null) {
+			row.comment = itemSessionControl.getAllowComment().booleanValue() ? OptionEnum.yes : OptionEnum.no;
+		} else {
+			row.comment = (part instanceof TestPart) ? OptionEnum.yes : OptionEnum.inherited;
+		}
+	}
+	
+	private static void review(ControlObjectRow row, AbstractPart part) {
+		ItemSessionControl itemSessionControl = part.getItemSessionControl();
+		if(itemSessionControl != null && itemSessionControl.getAllowReview() != null) {
+			row.review = itemSessionControl.getAllowReview().booleanValue() ? OptionEnum.yes : OptionEnum.no;
+		} else {
+			row.review = (part instanceof TestPart) ? OptionEnum.no : OptionEnum.inherited;
+		}
+	}
+	
+	private static void solution(ControlObjectRow row, AbstractPart part) {
+		ItemSessionControl itemSessionControl = part.getItemSessionControl();
+		if(itemSessionControl != null && itemSessionControl.getShowSolution() != null) {
+			row.solution = itemSessionControl.getShowSolution().booleanValue() ? OptionEnum.yes : OptionEnum.no;
+		} else {
+			row.solution = (part instanceof TestPart) ? OptionEnum.no : OptionEnum.inherited;
+		}
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+
+	public String getIdentifier() {
+		return part.getIdentifier().toString();
+	}
+	
+	public ControlObject<?> getControlObject() {
+		return part;
+	}
+	
+	public String getIconCssClass() {
+		return iconCssClass;
+	}
+	
+	public Double getMaxScore() {
+		return maxScore;
+	}
+	
+	public void setMaxScore(Double maxScore) {
+		this.maxScore = maxScore;
+	}
+	
+	public QTI21QuestionType getType() {
+		return type;
+	}
+	
+	public Boolean getFeedbacks() {
+		return feedbacks;
+	}
+	
+	public OptionEnum getSkipping() {
+		return skipping;
+	}
+	
+	public OptionEnum getComment() {
+		return comment;
+	}
+	
+	public OptionEnum getReview() {
+		return review;
+	}
+	
+	public OptionEnum getSolution() {
+		return solution;
+	}
+	
+	public MaxAttemptOption getAttemptOption() {
+		return attemptOption;
+	}
+	
+	public int getDepth() {
+		int depth = 0;
+		for(ControlObject<?> parent=part.getParent(); parent != null; parent = parent.getParent()) {
+			depth++;
+		}
+		return depth;
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/HierarchicalPartCellRenderer.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/HierarchicalPartCellRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2341a6f8b4765746db011bca131f39d0788d35ae
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/HierarchicalPartCellRenderer.java
@@ -0,0 +1,49 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class HierarchicalPartCellRenderer implements FlexiCellRenderer {
+
+	@Override
+	public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source,
+			URLBuilder ubu, Translator translator) {
+		if(cellValue instanceof ControlObjectRow) {
+			ControlObjectRow controlObject = (ControlObjectRow)cellValue;
+			int depth = controlObject.getDepth();
+			target.append("<div class='o_table_flexi_l").append(depth).append("'><span><i class='o_icon ").append(controlObject.getIconCssClass()).append("'> </i> ")
+			      .append(controlObject.getTitle()).append("</span></div>");
+		} else if(cellValue instanceof String) {
+			target.append((String)cellValue);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptOption.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptOption.java
new file mode 100644
index 0000000000000000000000000000000000000000..b047ef7210c06545aa467a2d58a592423a68095c
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptOption.java
@@ -0,0 +1,50 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MaxAttemptOption {
+	
+	private final OptionEnum option;
+	private final Integer maxAttempts;
+	
+	private MaxAttemptOption(OptionEnum option, Integer maxAttempts) {
+		this.option = option;
+		this.maxAttempts = maxAttempts;
+	}
+	
+	public static MaxAttemptOption valueOf(OptionEnum option, Integer maxAttempts) {
+		return new MaxAttemptOption(option, maxAttempts);
+	}
+	
+	public OptionEnum getOption() {
+		return option;
+	}
+	
+	public Integer getMaxAttempts() {
+		return maxAttempts;
+	}
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptsCellRenderer.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptsCellRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..f493aaeb508f8d398eb42da5def176834519850b
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/MaxAttemptsCellRenderer.java
@@ -0,0 +1,53 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MaxAttemptsCellRenderer extends OptionCellRenderer {
+	
+	public MaxAttemptsCellRenderer(Translator translator) {
+		super(translator);
+	}
+
+	@Override
+	public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source,
+			URLBuilder ubu, Translator trans) {
+		if(cellValue instanceof MaxAttemptOption) {
+			MaxAttemptOption attempts = (MaxAttemptOption)cellValue;
+			OptionEnum option = attempts.getOption();
+			if(option == OptionEnum.yes) {
+				target.append(attempts.getMaxAttempts());
+			} else {
+				super.render(renderer, target, option, row, source, ubu, trans);
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionCellRenderer.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionCellRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..63555d8dcde51f65dddf879c3221ee45f7c49b67
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionCellRenderer.java
@@ -0,0 +1,51 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class OptionCellRenderer implements FlexiCellRenderer {
+	
+	private final Translator translator;
+	
+	public OptionCellRenderer(Translator translator) {
+		this.translator = translator;
+	}
+
+	@Override
+	public void render(Renderer renderer, StringOutput target, Object cellValue, int row, FlexiTableComponent source,
+			URLBuilder ubu, Translator trans) {
+		if(cellValue instanceof OptionEnum) {
+			OptionEnum option = (OptionEnum)cellValue;
+			target.append(translator.translate("configuration.option.".concat(option.name())));
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionEnum.java b/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..9464b5ab60aeb5430b33c7e84c764e24fef14109
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/OptionEnum.java
@@ -0,0 +1,34 @@
+/**
+ * <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.ims.qti21.ui.editor.overview;
+
+/**
+ * 
+ * Initial date: 15 janv. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public enum OptionEnum {
+	
+	yes,
+	no,
+	inherited
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/max_time_limit.html b/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/max_time_limit.html
new file mode 100644
index 0000000000000000000000000000000000000000..58dcb822d4230bcf230b34f878c0c60dac508eb7
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/max_time_limit.html
@@ -0,0 +1 @@
+<div class="form-inline">$r.render("time.limit.hour") $r.translate("hour.short") $r.render("time.limit.minute") $r.translate("minute.short")</div>
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/overview.html b/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/overview.html
new file mode 100644
index 0000000000000000000000000000000000000000..52631ccca1ac114020072b8fc06c7110b08f9926
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/overview/_content/overview.html
@@ -0,0 +1,2 @@
+$r.render("infos")
+$r.render("overview")
\ No newline at end of file
diff --git a/src/test/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManagerTest.java b/src/test/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManagerTest.java
deleted file mode 100644
index 77c911846bc8889c854996105d134f3fef5715d8..0000000000000000000000000000000000000000
--- a/src/test/java/org/olat/core/commons/services/taskexecutor/TaskExecutorManagerTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * <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.core.commons.services.taskexecutor;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.olat.core.commons.services.taskexecutor.manager.TaskExecutorManagerImpl;
-import org.olat.test.OlatTestCase;
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * 
- * Initial date: 02.07.2013<br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class TaskExecutorManagerTest extends OlatTestCase {
-	
-	@Autowired
-	private TaskExecutorManagerImpl taskExecutorManager;
-	
-	@Test
-	public void testRunTask() {
-		final CountDownLatch finishCount = new CountDownLatch(1);
-		taskExecutorManager.execute(new DummyTask(finishCount));
-		
-		try {
-			boolean zero = finishCount.await(10, TimeUnit.SECONDS);
-			Assert.assertTrue(zero);
-		} catch (InterruptedException e) {
-			Assert.fail("Takes too long (more than 10sec)");
-		}
-	}
-	
-	public static class DummyTask implements Runnable {
-		
-		private final CountDownLatch finishCount;
-		
-		public DummyTask(CountDownLatch finishCount) {
-			this.finishCount = finishCount;
-		}
-		
-		@Override
-		public void run() {
-			finishCount.countDown();
-		}
-	}
-}
diff --git a/src/test/java/org/olat/core/commons/services/taskexecutor/PersistentTaskDAOTest.java b/src/test/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAOTest.java
similarity index 97%
rename from src/test/java/org/olat/core/commons/services/taskexecutor/PersistentTaskDAOTest.java
rename to src/test/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAOTest.java
index 2df6a03b6081c1af1700d12733b6db8fb3bc40a2..d7d2e2fea6972218736d8c1ffaa2a7ff3210682e 100644
--- a/src/test/java/org/olat/core/commons/services/taskexecutor/PersistentTaskDAOTest.java
+++ b/src/test/java/org/olat/core/commons/services/taskexecutor/manager/PersistentTaskDAOTest.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.core.commons.services.taskexecutor;
+package org.olat.core.commons.services.taskexecutor.manager;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -29,6 +29,8 @@ import javax.persistence.EntityNotFoundException;
 import org.junit.Assert;
 import org.junit.Test;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.taskexecutor.Task;
+import org.olat.core.commons.services.taskexecutor.TaskStatus;
 import org.olat.core.commons.services.taskexecutor.manager.PersistentTaskDAO;
 import org.olat.core.commons.services.taskexecutor.model.PersistentTask;
 import org.olat.core.id.Identity;
@@ -113,7 +115,7 @@ public class PersistentTaskDAOTest extends OlatTestCase  {
 		PersistentTask task = persistentTaskDao.createTask(taskName, new DummyTask());
 		dbInstance.commitAndCloseSession();
 		
-		PersistentTask todo = persistentTaskDao.pickTaskForRun(task.getKey());
+		PersistentTask todo = persistentTaskDao.pickTaskForRun(task);
 
 		Assert.assertNotNull(todo);
 		Assert.assertEquals(task.getKey(), todo.getKey());
@@ -128,7 +130,7 @@ public class PersistentTaskDAOTest extends OlatTestCase  {
 		dbInstance.commitAndCloseSession();
 		
 		//update
-		PersistentTask todo = persistentTaskDao.pickTaskForRun(task.getKey());
+		PersistentTask todo = persistentTaskDao.pickTaskForRun(task);
 		DummyTask taskToUpdate = new DummyTask();
 		taskToUpdate.setMarkerValue("new marker");
 		persistentTaskDao.updateTask(todo, taskToUpdate, null, null);
@@ -165,7 +167,8 @@ public class PersistentTaskDAOTest extends OlatTestCase  {
 		int count = 0;
 		List<Long> todos = persistentTaskDao.tasksToDo();
 		for(Long todo:todos) {
-			PersistentTask taskToDo = persistentTaskDao.pickTaskForRun(todo);
+			PersistentTask loadedTask = persistentTaskDao.loadTaskById(todo);
+			PersistentTask taskToDo = persistentTaskDao.pickTaskForRun(loadedTask);
 			persistentTaskDao.taskDone(taskToDo);
 			count++;
 		}
@@ -452,7 +455,8 @@ public class PersistentTaskDAOTest extends OlatTestCase  {
 		int count = 0;
 		List<Long> todos = persistentTaskDao.tasksToDo();
 		for(Long todo:todos) {
-			PersistentTask taskToDo = persistentTaskDao.pickTaskForRun(todo);
+			PersistentTask loadedTask = persistentTaskDao.loadTaskById(todo);
+			PersistentTask taskToDo = persistentTaskDao.pickTaskForRun(loadedTask);
 			persistentTaskDao.taskDone(taskToDo);
 			count++;
 		}
diff --git a/src/test/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerTest.java b/src/test/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..63afc7f508be2f216a2b1d6624175bce771f4bdb
--- /dev/null
+++ b/src/test/java/org/olat/core/commons/services/taskexecutor/manager/TaskExecutorManagerTest.java
@@ -0,0 +1,153 @@
+/**
+ * <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.core.commons.services.taskexecutor.manager;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.taskexecutor.LongRunnable;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 02.07.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class TaskExecutorManagerTest extends OlatTestCase {
+	
+	private static final OLog log = Tracing.createLoggerFor(TaskExecutorManagerTest.class);
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private PersistentTaskDAO persistentTaskDao;
+	@Autowired
+	private TaskExecutorManagerImpl taskExecutorManager;
+	
+	@Test
+	public void testRunTask() {
+		final CountDownLatch finishCount = new CountDownLatch(1);
+		taskExecutorManager.execute(new DummyTask(finishCount));
+		
+		try {
+			boolean zero = finishCount.await(10, TimeUnit.SECONDS);
+			Assert.assertTrue(zero);
+		} catch (InterruptedException e) {
+			Assert.fail("Takes too long (more than 10sec)");
+		}
+	}
+	
+
+	private final static int numOfMpTasks = 100;
+	private final static Map<String,AtomicInteger> taskCounter = new ConcurrentHashMap<>();
+	private final static CountDownLatch mpFinishCount = new CountDownLatch(numOfMpTasks);
+	
+	@Test
+	public void testRunParallelTasks() {
+		for(int i=numOfMpTasks; i-->0;) {
+			String taskName = "Task-" + i;
+			persistentTaskDao.createTask(taskName, new DummySerializableTask(taskName));
+			taskCounter.put(taskName, new AtomicInteger(0));
+			dbInstance.commitAndCloseSession();
+		}
+
+		final int numOfExecutors = 5;
+		final Thread[] executors = new Thread[numOfExecutors];
+		for(int i=numOfExecutors; i-->0; ) {
+			executors[i] = new Thread(new ProcessTask(taskExecutorManager));
+		}
+		
+		try {
+			for(int i=numOfExecutors; i-->0; ) {
+				executors[i].start();
+			}
+
+			boolean zero = mpFinishCount.await(120, TimeUnit.SECONDS);
+			Assert.assertTrue(zero);
+			
+			// make sure the task is done only once
+			for(AtomicInteger count:taskCounter.values()) {
+				Assert.assertEquals(1, count.get());
+			}
+			log.info("All task done: " + taskCounter.size());
+		} catch (InterruptedException e) {
+			Assert.fail("Takes too long (more than 10sec)");
+		}
+	}
+	
+	public static class ProcessTask implements Runnable {
+		
+		private final TaskExecutorManagerImpl executor;
+		
+		public ProcessTask(TaskExecutorManagerImpl executor) {
+			this.executor = executor;
+		}
+
+		@Override
+		public void run() {
+			executor.processTaskToDo();
+		}
+	}
+	
+	public static class DummySerializableTask implements LongRunnable, Runnable {
+
+		private static final long serialVersionUID = 7459138015999298102L;
+		
+		private final String taskName;
+		public DummySerializableTask(String taskName) {
+			this.taskName = taskName;
+		}
+		
+		@Override
+		public void run() {
+			try {
+				Thread.sleep(50);
+			} catch (InterruptedException e) {
+				//
+			}
+			taskCounter.get(taskName).incrementAndGet();
+			mpFinishCount.countDown();
+		}
+	}
+	
+	public static class DummyTask implements Runnable {
+		
+		private final CountDownLatch finishCount;
+		
+		public DummyTask(CountDownLatch finishCount) {
+			this.finishCount = finishCount;
+		}
+		
+		@Override
+		public void run() {
+			finishCount.countDown();
+		}
+	}
+}
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index 4e963be9040df166ec33cc2261d5a5ab02e306be..eeff6ddc72480072ef4e703fdce3bcab091511c8 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -110,8 +110,8 @@ import org.junit.runners.Suite;
 	org.olat.core.commons.services.webdav.manager.WebDAVAuthManagerTest.class,
 	org.olat.core.commons.services.webdav.servlets.RequestUtilsTest.class,
 	org.olat.core.commons.services.sms.manager.MessageLogDAOTest.class,
-	org.olat.core.commons.services.taskexecutor.PersistentTaskDAOTest.class,
-	org.olat.core.commons.services.taskexecutor.TaskExecutorManagerTest.class,
+	org.olat.core.commons.services.taskexecutor.manager.PersistentTaskDAOTest.class,
+	org.olat.core.commons.services.taskexecutor.manager.TaskExecutorManagerTest.class,
 	org.olat.core.commons.services.text.TextServiceTest.class,
 	org.olat.admin.user.delete.service.UserDeletionManagerTest.class,
 	org.olat.group.BusinessGroupManagedFlagsTest.class,