diff --git a/pom.xml b/pom.xml
index 318a9654c8e2e75493a0a8727e851700d6d6b177..9345939716a4fe3530fae3f64137e228d8c88060 100644
--- a/pom.xml
+++ b/pom.xml
@@ -179,6 +179,13 @@
 												<include>${basedir}/target/jquery/tinymce4/tinymce/plugins/quotespliter/plugin.min.js</include>
 											</includes>
 										</aggregation>
+										<aggregation>
+											<output>${basedir}/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.min.js</output>
+											<removeIncluded>false</removeIncluded>
+											<includes>
+												<include>${basedir}/target/jquery/jquery/openolat/jquery.statistics.chart.min.js</include>
+											</includes>
+										</aggregation>
 									</aggregations>
 								</configuration>
 							</execution>
diff --git a/src/main/java/de/bps/course/nodes/ChecklistCourseNode.java b/src/main/java/de/bps/course/nodes/ChecklistCourseNode.java
index 5d31af9878ce1e9f6015257a642ee0928ac68fea..796e6532d41020b799eab5dc9897ca28eccf3206 100644
--- a/src/main/java/de/bps/course/nodes/ChecklistCourseNode.java
+++ b/src/main/java/de/bps/course/nodes/ChecklistCourseNode.java
@@ -160,7 +160,8 @@ public class ChecklistCourseNode extends AbstractAccessableCourseNode {
 			} else 
 			// this is part of a copied course, the original checklist will be copied
 			if(getModuleConfiguration().get(CONF_CHECKLIST) != null) {
-				Checklist orgChecklist = ChecklistManager.getInstance().loadChecklist((Checklist) getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST));
+				Checklist confChecklist = (Checklist)getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST);
+				Checklist orgChecklist = ChecklistManager.getInstance().loadChecklist(confChecklist);
 				checklist = ChecklistManager.getInstance().copyChecklist(orgChecklist);
 			} else {
 				// no checklist available, create new one
diff --git a/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java b/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
index 8a5fd268c4b844322d5e1c4ba8986f86c321ee59..76022f399f3a17ed3b5114dabd8dfb43c23d47c6 100644
--- a/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
+++ b/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
@@ -315,7 +315,7 @@ public class DENManageParticipantsController extends BasicController {
 		MailTemplate mailTempl = denManager.getAddedMailTemplate(ureq, subjectStr, getTranslator());
 		removeAsListenerAndDispose(addedNotificationCtr);
 
-		addedNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false);
+		addedNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false, true);
 		listenTo(addedNotificationCtr);
 		
 		VelocityContainer sendNotificationVC = createVelocityContainer("sendnotification");
@@ -331,7 +331,7 @@ public class DENManageParticipantsController extends BasicController {
 	private void createRemovedNotificationMail(UserRequest ureq, String subjectStr) {
 		MailTemplate mailTempl = denManager.getRemovedMailTemplate(ureq, subjectStr, getTranslator());
 		removeAsListenerAndDispose(addedNotificationCtr);
-		addedNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false);
+		addedNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false, true);
 		listenTo(addedNotificationCtr);
 		
 		VelocityContainer sendNotificationVC = createVelocityContainer("sendnotification");
diff --git a/src/main/java/de/bps/olat/modules/cl/ChecklistManageCheckpointsController.java b/src/main/java/de/bps/olat/modules/cl/ChecklistManageCheckpointsController.java
index 034c2e39dc50dfe4c414a8614485768e73fe61c3..6bcb09491551d09e4fa01243633ab11794834bba 100644
--- a/src/main/java/de/bps/olat/modules/cl/ChecklistManageCheckpointsController.java
+++ b/src/main/java/de/bps/olat/modules/cl/ChecklistManageCheckpointsController.java
@@ -250,7 +250,9 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		}
 
 		int j = 500;
-		for( Checkpoint checkpoint : checklist.getCheckpoints() ) {
+
+		List<Checkpoint> checkpointList = checklist.getCheckpointsSorted(ChecklistUIFactory.comparatorTitleAsc);
+		for( Checkpoint checkpoint : checkpointList ) {
 			String pointTitle = checkpoint.getTitle() == null ? "" : checkpoint.getTitle();
 			manageChecklistTable.addColumnDescriptor(new ChecklistMultiSelectColumnDescriptor(pointTitle, j++));
 			cols++;
@@ -259,14 +261,15 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		cols++;
 		
 		manageChecklistTable.setMultiSelect(false);
-		manageTableData = new ChecklistManageTableDataModel(checklist, lstIdents, userPropertyHandlers, cols);
+		manageTableData = new ChecklistManageTableDataModel(checkpointList, lstIdents, userPropertyHandlers, cols);
 		manageChecklistTable.setTableDataModel(manageTableData);
 		
 		panel.setContent(manageChecklistTable.getInitialComponent());
 	}
 	
 	private void initEditTable(UserRequest ureq, Identity identity) {
-		editTableData = new ChecklistRunTableDataModel(this.checklist.getCheckpoints(), getTranslator());
+		List<Checkpoint> checkpoints = checklist.getCheckpoints();
+		editTableData = new ChecklistRunTableDataModel(checkpoints, getTranslator());
 		
 		TableGuiConfiguration tableConfig = new TableGuiConfiguration();
 		tableConfig.setTableEmptyMessage(translate("cl.table.empty"));
@@ -286,8 +289,8 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		editChecklistTable.addMultiSelectAction("cl.save.close", "save");
 		editChecklistTable.setTableDataModel(editTableData);
 		
-		for(int i = 0; i < this.checklist.getCheckpoints().size(); i++) {
-			Checkpoint checkpoint = (Checkpoint) editTableData.getObject(i);
+		for(int i = 0; i<checkpoints.size(); i++) {
+			Checkpoint checkpoint = editTableData.getObject(i);
 			boolean selected = checkpoint.getSelectionFor(identity).booleanValue();
 			editChecklistTable.setMultiSelectSelectedAt(i, selected);
 		}
@@ -301,7 +304,7 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		ChecklistManager manager = ChecklistManager.getInstance();
 		int size = checklist.getCheckpoints().size();
 		for(int i = 0; i < size; i++) {
-			Checkpoint checkpoint = this.checklist.getCheckpoints().get(i);
+			Checkpoint checkpoint = checklist.getCheckpoints().get(i);
 			Boolean selected = checkpoint.getSelectionFor(identity);
 			if(selected.booleanValue() != selection.get(i)) {
 				checkpoint.setSelectionFor(identity, selection.get(i));
@@ -314,8 +317,10 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		int cdcnt = manageTableData.getColumnCount();
 		int rcnt = manageTableData.getRowCount();
 		StringBuilder sb = new StringBuilder();
+		boolean isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
+		List<UserPropertyHandler> userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, isAdministrativeUser);
 		// additional informations
-		sb.append(translate("cl.course.title")).append('\t').append(this.course.getCourseTitle());
+		sb.append(translate("cl.course.title")).append('\t').append(course.getCourseTitle());
 		sb.append('\n');
 		String listTitle = checklist.getTitle() == null ? "" : checklist.getTitle();
 		sb.append(translate("cl.title")).append('\t').append(listTitle);
@@ -329,7 +334,14 @@ public class ChecklistManageCheckpointsController extends BasicController {
 		}
 		sb.append('\n');
 		// checkpoint description
-		sb.append('\t');
+		if(isAdministrativeUser) {
+			sb.append('\t');
+		}
+		for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
+			if (userPropertyHandler == null) continue;
+			sb.append('\t');
+		}
+
 		for (Checkpoint checkpoint : checklist.getCheckpoints()) {
 			sb.append('\t').append(checkpoint.getDescription());
 		}
diff --git a/src/main/java/de/bps/olat/modules/cl/ChecklistManageTableDataModel.java b/src/main/java/de/bps/olat/modules/cl/ChecklistManageTableDataModel.java
index 09b51054fb2a08c55d5d49deacd180642704aa45..7ea3c1db7d6cee965a76d542f73592675274076b 100644
--- a/src/main/java/de/bps/olat/modules/cl/ChecklistManageTableDataModel.java
+++ b/src/main/java/de/bps/olat/modules/cl/ChecklistManageTableDataModel.java
@@ -35,7 +35,7 @@ public class ChecklistManageTableDataModel extends DefaultTableDataModel<Row> {
 	private int colCount;
 	private int rowCount;
 
-	public ChecklistManageTableDataModel(Checklist checklist, List<Identity> participants,
+	public ChecklistManageTableDataModel(List<Checkpoint> checkpointList, List<Identity> participants,
 			List<UserPropertyHandler> userPropertyHandlers, int cols) {
 		super(Collections.<Row>emptyList());
 		
@@ -44,7 +44,7 @@ public class ChecklistManageTableDataModel extends DefaultTableDataModel<Row> {
 		
 		List<Row> entries = new ArrayList<>(rowCount);
 		for( Identity identity : participants ) {
-			entries.add(new Row(identity, userPropertyHandlers, checklist, Locale.ENGLISH));
+			entries.add(new Row(identity, userPropertyHandlers, checkpointList, Locale.ENGLISH));
 		}
 		setObjects(entries);
 	}
@@ -86,7 +86,7 @@ public class ChecklistManageTableDataModel extends DefaultTableDataModel<Row> {
 		private final String[] identityProps;
 		private final Boolean[] checkpoints;
 		
-		public Row(Identity identity, List<UserPropertyHandler> userPropertyHandlers, Checklist checklist, Locale locale) {
+		public Row(Identity identity, List<UserPropertyHandler> userPropertyHandlers, List<Checkpoint> checkpointList, Locale locale) {
 			this.identityKey = identity.getKey();
 			this.identityName = identity.getName();
 			
@@ -95,7 +95,6 @@ public class ChecklistManageTableDataModel extends DefaultTableDataModel<Row> {
 				identityProps[i] = userPropertyHandlers.get(i).getUserProperty(identity.getUser(), locale);
 			}
 			
-			List<Checkpoint> checkpointList = checklist.getCheckpointsSorted(ChecklistUIFactory.comparatorTitleAsc);
 			checkpoints = new Boolean[checkpointList.size()];
 			for( int i=checkpointList.size(); i-->0; ) {
 				checkpoints[i] =  checkpointList.get(i).getSelectionFor(identity);
diff --git a/src/main/java/de/bps/olat/modules/cl/ChecklistManager.java b/src/main/java/de/bps/olat/modules/cl/ChecklistManager.java
index c0441d85f05f711fcd6e015a991b0accfe9af2f9..457bde664829c848f1478843ded1069f8230a431 100644
--- a/src/main/java/de/bps/olat/modules/cl/ChecklistManager.java
+++ b/src/main/java/de/bps/olat/modules/cl/ChecklistManager.java
@@ -95,7 +95,12 @@ public class ChecklistManager {
 	 */
 	public Checklist saveChecklist(Checklist cl) {
 		cl.setLastModified(new Date());
-		return DBFactory.getInstance().getCurrentEntityManager().merge(cl);
+		if(cl.getKey() == null) {
+			DBFactory.getInstance().getCurrentEntityManager().persist(cl);
+		} else {
+			cl = DBFactory.getInstance().getCurrentEntityManager().merge(cl);
+		}
+		return cl;
 	}
 	
 	/**
diff --git a/src/main/java/org/olat/admin/securitygroup/gui/GroupController.java b/src/main/java/org/olat/admin/securitygroup/gui/GroupController.java
index 86f2d8bf895d52ea4a24880a984ba1e9cd86092e..f03cf5aed5d00ec7b01e588e0e5978803b98b0bc 100644
--- a/src/main/java/org/olat/admin/securitygroup/gui/GroupController.java
+++ b/src/main/java/org/olat/admin/securitygroup/gui/GroupController.java
@@ -325,7 +325,7 @@ public class GroupController extends BasicController {
 							doBuildConfirmDeleteDialog(ureq);
 						} else {
 							removeAsListenerAndDispose(removeUserMailCtr);
-							removeUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, removeUserMailDefaultTempl, true, false);							
+							removeUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, removeUserMailDefaultTempl, true, false, true);							
 							listenTo(removeUserMailCtr);
 							
 							removeAsListenerAndDispose(cmc);
@@ -406,7 +406,7 @@ public class GroupController extends BasicController {
 					doAddIdentitiesToGroup(ureq, toAdd, null);
 				} else {
 					removeAsListenerAndDispose(addUserMailCtr);
-					addUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, addUserMailDefaultTempl, true, mandatoryEmail);					
+					addUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, addUserMailDefaultTempl, true, mandatoryEmail, true);					
 					listenTo(addUserMailCtr);
 					
 					removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/admin/securitygroup/gui/WaitingGroupController.java b/src/main/java/org/olat/admin/securitygroup/gui/WaitingGroupController.java
index 1ce5817cce028772e7e4638d7853ec02c744f7e7..5bf177560c208781deb21d32856fd0517d0b64a3 100644
--- a/src/main/java/org/olat/admin/securitygroup/gui/WaitingGroupController.java
+++ b/src/main/java/org/olat/admin/securitygroup/gui/WaitingGroupController.java
@@ -96,7 +96,7 @@ public class WaitingGroupController extends GroupController {
 						toTransfer = objects;
 						
 						removeAsListenerAndDispose(transferMailCtr);
-						transferMailCtr = new MailNotificationEditController(getWindowControl(), ureq, transferUserMailTempl, true, mandatoryEmail);
+						transferMailCtr = new MailNotificationEditController(getWindowControl(), ureq, transferUserMailTempl, true, mandatoryEmail, true);
 						listenTo(transferMailCtr);
 						
 						removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/admin/user/delete/SelectionController.java b/src/main/java/org/olat/admin/user/delete/SelectionController.java
index 68b986dd4de036a09bb77bb955b0d447c713e83f..0207ee6da5520188daed4c71dd5ba6b4ffb4884a 100644
--- a/src/main/java/org/olat/admin/user/delete/SelectionController.java
+++ b/src/main/java/org/olat/admin/user/delete/SelectionController.java
@@ -208,7 +208,7 @@ public class SelectionController extends BasicController {
 			deleteMailTemplate.addToContext("durationdeleteemail", Integer.toString(UserDeletionManager.getInstance().getDeleteEmailDuration() ));
 
 			removeAsListenerAndDispose(deleteUserMailCtr);
-			deleteUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, deleteMailTemplate, true, false);
+			deleteUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, deleteMailTemplate, true, false, false);
 			listenTo(deleteUserMailCtr);
 			
 			removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/admin/user/delete/TabbedPaneController.java b/src/main/java/org/olat/admin/user/delete/TabbedPaneController.java
index 3f4866031f7d6b343a0a2c218bff229c175d132b..15e3644fb4a8728c4dc31127af7cee83aaf752fb 100644
--- a/src/main/java/org/olat/admin/user/delete/TabbedPaneController.java
+++ b/src/main/java/org/olat/admin/user/delete/TabbedPaneController.java
@@ -40,7 +40,7 @@ import org.olat.core.gui.control.DefaultController;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
-import org.olat.core.gui.translator.PackageTranslator;
+import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.Util;
@@ -60,7 +60,7 @@ public class TabbedPaneController extends DefaultController implements Controlle
 	
 	private VelocityContainer myContent;
 	
-	private PackageTranslator translator;
+	private Translator translator;
 
 	// controllers used in tabbed pane
 	private TabbedPane userDeleteTabP;
@@ -78,7 +78,7 @@ public class TabbedPaneController extends DefaultController implements Controlle
 	public TabbedPaneController(UserRequest ureq, WindowControl wControl) {
 		super(wControl);
 	
-		translator = new PackageTranslator(PACKAGE, ureq.getLocale());
+		translator = Util.createPackageTranslator(TabbedPaneController.class, ureq.getLocale());
 
 		Boolean canDelete = BaseSecurityModule.USERMANAGER_CAN_DELETE_USER;
 		if ( canDelete.booleanValue() || ureq.getUserSession().getRoles().isOLATAdmin() ) {
diff --git a/src/main/java/org/olat/basesecurity/ui/GroupController.java b/src/main/java/org/olat/basesecurity/ui/GroupController.java
index fd50342809d873a53694b79d3dd96f36ce82c847..f4cdea58751e1309087c32b3d5a697d657106435 100644
--- a/src/main/java/org/olat/basesecurity/ui/GroupController.java
+++ b/src/main/java/org/olat/basesecurity/ui/GroupController.java
@@ -334,7 +334,7 @@ public class GroupController extends BasicController {
 							doBuildConfirmDeleteDialog(ureq);
 						} else {
 							removeAsListenerAndDispose(removeUserMailCtr);
-							removeUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, removeUserMailDefaultTempl, true, false);							
+							removeUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, removeUserMailDefaultTempl, true, false, true);							
 							listenTo(removeUserMailCtr);
 							
 							removeAsListenerAndDispose(cmc);
@@ -415,7 +415,7 @@ public class GroupController extends BasicController {
 					doAddIdentitiesToGroup(ureq, toAdd, null);
 				} else {
 					removeAsListenerAndDispose(addUserMailCtr);
-					addUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, addUserMailDefaultTempl, true, mandatoryEmail);					
+					addUserMailCtr = new MailNotificationEditController(getWindowControl(), ureq, addUserMailDefaultTempl, true, mandatoryEmail, true);					
 					listenTo(addUserMailCtr);
 					
 					removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java b/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java
index 0b2393cc5ee744bce0144eecc725b1dfdf5f6c19..5aa1f4b4f5d6c71db7930be6ac98e319cd1667c7 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/FolderRunController.java
@@ -270,6 +270,15 @@ public class FolderRunController extends BasicController implements Activateable
 		putInitialPanel(folderContainer);
 	}
 	
+	/**
+	 * Remove the subscription panel but let the subscription context active
+	 */
+	public void disableSubscriptionController() {
+		if(csController != null) {
+			folderContainer.remove(csController.getInitialComponent());
+		}
+	}
+	
 	public void setResourceURL(String resourceUrl) {
 		if(searchC != null) {
 			searchC.setResourceUrl(resourceUrl);
diff --git a/src/main/java/org/olat/core/gui/components/chart/BarChartComponent.java b/src/main/java/org/olat/core/gui/components/chart/BarChartComponent.java
index 7a4d779b70365d7559a2bd0d310acc788135b5aa..69ddcf7bbbf718794ad5f2ab301793cd503dc8bb 100644
--- a/src/main/java/org/olat/core/gui/components/chart/BarChartComponent.java
+++ b/src/main/java/org/olat/core/gui/components/chart/BarChartComponent.java
@@ -23,22 +23,21 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.olat.core.gui.components.ComponentRenderer;
-import org.olat.core.logging.AssertException;
 
 /**
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class BarChartComponent extends AbstractD3Component {
+public class BarChartComponent extends DefaultD3Component {
 	
 	private static final ComponentRenderer renderer = new BarChartComponentRenderer();
 
-	private Scale yScale = Scale.plain;
 	private List<BarSeries> seriesList = new ArrayList<>();
 	
 	private String defaultBarClass = "bar_default";
 	private String yLegend;
+	private String xLegend;
 	
 	public BarChartComponent(String name) {
 		super(name);
@@ -60,16 +59,12 @@ public class BarChartComponent extends AbstractD3Component {
 		this.yLegend = yLegend;
 	}
 	
-	public Scale getYScale() {
-		return yScale;
+	public String getXLegend() {
+		return xLegend;
 	}
 
-	public void setYScale(Scale yScale) {
-		if(yScale == Scale.plain || yScale == Scale.percent) {
-			this.yScale = yScale;
-		} else {
-			throw new AssertException("Scale not supported by bar chart: " + yScale);
-		}
+	public void setXLegend(String xLegend) {
+		this.xLegend = xLegend;
 	}
 	
 	public List<BarSeries> getSeries() {
diff --git a/src/main/java/org/olat/core/gui/components/chart/BarChartComponentRenderer.java b/src/main/java/org/olat/core/gui/components/chart/BarChartComponentRenderer.java
index ddc2472949ad98b052664ba79234e665c85d17a4..02603b009f49bfc773937bc5fdd6d300690a6a19 100644
--- a/src/main/java/org/olat/core/gui/components/chart/BarChartComponentRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/chart/BarChartComponentRenderer.java
@@ -29,6 +29,7 @@ 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;
 
 /**
  * 
@@ -41,14 +42,10 @@ public class BarChartComponentRenderer extends DefaultComponentRenderer {
 			Translator translator, RenderResult renderResult, String[] args) {
 		
 		BarChartComponent chartCmp = (BarChartComponent)source;
-		renderD3js(renderer, sb, chartCmp);
-	}
-	
-	private void renderD3js(Renderer renderer, StringOutput sb, BarChartComponent chartCmp) {
 		List<BarSeries> seriesList = chartCmp.getSeries();
 		
-		Scale yScale = chartCmp.getYScale();
 		String yLegend = chartCmp.getYLegend();
+		String xLegend = chartCmp.getXLegend();
 
 		Stringuified infos = BarSeries.getDatasAndColors(seriesList, chartCmp.getDefaultBarClass());
 
@@ -79,9 +76,7 @@ public class BarChartComponentRenderer extends DefaultComponentRenderer {
 		  .append("var yAxis = d3.svg.axis()\n")
 		  .append("    .scale(y)\n")
 		  .append("    .orient('left')\n");
-		if(yScale == Scale.percent) {
-			sb.append("    .ticks(10, '%');\n");
-		}
+
 		sb.append("\n")
 		  .append("var svg = d3.select('#d").append(cmpId).append("d3holder').append('svg')\n")
 		  .append("    .attr('width', width + margin.left + margin.right)\n")
@@ -92,32 +87,45 @@ public class BarChartComponentRenderer extends DefaultComponentRenderer {
 		  .append("var data = [").append(infos.getData()).append("]\n")
 		  .append("x.domain(data.map(function(d) { return d[0]; }));\n")
 		  .append("y.domain([0, d3.max(data, function(d) { return ").append(sum).append("; })]);\n")
-		  .append("\n")
-		  
-		  .append("svg.append('g')\n")
+		  .append("\n");
+
+		//append x axis and legend
+		sb.append("svg.append('g')\n")
 		  .append("   .attr('class', 'x axis')\n")
 		  .append("   .attr('transform', 'translate(0,' + height + ')')\n")
-		  .append("   .call(xAxis);\n")
-		  .append("\n")
-		  //append y legend
-		  .append("svg.append('g')\n")
+		  .append("   .call(xAxis);\n");
+		if(StringHelper.containsNonWhitespace(xLegend)) {
+			sb.append("  .append('text')\n")
+			  .append("    .attr('y', 0)\n")
+			  .append("    .attr('x', 0 - (width / 2))\n")
+			  .append("    .attr('dy', '1em')\n")
+			  .append("    .style('text-anchor', 'middle')\n")
+			  .append("    .text('").append(xLegend).append("');\n");
+		}
+		
+		//append y axis and legend
+		sb.append("svg.append('g')\n")
 		  .append("    .attr('class', 'y axis')\n")
-		  .append("    .call(yAxis)\n")
-		  .append("  .append('text')\n")
-		  .append("    .attr('transform', 'rotate(-90)')\n")
-		  .append("    .attr('y', 0 - margin.left)\n")
-		  .append("    .attr('x', 0 - (height / 2))\n")
-		  .append("    .attr('dy', '1em')\n")
-		  .append("    .style('text-anchor', 'middle')\n")
-		  .append("    .text('").append(yLegend).append("');\n")
-		  
-		  .append("\n");
+		  .append("    .call(yAxis)\n");
+		if(StringHelper.containsNonWhitespace(yLegend)) {
+			sb.append("  .append('text')\n")
+			  .append("    .attr('transform', 'rotate(-90)')\n")
+			  .append("    .attr('y', 0 - margin.left)\n")
+			  .append("    .attr('x', 0 - (height / 2))\n")
+			  .append("    .attr('dy', '1em')\n")
+			  .append("    .style('text-anchor', 'middle')\n")
+			  .append("    .text('").append(yLegend).append("');\n")
+			  .append("\n");
+		}
 
 		appendSeries(sb, infos.getColors(), chartCmp);
 		
 		sb.append("});\n")
 		  .append("/* ]]> */")
 		  .append("</script>\n");
+		
+		//System.out.println("--------------------------");
+		//System.out.println(sb.toString());
 	}
 	
 	private void appendSeries(StringOutput sb, StringBuilder colors, BarChartComponent chartCmp) {
diff --git a/src/main/java/org/olat/core/gui/components/chart/BarSeries.java b/src/main/java/org/olat/core/gui/components/chart/BarSeries.java
index a17c2f02bec946f7f6fe4d7bc77afc684c25092f..665184737c7fd74e53f0d12c5e0e231d2eeb1276 100644
--- a/src/main/java/org/olat/core/gui/components/chart/BarSeries.java
+++ b/src/main/java/org/olat/core/gui/components/chart/BarSeries.java
@@ -70,8 +70,26 @@ public class BarSeries {
 		return Double.NaN;
 	}
 	
+	public static final String datasToString(double[] values) {
+		StringBuilder sb = new StringBuilder();
+		for(double value:values) {
+			if(sb.length() > 0) sb.append(",");
+			sb.append(value);
+		}
+		return sb.toString();
+	}
+	
+	
+	public static final String datasToString(long[] values) {
+		StringBuilder sb = new StringBuilder();
+		for(long value:values) {
+			if(sb.length() > 0) sb.append(",");
+			sb.append((double)value);
+		}
+		return sb.toString();
+	}
 
-	protected static Stringuified getDatasAndColors(List<BarSeries> seriesList, String defaultBarClass) {
+	public static Stringuified getDatasAndColors(List<BarSeries> seriesList, String defaultBarClass) {
 		Map<Comparable<?>,String> thickSet = new HashMap<>();
 		Map<Comparable<?>,String> colorsMap = new HashMap<>();
 		for(BarSeries series:seriesList) {
@@ -92,6 +110,7 @@ public class BarSeries {
 		
 		List<Comparable<?>> thickList = new ArrayList<>(thickSet.keySet());
 		Collections.sort(thickList);
+		Collections.reverse(thickList);
 
 		StringBuilder data = new StringBuilder();
 		for(int i=0; i<thickList.size(); i++) {
@@ -120,7 +139,7 @@ public class BarSeries {
 		return new Stringuified(data, colors);
 	}
 	
-	protected static class Stringuified {
+	public static class Stringuified {
 		private final StringBuilder colors;
 		private final StringBuilder data;
 		
diff --git a/src/main/java/org/olat/core/gui/components/chart/AbstractD3Component.java b/src/main/java/org/olat/core/gui/components/chart/DefaultD3Component.java
similarity index 87%
rename from src/main/java/org/olat/core/gui/components/chart/AbstractD3Component.java
rename to src/main/java/org/olat/core/gui/components/chart/DefaultD3Component.java
index 3e1e6c184d1951d1758bd23bed985edc718ec6d0..5a6fe7e5b4bac09b81b2879d8df09b1d4be4c093 100644
--- a/src/main/java/org/olat/core/gui/components/chart/AbstractD3Component.java
+++ b/src/main/java/org/olat/core/gui/components/chart/DefaultD3Component.java
@@ -22,6 +22,7 @@ package org.olat.core.gui.components.chart;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.AbstractComponent;
 import org.olat.core.gui.components.ComponentRenderer;
+import org.olat.core.gui.components.DefaultComponentRenderer;
 import org.olat.core.gui.render.ValidationResult;
 
 /**
@@ -31,9 +32,11 @@ import org.olat.core.gui.render.ValidationResult;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public abstract class AbstractD3Component extends AbstractComponent {
+public class DefaultD3Component extends AbstractComponent {
 	
-	public AbstractD3Component(String name) {
+	private static final ComponentRenderer EMPTY_RENDERER = new DefaultComponentRenderer();
+	
+	public DefaultD3Component(String name) {
 		super(name);
 	}
 	
@@ -43,7 +46,9 @@ public abstract class AbstractD3Component extends AbstractComponent {
 	}
 
 	@Override
-	public abstract ComponentRenderer getHTMLRendererSingleton();
+	public ComponentRenderer getHTMLRendererSingleton() {
+		return EMPTY_RENDERER;
+	}
 
 	@Override
 	public void validate(UserRequest ureq, ValidationResult vr) {
@@ -78,5 +83,4 @@ public abstract class AbstractD3Component extends AbstractComponent {
         }
 		return false;
 	}
-
 }
diff --git a/src/main/java/org/olat/core/gui/components/chart/HistogramComponent.java b/src/main/java/org/olat/core/gui/components/chart/HistogramComponent.java
deleted file mode 100644
index 3689373eafaf915dcf3d2fcbb5833d6324c2f6ee..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/core/gui/components/chart/HistogramComponent.java
+++ /dev/null
@@ -1,130 +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.gui.components.chart;
-
-import org.olat.core.gui.components.ComponentRenderer;
-
-/**
- * 
- * Make an histogram from a list of values (doubles or longs but not booth)
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class HistogramComponent extends AbstractD3Component {
-	
-	private static final HistogramComponentRenderer RENDERER = new HistogramComponentRenderer();
-	
-	private double[] doubleValues;
-	private long[] longValues;
-	
-	private double maxValue;
-	private String defaultBarClass = "bar_default";
-	
-	private Scale xScale;
-	private String yLegend;
-	
-	private double cutValue;
-	private String lowBarClass;
-	private String highBarClass;
-	
-	public HistogramComponent(String name) {
-		super(name);
-	}
-
-	public String getDefaultBarClass() {
-		return defaultBarClass;
-	}
-
-	public void setDefaultBarClass(String defaultBarClass) {
-		this.defaultBarClass = defaultBarClass;
-	}
-
-	public double getCutValue() {
-		return cutValue;
-	}
-
-	public String getLowBarClass() {
-		return lowBarClass;
-	}
-
-	public String getHighBarClass() {
-		return highBarClass;
-	}
-	
-	/**
-	 * Set a cut value for the x axis. Value lower than the cut value
-	 * will get the lowBarClass, and bigger the highBarClass.
-	 * 
-	 * @param lowBarClass
-	 * @param cutValue
-	 * @param highBarClass
-	 */
-	public void setCutValue(String lowBarClass, double cutValue, String highBarClass) {
-		this.cutValue = cutValue;
-		this.lowBarClass = lowBarClass;
-		this.highBarClass = highBarClass;
-	}
-
-	public Scale getXScale() {
-		return xScale;
-	}
-
-	public void setXScale(Scale xScale) {
-		this.xScale = xScale;
-	}
-
-	public String getYLegend() {
-		return yLegend;
-	}
-
-	public void setYLegend(String yLegend) {
-		this.yLegend = yLegend;
-	}
-
-	public double getMaxValue() {
-		return maxValue;
-	}
-
-	public void setMaxValue(double maxValue) {
-		this.maxValue = maxValue;
-	}
-
-	public double[] getDoubleValues() {
-		return doubleValues;
-	}
-
-	public void setDoubleValues(double[] doubleValues) {
-		this.doubleValues = doubleValues;
-	}
-
-	public long[] getLongValues() {
-		return longValues;
-	}
-
-	public void setLongValues(long[] longValues) {
-		this.longValues = longValues;
-	}
-
-	@Override
-	public ComponentRenderer getHTMLRendererSingleton() {
-		return RENDERER;
-	}
-}
diff --git a/src/main/java/org/olat/core/gui/components/chart/HistogramComponentRenderer.java b/src/main/java/org/olat/core/gui/components/chart/HistogramComponentRenderer.java
deleted file mode 100644
index b9fbf4c248d0cd645f83a327cbdfac2416f86801..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/core/gui/components/chart/HistogramComponentRenderer.java
+++ /dev/null
@@ -1,206 +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.gui.components.chart;
-
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.DefaultComponentRenderer;
-import org.olat.core.gui.render.RenderResult;
-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;
-
-/**
- * 
- * Renderer a list of lons or doubles as an histogramm with d3js
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class HistogramComponentRenderer extends DefaultComponentRenderer {
-
-	@Override
-	public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu,
-			Translator translator, RenderResult renderResult, String[] args) {
-
-		HistogramComponent chartCmp = (HistogramComponent)source;
-		renderD3js(renderer, sb, chartCmp);
-	}
-	
-	private StringBuilder getValues(HistogramComponent chartCmp) {
-		StringBuilder sb = new StringBuilder();
-		if(chartCmp.getDoubleValues() != null) {
-			for(double value:chartCmp.getDoubleValues()) {
-				if(sb.length() > 0) sb.append(",");
-				sb.append(value);
-			}	
-		} else if(chartCmp.getLongValues() != null) {
-			for(long value:chartCmp.getLongValues()) {
-				if(sb.length() > 0) sb.append(",");
-				sb.append((double)value);
-			}
-		}
-		return sb;
-	}
-	
-	private double getMaxValue(HistogramComponent chartCmp) {
-		double maxValue = 0.0;
-		if(chartCmp.getMaxValue() > 0) {
-			maxValue =  chartCmp.getMaxValue();
-		} else if(chartCmp.getDoubleValues() != null) {
-			for(double value:chartCmp.getDoubleValues()) {
-				maxValue = Math.max(maxValue, value);
-			}	
-		} else if(chartCmp.getLongValues() != null) {
-			for(long value:chartCmp.getLongValues()) {
-				maxValue = Math.max(maxValue, value);
-			}
-		}
-		
-		double ceiledRoundedMaxValue = Math.ceil(maxValue);
-		return ceiledRoundedMaxValue;
-	}
-	
-	private StringOutput getFillFunction(HistogramComponent chartCmp) {
-		StringOutput sb = new StringOutput();
-		if(chartCmp.getCutValue() > 0.0001) {
-			String cutValue = Double.toString(chartCmp.getCutValue());
-			sb.append("function(d, i) { if(data[i].x < ").append(cutValue).append(") ")
-			  .append("  return 'bar ").append(chartCmp.getLowBarClass()).append("';")
-			  .append(" return 'bar ").append(chartCmp.getHighBarClass()).append("';}");
-		} else {
-			sb.append("'bar ").append(chartCmp.getDefaultBarClass()).append("'");
-		}
-		return sb;
-	}
-	
-	private void renderD3js(Renderer renderer, StringOutput sb, HistogramComponent chartCmp) {
-		
-		String cmpId = chartCmp.getDispatchID();
-		StringBuilder values = getValues(chartCmp);
-		double maxValue = getMaxValue(chartCmp);
-		String yLegend = chartCmp.getYLegend();
-		
-		if(maxValue <= 0.1) {
-			sb.append("No data");
-			return;//no values
-		}
-
-		sb.append("<div id='d").append(cmpId).append("d3holder' class='d3chart' style='width:600px;height:300px'></div>\n")
-		  .append("<script type='text/javascript'>\n")
-		  .append("/* <![CDATA[ */ ")
-		  .append("jQuery(function () {\n")
-		  .append("var placeholderheight = jQuery('#d").append(cmpId).append("d3holder').height();\n")
-		  .append("var placeholderwidth = jQuery('#d").append(cmpId).append("d3holder').width();\n");
-
-		
-		sb.append("var values =[").append(values).append("];\n")
-		// Formatters for counts and times (converting numbers to Dates).
-		  .append("var formatCount = d3.format(',.f'),\n")
-		  .append("  formatTime = d3.time.format('%H:%M'),\n")
-		  .append("  formatMinutes = function(d) { return formatTime(new Date(2012, 0, 1, 0, d)); };\n")
-
-		  .append("var margin = {top: 10, right: 40, bottom: 30, left: 70},\n")
-		  .append("  width = placeholderwidth - margin.left - margin.right,\n")
-		  .append("  height = placeholderheight - margin.top - margin.bottom;\n")
-
-		  .append("var x = d3.scale.linear()\n")
-		  .append("  .domain([0, ").append(Double.toString(maxValue)).append("])\n")
-		  .append("  .range([0, width]);\n")
-
-		  //generate a histogram using twenty uniformly-spaced bins.
-		  .append("var data = d3.layout.histogram()\n")
-		  .append("  .bins(x.ticks(20))\n")
-		  .append("  (values);\n")
-
-		  .append("var sum = d3.sum(data, function(d) { return d.y; });\n")
-
-		  .append("var y = d3.scale.linear()\n")
-		  .append("  .domain([0, d3.max(data, function(d) { return d.y; })])\n")
-		  .append("  .range([height, 0]);\n")
-
-		  .append("var y2 = d3.scale.linear()\n")
-		  .append("  .domain([0, d3.max(data, function(d) { return d.y / sum; })])\n")
-		  .append("  .range([height, 0]);\n")
-
-		  .append("var xAxis = d3.svg.axis()\n")
-		  .append("  .scale(x)\n")
-		  .append("  .orient('bottom')\n");
-		if(chartCmp.getXScale() == Scale.hour) {
-			sb.append("  .tickFormat(formatMinutes);\n");
-		} else {
-			sb.append("  .tickFormat(d3.format('.01f'));\n");
-		}
-		sb.append("var yAxis = d3.svg.axis()\n")
-		  .append("  .scale(y)\n")
-		  .append("  .orient('right')\n")
-		  .append("  .ticks(10);\n")
-		    
-		  .append("var y2Axis = d3.svg.axis()\n")
-		  .append("  .scale(y2)\n")
-		  .append("  .orient('left')\n")
-		  .append("  .ticks(10, '%');\n")
-
-		  .append("var svg = d3.select('#d").append(cmpId).append("d3holder').append('svg')\n")
-		  .append("  .attr('width', width + margin.left + margin.right)\n")
-		  .append("  .attr('height', height + margin.top + margin.bottom)\n")
-		  .append(" .append('g')\n")
-		  .append("  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');\n")
-
-		  .append("var bar = svg.selectAll('.bar')\n")
-		  .append("  .data(data)\n")
-		  .append(" .enter().append('g')\n")
-		  .append("  .attr('class', ").append(getFillFunction(chartCmp)).append(")\n")
-
-		  .append("  .attr('transform', function(d) { return 'translate(' + x(d.x) + ',' + y(d.y) + ')'; })\n")
-		  .append("  .append('rect')\n")
-		  .append("  .attr('x', 1)\n")
-		  .append("  .attr('width', x(data[0].dx) - 1)\n")
-		  .append("  .attr('height', function(d) { return height - y(d.y); });\n")
-
-		  //x axis
-		  .append("svg.append('g')\n")
-		  .append("  .attr('class', 'x axis')\n")
-		  .append("  .attr('transform', 'translate(0,' + height + ')')\n")
-		  .append("  .call(xAxis);\n")
-
-		  //right y axis
-		  .append("svg.append('g')\n")
-		  .append("  .attr('class', 'y axis')\n")
-		  .append("  .attr('transform', 'translate(' + width + ',0)')\n")
-		  .append(" .call(yAxis)\n")
-
-		  //left y axis with legend
-		  .append("svg.append('g')\n")
-		  .append("  .attr('class', 'y axis')\n")
-		  .append("  .call(y2Axis)\n")
-		  .append(" .append('text')\n")
-		  .append("  .attr('transform', 'rotate(-90)')\n")
-		  .append("  .attr('y', 0 - margin.left)\n")
-		  .append("  .attr('x', 0 - (height / 2))\n")
-		  .append("  .attr('dy', '1em')\n")
-		  .append("  .style('text-anchor', 'middle')\n")
-		  .append("  .text('").append(yLegend).append("');\n");
-
-		sb.append("});\n")
-		  .append("/* ]]> */")
-		  .append("</script>\n");
-	}
-}
diff --git a/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponent.java b/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponent.java
deleted file mode 100644
index a83dcff1e736203e674e2545a35470aae9fca5c0..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponent.java
+++ /dev/null
@@ -1,86 +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.gui.components.chart;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.olat.core.gui.components.ComponentRenderer;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class HorizontalBarChartComponent extends AbstractD3Component {
-	
-	private static final HorizontalBarChartComponentRenderer RENDERER = new HorizontalBarChartComponentRenderer();
-	
-	private Scale xScale = Scale.plain;
-	private List<BarSeries> seriesList = new ArrayList<>();
-	
-	private String defaultBarClass = "bar_default";
-	private String xLegend;
-	
-	public HorizontalBarChartComponent(String name) {
-		super(name);
-	}
-
-	public Scale getXScale() {
-		return xScale;
-	}
-
-	public void setXScale(Scale xScale) {
-		this.xScale = xScale;
-	}
-	
-	public String getXLegend() {
-		return xLegend;
-	}
-
-	public void setXLegend(String xLegend) {
-		this.xLegend = xLegend;
-	}
-
-	public List<BarSeries> getSeries() {
-		return seriesList;
-	}
-
-	public void addSeries(BarSeries... series) {
-		if(series != null && series.length > 0 && series[0] != null) {
-			for(BarSeries s:series) {
-				seriesList.add(s);
-			}
-		}
-	}
-
-	public String getDefaultBarClass() {
-		return defaultBarClass;
-	}
-
-	public void setDefaultBarClass(String defaultBarClass) {
-		this.defaultBarClass = defaultBarClass;
-	}
-
-	@Override
-	public ComponentRenderer getHTMLRendererSingleton() {
-		return RENDERER;
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponentRenderer.java b/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponentRenderer.java
deleted file mode 100644
index 54c81edb19b71dea5c7c687bb94b4c3a4f9b2615..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/core/gui/components/chart/HorizontalBarChartComponentRenderer.java
+++ /dev/null
@@ -1,165 +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.gui.components.chart;
-
-import java.util.List;
-
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.DefaultComponentRenderer;
-import org.olat.core.gui.components.chart.BarSeries.Stringuified;
-import org.olat.core.gui.render.RenderResult;
-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;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class HorizontalBarChartComponentRenderer extends DefaultComponentRenderer {
-
-	@Override
-	public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu,
-			Translator translator, RenderResult renderResult, String[] args) {
-		
-		HorizontalBarChartComponent chartCmp = (HorizontalBarChartComponent)source;
-		renderD3js(renderer, sb, chartCmp);
-	}
-
-	private void renderD3js(Renderer renderer, StringOutput sb, HorizontalBarChartComponent chartCmp) {
-		String cmpId = chartCmp.getDispatchID();
-		List<BarSeries> seriesList = chartCmp.getSeries();
-		Stringuified infos = BarSeries.getDatasAndColors(seriesList, chartCmp.getDefaultBarClass());
-		int maxNumOfPoints = getNumOfPoints(chartCmp);
-		int height = 50 + (25 * maxNumOfPoints);
-		sb.append("<div id='d").append(cmpId).append("d3holder' class='d3chart' style='width:600px;height:").append(height).append("px'></div>\n")
-		  .append("<script type='text/javascript'>\n")
-		  .append("/* <![CDATA[ */ ")
-		  .append("jQuery(function () {\n")
-		  .append("var placeholderheight = jQuery('#d").append(cmpId).append("d3holder').height();\n")
-		  .append("var placeholderwidth = jQuery('#d").append(cmpId).append("d3holder').width();\n");
-		
-		sb.append("var data = [").append(infos.getData()).append("];\n");
-		
-		sb.append("var margin = {top: 20, right: 20, bottom: 30, left: 300},\n")
-		  .append("   width = placeholderwidth - margin.left - margin.right,\n")
-		  .append("   height = placeholderheight - margin.top - margin.bottom;\n")
-
-		  .append("var x = d3.scale.linear()\n")
-		  .append("   .range([0, width], .1);\n")
-
-		  .append("var y = d3.scale.ordinal()\n")
-		  .append("   .rangeRoundBands([height, 0]);\n")
-
-		  .append("var xAxis = d3.svg.axis()\n")
-		  .append("   .scale(x)\n")
-		  .append("   .orient('bottom')\n")
-		  .append("   .ticks(5);\n")
-
-		  .append("var yAxis = d3.svg.axis()\n")
-		  .append("   .scale(y)\n")
-		  .append("   .orient('left');\n")
-
-		  .append("var svg = d3.select('#d").append(cmpId).append("d3holder').append('svg')\n")
-		  .append("   .attr('width', width + margin.left + margin.right)\n")
-		  .append("   .attr('height', height + margin.top + margin.bottom)\n")
-		  .append(" .append('g')\n")
-		  .append("   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');\n")
-
-		  .append("x.domain([0, d3.max(data, function(d) { return d[1]; })]);\n")
-		  .append("y.domain(data.map(function(d) { return d[0]; }));\n")
-
-		  .append("svg.append('g')\n")
-		  .append("     .attr('class', 'x axis')\n")
-		  .append("     .attr('transform', 'translate(0,' + height + ')')\n")
-		  .append("     .call(xAxis);\n")
-
-		  .append("svg.append('g')\n")
-		  .append("     .attr('class', 'y axis')\n")
-		  .append("     .call(yAxis);\n");
-		appendSeries( sb, infos.getColors(), chartCmp);
-/*
-		  .append("svg.selectAll('.bar')\n")
-		  .append("     .data(data)\n")
-		  .append("   .enter().append('rect')\n")
-		  .append("     .attr('class', 'bar bar_default')\n")
-		  .append("     .attr('x', 0)\n")
-		  .append("     .attr('width', function(d) { return width - x(d[1]); })\n")
-		  .append("     .attr('y', function(d) { return y(d[0]); })\n")
-		  .append("     .attr('height', y.rangeBand() - 2);\n");
-		*/
-
-		sb.append("});\n")
-		  .append("/* ]]> */")
-		  .append("</script>\n");
-	}
-	
-	private int getNumOfPoints(HorizontalBarChartComponent chartCmp) {
-		int maxNumOfPoints = 0;
-		List<BarSeries> seriesList = chartCmp.getSeries();
-		for(BarSeries series:seriesList) {
-			int numOfPoints = series.getPoints().size();
-			maxNumOfPoints = Math.max(maxNumOfPoints, numOfPoints);
-		}
-		return maxNumOfPoints;
-	}
-	
-	private void appendSeries(StringOutput sb, StringBuilder colors, HorizontalBarChartComponent chartCmp) {
-		if(colors.length() > 0) {
-			sb.append("var colors = [").append(colors).append("];");
-		}
-		
-		List<BarSeries> seriesList = chartCmp.getSeries();
-		for(int i=0; i<seriesList.size(); i++) {
-			String color = seriesList.get(i).getCssClass();
-			if(color == null) {
-				color = chartCmp.getDefaultBarClass();
-			}
-			
-			String correction = getCorrection(i);
-		
-			sb.append("svg.selectAll('.bar").append(i).append("')\n")
-			  .append("    .data(data)\n")
-			  .append("  .enter().append('rect')\n");
-			
-			if(colors.length() == 0) {
-				sb.append("    .attr('class', 'bar bar").append(i).append(" ").append(color).append("')\n");
-			} else {
-				sb.append("    .attr('class', function(d, i){ if(colors.length > i) { return colors[i]; } return 'bar bar").append(i).append(" ").append(color).append("'; })\n");
-			}
-
-			sb.append("    .attr('fill', '").append(color).append("')\n")
-			  .append("    .attr('x', ").append(correction).append(")\n")
-			  .append("    .attr('y', function(d) { return y(d[0]); })\n")
-			  .append("    .attr('width', function(d) { return x(d[").append((i+1)).append("]); })\n")
-			  //.append("    .attr('height', function(d) { return height - y(d[").append((i+1)).append("]); });\n");
-			  .append("     .attr('height', y.rangeBand() - 2);\n");
-		}
-	}
-	
-	private String getCorrection(int i) {
-		if(i == 0) return "0";
-		if(i == 1) return "function(d) { return x(d[1]); }";
-		if(i == 2) return "function(d) { return x(d[1] + d[2]); }";
-		return "";
-	}
-}
diff --git a/src/main/java/org/olat/core/gui/components/chart/Scale.java b/src/main/java/org/olat/core/gui/components/chart/StatisticsComponent.java
similarity index 56%
rename from src/main/java/org/olat/core/gui/components/chart/Scale.java
rename to src/main/java/org/olat/core/gui/components/chart/StatisticsComponent.java
index 60dec5ba3295d24f21b1de3899f1839d90110a16..e1fbdceff58277e0bdc4cfdeda69097a34f049fc 100644
--- a/src/main/java/org/olat/core/gui/components/chart/Scale.java
+++ b/src/main/java/org/olat/core/gui/components/chart/StatisticsComponent.java
@@ -19,13 +19,33 @@
  */
 package org.olat.core.gui.components.chart;
 
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.ComponentRenderer;
+import org.olat.core.gui.components.DefaultComponentRenderer;
+import org.olat.core.gui.render.ValidationResult;
+
 /**
  * 
+ * Initial date: 07.03.2014<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public enum Scale {
-	plain,
-	percent,
-	hour
+public class StatisticsComponent extends DefaultD3Component {
+	
+	private static final ComponentRenderer RENDERER = new DefaultComponentRenderer();
+	
+	public StatisticsComponent(String name) {
+		super(name);
+	}
+	
+	@Override
+	public ComponentRenderer getHTMLRendererSingleton() {
+		return RENDERER;
+	}
+	
+	@Override
+	public void validate(UserRequest ureq, ValidationResult vr) {
+		super.validate(ureq, vr);
+		vr.getJsAndCSSAdder().addRequiredStaticJsFile("js/jquery/openolat/jquery.statistics.chart.min.js");
+	}
 }
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormLink.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormLink.java
index c765ceab03442f67755e8ffc5f6a59464a79b067..21933f8630f59ea60fddd39ab3458e00f8b9544d 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormLink.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FormLink.java
@@ -40,6 +40,11 @@ public interface FormLink extends FormItem{
 	 * @param customDisabledLinkCSS
 	 */
 	public void setCustomDisabledLinkCSS(String customDisabledLinkCSS);
+	
+	/**
+	 * @return The i18n key for the link text
+	 */
+	public String getI18nKey();
 
 	/**
 	 * Set the i18n key for the link text
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
index 0a5cc7ab93fd81a1f4ddf6280681e889e1285d69..8981a6252945e8f6537053a751f5cc61625e2252 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
@@ -197,6 +197,10 @@ public class FormLinkImpl extends FormItemImpl implements FormLink {
 			this.component.setCustomDisabledLinkCSS(customDisabledLinkCSS);
 		}
 	}
+	
+	public String getI18nKey() {
+		return i18n;
+	}
 
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.elements.FormLink#setI18nKey(java.lang.String)
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/TextBoxListElementImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/TextBoxListElementImpl.java
index 958c410c9897f0962aad0918820545614de76659..e50254d3be64d646fa4c7bad6092d320786825c0 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/TextBoxListElementImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/TextBoxListElementImpl.java
@@ -89,7 +89,7 @@ public class TextBoxListElementImpl extends AbstractTextElement implements TextB
 	@Override
 	public String getValue(){
 	//	String paramVal = getRootForm().getRequestParameter("textboxlistinput" + getFormDispatchId());
-		return StringUtils.join(this.component.getCurrentItemValues(),", ");
+		return StringUtils.join(component.getCurrentItemValues(),", ");
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/core/gui/components/image/ImageComponent.java b/src/main/java/org/olat/core/gui/components/image/ImageComponent.java
index 5f72e399c5913571f3e3dd7d03b2eea62cece5fa..fd6e91170a76c21d2e845c0d9bb634a336995305 100644
--- a/src/main/java/org/olat/core/gui/components/image/ImageComponent.java
+++ b/src/main/java/org/olat/core/gui/components/image/ImageComponent.java
@@ -222,23 +222,24 @@ public class ImageComponent extends AbstractComponent {
 	
 	private Size getImageSize(String suffix) {
 		Size result = null;
-    Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
-    if (iter.hasNext()) {
-        ImageReader reader = iter.next();
-        try {
-            ImageInputStream stream = new MemoryCacheImageInputStream(mediaResource.getInputStream());
-            reader.setInput(stream);
-            int width = reader.getWidth(reader.getMinIndex());
-            int height = reader.getHeight(reader.getMinIndex());
-            result = new Size(width, height, false);
-        } catch (IOException e) {
-            log.error(e.getMessage());
-        } finally {
-            reader.dispose();
-        }
-    } else {
-        log.error("No reader found for given format: " + suffix);
-    }
-    return result;
+		Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
+		if (iter.hasNext()) {
+			ImageReader reader = iter.next();
+			try {
+				ImageInputStream stream = new MemoryCacheImageInputStream(mediaResource.getInputStream());
+				reader.setInput(stream);
+				int readerMinIndex = reader.getMinIndex();
+				int width = reader.getWidth(readerMinIndex);
+				int height = reader.getHeight(readerMinIndex);
+				result = new Size(width, height, false);
+			} catch (IOException e) {
+				log.error(e.getMessage());
+			} finally {
+				reader.dispose();
+			}
+		} else {
+			log.error("No reader found for given format: " + suffix);
+		}
+		return result;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListComponent.java b/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListComponent.java
index f8d3bef7b548fbf0a5100714db1de5a2794d6075..214de503c04b2dce5a3dd191e8ad66af34eae835 100644
--- a/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListComponent.java
+++ b/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListComponent.java
@@ -215,11 +215,14 @@ public abstract class TextBoxListComponent extends FormBaseComponentImpl {
 	 */
 	private String getAutoCompletionItemCaptionByValue(String itemValue) {
 		String autoCompletionItemCaption = "";
-		if (this.getAutoCompleteContent() == null)
+		Map<String,String> content = getAutoCompleteContent();
+		if (content == null) {
 			return autoCompletionItemCaption;
-		for (Entry<String, String> autoCompletionItemEntry : this.getAutoCompleteContent().entrySet()) {
-			if (autoCompletionItemEntry.getValue().equals(itemValue))
+		}
+		for (Entry<String, String> autoCompletionItemEntry : content.entrySet()) {
+			if (autoCompletionItemEntry.getValue().equals(itemValue)) {
 				autoCompletionItemCaption = autoCompletionItemEntry.getKey();
+			}
 		}
 		return autoCompletionItemCaption;
 	}
@@ -442,7 +445,9 @@ public abstract class TextBoxListComponent extends FormBaseComponentImpl {
 			List<String> filtered = new ArrayList<String>();
 			for(String item:content.keySet()) {
 				String antiItem = filter.filter(item);
-				filtered.add(StringHelper.escapeHtml(antiItem));
+				if(StringHelper.containsNonWhitespace(antiItem)) {
+					filtered.add(antiItem);
+				}
 			}
 			return StringUtils.join(filtered, ", ");
 		} else
diff --git a/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListRenderer.java b/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListRenderer.java
index 29530308a021666b2df2ba69f2494d36e40efb2f..c66c32df486ee58b1c90b34dc2a94f8aad640b09 100644
--- a/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/textboxlist/TextBoxListRenderer.java
@@ -102,10 +102,11 @@ public class TextBoxListRenderer implements ComponentRenderer {
 		TextBoxListElementImpl te = ((TextBoxListElementComponent)tblComponent).getTextElementImpl();
 		Form rootForm = te.getRootForm();
 		String dispatchId = tblComponent.getFormDispatchId();
+		String initialValue = tblComponent.getInitialItemsAsString();
 
 		output.append("<input type=\"text\" id=\"textboxlistinput").append(dispatchId).append("\" ")
 		      .append("name='textboxlistinput").append(dispatchId).append("' ")
-		      .append("value='").append(tblComponent.getInitialItemsAsString()).append("'")
+		      .append("value='").append(initialValue).append("'")
 		      .append("/>\n");
 
 		// OO-137 : here, we display the currentItems. (at first render, this is equal to initialItems)
diff --git a/src/main/java/org/olat/core/util/image/spi/ImageHelperImpl.java b/src/main/java/org/olat/core/util/image/spi/ImageHelperImpl.java
index d53bc38e17f81332d28e9e2186fd3574b8656065..b38747605bf2b8bf4ed6eb9a7878d3d107a511bf 100644
--- a/src/main/java/org/olat/core/util/image/spi/ImageHelperImpl.java
+++ b/src/main/java/org/olat/core/util/image/spi/ImageHelperImpl.java
@@ -43,6 +43,7 @@ import javax.imageio.IIOImage;
 import javax.imageio.ImageIO;
 import javax.imageio.ImageReadParam;
 import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
 import javax.imageio.ImageWriteParam;
 import javax.imageio.ImageWriter;
 import javax.imageio.stream.FileImageInputStream;
@@ -359,45 +360,55 @@ public class ImageHelperImpl implements ImageHelperSPI {
 		return computeScaledSize(width, height, maxWidth, maxHeight);
 	}
 	
+	private static SizeAndBufferedImage calcScaledSize(ImageInputStream stream,
+			String suffix, int maxWidth, int maxHeight) {
+		Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
+		if(iter.hasNext()) {
+			ImageReader reader = iter.next();
+			try {
+				reader.setInput(stream, true, true);
+				int width = reader.getWidth(reader.getMinIndex());
+				int height = reader.getHeight(reader.getMinIndex());
+				Size size = new Size(width, height, false);
+				Size scaledSize = computeScaledSize(width, height, maxWidth, maxHeight);
+				SizeAndBufferedImage all = new SizeAndBufferedImage(size, scaledSize);
+				
+				int readerMinIndex = reader.getMinIndex();
+				ImageReadParam param = reader.getDefaultReadParam();
+                Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
+                while (imageTypes.hasNext()) {
+                    ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
+                    int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
+                    if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
+                        param.setDestinationType(imageTypeSpecifier);
+                        break;
+                    }
+                }
 
-	
-	private static SizeAndBufferedImage calcScaledSize(ImageInputStream stream, String suffix, int maxWidth, int maxHeight) {
-    Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
-    if (iter.hasNext()) {
-        ImageReader reader = iter.next();
-        try {
-            reader.setInput(stream);
-            int width = reader.getWidth(reader.getMinIndex());
-            int height = reader.getHeight(reader.getMinIndex());
-            Size size = new Size(width, height, false);
-            Size scaledSize = computeScaledSize(width, height, maxWidth, maxHeight);
-            SizeAndBufferedImage  all = new SizeAndBufferedImage(size, scaledSize);
-            
-            double memoryKB = (width * height * 4) / 1024d;
-            if(memoryKB > 2000) {//check limit at 20MB
-            	double free = Runtime.getRuntime().freeMemory() / 1024d;
-            	if(free > memoryKB) {
-                all.setImage(reader.read(reader.getMinIndex()));
-            	} else {
-            		//make sub sampling to save memory
-            		int ratio = (int)Math.round(Math.sqrt(memoryKB / free));
-            		ImageReadParam param = reader.getDefaultReadParam();
-            		param.setSourceSubsampling(ratio, ratio, 0, 0);
-                all.setImage(reader.read(reader.getMinIndex(), param));
-            	}
-            } else {
-            	all.setImage(reader.read(reader.getMinIndex()));
-            }
-            return all;
-        } catch (IOException e) {
-            log.error(e.getMessage());
-        } finally {
-            reader.dispose();
-        }
-    } else {
-        log.error("No reader found for given format: " + suffix, null);
-    }
-    return null;
+				double memoryKB = (width * height * 4) / 1024d;
+				if (memoryKB > 2000) {// check limit at 20MB
+					double free = Runtime.getRuntime().freeMemory() / 1024d;
+					if (free > memoryKB) {
+						all.setImage(reader.read(readerMinIndex, param));
+					} else {
+						// make sub sampling to save memory
+						int ratio = (int) Math.round(Math.sqrt(memoryKB / free));
+						param.setSourceSubsampling(ratio, ratio, 0, 0);
+						all.setImage(reader.read(readerMinIndex, param));
+					}
+				} else {
+					all.setImage(reader.read(readerMinIndex, param));
+				}
+				return all;
+			} catch (IOException e) {
+				log.error(e.getMessage(), e);
+			} finally {
+				reader.dispose();
+			}
+		} else {
+			log.error("No reader found for given format: " + suffix, null);
+		}
+		return null;
 	}
 	
 	private static Size computeScaledSize(int width, int height, int maxWidth, int maxHeight) {
diff --git a/src/main/java/org/olat/core/util/mail/MailNotificationEditController.java b/src/main/java/org/olat/core/util/mail/MailNotificationEditController.java
index 2870e6b4f31461654ad8cfdb4b1dce05043fb892..dcb77787a6eeefd220a497b00e11109a0ca7d0f0 100644
--- a/src/main/java/org/olat/core/util/mail/MailNotificationEditController.java
+++ b/src/main/java/org/olat/core/util/mail/MailNotificationEditController.java
@@ -65,7 +65,7 @@ public class MailNotificationEditController extends BasicController {
 	 * @param useCancel
 	 */
 	public MailNotificationEditController(WindowControl wControl, UserRequest ureq, MailTemplate mailTemplate,
-			boolean useCancel, boolean mandatory) {
+			boolean useCancel, boolean mandatory, boolean cc) {
 		super(ureq, wControl);
 		this.mailTemplate = mailTemplate;
 		orgMailSubject = mailTemplate.getSubjectTemplate();
@@ -73,7 +73,7 @@ public class MailNotificationEditController extends BasicController {
 		cpFrom = mailTemplate.getCpfrom();
 		
 		mainVC = createVelocityContainer("mailnotification");
-		mailForm = new MailTemplateForm(ureq, wControl, mailTemplate, useCancel, mandatory);
+		mailForm = new MailTemplateForm(ureq, wControl, mailTemplate, useCancel, mandatory, cc);
 		listenTo(mailForm);
 		
 		mainVC.put("mailForm", mailForm.getInitialComponent());
diff --git a/src/main/java/org/olat/core/util/mail/MailTemplateForm.java b/src/main/java/org/olat/core/util/mail/MailTemplateForm.java
index d05316f239c976871719114c7ff7e1afa155f811..9f559a4e51b89c4f9e93f2479479dea6b06c5d8d 100644
--- a/src/main/java/org/olat/core/util/mail/MailTemplateForm.java
+++ b/src/main/java/org/olat/core/util/mail/MailTemplateForm.java
@@ -55,6 +55,7 @@ public class MailTemplateForm extends FormBasicController {
 	private SelectionElement ccSender;
 	private final static String NLS_CONTACT_SEND_CP_FROM = "contact.cp.from";
 	
+	private final boolean cc;
 	private final boolean useCancel;
 	private final boolean useSubmit;
 	private MailTemplate template;
@@ -66,8 +67,10 @@ public class MailTemplateForm extends FormBasicController {
 	 * @param useCancel 
 	 * @param listeningController Controller that listens to form events
 	 */
-	public MailTemplateForm(UserRequest ureq, WindowControl wControl, MailTemplate template, boolean useCancel, boolean mandatoryEmail) {
+	public MailTemplateForm(UserRequest ureq, WindowControl wControl, MailTemplate template,
+			boolean useCancel, boolean mandatoryEmail, boolean cc) {
 		super(ureq, wControl);
+		this.cc = cc;
 		this.template = template;
 		this.useCancel = useCancel;
 		this.useSubmit = true;
@@ -77,6 +80,7 @@ public class MailTemplateForm extends FormBasicController {
 	
 	public MailTemplateForm(UserRequest ureq, WindowControl wControl, MailTemplate template, boolean mandatoryEmail, Form rootForm) {
 		super(ureq, wControl, LAYOUT_DEFAULT, null, rootForm);
+		this.cc = true;
 		this.template = template;
 		useCancel = useSubmit = false;
 		this.mandatoryEmail = mandatoryEmail;
@@ -90,7 +94,7 @@ public class MailTemplateForm extends FormBasicController {
 	public void updateTemplateFromForm(MailTemplate template) {
 		template.setSubjectTemplate(subjectElem.getValue());
 		template.setBodyTemplate(bodyElem.getValue());
-		template.setCpfrom(ccSender.isSelected(0));
+		template.setCpfrom(ccSender.isVisible() && ccSender.isSelected(0));
 	}
 
 	@Override
@@ -145,6 +149,7 @@ public class MailTemplateForm extends FormBasicController {
 		bodyElem.setMandatory(true);
 		
 		ccSender = uifactory.addCheckboxesVertical("tcpfrom", "", formLayout, new String[]{"xx"}, new String[]{translate(NLS_CONTACT_SEND_CP_FROM)}, null, 1);
+		ccSender.setVisible(cc);
 		
 		FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
 		formLayout.add(buttonGroupLayout);
@@ -163,7 +168,7 @@ public class MailTemplateForm extends FormBasicController {
 		boolean sm = mandatoryEmail || sendMail.isSelected(0);
 		subjectElem.setVisible(sm);
 		bodyElem.setVisible(sm);
-		ccSender.setVisible(sm);	
+		ccSender.setVisible(sm && cc);	
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/course/assessment/AssessmentEditController.java b/src/main/java/org/olat/course/assessment/AssessmentEditController.java
index d8e6c6ffbc69fe0a6751eb9702566cff8d5922de..64a7e40dc0dc20c6382c4a6dcebd148f2964725c 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentEditController.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentEditController.java
@@ -228,8 +228,9 @@ public class AssessmentEditController extends BasicController {
 		} else if (source == detailsEditController) {
 			//fxdiff FXOLAT-108: reset SCORM test
 			if(event == Event.CHANGED_EVENT) {
-				doUpdateAssessmentData(ureq.getIdentity());
-				fireEvent(ureq, Event.CHANGED_EVENT);
+				assessmentForm.reloadData();
+			} else if(event == Event.DONE_EVENT) {
+				fireEvent(ureq, Event.DONE_EVENT);
 			}
 		} else if (source == alreadyLockedDialogController) {
 			if (event == Event.CANCELLED_EVENT || DialogBoxUIFactory.isOkEvent(event)) {
@@ -252,10 +253,9 @@ public class AssessmentEditController extends BasicController {
 		ScoreEvaluation scoreEval = null;
 		Float newScore = null;
 		Boolean newPassed = null;
-		//String userName = userCourseEnvironment.getIdentityEnvironment().getIdentity().getName();
-
+		
 		if (assessmentForm.isHasAttempts() && assessmentForm.isAttemptsDirty()) {
-			this.courseNode.updateUserAttempts(new Integer(assessmentForm.getAttempts()), userCourseEnvironment, coachIdentity);
+			courseNode.updateUserAttempts(new Integer(assessmentForm.getAttempts()), userCourseEnvironment, coachIdentity);
 		}
 
 		if (assessmentForm.isHasScore() && assessmentForm.isScoreDirty()) {
@@ -295,11 +295,11 @@ public class AssessmentEditController extends BasicController {
 		if (assessmentForm.isCoachCommentDirty()) {
 			String newCoachComment = assessmentForm.getCoachComment().getValue();
 			// Update properties in db
-			this.courseNode.updateUserCoachComment(newCoachComment, userCourseEnvironment);
+			courseNode.updateUserCoachComment(newCoachComment, userCourseEnvironment);
 		}
 		
 		// Refresh score view
-		userCourseEnvironment.getScoreAccounting().scoreInfoChanged(this.courseNode, scoreEval);
+		userCourseEnvironment.getScoreAccounting().scoreInfoChanged(courseNode, scoreEval);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/assessment/AssessmentForm.java b/src/main/java/org/olat/course/assessment/AssessmentForm.java
index 02ba7d22aa993dd6cff9d1b5219e5cdf075cf61b..9bcbc40541e1c4864b9b0357480ab4948922f3ab 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentForm.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentForm.java
@@ -80,10 +80,10 @@ public class AssessmentForm extends FormBasicController {
 	AssessmentForm(UserRequest ureq, WindowControl wControl, AssessableCourseNode assessableCourseNode, AssessedIdentityWrapper assessedIdentityWrapper) {
 		super(ureq, wControl);
 		
-		this.hasAttempts = assessableCourseNode.hasAttemptsConfigured();
-		this.hasScore = assessableCourseNode.hasScoreConfigured();
-		this.hasPassed = assessableCourseNode.hasPassedConfigured();
-		this.hasComment = assessableCourseNode.hasCommentConfigured();
+		hasAttempts = assessableCourseNode.hasAttemptsConfigured();
+		hasScore = assessableCourseNode.hasScoreConfigured();
+		hasPassed = assessableCourseNode.hasPassedConfigured();
+		hasComment = assessableCourseNode.hasCommentConfigured();
 		
 		this.assessedIdentityWrapper = assessedIdentityWrapper;
 		this.assessableCourseNode = assessableCourseNode;
@@ -204,6 +204,30 @@ public class AssessmentForm extends FormBasicController {
 		}
 		return Float.parseFloat(scoreStr);
 	}
+	
+	protected void reloadData() {
+		UserCourseEnvironment userCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();
+		ScoreEvaluation scoreEval = userCourseEnv.getScoreAccounting().evalCourseNode(assessableCourseNode);
+		if (scoreEval == null) scoreEval = new ScoreEvaluation(null, null);
+		
+		if (hasAttempts) {
+			attemptsValue = assessableCourseNode.getUserAttempts(userCourseEnv);
+			attempts.setIntValue(attemptsValue == null ? 0 : attemptsValue.intValue());
+		}
+		
+		if (hasScore) {
+			scoreValue = scoreEval.getScore();
+			if (scoreValue != null) {
+				score.setValue(AssessmentHelper.getRoundedScore(scoreValue));
+			} 
+		}
+		
+		if (hasPassed) {
+			Boolean passedValue = scoreEval.getPassed();
+			passed.select(passedValue == null ? "undefined" :passedValue.toString(), true);
+			passed.setEnabled(cut == null);
+		}
+	}
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
diff --git a/src/main/java/org/olat/course/assessment/AssessmentMainController.java b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
index 862b17df0b274144cee5972b886947adf38fc062..ca80d081712b76571888a1043e9f5eec83b69a25 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentMainController.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
@@ -562,7 +562,7 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 			}
 		}
 		else if (source == assessmentEditController) {
-			if (event.equals(Event.CHANGED_EVENT)) {
+			if (event.equals(Event.CHANGED_EVENT) || event.equals(Event.DONE_EVENT)) {
 				// refresh identity in list model
 				if (userListCtr != null 
 						&& userListCtr.getTableDataModel() instanceof AssessedIdentitiesTableDataModel) {
@@ -571,8 +571,8 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 					if (aiwList.contains(this.assessedIdentityWrapper)) {
 						ICourse course = CourseFactory.loadCourse(ores);
 						aiwList.remove(this.assessedIdentityWrapper);
-						this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(this.assessedIdentityWrapper.getIdentity(),
-						this.localUserCourseEnvironmentCache, initialLaunchDates, course, currentCourseNode);
+						assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentityWrapper.getIdentity(),
+						localUserCourseEnvironmentCache, initialLaunchDates, course, currentCourseNode);
 						aiwList.add(this.assessedIdentityWrapper);
 						userListCtr.modelChanged();
 					}
diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
index 0373834e7a7c3bb297eacf8749439ea94d46ee07..6219e8b9e59f434380d29b23efd37cbe111c1364 100644
--- a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
+++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
@@ -154,6 +154,9 @@ public class IdentityAssessmentEditController extends BasicController {
 			} else if (event.equals(Event.CHANGED_EVENT)) {
 				doIdentityAssessmentOverview(ureq, true);
 				fireEvent(ureq, Event.CHANGED_EVENT);
+			} else if (event.equals(Event.DONE_EVENT)) {
+				doIdentityAssessmentOverview(ureq, true);
+				fireEvent(ureq, Event.DONE_EVENT);
 			}
 		}
 	}
diff --git a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
index 28bbfa55010e28eb1b51201ba551fd2273b58d3b..16367b38d307c9810ed28a380c44d77e8125cf44 100644
--- a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
@@ -340,10 +340,7 @@ public class CheckListCourseNode extends AbstractAccessableCourseNode implements
 	 */
 	@Override
 	public boolean isEditableConfigured() {
-		// manual scoring fields can be edited manually
-		ModuleConfiguration config = getModuleConfiguration();
-		Boolean manualCorr = (Boolean)config.get(CheckListCourseNode.CONFIG_KEY_PASSED_MANUAL_CORRECTION);
-		return true;//manualCorr != null && manualCorr.booleanValue();
+		return true;
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java
index 51f2f7c8c92164c88c370fcf0c6b0bfe9c4e7fb9..a699250b1d6a383e5f3f8c50c84a5b948d603b5f 100644
--- a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java
@@ -219,7 +219,7 @@ public class IQSELFCourseNode extends AbstractAccessableCourseNode implements Se
 		
 		try {
 			QTIExportFormatter qef = new QTIExportFormatterCSVType2(locale, null, "\t", "\"", "\\", "\r\n", false);
-			return qem.selectAndExportResults(qef, course.getResourceableId(), getShortTitle(), getIdent(), re, exportStream, charset, ".xls");
+			return qem.selectAndExportResults(qef, course.getResourceableId(), getShortTitle(), getIdent(), re, exportStream, ".xls");
 		} catch (IOException e) {
 			log.error("", e);
 			return false;
diff --git a/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java b/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java
index fa7012ef0663121a164243eb086e7cad072af292..d7e31842536d81a1103ab6e1c79d7c2198ef5fc7 100644
--- a/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java
@@ -137,7 +137,7 @@ public class IQSURVCourseNode extends AbstractAccessableCourseNode implements QT
 		QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(courseOres.getResourceableId(), getIdent());
 		searchParams.setLimitToGroups(options.getParticipantsGroups());
 
-		QTIStatisticResourceResult result = new QTIStatisticResourceResult(this, searchParams);
+		QTIStatisticResourceResult result = new QTIStatisticResourceResult(courseOres, this, searchParams);
 		return result;
 	}
 
@@ -243,7 +243,7 @@ public class IQSURVCourseNode extends AbstractAccessableCourseNode implements QT
 
 		QTIExportFormatter qef = new QTIExportFormatterCSVType3(locale, null,"\t", "\"", "\\", "\r\n", false);
 		try {
-			return qem.selectAndExportResults(qef, course.getResourceableId(), getShortTitle(), getIdent(), re, exportStream, charset, ".xls");
+			return qem.selectAndExportResults(qef, course.getResourceableId(), getShortTitle(), getIdent(), re, exportStream, ".xls");
 		} catch (IOException e) {
 			log.error("", e);
 			return false;
diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
index 304180e0293a6cbc711b83b4e8a405ebecf17fdc..86fedd266d89977f051e974ab5a9affd597f893b 100644
--- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
@@ -151,7 +151,7 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 		QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(courseOres.getResourceableId(), getIdent());
 		searchParams.setLimitToGroups(options.getParticipantsGroups());
 
-		QTIStatisticResourceResult result = new QTIStatisticResourceResult(this, searchParams);
+		QTIStatisticResourceResult result = new QTIStatisticResourceResult(courseOres, this, searchParams);
 		return result;
 	}
 	
@@ -424,7 +424,7 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 				String shortTitle = getShortTitle();
 				QTIExportManager qem = QTIExportManager.getInstance();
 				QTIExportFormatter qef = new QTIExportFormatterCSVType1(locale, "\t", "\"", "\\", "\r\n", false);
-				return qem.selectAndExportResults(qef, courseResourceableId, shortTitle, getIdent(), re, exportStream, charset, ".xls");
+				return qem.selectAndExportResults(qef, courseResourceableId, shortTitle, getIdent(), re, exportStream, ".xls");
 			}
 		} catch (IOException e) {
 			log.error("", e);
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 0a5a25fecf9dc020e72e9ee3ac41af9f66255c6f..a9819cc49fb5d47414e2702e2490931a07a71cf3 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
@@ -265,11 +265,11 @@ public class CheckboxManagerImpl implements CheckboxManager {
 			DBCheckbox lockedCheckbox = loadForUpdate(checkbox);
 			if(lockedCheckbox != null) {
 				//locked -> reload to make sure nobody create it
-				DBCheck reloaedCheck = loadCheck(checkbox, owner);
-				if(reloaedCheck == null) {
+				DBCheck reloadedCheck = loadCheck(checkbox, owner);
+				if(reloadedCheck == null) {
 					createCheck(lockedCheckbox, owner, score, checked);
 				} else {
-					currentCheck = reloaedCheck;
+					currentCheck = reloadedCheck;
 				}
 			}
 		}
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/AssessedIdentityCheckListController.java b/src/main/java/org/olat/course/nodes/cl/ui/AssessedIdentityCheckListController.java
index 5a848d6a837fa236373735ac40abcca653985194..a64ea1f3da16054283586e3a1a2352851d6616fb 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/AssessedIdentityCheckListController.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/AssessedIdentityCheckListController.java
@@ -27,6 +27,7 @@ import java.util.Map;
 
 import org.olat.core.CoreSpringFactory;
 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.MultipleSelectionElement;
 import org.olat.core.gui.components.form.flexible.elements.TextElement;
@@ -139,12 +140,22 @@ public class AssessedIdentityCheckListController extends FormBasicController {
 		boxEl.addActionListener(FormEvent.ONCHANGE);
 		
 		TextElement pointEl = null;
-		if(withScore) {
+		if(withScore && checkbox.getPoints() != null) {
 			String pointId = "point_" + checkbox.getCheckboxId();
-			String points = AssessmentHelper.getRoundedScore(check.getScore());
+			String points;
+			if(check != null && check.getChecked() != null && check.getChecked().booleanValue()) {
+				points = AssessmentHelper.getRoundedScore(check.getScore());
+			} else {
+				points = null;
+			}
 			pointEl = uifactory.addTextElement(pointId, null, 16, points, formLayout);
 			pointEl.setDisplaySize(5);
-			pointEl.setExampleKey("checklist.point.example", new String[]{ "0", "1"});
+			
+			Float maxScore = checkbox.getPoints();
+			if(maxScore != null) {
+				String maxValue = AssessmentHelper.getRoundedScore(maxScore);
+				pointEl.setExampleKey("checklist.point.example", new String[]{ "0", maxValue});
+			}
 		}
 		
 		CheckboxWrapper wrapper = new CheckboxWrapper(checkbox, check, boxEl, pointEl);
@@ -201,7 +212,34 @@ public class AssessedIdentityCheckListController extends FormBasicController {
 		checkboxManager.check(courseOres, courseNode.getIdent(), batchElements);
 		
 		courseNode.updateScoreEvaluation(userCourseEnv, assessedIdentity);
-		fireEvent(ureq, Event.DONE_EVENT);
+		fireEvent(ureq, Event.CHANGED_EVENT);
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(source instanceof MultipleSelectionElement) {
+			MultipleSelectionElement boxEl = (MultipleSelectionElement)source;
+			CheckboxWrapper wrapper = (CheckboxWrapper)boxEl.getUserObject();
+			doUpdateCheck(wrapper, boxEl.isAtLeastSelected(1));
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+	
+	private void doUpdateCheck(CheckboxWrapper wrapper, boolean check) {
+		if(wrapper.getPointEl() == null) return;//nothing to do
+
+		if(check) {
+			if(!StringHelper.containsNonWhitespace(wrapper.getPointEl().getValue())) {
+				Checkbox checkbox = wrapper.getCheckbox();
+				Float points = checkbox.getPoints();
+				if(points != null) {
+					String val = AssessmentHelper.getRoundedScore(points);
+					wrapper.getPointEl().setValue(val);
+				}
+			}
+		} else if(wrapper.getPointEl() != null) {
+			wrapper.getPointEl().setValue("");
+		}
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/CheckListAssessmentController.java b/src/main/java/org/olat/course/nodes/cl/ui/CheckListAssessmentController.java
index e2013a080baa004454718c1d08a04a78b58fc66d..6aecd793c2c0deb37a5a8085dce60e831a603368 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/CheckListAssessmentController.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/CheckListAssessmentController.java
@@ -68,6 +68,8 @@ import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
 import org.olat.core.id.UserConstants;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
 import org.olat.course.nodes.CheckListCourseNode;
 import org.olat.course.nodes.MSCourseNode;
 import org.olat.course.nodes.cl.CheckboxManager;
@@ -567,11 +569,13 @@ public class CheckListAssessmentController extends FormBasicController implement
 	
 	private void doExportPDF(UserRequest ureq) {
 		try {
+			ICourse course = CourseFactory.loadCourse(courseOres);
+			
 			String name = courseNode.getShortTitle();
 			CheckboxPDFExport pdfExport = new CheckboxPDFExport(name, getTranslator(), userPropertyHandlers);
 			pdfExport.setAuthor(userManager.getUserDisplayName(getIdentity()));
 			pdfExport.setCourseNodeTitle(courseNode.getShortTitle());
-			pdfExport.setCourseTitle(courseNode.getLongTitle());
+			pdfExport.setCourseTitle(course.getCourseTitle());
 			pdfExport.setCourseNodeTitle(courseNode.getShortTitle());
 			String groupName = table.getSelectedFilterValue();
 			pdfExport.setGroupName(groupName);
@@ -584,11 +588,13 @@ public class CheckListAssessmentController extends FormBasicController implement
 	
 	private void doCheckedExportPDF(UserRequest ureq) {
 		try {
+			ICourse course = CourseFactory.loadCourse(courseOres);
+			
 			String name = courseNode.getShortTitle();
 			CheckedPDFExport pdfExport = new CheckedPDFExport(name, getTranslator(), withScore, userPropertyHandlers);
 			pdfExport.setAuthor(userManager.getUserDisplayName(getIdentity()));
 			pdfExport.setCourseNodeTitle(courseNode.getShortTitle());
-			pdfExport.setCourseTitle(courseNode.getLongTitle());
+			pdfExport.setCourseTitle(course.getCourseTitle());
 			pdfExport.create(checkboxList, model.getObjects());
 			ureq.getDispatchResult().setResultingMediaResource(pdfExport);
 		} catch (IOException | COSVisitorException | TransformerException e) {
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/CheckListBoxListEditController.java b/src/main/java/org/olat/course/nodes/cl/ui/CheckListBoxListEditController.java
index 64ac429fea17a8a29ebc52a7d96d07859866d8b1..fbd90d6b5a0f2abe7ab985e0c341f20d38267a68 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/CheckListBoxListEditController.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/CheckListBoxListEditController.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.form.flexible.FormItem;
@@ -36,18 +37,28 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFle
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 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.form.flexible.impl.elements.table.StaticFlexiColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer;
 import org.olat.core.gui.components.link.Link;
 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.generic.closablewrapper.CloseableModalController;
 import org.olat.core.id.OLATResourceable;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.core.util.vfs.VFSMediaResource;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
 import org.olat.course.nodes.CheckListCourseNode;
 import org.olat.course.nodes.MSCourseNode;
+import org.olat.course.nodes.cl.CheckboxManager;
 import org.olat.course.nodes.cl.model.Checkbox;
 import org.olat.course.nodes.cl.model.CheckboxList;
 import org.olat.course.nodes.cl.ui.CheckboxConfigDataModel.Cols;
+import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.modules.ModuleConfiguration;
 
 /**
@@ -71,6 +82,8 @@ public class CheckListBoxListEditController extends FormBasicController {
 	private ModuleConfiguration config;
 	private final OLATResourceable courseOres;
 	private final CheckListCourseNode courseNode;
+
+	private final CheckboxManager checkboxManager;
 	
 	public CheckListBoxListEditController(UserRequest ureq, WindowControl wControl,
 			OLATResourceable courseOres, CheckListCourseNode courseNode, boolean inUse) {
@@ -79,6 +92,7 @@ public class CheckListBoxListEditController extends FormBasicController {
 		this.courseOres = courseOres;
 		this.courseNode = courseNode;
 		config = courseNode.getModuleConfiguration();
+		checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class);
 		
 		initForm(ureq);
 	}
@@ -106,7 +120,9 @@ public class CheckListBoxListEditController extends FormBasicController {
 		pointColModel = new DefaultFlexiColumnModel(visible, Cols.points.i18nKey(), Cols.points.ordinal(), false, null);
 		columnsModel.addFlexiColumnModel(pointColModel);
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.release.i18nKey(), Cols.release.ordinal()));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.file.i18nKey(), Cols.file.ordinal()));
+		columnsModel.addFlexiColumnModel(new StaticFlexiColumnModel(Cols.file.i18nKey(),
+				Cols.file.ordinal(), "download", false, null,
+				new StaticFlexiCellRenderer("download", new TextFlexiCellRenderer())));
 		columnsModel.addFlexiColumnModel(new StaticFlexiColumnModel("edit", translate("edit"), "edit"));
 		
 		CheckboxList list = (CheckboxList)config.get(CheckListCourseNode.CONFIG_KEY_CHECKBOX);
@@ -141,6 +157,9 @@ public class CheckListBoxListEditController extends FormBasicController {
 				if("edit".equals(cmd)) {
 					Checkbox row = model.getObject(se.getIndex());
 					doOpenEdit(ureq, row, false, translate("edit.checkbox"));
+				} else if("download".equals(cmd)) {
+					Checkbox row = model.getObject(se.getIndex());
+					doDownloadFile(ureq, row);
 				}
 			}
 		}
@@ -183,6 +202,18 @@ public class CheckListBoxListEditController extends FormBasicController {
 		boxTable.reset();
 	}
 	
+	private void doDownloadFile(UserRequest ureq, Checkbox checkbox) {
+		ICourse course = CourseFactory.loadCourse(courseOres);
+		CourseEnvironment courseEnv = course.getCourseEnvironment();
+		VFSContainer container = checkboxManager.getFileContainer(courseEnv, courseNode, checkbox);
+		VFSItem item = container.resolve(checkbox.getFilename());
+		if(item instanceof VFSLeaf) {
+			VFSMediaResource rsrc = new VFSMediaResource((VFSLeaf)item);
+			rsrc.setDownloadable(true);
+			ureq.getDispatchResult().setResultingMediaResource(rsrc);
+		}
+	}
+	
 	private void doEdit(UserRequest ureq, Checkbox checkbox) {
 		CheckboxList list = (CheckboxList)config.get(CheckListCourseNode.CONFIG_KEY_CHECKBOX);
 		if(list == null) {
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/CheckListRunController.java b/src/main/java/org/olat/course/nodes/cl/ui/CheckListRunController.java
index 379d0d9d6ab44f42e5c9ece5a89418786e56064a..fac9c416407a445ff2d14fc68f85a2a868061b64 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/CheckListRunController.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/CheckListRunController.java
@@ -27,7 +27,6 @@ import java.util.List;
 import java.util.Map;
 
 import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -135,6 +134,9 @@ public class CheckListRunController extends FormBasicController implements Contr
 			layoutCont.contextPut("readOnly", new Boolean(readOnly));
 			if(dueDate != null) {
 				layoutCont.contextPut("dueDate", dueDate);
+				if(dueDate.compareTo(new Date()) < 0) {
+					layoutCont.contextPut("afterDueDate", Boolean.TRUE);
+				}
 			}
 			layoutCont.contextPut("withScore", new Boolean(withScore));
 			
@@ -158,7 +160,6 @@ public class CheckListRunController extends FormBasicController implements Contr
 				layoutCont.contextPut("enableScoreInfo", Boolean.TRUE);
 				exposeConfigToVC(layoutCont);
 				exposeUserDataToVC(layoutCont);
-		
 			} else {
 				layoutCont.contextPut("enableScoreInfo", Boolean.FALSE);
 			}
@@ -269,9 +270,11 @@ public class CheckListRunController extends FormBasicController implements Contr
 			theOne = wrapper.getDbCheckbox();
 		}
 
-		Float score = wrapper.getCheckbox().getPoints();
+
+		Float score = checked ? wrapper.getCheckbox().getPoints() : 0f;
 		checkboxManager.check(theOne, getIdentity(), score, new Boolean(checked));
-		DBFactory.getInstance().commit();//make sure all results is on the database before calculating some scores
+		//make sure all results is on the database before calculating some scores
+		//manager commit already DBFactory.getInstance().commit();
 		
 		courseNode.updateScoreEvaluation(userCourseEnv, getIdentity());
 		
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/CheckboxConfigDataModel.java b/src/main/java/org/olat/course/nodes/cl/ui/CheckboxConfigDataModel.java
index 5f7f7e7e943b940a6b508b6835e6706e20102683..ebeec874f50279f547a8da30997f14244c9b386d 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/CheckboxConfigDataModel.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/CheckboxConfigDataModel.java
@@ -60,9 +60,7 @@ public class CheckboxConfigDataModel extends DefaultFlexiTableDataModel<Checkbox
 				}
 				return translator.translate("release." + release.name());
 			}
-			case file: {
-				return "";
-			}
+			case file: return box.getFilename();
 		}
 		return box;
 	}
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/CheckboxEditController.java b/src/main/java/org/olat/course/nodes/cl/ui/CheckboxEditController.java
index 7313f880ef7208961291e036812d5315590ce7a5..f0aaaabed9e7b5661b9767ceadc85abd50fc2ebe 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/CheckboxEditController.java
+++ b/src/main/java/org/olat/course/nodes/cl/ui/CheckboxEditController.java
@@ -46,9 +46,12 @@ import org.olat.core.logging.activity.CourseLoggingAction;
 import org.olat.core.logging.activity.ILoggingAction;
 import org.olat.core.logging.activity.StringResourceableType;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSManager;
+import org.olat.core.util.vfs.VFSMediaResource;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.nodes.CheckListCourseNode;
@@ -65,12 +68,15 @@ import org.olat.util.logging.activity.LoggingResourceable;
  */
 public class CheckboxEditController extends FormBasicController {
 	
-	private FormLink deleteLink;
+	private FormLink deleteLink, deleteFileLink, downloadFileLink;
 	private TextElement titleEl, pointsEl;
 	private SingleSelection releaseEl, labelEl;
 	private MultipleSelectionElement awardPointEl;
 	private RichTextElement descriptionEl;
 	private FileElement fileEl;
+	private FormLayoutContainer deleteFileCont;
+	
+	private Boolean deleteFile;
 	
 	private final Checkbox checkbox;
 	private final boolean withScore;
@@ -143,6 +149,14 @@ public class CheckboxEditController extends FormBasicController {
 				ureq.getUserSession(), getWindowControl());
 
 		fileEl = uifactory.addFileElement("file", formLayout);
+		fileEl.addActionListener(FormEvent.ONCHANGE);
+
+		String template = velocity_root + "/delete_file.html";
+		deleteFileCont = FormLayoutContainer.createCustomFormLayout("delete", getTranslator(), template);
+		formLayout.add(deleteFileCont);
+		downloadFileLink = uifactory.addFormLink("download", checkbox.getFilename(), null, deleteFileCont, Link.NONTRANSLATED);
+		deleteFileLink = uifactory.addFormLink("deleteFile", "delete", null, deleteFileCont, Link.BUTTON);
+		deleteFileCont.setVisible(StringHelper.containsNonWhitespace(checkbox.getFilename()));
 		
 		FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
 		formLayout.add(buttonsCont);
@@ -197,6 +211,17 @@ public class CheckboxEditController extends FormBasicController {
 		}
 		checkbox.setDescription(descriptionEl.getValue());
 		
+		if(Boolean.TRUE.equals(deleteFile)) {
+			checkbox.setFilename(null);
+			
+			ICourse course = CourseFactory.loadCourse(courseOres);
+			CourseEnvironment courseEnv = course.getCourseEnvironment();
+			VFSContainer container = checkboxManager.getFileContainer(courseEnv, courseNode, checkbox);
+			for (VFSItem chd:container.getItems()) {
+				chd.delete();
+			}
+		}
+		
 		File uploadedFile = fileEl.getUploadFile();
 		if(uploadedFile != null) {
 			String filename = fileEl.getUploadFileName();
@@ -236,9 +261,39 @@ public class CheckboxEditController extends FormBasicController {
 				ThreadLocalUserActivityLogger.log(CourseLoggingAction.CHECKLIST_CHECKBOX_DELETED, getClass(), LoggingResourceable.wrap(courseNode),
 						LoggingResourceable.wrapNonOlatResource(StringResourceableType.checkbox, checkbox.getCheckboxId(), checkbox.getTitle()));
 			}
+		} else if(downloadFileLink == source) {
+			doDownloadFile(ureq);
+		} else if(deleteFileLink == source) {
+			deleteFile();
 		} else if(awardPointEl == source) {
 			pointsEl.setVisible(withScore && awardPointEl.isAtLeastSelected(1));
+		} else if(fileEl == source) {
+			String filename = fileEl.getUploadFileName();
+			downloadFileLink.setI18nKey(filename);
+			downloadFileLink.setEnabled(false);
 		}
 		super.formInnerEvent(ureq, source, event);
 	}
+	
+	private void deleteFile() {
+		deleteFile = Boolean.TRUE;
+		deleteFileCont.setVisible(false);
+
+		String filename = fileEl.getUploadFileName();
+		if(filename != null && filename.equals(downloadFileLink.getI18nKey())) {
+			fileEl.reset();
+		}
+	}
+	
+	private void doDownloadFile(UserRequest ureq) {
+		ICourse course = CourseFactory.loadCourse(courseOres);
+		CourseEnvironment courseEnv = course.getCourseEnvironment();
+		VFSContainer container = checkboxManager.getFileContainer(courseEnv, courseNode, checkbox);
+		VFSItem item = container.resolve(checkbox.getFilename());
+		if(item instanceof VFSLeaf) {
+			VFSMediaResource rsrc = new VFSMediaResource((VFSLeaf)item);
+			rsrc.setDownloadable(true);
+			ureq.getDispatchResult().setResultingMediaResource(rsrc);
+		}
+	}
 }
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/_content/delete_file.html b/src/main/java/org/olat/course/nodes/cl/ui/_content/delete_file.html
new file mode 100644
index 0000000000000000000000000000000000000000..37d190a789fe94ea974ffa0ddb9faeedb78968be
--- /dev/null
+++ b/src/main/java/org/olat/course/nodes/cl/ui/_content/delete_file.html
@@ -0,0 +1 @@
+$r.render("download") $r.render("deleteFile")
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/_content/run.html b/src/main/java/org/olat/course/nodes/cl/ui/_content/run.html
index 046959022bd78a5c60497a00fb441afbf786bca1..96aa94b3054f28cb71bb4ec39fde72343763479e 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/_content/run.html
+++ b/src/main/java/org/olat/course/nodes/cl/ui/_content/run.html
@@ -4,26 +4,11 @@
 		<h4>$r.translate("score.title")</h4>
 		<table>
 			<tbody>
-				<tr>
-					<td>
-						$r.translate("score.min"):
-					</td>
-					<td>
-						$scoreMin
-					</td>
-				</tr>
-				<tr>
-					<td>
-						$r.translate("score.max"): 
-					</td>
-					<td>
-						$scoreMax
-					</td>
-				</tr>
-				<tr>
-					<td>			
-						$r.translate("score.yourscore"): 
-					</td>
+				<tr><td>$r.translate("score.min"):</td>
+					<td>$scoreMin</td></tr>
+				<tr><td>$r.translate("score.max"):</td>
+					<td>$scoreMax</td></tr>
+				<tr><td>$r.translate("score.yourscore"):</td>
 					<td>
 						#if($score)		
 							$score
@@ -41,19 +26,10 @@
 		<table>
 			<tbody>	
 			#if ($passedCutValue)
-				<tr>
-					<td>
-						$r.translate("passed.cut"):
-					</td>
-					<td>
-						$passedCutValue
-					</td>
-				</tr>
+				<tr><td>$r.translate("passed.cut"):</td>
+					<td>$passedCutValue</td></tr>
 			#end
-				<tr>
-					<td>			
-						$r.translate("passed.yourpassed"):
-					</td>
+				<tr><td>$r.translate("passed.yourpassed"):</td>
 					<td>
 						#if($hasPassedValue && $passed == true)		
 							<span class="o_passed">$r.translate("passed.yes")</span>
@@ -88,7 +64,7 @@
 
 #if($dueDate)
 	<p>$r.translate("run.due.date.desc")</p>
-	<p class="o_cl_duedate">$r.translate("run.due.date",$r.formatDateAndTime($dueDate))</p>
+	<p class="o_cl_duedate #if($afterDueDate) o_cl_duedate_passed #end">$r.translate("run.due.date", $r.formatDateAndTime($dueDate))</p>
 #end
 
 <div class="b_clearfix">
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_de.properties
index 5d1185e88b80e71103c3c1ab1feb6419dfc7016f..b42ec5b3ef348bee280a5e20096d3493e7863816 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_de.properties
@@ -30,7 +30,7 @@ pane.tab.accessibility=Zugang
 pane.tab.clconfig=Konfiguration
 pane.tab.checkbox=Checkboxen
 config.title=Konfiguration der Checkliste
-config.description=W\u00E4hlen Sie ob die Checkliste bis zu einem bestimmten Datum ausgef\u00FCllt sein muss und ob der Benutzer daf\u00FCr Punkte oder eine Bestanden Information erhalten soll.
+config.description=W\u00E4hlen Sie ob die Checkliste bis zu einem bestimmten Datum ausgef\u00FCllt sein muss und ob der Benutzer daf\u00FCr Punkte oder eine Information zum Bestanden-Status erhalten soll.
 config.due.date=Abgabedatum
 config.due.date.on=Checkliste bei Abgabedatum f\u00FCr Benutzer sperren
 config.passed=Bestanden / Nicht bestanden ausgeben
@@ -47,10 +47,12 @@ config.comment=Individueller Kommentar
 config.tip.user=Hinweis f\u00FCr alle Benutzer
 config.tip.coach=Hinweis f\u00FCr Betreuer
 config.checkbox.title=$\:pane.tab.checkbox
-config.checkbox.description=W\u00E4hlen Sie "$\:add.checkbox" um eine neue Checkbox zu erzeugen oder "$\:edit.checkbox" um eine bestehende Checkbox zu ver\u00E4ndern.
-config.warning.inuse=Es gibt schon Benutzer, die eine Checkbox geklickt haben. Eine Anderung der Punkte kann deren Resultate beinflussen.
+config.checkbox.description=W\u00E4hlen Sie "$\:add.checkbox" um eine neue Checkbox zu erzeugen oder "$org.olat.core\:edit" um eine bestehende Checkbox zu ver\u00E4ndern.
+config.warning.inuse=Es gibt schon Benutzer, die eine Checkbox geklickt haben. Eine \u00C4nderung der Punkte kann deren Resultate beinflussen.
 checkbox.title=Titel
 
+comment.title=$org.olat.course.nodes.ms\:comment.title
+comment.nocomment=$org.olat.course.nodes.ms\:comment.nocomment
 score.title=$org.olat.course.nodes.ms\:score.title
 score.noscoreinfoyet=$org.olat.course.nodes.ms\:score.noscore
 score.min=$org.olat.course.nodes.ms\:score.min
@@ -60,6 +62,7 @@ passed.title=$org.olat.course.nodes.ms\:passed.title
 passed.cut=$org.olat.course.nodes.ms\:passed.cut
 passed.yourpassed=$org.olat.course.nodes.ms\:passed.yourpassed
 passed.no=$org.olat.course.nodes.ms\:passed.no
+passed.nopassed=$org.olat.course.nodes.ms\:passed.nopassed
 passed.yes=$org.olat.course.nodes.ms\:passed.yes
 info.title=$org.olat.course.nodes.ms\:info.title
 
@@ -103,7 +106,7 @@ run.mark=Markierung
 run.info=Information
 run.due.date=Abgabedatum: <span class="o_cl_duedate">{0}</span>
 run.due.date.desc=Diese Checkliste ist mit einem Abgabedatum versehen. Nach dem Abgabedatum kann die Checkliste nicht mehr von Ihnen ver\u00E4ndert werden.
-coach.desc=In der unten stehenden Listen finden Sie die von Ihnen betreuten Teilnehmer dieses Kurses. Wählen Sie bearbeiten um die Checkbox-Auswahl oder Punkte eines Teilnehmers zu verändern.
+coach.desc=In der unten stehenden Listen finden Sie die von Ihnen betreuten Teilnehmer dieses Kurses. Wählen Sie "$\:table.header.edit.checkbox" um die Checkbox-Auswahl oder Punkte eines Teilnehmers zu verändern.
 coach.due.date.desc=Diese Checkliste ist mit einem Abgabedatum versehen. Als Betreuer sollten Sie Änderungen erst nach Ablauf des Abgabedatums vornehmen.
 checklist=Checkliste
 checklist.point.example=Punkte (min: {0} / max: {1})
diff --git a/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_en.properties
index 1a1c974c6d96f3c0a26f7545766ec74e264b45ec..4406b9f34d9440284039689d0fa5952a7d83e36f 100644
--- a/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/nodes/cl/ui/_i18n/LocalStrings_en.properties
@@ -1,13 +1,19 @@
 #Mon Mar 03 11:18:52 CET 2014
 add.checkbox=Add checkbox
+assessment=Assessment
 award.point.on=Grant score on check
 box.check=Check
 box.points=Score
 box.points.info=({0} Points)
+box.assessment=Edit per checkbox
 checkbox.title=Title
 checklist=Checklists
 checklist.point.example=Points (min\: {0} / max\: {1})
 chelp.cl-checkbox.title=Checklist configuration
+coach.desc=In the list below you will find all participants of this course coached by you. Select "$\:table.header.edit.checkbox" in order to change check marks or the score of a participant.
+coach.due.date.desc=Please notice that this checklist has a deadline. As a coach, you should make changes only after the deadline expiration.
+comment.title=$org.olat.course.nodes.ms\:comment.title
+comment.nocomment=$org.olat.course.nodes.ms\:comment.nocomment
 condition.accessibility.title=Access
 config.checkbox.title=$\:pane.tab.checkbox
 config.comment=Individual comment
@@ -26,6 +32,9 @@ config.points.on=Checkboxes total
 config.tip.coach=Notice for tutors
 config.tip.user=Notice for all users
 config.title=Checklist configuration
+config.warning.inuse=Users have already checked one or more boxes. Changing scores may affect their results.
+config.checkbox.description=Click "$\:add.checkbox" to create a new checkbox or select "$org.olat.core\:edit" to configure an existing checkbox.
+config.description=Select whether a deadline applies to the checklist, after which it will be locked for users. Information on passed status or score can be activated here.
 description=Description
 file=File
 filter.all=Show all
@@ -46,11 +55,17 @@ passed.no=$org.olat.course.nodes.ms\:passed.no
 passed.title=$org.olat.course.nodes.ms\:passed.title
 passed.yes=$org.olat.course.nodes.ms\:passed.yes
 passed.yourpassed=$org.olat.course.nodes.ms\:passed.yourpassed
+pdf.export=PDF overview
+pdf.export.checked=PDF marked checkboxes
 points=Score
 release=Access
 release.coachOnly=Coach only
 release.userAndCoach=Participant and coach
 run.due.date=Deadline\: <span class\="o_cl_duedate">{0}</span>
+run.due.date.desc=Please notice that this checklist has a deadline. You will not be able to make changes to the checklist after the deadline expiration.
+run.mark=Check marks
+run.run=My checklist
+run.coach=Manage checklists
 score.max=$org.olat.course.nodes.ms\:score.max
 score.min=$org.olat.course.nodes.ms\:score.min
 score.noscoreinfoyet=$org.olat.course.nodes.ms\:score.noscore
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 0600e27a3bd75a6f6366b673e148d1172cca678b..2558f3e48d8f195529587cc1f543da97d352fc3f 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQEditController.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQEditController.java
@@ -511,10 +511,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl
 							onyxSuccess = surveyDir.listFiles().length;
 						}
 					} else {
-						List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), repKey, 1);
-						if (results != null && results.size() > 0) {
-							onyxSuccess = results.size();
-						}
+						onyxSuccess = QTIResultManager.getInstance().countResults(course.getResourceableId(), courseNode.getIdent(), repKey);
 					}
 				}
 				if (moduleConfiguration.get(CONFIG_KEY_TYPE_QTI) != null
@@ -524,7 +521,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl
 					replaceWizard.addControllerListener(this);
 					cmc = new CloseableModalController(getWindowControl(), translate("close"), replaceWizard.getInitialComponent());
 				} else {
-					List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), repKey, 1);
+					List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), repKey, null, 1);
 					// test was passed from an user
 					boolean passed = (results != null && results.size() > 0) ? true : false;
 					// test was started and not passed
diff --git a/src/main/java/org/olat/course/nodes/iq/IQEditReplaceWizard.java b/src/main/java/org/olat/course/nodes/iq/IQEditReplaceWizard.java
index 22b98af4ea1b71a8ef7f5198b57528f7cc5ef860..7a6abea65951f50eeea04f7375f5bea5b10fb03a 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQEditReplaceWizard.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQEditReplaceWizard.java
@@ -276,7 +276,7 @@ public class IQEditReplaceWizard extends WizardController {
 		};
 		
 		removeAsListenerAndDispose(mailCtr);
-		mailCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false);
+		mailCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false, true);
 		listenTo(mailCtr);
 	
 		vcStep3 = createVelocityContainer("replacewizard_step3");
diff --git a/src/main/java/org/olat/course/nodes/ta/DropboxScoringViewController.java b/src/main/java/org/olat/course/nodes/ta/DropboxScoringViewController.java
index 58c3d357af5352f553cf1e411811096e158e2d11..3e3093cb19f943a94f349ccb5737edaf94d64e76 100644
--- a/src/main/java/org/olat/course/nodes/ta/DropboxScoringViewController.java
+++ b/src/main/java/org/olat/course/nodes/ta/DropboxScoringViewController.java
@@ -170,6 +170,7 @@ public class DropboxScoringViewController extends BasicController {
 		namedReturnbox.setLocalSecurityCallback(getReturnboxVfsSecurityCallback(rootReturnbox.getRelPath()));
 
 		returnboxFolderRunController = new FolderRunController(namedReturnbox, false, ureq, getWindowControl());
+		returnboxFolderRunController.disableSubscriptionController();
 		listenTo(returnboxFolderRunController);
 		
 		myContent.put("returnbox", returnboxFolderRunController.getInitialComponent());
@@ -201,7 +202,9 @@ public class DropboxScoringViewController extends BasicController {
 	}
 
 	protected VFSSecurityCallback getReturnboxVfsSecurityCallback(String returnboxRelPath) {
-		return new ReturnboxFullAccessCallback(returnboxRelPath);
+		SubscriptionContext subscriptionContext = ReturnboxFileUploadNotificationHandler
+				.getSubscriptionContext(userCourseEnv.getCourseEnvironment(), node, getIdentity());
+		return new ReturnboxFullAccessCallback(returnboxRelPath, subscriptionContext);
 	}
 
 	/**
@@ -402,8 +405,10 @@ class ReadOnlyAndDeleteCallback implements VFSSecurityCallback {
 class ReturnboxFullAccessCallback implements VFSSecurityCallback {
 
 	private Quota quota;
+	private final SubscriptionContext subscriptionContext;
 
-	public ReturnboxFullAccessCallback(String relPath) {
+	public ReturnboxFullAccessCallback(String relPath, SubscriptionContext subscriptionContext) {
+		this.subscriptionContext = subscriptionContext;
 		QuotaManager qm = QuotaManager.getInstance();
 		quota = qm.getCustomQuota(relPath);
 		if (quota == null) { // if no custom quota set, use the default quotas...
@@ -454,6 +459,6 @@ class ReturnboxFullAccessCallback implements VFSSecurityCallback {
 	 * @see org.olat.modules.bc.callbacks.SecurityCallback#getSubscriptionContext()
 	 */
 	public SubscriptionContext getSubscriptionContext() {
-		return null;
+		return subscriptionContext;
 	} 
 }
diff --git a/src/main/java/org/olat/course/nodes/ta/ReturnboxController.java b/src/main/java/org/olat/course/nodes/ta/ReturnboxController.java
index 4227cb297b522048801b3b02ca0f2d6cc23d5c3f..216e0c07e1191703928ee815d9b9f261eac193fb 100644
--- a/src/main/java/org/olat/course/nodes/ta/ReturnboxController.java
+++ b/src/main/java/org/olat/course/nodes/ta/ReturnboxController.java
@@ -103,7 +103,7 @@ public class ReturnboxController extends BasicController {
 		// notification
 		if ( !previewMode && !ureq.getUserSession().getRoles().isGuestOnly()) {
 			// offer subscription, but not to guests
-			subsContext = ReturnboxFileUploadNotificationHandler.getSubscriptionContext(userCourseEnv, node, ureq.getIdentity());
+			subsContext = ReturnboxFileUploadNotificationHandler.getSubscriptionContext(userCourseEnv.getCourseEnvironment(), node, ureq.getIdentity());
 			if (subsContext != null) {
 				contextualSubscriptionCtr = AbstractTaskNotificationHandler.createContextualSubscriptionController(ureq, wControl, getReturnboxPathFor(
 						userCourseEnv.getCourseEnvironment(), node,ureq.getIdentity()), subsContext, ReturnboxController.class);
diff --git a/src/main/java/org/olat/course/nodes/ta/ReturnboxFileUploadNotificationHandler.java b/src/main/java/org/olat/course/nodes/ta/ReturnboxFileUploadNotificationHandler.java
index e301c0a78147940ba7f0327299c1bf28a1b39b85..2a9f209f568ba43cc6c982843cd4ab15537eef6c 100644
--- a/src/main/java/org/olat/course/nodes/ta/ReturnboxFileUploadNotificationHandler.java
+++ b/src/main/java/org/olat/course/nodes/ta/ReturnboxFileUploadNotificationHandler.java
@@ -33,7 +33,7 @@ import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.course.CourseModule;
 import org.olat.course.nodes.CourseNode;
-import org.olat.course.run.userview.UserCourseEnvironment;
+import org.olat.course.run.environment.CourseEnvironment;
 
 /**
  * Description:<br>
@@ -52,8 +52,8 @@ public class ReturnboxFileUploadNotificationHandler extends AbstractTaskNotifica
 		//empty block
 	}
 
-	protected static SubscriptionContext getSubscriptionContext(UserCourseEnvironment userCourseEnv, CourseNode node, Identity identity) {
-	  return CourseModule.createSubscriptionContext(userCourseEnv.getCourseEnvironment(), node, "Returnbox-" + identity.getKey());
+	protected static SubscriptionContext getSubscriptionContext(CourseEnvironment courseEnv, CourseNode node, Identity identity) {
+	  return CourseModule.createSubscriptionContext(courseEnv, node, "Returnbox-" + identity.getKey());
 	}
 	
 	protected String getCssClassIcon() {
diff --git a/src/main/java/org/olat/course/nodes/ta/TACourseNodeEditController.java b/src/main/java/org/olat/course/nodes/ta/TACourseNodeEditController.java
index d92abd4fb9d0838a625d8a410f0bc7a4a84120c7..77abf5efcaa9711b22561b81f8c426c38e34fed4 100644
--- a/src/main/java/org/olat/course/nodes/ta/TACourseNodeEditController.java
+++ b/src/main/java/org/olat/course/nodes/ta/TACourseNodeEditController.java
@@ -449,7 +449,7 @@ public class TACourseNodeEditController extends ActivateableTabbableDefaultContr
 			  RepositoryEntry repositoryEntry = RepositoryManager.getInstance().lookupRepositoryEntry(course, true);
 			  String courseURL = Settings.getServerContextPathURI() + "/url/RepositoryEntry/" + repositoryEntry.getKey();
 			  MailTemplate mailTemplate = this.createTaskDeletedMailTemplate(urequest, course.getCourseTitle(), courseURL, deletedTaskFile);
-			  mailCtr = new MailNotificationEditController(getWindowControl(), urequest, mailTemplate, true, false);
+			  mailCtr = new MailNotificationEditController(getWindowControl(), urequest, mailTemplate, true, false, true);
 			  listenTo(mailCtr);
 			  cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtr.getInitialComponent());
 			  listenTo(cmc);			
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java b/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java
index fba7ec95f742902abeb9bd649657c427aefff3cb..ef667860e56ce7b58f7ca8217b181d9314805ded 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupImportExport.java
@@ -139,6 +139,8 @@ public class BusinessGroupImportExport {
 			try {
 				Field field = toolsConfig.getClass().getField(availableTools[i]);
 				field.setBoolean(toolsConfig, ct.isToolEnabled(availableTools[i]));
+			} catch(NoSuchFieldException e) {
+				//no field to fill (hasOpenMeetings is not set for backwards compatibility)
 			} catch (Exception e) {
 				log.error("", e);
 			}
diff --git a/src/main/java/org/olat/ims/qti/QTIChangeLogMessage.java b/src/main/java/org/olat/ims/qti/QTIChangeLogMessage.java
index 37f82c2ef8925eb15cd64127f9a04466f15cc5c6..ae51256d2939994467d075751e963a834080b942 100644
--- a/src/main/java/org/olat/ims/qti/QTIChangeLogMessage.java
+++ b/src/main/java/org/olat/ims/qti/QTIChangeLogMessage.java
@@ -40,7 +40,7 @@ package org.olat.ims.qti;
  * 
  * @author patrick
  */
-public class QTIChangeLogMessage implements Comparable{
+public class QTIChangeLogMessage implements Comparable<QTIChangeLogMessage> {
 
 	private boolean isPublic;
 	private String logMessage;
@@ -87,8 +87,8 @@ public class QTIChangeLogMessage implements Comparable{
 	 * 
 	 * @see java.lang.Comparable#compareTo(java.lang.Object)
 	 */
-	public int compareTo(Object arg0) {
-		QTIChangeLogMessage b = (QTIChangeLogMessage)arg0;
+	@Override
+	public int compareTo(QTIChangeLogMessage b) {
 		long diff = this.getTimestmp() - b.getTimestmp();
 		//this ordering makes Arrays.sort(..) to sort the change log messages ascending
 		//whereas ascending means older timestamp before newer timestamp
diff --git a/src/main/java/org/olat/ims/qti/QTIResult.java b/src/main/java/org/olat/ims/qti/QTIResult.java
index 4cf75293e30ac15efbfa2fe5f54ce9ceb737ef21..92aa342c1e8dd3b6de1777c092b267528781441a 100644
--- a/src/main/java/org/olat/ims/qti/QTIResult.java
+++ b/src/main/java/org/olat/ims/qti/QTIResult.java
@@ -34,7 +34,9 @@ import org.olat.core.commons.persistence.PersistentObject;
  *
  * @author gnaegi
  */
-public class QTIResult  extends PersistentObject{ 
+public class QTIResult extends PersistentObject{ 
+
+	private static final long serialVersionUID = 5999697754463201896L;
 
 	private QTIResultSet resultSet;
 	
@@ -169,4 +171,20 @@ public class QTIResult  extends PersistentObject{
 		this.lastModified = lastModified;
 	}
 
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 97520 : getKey().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof QTIResult) {
+			QTIResult result = (QTIResult)obj;
+			return getKey() != null && getKey().equals(result.getKey());
+		}
+		return false;
+	}
 }
diff --git a/src/main/java/org/olat/ims/qti/QTIResultManager.java b/src/main/java/org/olat/ims/qti/QTIResultManager.java
index 099ea36d5a461c61f86f31ae59d10a82af4b9e43..48f7825641c1a3be7e0234662a873ed4fcb50920 100644
--- a/src/main/java/org/olat/ims/qti/QTIResultManager.java
+++ b/src/main/java/org/olat/ims/qti/QTIResultManager.java
@@ -27,18 +27,19 @@ package org.olat.ims.qti;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import org.hibernate.type.StandardBasicTypes;
-import org.hibernate.type.Type;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+
+import org.olat.basesecurity.Group;
 import org.olat.core.commons.persistence.DB;
-import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.UserConstants;
+import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
-import org.olat.core.manager.BasicManager;
 import org.olat.user.UserDataDeletable;
 
 /**
@@ -46,9 +47,13 @@ import org.olat.user.UserDataDeletable;
  * 
  * @author Alexander Schneider
  */
-public class QTIResultManager extends BasicManager implements UserDataDeletable {
+public class QTIResultManager implements UserDataDeletable {
+	
+	private static final OLog log = Tracing.createLoggerFor(QTIResultManager.class);
 
 	private static QTIResultManager instance;
+	
+	private DB dbInstance;
 
 	/**
 	 * Constructor for QTIResultManager.
@@ -63,6 +68,14 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	public static QTIResultManager getInstance() {
 		return instance;
 	}
+	
+	/**
+	 * [user by Spring]
+	 * @param dbInstance
+	 */
+	public void setDbInstance(DB dbInstance) {
+		this.dbInstance = dbInstance;
+	}
 
 	/**
 	 * @param olatResource
@@ -71,7 +84,16 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @return True if true, false otherwise.
 	 */
 	public boolean hasResultSets(Long olatResource, String olatResourceDetail, Long repositoryRef) {
-		return (getResultSets(olatResource, olatResourceDetail, repositoryRef, null).size() > 0);
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(rset.key) from ").append(QTIResultSet.class.getName()).append(" as rset ")
+		  .append("where rset.olatResource=:resId and rset.olatResourceDetail=:resSubPath and rset.repositoryRef=:repoKey");
+
+		Number count = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Number.class)
+				.setParameter("resId", olatResource)
+				.setParameter("resSubPath", olatResourceDetail)
+				.setParameter("repoKey", repositoryRef).getSingleResult();
+		return count == null ? false : count.intValue() > 0;
 	}
 
 	/**
@@ -83,27 +105,22 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @return List of resultsets
 	 */
 	public List<QTIResultSet> getResultSets(Long olatResource, String olatResourceDetail, Long repositoryRef, Identity identity) {
-		Long olatRes = olatResource;
-		String olatResDet = olatResourceDetail;
-		Long repRef = repositoryRef;
-
-		DB db = DBFactory.getInstance();
-
-		StringBuilder slct = new StringBuilder();
-		slct.append("select rset from ");
-		slct.append("org.olat.ims.qti.QTIResultSet rset ");
-		slct.append("where ");
-		slct.append("rset.olatResource=? ");
-		slct.append("and rset.olatResourceDetail=? ");
-		slct.append("and rset.repositoryRef=? ");
+		StringBuilder sb = new StringBuilder();
+		sb.append("select rset from ").append(QTIResultSet.class.getName()).append(" as rset ")
+		  .append("where rset.olatResource=:resId and rset.olatResourceDetail=:resSubPath and rset.repositoryRef=:repoKey");
 		if (identity != null) {
-			slct.append("and rset.identity.key=? ");
-			return db.find(slct.toString(), new Object[] { olatRes, olatResDet, repRef, identity.getKey() }, new Type[] { StandardBasicTypes.LONG, StandardBasicTypes.STRING,
-				StandardBasicTypes.LONG, StandardBasicTypes.LONG });
-		} else {
-			return db.find(slct.toString(), new Object[] { olatRes, olatResDet, repRef }, new Type[] { StandardBasicTypes.LONG, StandardBasicTypes.STRING,
-				StandardBasicTypes.LONG });
+			sb.append(" and rset.identity.key=:identityKey ");
 		}
+		
+		TypedQuery<QTIResultSet> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), QTIResultSet.class)
+				.setParameter("resId", olatResource)
+				.setParameter("resSubPath", olatResourceDetail)
+				.setParameter("repoKey", repositoryRef);
+		if (identity != null) {
+			query.setParameter("identityKey", identity.getKey());
+		}
+		return query.getResultList();
 	}
 
 	/**
@@ -114,43 +131,66 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @param repositoryRef
 	 * @return List of QTIResult objects
 	 */
-	public List<QTIResult> selectResults(Long olatResource, String olatResourceDetail, Long repositoryRef, int type) {
-		Long olatRes = olatResource;
-		String olatResDet = olatResourceDetail;
-		Long repRef = repositoryRef;
-
-		DB db = DBFactory.getInstance();
-		// join with user to sort by name
-		StringBuilder slct = new StringBuilder();
-		slct.append("select res from ");
-		slct.append("org.olat.ims.qti.QTIResultSet rset, ");
-		slct.append("org.olat.ims.qti.QTIResult res, ");
-		slct.append("org.olat.core.id.Identity identity, ");
-		slct.append("org.olat.user.UserImpl usr ");
-		slct.append("where ");
-		slct.append("rset.key = res.resultSet ");
-		slct.append("and rset.identity = identity.key ");
-		slct.append("and identity.user = usr.key ");
-		slct.append("and rset.olatResource=? ");
-		slct.append("and rset.olatResourceDetail=? ");
-		slct.append("and rset.repositoryRef=? ");
-		 // 1 -> iqtest, 2 -> iqself
-		if(type == 1 || type == 2)
-		    slct.append("order by usr.userProperties['").append(UserConstants.LASTNAME).append("'] , rset.assessmentID, res.itemIdent");
+	public List<QTIResult> selectResults(Long olatResource, String olatResourceDetail, Long repositoryRef,
+			List<Group> limitToSecGroups, int type) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select res from ").append(QTIResult.class.getName()).append(" as res ")
+		  .append(" inner join res.resultSet as rset")
+		  .append(" inner join rset.identity as ident")
+		  .append(" inner join ident.user as usr")
+		  .append(" where rset.olatResource=:resId and rset.olatResourceDetail=:resSubPath and rset.repositoryRef=:repoKey");
+		if(limitToSecGroups != null && limitToSecGroups.size() > 0) {
+			sb.append(" and rset.identity.key in ( select membership.identity.key from bgroupmember membership ")
+			  .append("   where membership.group in (:baseGroups)")
+			  .append(" )");
+		}
+		
+		if(type == 1 || type == 2) {
+			 // 1 -> iqtest, 2 -> iqself
+		    sb.append(" order by usr.userProperties['").append(UserConstants.LASTNAME).append("'] , rset.assessmentID, res.itemIdent");
+		} else {
 			//3 -> iqsurv: the alphabetical assortment above could destroy the anonymization
-	    // if names and quantity of the persons is well-known
-		else 
-		    slct.append("order by rset.creationDate, rset.assessmentID, res.itemIdent");
+		    // if names and quantity of the persons is well-known
+		    sb.append(" order by rset.creationDate, rset.assessmentID, res.itemIdent");
+		}
 
-		List results = null;
-		results = db.find(slct.toString(), new Object[] { olatRes, olatResDet, repRef }, new Type[] { StandardBasicTypes.LONG, StandardBasicTypes.STRING,
-			StandardBasicTypes.LONG });
+		TypedQuery<QTIResult> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), QTIResult.class)
+				.setParameter("resId", olatResource)
+				.setParameter("resSubPath", olatResourceDetail)
+				.setParameter("repoKey", repositoryRef);
 		
-		return results;
+		if(limitToSecGroups != null && limitToSecGroups.size() > 0) {
+			query.setParameter("baseGroups", limitToSecGroups);
+		}
+		
+		return query.getResultList();
+	}
+	
+	/**
+	 * Same as above but only count the number of results
+	 * @param olatResource
+	 * @param olatResourceDetail
+	 * @param repositoryRef
+	 * @return
+	 */
+	public int countResults(Long olatResource, String olatResourceDetail, Long repositoryRef) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(res.key) from ").append(QTIResult.class.getName()).append(" as res ")
+		  .append(" inner join res.resultSet as rset")
+		  .append(" inner join rset.identity as ident")
+		  .append(" inner join ident.user as usr")
+		  .append(" where rset.olatResource=:resId and rset.olatResourceDetail=:resSubPath and rset.repositoryRef=:repoKey");
+
+		Number count = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class)
+				.setParameter("resId", olatResource)
+				.setParameter("resSubPath", olatResourceDetail)
+				.setParameter("repoKey", repositoryRef)
+				.getSingleResult();
+		return count == null ? 0 : count.intValue();
 	}
 
 	/**
-	 * deletes all Results and ResultSets of a test, selftest or survey
+	 * Deletes all Results and ResultSets of a test, selftest or survey
 	 * 
 	 * @param olatRes
 	 * @param olatResDet
@@ -158,37 +198,28 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @return deleted ResultSets
 	 */
 	public int deleteAllResults(Long olatRes, String olatResDet, Long repRef) {
-		DB db = DBFactory.getInstance();
-
-		StringBuilder slct = new StringBuilder();
-		slct.append("select rset from ");
-		slct.append("org.olat.ims.qti.QTIResultSet rset ");
-		slct.append("where ");
-		slct.append("rset.olatResource=? ");
-		slct.append("and rset.olatResourceDetail=? ");
-		slct.append("and rset.repositoryRef=? ");
-
-		List results = null;
-		results = db.find(slct.toString(), new Object[] { olatRes, olatResDet, repRef }, new Type[] { StandardBasicTypes.LONG, StandardBasicTypes.STRING,
-			StandardBasicTypes.LONG });
-
-		String delRes = "from res in class org.olat.ims.qti.QTIResult where res.resultSet.key = ?";
-		String delRset = "from rset in class org.olat.ims.qti.QTIResultSet where rset.key = ?";
-
-		int deletedRset = 0;
+		StringBuilder sb = new StringBuilder();
+		sb.append("select rset from ").append(QTIResultSet.class.getName()).append(" as rset ");
+		sb.append(" where rset.olatResource=:resId and rset.olatResourceDetail=:resSubPath and rset.repositoryRef=:repoKey ");
+		
+		EntityManager em = dbInstance.getCurrentEntityManager();
+		List<QTIResultSet> sets = em.createQuery(sb.toString(), QTIResultSet.class).setParameter("resId", olatRes)
+				.setParameter("resSubPath", olatResDet)
+				.setParameter("repoKey", repRef)
+				.getResultList();
 
-		for (Iterator iter = results.iterator(); iter.hasNext();) {
-			QTIResultSet rSet = (QTIResultSet) iter.next();
-			Long rSetKey = rSet.getKey();
-			db.delete(delRes, rSetKey, StandardBasicTypes.LONG);
-			db.delete(delRset, rSetKey, StandardBasicTypes.LONG);
-			deletedRset++;
+		StringBuilder delSb = new StringBuilder();
+		delSb.append("delete from ").append(QTIResult.class.getName()).append(" as res where res.resultSet.key=:setKey");
+		Query delResults = em.createQuery(delSb.toString());
+		for (QTIResultSet set:sets) {
+			delResults.setParameter("setKey", set.getKey()).executeUpdate();
+			em.remove(set);
 		}
-		return deletedRset;
+		return sets.size();
 	}
 	
 	/**
-	 * Deletes all Results and ResultSets for certain QTI-ResultSet.
+	 * Deletes all Results AND all ResultSets for certain QTI-ResultSet.
 	 * @param qtiResultSet
 	 */
 	public void deleteResults(QTIResultSet qtiResultSet) {
@@ -201,13 +232,13 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @param answerCode
 	 * @return translation
 	 */
-	public static Map parseResponseStrAnswers(String answerCode) {
+	public static Map<String,String> parseResponseStrAnswers(String answerCode) {
 		// calculate the correct answer, if eventually needed
 		int modus = 0;
 		int startIdentPosition = 0;
 		int startCharacterPosition = 0;
 		String tempIdent = null;
-		Map result = new HashMap();
+		Map<String,String> result = new HashMap<String,String>();
 		char c;
 
 		for (int i = 0; i < answerCode.length(); i++) {
@@ -248,11 +279,11 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @param answerCode
 	 * @return translation
 	 */
-	public static List parseResponseLidAnswers(String answerCode) {
+	public static List<String> parseResponseLidAnswers(String answerCode) {
 		// calculate the correct answer, if eventually needed
 		int modus = 0;
 		int startCharacterPosition = 0;
-		List result = new ArrayList();
+		List<String> result = new ArrayList<String>();
 		char c;
 
 		for (int i = 0; i < answerCode.length(); i++) {
@@ -287,8 +318,14 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @param assessmentID
 	 * @return
 	 */
-	public List findQtiResultSets(Identity identity) {
-		return DBFactory.getInstance().find("from q in class org.olat.ims.qti.QTIResultSet where q.identity =?", identity.getKey(), StandardBasicTypes.LONG);
+	public List<QTIResultSet> findQtiResultSets(Identity identity) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select rset from ").append(QTIResultSet.class.getName()).append(" as rset")
+		  .append(" where rset.identity.key=:identityKey");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), QTIResultSet.class)
+				.setParameter("identityKey", identity.getKey())
+				.getResultList();
 	}
 
 	/**
@@ -296,22 +333,29 @@ public class QTIResultManager extends BasicManager implements UserDataDeletable
 	 * @param identity
 	 */
 	public void deleteUserData(Identity identity, String newDeletedUserName) {
-		List qtiResults = findQtiResultSets(identity);
-		for (Iterator iter = qtiResults.iterator(); iter.hasNext();) {
-			deleteResultSet((QTIResultSet)iter.next());
-		}	
-		Tracing.logDebug("Delete all QTI result data in db for identity=" + identity, this.getClass());
+		List<QTIResultSet> qtiResults = findQtiResultSets(identity);
+		for (QTIResultSet set:qtiResults) {
+			deleteResultSet(set);
+		}
+		if(log.isDebug()) {
+			log.debug("Delete all QTI result data in db for identity=" + identity);
+		}
 	}
 
 	/**
 	 * Delete all qti-results and qti-result-set entry for certain result-set.
 	 * @param rSet 
 	 */
-	private void deleteResultSet(QTIResultSet rSet) {
-		Long rSetKey = rSet.getKey();
-		DB db = DBFactory.getInstance();
-		db.delete("from res in class org.olat.ims.qti.QTIResult where res.resultSet.key = ?", rSetKey, StandardBasicTypes.LONG);
-		db.delete("from rset in class org.olat.ims.qti.QTIResultSet where rset.key = ?", rSetKey, StandardBasicTypes.LONG);
+	public void deleteResultSet(QTIResultSet rSet) {
+		EntityManager em = dbInstance.getCurrentEntityManager();
+		
+		StringBuilder delResultsSb = new StringBuilder();
+		delResultsSb.append("delete from ").append(QTIResult.class.getName()).append(" as res where res.resultSet.key=:setKey");
+		em.createQuery(delResultsSb.toString()).setParameter("setKey", rSet.getKey()).executeUpdate();
+		
+		StringBuilder delSetSb = new StringBuilder();
+		delSetSb.append("delete from ").append(QTIResultSet.class.getName()).append(" as rset where rset.key=:setKey");
+		em.createQuery(delSetSb.toString()).setParameter("setKey", rSet.getKey()).executeUpdate();
 	}
 	
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/_spring/qtiContext.xml b/src/main/java/org/olat/ims/qti/_spring/qtiContext.xml
index a02d63b37d678c2f06fab685f06e30360133465d..a9d3775a7f90b2d4802591b4dc3af7eb66161ccc 100644
--- a/src/main/java/org/olat/ims/qti/_spring/qtiContext.xml
+++ b/src/main/java/org/olat/ims/qti/_spring/qtiContext.xml
@@ -35,6 +35,8 @@
          </property>
 	</bean>
 		
-	<bean id="qtiResultManager" class="org.olat.ims.qti.QTIResultManager"/>
+	<bean id="qtiResultManager" class="org.olat.ims.qti.QTIResultManager">
+		<property name="dbInstance" ref="database"/>	
+	</bean>
 
 </beans>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
index 42a03bd02f35cc434a0c2f1424ea7ed2b44af03f..c664170d8682d1a3ff227d9a79e24584fa81dd19 100644
--- a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
+++ b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
@@ -93,7 +93,6 @@ import org.olat.course.tree.TreePosition;
 import org.olat.fileresource.types.FileResource;
 import org.olat.ims.qti.QTIChangeLogMessage;
 import org.olat.ims.qti.QTIConstants;
-import org.olat.ims.qti.QTIResult;
 import org.olat.ims.qti.QTIResultManager;
 import org.olat.ims.qti.editor.beecom.objects.Assessment;
 import org.olat.ims.qti.editor.beecom.objects.ChoiceQuestion;
@@ -257,9 +256,8 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 				CourseNode courseNode = course.getEditorTreeModel().getCourseNode(ref.getUserdata());
 				String repositorySoftKey = (String) courseNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY);
 				Long repKey = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repositorySoftKey, true).getKey();
-				List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), repKey, 1);
 				restrictedEdit = ((CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(course, null))
-						|| (results != null && results.size() > 0)) ? true : false;
+						|| QTIResultManager.getInstance().countResults(course.getResourceableId(), courseNode.getIdent(), repKey) > 0) ? true : false;
 			}
 			if(restrictedEdit) {
 				break;
diff --git a/src/main/java/org/olat/ims/qti/export/QTIArchiveWizardController.java b/src/main/java/org/olat/ims/qti/export/QTIArchiveWizardController.java
index 6246846ab609ad3c3feb2712d7e7060d952d5a4f..9a1682ce389be1dce00c5cb259f65f75988c4879 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIArchiveWizardController.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIArchiveWizardController.java
@@ -123,7 +123,7 @@ public class QTIArchiveWizardController extends BasicController {
 	private File exportDir;
 	private String targetFileName;
 	private int type;
-	private Map qtiItemConfigs;
+	private Map<Class<?>, QTIExportItemFormatConfig> qtiItemConfigs;
 	private List<QTIResult> results;
 	private List<QTIItemObject> qtiItemObjectList;
 	private Link showFileButton;
@@ -258,7 +258,7 @@ public class QTIArchiveWizardController extends BasicController {
 							// </OLATBPS-498>
 						}
 				    } else {
-						success = hasResultSets(ureq);
+						success = hasResultSets();
 				    }
 				    
 				    if (success) {
@@ -273,12 +273,12 @@ public class QTIArchiveWizardController extends BasicController {
 						} else {
 				    
 							QTIResultManager qrm = QTIResultManager.getInstance();
-							results = qrm.selectResults(olatResource, currentCourseNode.getIdent(), repKey, type);
+							results = qrm.selectResults(olatResource, currentCourseNode.getIdent(), repKey, null, type);
 							QTIResult res0 = results.get(0);
 							
 							qtiItemObjectList = new QTIObjectTreeBuilder().getQTIItemObjectList(new Long(res0.getResultSet().getRepositoryRef()));
 							
-							qtiItemConfigs = getQTIItemConfigs();
+							qtiItemConfigs = getQTIItemConfigs(qtiItemObjectList);
 						
 							if(dummyMode){
 								finishedVC = createVelocityContainer("finished");
@@ -431,7 +431,7 @@ public class QTIArchiveWizardController extends BasicController {
 		return filename;
 	}
 
-	private boolean hasResultSets(UserRequest ureq) {
+	private boolean hasResultSets() {
 		if (currentCourseNode instanceof IQTESTCourseNode) {
 			type = 1;
 		} else if (currentCourseNode instanceof IQSELFCourseNode) {
@@ -454,7 +454,7 @@ public class QTIArchiveWizardController extends BasicController {
 		}
 	}
 
-	private String convert2CtrlChars(String source) {
+	public static String convert2CtrlChars(String source) {
 		if (source == null) return null;
 		StringBuilder sb = new StringBuilder(300);
 		int len = source.length();
@@ -481,59 +481,51 @@ public class QTIArchiveWizardController extends BasicController {
 		return sb.toString();
 	}
 	
-	private Map getQTIItemConfigs(){
-		Map itConfigs = new HashMap();
+	public static Map<Class<?>, QTIExportItemFormatConfig> getQTIItemConfigs(List<QTIItemObject> itemList){
+		Map<Class<?>, QTIExportItemFormatConfig> itConfigs = new HashMap<>();
   	
-		for (Iterator iter = qtiItemObjectList.iterator(); iter.hasNext();) {
-			QTIItemObject item = (QTIItemObject) iter.next();
+		for (Iterator<QTIItemObject> iter = itemList.iterator(); iter.hasNext();) {
+			QTIItemObject item = iter.next();
 			if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_SCQ)){
 				if (itConfigs.get(QTIExportSCQItemFormatConfig.class) == null){
 					QTIExportSCQItemFormatConfig confSCQ = new QTIExportSCQItemFormatConfig(true, false, false, false);
-			  	itConfigs.put(QTIExportSCQItemFormatConfig.class, confSCQ);
+					itConfigs.put(QTIExportSCQItemFormatConfig.class, confSCQ);
 				}
-			}
-			else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_MCQ)){
+			} else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_MCQ)){
 				if (itConfigs.get(QTIExportMCQItemFormatConfig.class) == null){
 					QTIExportMCQItemFormatConfig confMCQ = new QTIExportMCQItemFormatConfig(true, false, false, false);
-			  	itConfigs.put(QTIExportMCQItemFormatConfig.class, confMCQ );
+					itConfigs.put(QTIExportMCQItemFormatConfig.class, confMCQ );
 				}
-			}
-			else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_KPRIM)){
+			} else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_KPRIM)){
 				if (itConfigs.get(QTIExportKPRIMItemFormatConfig.class) == null){
 					QTIExportKPRIMItemFormatConfig confKPRIM = new QTIExportKPRIMItemFormatConfig(true, false, false, false);
-			  	itConfigs.put(QTIExportKPRIMItemFormatConfig.class, confKPRIM);
+					itConfigs.put(QTIExportKPRIMItemFormatConfig.class, confKPRIM);
 				}
-			}
-			else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_ESSAY)){
+			} else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_ESSAY)){
 				if (itConfigs.get(QTIExportEssayItemFormatConfig.class) == null){
 					QTIExportEssayItemFormatConfig confEssay = new QTIExportEssayItemFormatConfig(true, false);
-			  	itConfigs.put(QTIExportEssayItemFormatConfig.class, confEssay);
+					itConfigs.put(QTIExportEssayItemFormatConfig.class, confEssay);
 				}
-			}
-			else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_FIB)){
+			} else if (item.getItemIdent().startsWith(ItemParser.ITEM_PREFIX_FIB)){
 				if (itConfigs.get(QTIExportFIBItemFormatConfig.class) == null){
 					QTIExportFIBItemFormatConfig confFIB = new QTIExportFIBItemFormatConfig(true, false, false);
-			  	itConfigs.put(QTIExportFIBItemFormatConfig.class, confFIB);
+					itConfigs.put(QTIExportFIBItemFormatConfig.class, confFIB);
 				}
 			}
 			//if cannot find the type via the ItemParser, look for the QTIItemObject type
 			else if (item.getItemType().equals(QTIItemObject.TYPE.A)){
 				QTIExportEssayItemFormatConfig confEssay = new QTIExportEssayItemFormatConfig(true, false);
-		  	itConfigs.put(QTIExportEssayItemFormatConfig.class, confEssay);
-			} 
-			else if (item.getItemType().equals(QTIItemObject.TYPE.R)){
+				itConfigs.put(QTIExportEssayItemFormatConfig.class, confEssay);
+			} else if (item.getItemType().equals(QTIItemObject.TYPE.R)){
 				QTIExportSCQItemFormatConfig confSCQ = new QTIExportSCQItemFormatConfig(true, false, false, false);
-		  	itConfigs.put(QTIExportSCQItemFormatConfig.class, confSCQ);
-			}
-			else if (item.getItemType().equals(QTIItemObject.TYPE.C)){
+				itConfigs.put(QTIExportSCQItemFormatConfig.class, confSCQ);
+			} else if (item.getItemType().equals(QTIItemObject.TYPE.C)){
 				QTIExportMCQItemFormatConfig confMCQ = new QTIExportMCQItemFormatConfig(true, false, false, false);
-		  	itConfigs.put(QTIExportMCQItemFormatConfig.class, confMCQ );
-			}
-			else if (item.getItemType().equals(QTIItemObject.TYPE.B)){
+				itConfigs.put(QTIExportMCQItemFormatConfig.class, confMCQ );
+			} else if (item.getItemType().equals(QTIItemObject.TYPE.B)){
 				QTIExportFIBItemFormatConfig confFIB = new QTIExportFIBItemFormatConfig(true, false, false);
-		  	itConfigs.put(QTIExportFIBItemFormatConfig.class, confFIB);
-			}
-			else{
+				itConfigs.put(QTIExportFIBItemFormatConfig.class, confFIB);
+			} else {
 				throw new OLATRuntimeException(null,"Can not resolve QTIItem type", null);
 			}
 		}
diff --git a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType1.java b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType1.java
index 38e590b000ac9ddf30ed7e4e511ca8be66590861..9bf5738c324f1f312d2982f5dc055fe719239313 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType1.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType1.java
@@ -118,12 +118,12 @@ public class QTIExportFormatterCSVType1 extends QTIExportFormatter {
 				hR1.append(emb);
 
 				if (qeif.getExportItemConfig(item).hasResponseCols()) {
-					List responseColumnHeaders = item.getResponseColumnHeaders();
-					for (Iterator iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
+					List<String> responseColumnHeaders = item.getResponseColumnHeaders();
+					for (Iterator<String> iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
 						// HeaderRow1
 						hR1.append(sep);
 						// HeaderRow2
-						String columnHeader = (String) iterator.next();
+						String columnHeader = iterator.next();
 						hR2.append(i);
 						hR2.append("_");
 						hR2.append(columnHeader);
@@ -231,14 +231,14 @@ public class QTIExportFormatterCSVType1 extends QTIExportFormatter {
 	}
 
 	public void visit(QTIExportItem eItem) {
-		List responseColumns = eItem.getResponseColumns();
+		List<String> responseColumns = eItem.getResponseColumns();
 		QTIExportItemFormatConfig itemFormatConfig = eItem.getConfig();
 
 		if (displayItem(itemFormatConfig)) {
 
 			if (itemFormatConfig.hasResponseCols()) {
-				for (Iterator iter = responseColumns.iterator(); iter.hasNext();) {
-					String responseColumn = (String) iter.next();
+				for (Iterator<String> iter = responseColumns.iterator(); iter.hasNext();) {
+					String responseColumn = iter.next();
 					sb.append(emb);
 					sb.append(escape(responseColumn));
 					sb.append(emb);
@@ -337,7 +337,7 @@ public class QTIExportFormatterCSVType1 extends QTIExportFormatter {
 			sb.append(car);
 			// CELFI#107 END
 
-			List responseLabelMaterials = element.getResponseLabelMaterials();
+			List<String> responseLabelMaterials = element.getResponseLabelMaterials();
 
 			for (int i = 0; i < element.getResponseIdentifier().size(); i++) {
 				sb.append(sep + sep);
diff --git a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType2.java b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType2.java
index 3b78ebb91f712140552f5e5402c0b452147674e7..fdbf68f78eae9d3ae180990e9779c45e4b4421ae 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType2.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType2.java
@@ -98,8 +98,8 @@ public class QTIExportFormatterCSVType2 extends QTIExportFormatter {
 		StringBuilder hR2 = new StringBuilder();
 		
 		int i = 1;
-		for (Iterator iter = qtiItemObjectList.iterator(); iter.hasNext();) {
-			QTIItemObject item = (QTIItemObject) iter.next();
+		for (Iterator<QTIItemObject> iter = qtiItemObjectList.iterator(); iter.hasNext();) {
+			QTIItemObject item = iter.next();
 
 			if (displayItem(qeif.getExportItemConfig(item))) {
 				hR1.append(emb);
@@ -120,12 +120,12 @@ public class QTIExportFormatterCSVType2 extends QTIExportFormatter {
 				hR1.append(emb);
 
 				if (qeif.getExportItemConfig(item).hasResponseCols()) {
-					List responseColumnHeaders = item.getResponseColumnHeaders();
-					for (Iterator iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
+					List<String> responseColumnHeaders = item.getResponseColumnHeaders();
+					for (Iterator<String> iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
 					  // HeaderRow1
 					  hR1.append(sep);				    
 					  // HeaderRow2
-						String columnHeader = (String) iterator.next();
+						String columnHeader = iterator.next();
 						hR2.append(i);
 						hR2.append("_");
 						hR2.append(columnHeader);
@@ -221,13 +221,13 @@ public class QTIExportFormatterCSVType2 extends QTIExportFormatter {
 	}
 
 	public void visit(QTIExportItem eItem) {
-		List responseColumns = eItem.getResponseColumns();
+		List<String> responseColumns = eItem.getResponseColumns();
 		QTIExportItemFormatConfig itemFormatConfig = eItem.getConfig();
 
 		if (displayItem(itemFormatConfig)) {
 			if (itemFormatConfig.hasResponseCols()) {
-				for (Iterator iter = responseColumns.iterator(); iter.hasNext();) {
-					String responseColumn = (String) iter.next();
+				for (Iterator<String> iter = responseColumns.iterator(); iter.hasNext();) {
+					String responseColumn = iter.next();
 					sb.append(emb);
 					sb.append(escape(responseColumn));
 					sb.append(emb);
@@ -285,8 +285,8 @@ public class QTIExportFormatterCSVType2 extends QTIExportFormatter {
 		sb.append(legend);
 		sb.append(car + car);
 		int y = 1;
-		for (Iterator iter = qtiItemObjectList.iterator(); iter.hasNext();) {
-			QTIItemObject element = (QTIItemObject) iter.next();
+		for (Iterator<QTIItemObject> iter = qtiItemObjectList.iterator(); iter.hasNext();) {
+			QTIItemObject element = iter.next();
 			
 			sb.append(element.getItemIdent());
 			sb.append(sep);
@@ -325,7 +325,7 @@ public class QTIExportFormatterCSVType2 extends QTIExportFormatter {
 			sb.append(car);
 			// CELFI#107 END
 
-			List responseLabelMaterials = element.getResponseLabelMaterials();
+			List<String> responseLabelMaterials = element.getResponseLabelMaterials();
 
 			for (int i = 0; i < element.getResponseIdentifier().size(); i++) {
 				sb.append(sep + sep);
diff --git a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType3.java b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType3.java
index 53503c69490b1c82f8baaee802d5b6ed56425d5c..698d03274d374ff5596d886dbd297b7d45a2d83f 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType3.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIExportFormatterCSVType3.java
@@ -121,12 +121,12 @@ public class QTIExportFormatterCSVType3 extends QTIExportFormatter{
 				hR1.append(emb);
 		
 				if (qeif.getExportItemConfig(item).hasResponseCols()){
-					List responseColumnHeaders = item.getResponseColumnHeaders();
-					for (Iterator iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
+					List<String> responseColumnHeaders = item.getResponseColumnHeaders();
+					for (Iterator<String> iterator = responseColumnHeaders.iterator(); iterator.hasNext();) {
 						// HeaderRow1
 						hR1.append(sep);
 					    // HeaderRow2
-					    String columnHeader = (String) iterator.next();
+					    String columnHeader = iterator.next();
 						hR2.append(i);
 						hR2.append("_");
 						hR2.append(columnHeader);
@@ -198,13 +198,13 @@ public class QTIExportFormatterCSVType3 extends QTIExportFormatter{
 	}
 
 	public void visit(QTIExportItem eItem) {
-		List responseColumns = eItem.getResponseColumns();
+		List<String> responseColumns = eItem.getResponseColumns();
 		QTIExportItemFormatConfig itemFormatConfig = eItem.getConfig();
 		
 		if(displayItem(itemFormatConfig)){	
 			if (itemFormatConfig.hasResponseCols()){
-				for (Iterator iter = responseColumns.iterator(); iter.hasNext();) {
-					String responseColumn = (String) iter.next();
+				for (Iterator<String> iter = responseColumns.iterator(); iter.hasNext();) {
+					String responseColumn = iter.next();
 					sb.append(emb);
 					sb.append(escape(responseColumn));
 					sb.append(emb);
@@ -284,7 +284,7 @@ public class QTIExportFormatterCSVType3 extends QTIExportFormatter{
 			sb.append(car);
 			// CELFI#107 END
 			
-			List responseLabelMaterials = element.getResponseLabelMaterials();
+			List<String> responseLabelMaterials = element.getResponseLabelMaterials();
 			
 			for (int i = 0; i < element.getResponseIdentifier().size() ; i++) {
 				sb.append(sep+sep);
diff --git a/src/main/java/org/olat/ims/qti/export/QTIExportItem.java b/src/main/java/org/olat/ims/qti/export/QTIExportItem.java
index a2ca4db785a92cad555b7bbfa9ed1cff798d3142..951ec0413b85328164822ade3fd4f33eb542f865 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIExportItem.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIExportItem.java
@@ -48,8 +48,8 @@ public class QTIExportItem {
 		this.item = item;
 	}
 	
-	public List getResponseColumns(){
-		return this.item.getResponseColumns(this.qtir);
+	public List<String> getResponseColumns(){
+		return item.getResponseColumns(qtir);
 	}
 	
 	public boolean hasResult(){
diff --git a/src/main/java/org/olat/ims/qti/export/QTIExportManager.java b/src/main/java/org/olat/ims/qti/export/QTIExportManager.java
index e2afdcbcd8ef5d164e6356f13c2e8905c5910bf4..fd1359a563bf038a8b00f6120e70f5182ce79474 100644
--- a/src/main/java/org/olat/ims/qti/export/QTIExportManager.java
+++ b/src/main/java/org/olat/ims/qti/export/QTIExportManager.java
@@ -27,11 +27,11 @@ package org.olat.ims.qti.export;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.zip.ZipEntry;
@@ -91,7 +91,7 @@ public class QTIExportManager extends BasicManager{
 			Long testReKey, File exportDirectory, String charset, String fileNameSuffix) {
 		boolean resultsFoundAndExported = false;
 		QTIResultManager qrm = QTIResultManager.getInstance();
-		List<QTIResult> results = qrm.selectResults(courseResId, olatResourceDetail, testReKey, qef.getType());
+		List<QTIResult> results = qrm.selectResults(courseResId, olatResourceDetail, testReKey, null, qef.getType());
 		if(results.size() > 0){
 			QTIResult res0 = results.get(0);
 			List<QTIItemObject> qtiItemObjectList = new QTIObjectTreeBuilder().getQTIItemObjectList(new Long(res0.getResultSet().getRepositoryRef()));
@@ -106,11 +106,11 @@ public class QTIExportManager extends BasicManager{
 	}
 	
 	public boolean selectAndExportResults(QTIExportFormatter qef, Long courseResId, String shortTitle,
-			String olatResourceDetail, RepositoryEntry testRe, ZipOutputStream exportStream, String charset,
+			String olatResourceDetail, RepositoryEntry testRe, ZipOutputStream exportStream,
 			String fileNameSuffix) throws IOException {
 		boolean resultsFoundAndExported = false;
 		QTIResultManager qrm = QTIResultManager.getInstance();
-		List<QTIResult> results = qrm.selectResults(courseResId, olatResourceDetail, testRe.getKey(), qef.getType());
+		List<QTIResult> results = qrm.selectResults(courseResId, olatResourceDetail, testRe.getKey(), null, qef.getType());
 		if(results.size() > 0){
 			List<QTIItemObject> qtiItemObjectList = new QTIObjectTreeBuilder().getQTIItemObjectList(testRe);
 			qef.setQTIItemObjectList(qtiItemObjectList);
@@ -138,17 +138,27 @@ public class QTIExportManager extends BasicManager{
 	 * @param fileNameSuffix
 	 * @return
 	 */
-		public String exportResults(QTIExportFormatter qef, List<QTIResult> results, List<QTIItemObject> qtiItemObjectList, String shortTitle, File exportDirectory, String charset, String fileNameSuffix) {
-			String targetFileName = null;
-	
-			qef.setQTIItemObjectList(qtiItemObjectList);
-			if (results.size() > 0) {
-				createContentOfExportFile(results,qtiItemObjectList,qef);
-				targetFileName = writeContentToFile(shortTitle, exportDirectory, charset, qef, fileNameSuffix);
-			}
-			return targetFileName;
+	public String exportResults(QTIExportFormatter qef, List<QTIResult> results, List<QTIItemObject> qtiItemObjectList, String shortTitle, File exportDirectory, String charset, String fileNameSuffix) {
+		String targetFileName = null;
+
+		qef.setQTIItemObjectList(qtiItemObjectList);
+		if (results.size() > 0) {
+			createContentOfExportFile(results,qtiItemObjectList,qef);
+			targetFileName = writeContentToFile(shortTitle, exportDirectory, charset, qef, fileNameSuffix);
 		}
+		return targetFileName;
+	}
 	
+	public void exportResults(QTIExportFormatter qef, List<QTIResult> results, List<QTIItemObject> qtiItemObjectList,
+			OutputStream exportStream)
+	throws IOException {
+		qef.setQTIItemObjectList(qtiItemObjectList);
+		if (results.size() > 0) {
+			createContentOfExportFile(results,qtiItemObjectList,qef);
+			IOUtils.write(qef.getReport(), exportStream);
+		}
+	}
+
 	
 	/**
 	 * @param locale Locale used for export file headers / default values
@@ -158,7 +168,6 @@ public class QTIExportManager extends BasicManager{
 	 * @return String
 	 */
 	private void createContentOfExportFile(List<QTIResult> qtiResults, List<QTIItemObject> qtiItemObjectList, QTIExportFormatter qef) {
-		
 		qef.openReport();
 		
 		//formatter has information about how to format the different qti objects
@@ -167,12 +176,8 @@ public class QTIExportManager extends BasicManager{
 				
 		while (qtiResults.size() > 0){
 			List<QTIResult> assessIDresults = stripNextAssessID(qtiResults);
-		
 			qef.openResultSet(new QTIExportSet(assessIDresults.get(0)));
-			
-			for (Iterator<QTIItemObject> iter = qtiItemObjectList.iterator(); iter.hasNext();) {
-				QTIItemObject element = iter.next();
-				
+			for(QTIItemObject element:qtiItemObjectList) {
 				QTIResult qtir = element.extractQTIResult(assessIDresults);
 				qef.visit(qeif.getExportItem(qtir,element));
 			}
diff --git a/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseLid.java b/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseLid.java
index 3782d9f479d8330a0ed3f24214c847c5466da2ab..6e9aa5d9786c25ca3077789437763eade62db377 100644
--- a/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseLid.java
+++ b/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseLid.java
@@ -52,10 +52,10 @@ public class ItemWithResponseLid implements QTIItemObject {
 	private String	questionText	= "";
 	// CELFI#107 END
 
-	private String	positionsOfResponses	= null;
-	private List<String>	responseColumnHeaders	= new ArrayList<String>(5);
-	private List<String>	responseLabelIdents	= new ArrayList<String>(5);
-	private List<String>	responseLabelMaterials	= new ArrayList<String>(5);
+	private String positionsOfResponses	= null;
+	private List<String> responseColumnHeaders = new ArrayList<String>(5);
+	private List<String> responseLabelIdents = new ArrayList<String>(5);
+	private List<String> responseLabelMaterials = new ArrayList<String>(5);
 
 	/**
 	 * Constructor for ItemWithResponseLid.
@@ -116,7 +116,7 @@ public class ItemWithResponseLid implements QTIItemObject {
 	/**
 	 * @see org.olat.ims.qti.export.helper.QTIItemObject#extractQTIResult(java.util.List)
 	 */
-	public QTIResult extractQTIResult(List resultSet) {
+	public QTIResult extractQTIResult(List<QTIResult> resultSet) {
 		for (Iterator<QTIResult> iter = resultSet.iterator(); iter.hasNext();) {
 			QTIResult element = iter.next();
 			if (element.getItemIdent().equals(itemIdent)) {
diff --git a/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseStr.java b/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseStr.java
index 398959afed02812930dca2aba29091526b0552ea..971423a8ecef7ed2a70e587888b9cfcb6853354c 100644
--- a/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseStr.java
+++ b/src/main/java/org/olat/ims/qti/export/helper/ItemWithResponseStr.java
@@ -51,11 +51,11 @@ public class ItemWithResponseStr implements QTIItemObject {
 	private String	quetionText				= "";
 	// CELFI#107 END
 
-	private List	responseColumnHeaders	= new ArrayList(5);
-	private List	responseStrIdents		= new ArrayList(5);
+	private List<String> responseColumnHeaders = new ArrayList<>(5);
+	private List<String> responseStrIdents = new ArrayList<>(5);
 
 	// CELFI#107
-	private List	responseLabelMaterials	= new ArrayList(5);
+	private List<String> responseLabelMaterials	= new ArrayList<>(5);
 
 	// CELFI#107 END
 
@@ -125,9 +125,9 @@ public class ItemWithResponseStr implements QTIItemObject {
 	/**
 	 * @see org.olat.ims.qti.export.helper.QTIItemObject#extractQTIResult(java.util.List)
 	 */
-	public QTIResult extractQTIResult(List resultSet) {
-		for (Iterator iter = resultSet.iterator(); iter.hasNext();) {
-			QTIResult element = (QTIResult) iter.next();
+	public QTIResult extractQTIResult(List<QTIResult> resultSet) {
+		for (Iterator<QTIResult> iter = resultSet.iterator(); iter.hasNext();) {
+			QTIResult element = iter.next();
 			if (element.getItemIdent().equals(itemIdent)) {
 				resultSet.remove(element);
 				return element;
@@ -136,7 +136,7 @@ public class ItemWithResponseStr implements QTIItemObject {
 		return null;
 	}
 
-	private void addTextAndTabs(List responseColumns, String s, int num) {
+	private void addTextAndTabs(List<String> responseColumns, String s, int num) {
 		for (int i = 0; i < num; i++) {
 			responseColumns.add(s);
 		}
@@ -154,15 +154,15 @@ public class ItemWithResponseStr implements QTIItemObject {
 		return this.quetionText;
 	}
 
-	public List getResponseColumnHeaders() {
+	public List<String> getResponseColumnHeaders() {
 		return responseColumnHeaders;
 	}
 
 	/**
 	 * @see org.olat.ims.qti.export.helper.QTIItemObject#getResponseColumns(org.olat.ims.qti.QTIResult)
 	 */
-	public List getResponseColumns(QTIResult qtiresult) {
-		List responseColumns = new ArrayList();
+	public List<String> getResponseColumns(QTIResult qtiresult) {
+		List<String> responseColumns = new ArrayList<String>();
 		if (qtiresult == null) {
 			// item has not been choosen
 			addTextAndTabs(responseColumns, "", getNumColumnHeaders());
@@ -170,9 +170,9 @@ public class ItemWithResponseStr implements QTIItemObject {
 			String answer = qtiresult.getAnswer();
 			if (answer.length() == 0) addTextAndTabs(responseColumns, ".", getNumColumnHeaders());
 			else {
-				Map answerMap = QTIResultManager.parseResponseStrAnswers(answer);
+				Map<String,String> answerMap = QTIResultManager.parseResponseStrAnswers(answer);
 				
-				for (Iterator iter = responseStrIdents.iterator(); iter.hasNext();) {
+				for (Iterator<String> iter = responseStrIdents.iterator(); iter.hasNext();) {
 					String element = (String) iter.next();
 					if (answerMap.containsKey(element)) {
 						responseColumns.add(answerMap.get(element));	
@@ -193,11 +193,11 @@ public class ItemWithResponseStr implements QTIItemObject {
 	/**
 	 * @see org.olat.ims.qti.export.helper.QTIItemObject#getResponseIdentifier()
 	 */
-	public List getResponseIdentifier() {
+	public List<String> getResponseIdentifier() {
 		return responseStrIdents;
 	}
 
-	public List getResponseLabelMaterials() {
+	public List<String> getResponseLabelMaterials() {
 		// CELFI#107
 		return responseLabelMaterials;
 	}
diff --git a/src/main/java/org/olat/ims/qti/export/helper/QTIItemObject.java b/src/main/java/org/olat/ims/qti/export/helper/QTIItemObject.java
index 537760b75f5b3eafdcb0812abb869b3abc62f755..490b5ca19911000cfb08403cf9bb71535eaef962 100644
--- a/src/main/java/org/olat/ims/qti/export/helper/QTIItemObject.java
+++ b/src/main/java/org/olat/ims/qti/export/helper/QTIItemObject.java
@@ -46,7 +46,7 @@ public interface QTIItemObject {
 	 * @param resultSet
 	 * @return
 	 */
-	public QTIResult extractQTIResult(List resultSet);
+	public QTIResult extractQTIResult(List<QTIResult> resultSet);
 	
 	/**
 	 * @return
@@ -75,13 +75,13 @@ public interface QTIItemObject {
 	 * 
 	 * @return List responseColumnHeaders
 	 */ 
-	public List getResponseColumnHeaders();
+	public List<String> getResponseColumnHeaders();
 	
 	/**
 	 * 
 	 * @return List responseColumns
 	 */
-	public List getResponseColumns(QTIResult qtiresult);
+	public List<String> getResponseColumns(QTIResult qtiresult);
 	
 	/**
 	 * @return String
@@ -99,14 +99,14 @@ public interface QTIItemObject {
 	 *  - in case of ItemWithResponseStr --> response_str ident
 	 *  - in case of ItemWithResponseLid --> response_label ident
 	 */
-	public List getResponseIdentifier();
+	public List<String> getResponseIdentifier();
 	
 	
 	/**
 	 * 
 	 * @return Null, if the item has no material, otherwise a list of materials
 	 */
-	public List getResponseLabelMaterials();
+	public List<String> getResponseLabelMaterials();
 
 	/**
 	 * 
diff --git a/src/main/java/org/olat/ims/qti/export/helper/QTIObjectTreeBuilder.java b/src/main/java/org/olat/ims/qti/export/helper/QTIObjectTreeBuilder.java
index ea79cb9942dc1f97a44bd12f76eff07836d546d9..195365fe63e936aeb66fb6038c8099973f341acb 100644
--- a/src/main/java/org/olat/ims/qti/export/helper/QTIObjectTreeBuilder.java
+++ b/src/main/java/org/olat/ims/qti/export/helper/QTIObjectTreeBuilder.java
@@ -95,7 +95,7 @@ public class QTIObjectTreeBuilder {
 		return getQTIItemObjectList(resolver);
 	}
 	
-	private final List<QTIItemObject> getQTIItemObjectList(Resolver resolver) {
+	public final List<QTIItemObject> getQTIItemObjectList(Resolver resolver) {
 		Document doc = resolver.getQTIDocument();
 		Element root = doc.getRootElement();
 		@SuppressWarnings("unchecked")
diff --git a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java
index 91a0b03e68ac96384728e1118119c5069ccdccb4..44954dc3efa197d83f3617193bc40e40c1c1ebb6 100644
--- a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java
+++ b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java
@@ -28,6 +28,8 @@ import org.olat.core.gui.components.tree.TreeModel;
 import org.olat.core.gui.components.tree.TreeNode;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.id.OLATResourceable;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.course.nodes.CourseNodeConfiguration;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.IQSELFCourseNode;
@@ -47,8 +49,6 @@ import org.olat.ims.qti.process.ImsRepositoryResolver;
 import org.olat.ims.qti.statistics.model.StatisticAssessment;
 import org.olat.ims.qti.statistics.ui.QTI12AssessmentStatisticsController;
 import org.olat.ims.qti.statistics.ui.QTI12ItemStatisticsController;
-import org.olat.ims.qti.statistics.ui.QTI12SurveyItemStatisticsController;
-import org.olat.ims.qti.statistics.ui.QTI12SurveyStatisticsController;
 import org.olat.ims.qti.statistics.ui.QTI21OnyxAssessmentStatisticsController;
 import org.olat.repository.RepositoryEntry;
 
@@ -62,6 +62,7 @@ import de.bps.onyx.plugin.OnyxModule;
 public class QTIStatisticResourceResult implements StatisticResourceResult {
 	
 	private final QTICourseNode courseNode;
+	private final OLATResourceable courseOres;
 	private final QTIStatisticsManager qtiStatisticsManager;
 	
 	private StatisticAssessment statisticAssessment;
@@ -72,9 +73,10 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 	
 	private QTIType type;
 	
-	public QTIStatisticResourceResult(QTICourseNode courseNode, QTIStatisticSearchParams searchParams) {
+	public QTIStatisticResourceResult(OLATResourceable courseOres, QTICourseNode courseNode, QTIStatisticSearchParams searchParams) {
 		this.courseNode = courseNode;
 		this.searchParams = searchParams;
+		this.courseOres = OresHelper.clone(courseOres);
 		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
 
 		qtiRepositoryEntry = courseNode.getReferencedRepositoryEntry();
@@ -99,6 +101,10 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 		return type;
 	}
 
+	public OLATResourceable getCourseOres() {
+		return courseOres;
+	}
+
 	public QTICourseNode getTestCourseNode() {
 		return courseNode;
 	}
@@ -111,6 +117,10 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 		return qtiDocument;
 	}
 	
+	public String getMediaBaseURL() {
+		return getResolver().getStaticsBaseURI() + "/";
+	}
+	
 	public QTIStatisticSearchParams getSearchParams() {
 		return searchParams;
 	}
@@ -156,9 +166,7 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 	
 	private Controller createAssessmentController(UserRequest ureq, WindowControl wControl, boolean printMode) {
 		Controller ctrl;
-		if(type == QTIType.survey) {
-			ctrl = new QTI12SurveyStatisticsController(ureq, wControl, this, printMode);
-		} else if (type == QTIType.onyx){
+		if (type == QTIType.onyx){
 			ctrl = new QTI21OnyxAssessmentStatisticsController(ureq, wControl, this, printMode);
 		} else {
 			ctrl = new QTI12AssessmentStatisticsController(ureq, wControl, this, printMode);
@@ -171,7 +179,7 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 	
 	private Controller createItemController(UserRequest ureq, WindowControl wControl, Item item, boolean printMode) {
 		if(type == QTIType.survey) {
-			return new QTI12SurveyItemStatisticsController(ureq, wControl, item, this, printMode);
+			return new QTI12ItemStatisticsController(ureq, wControl, item, this, printMode);
 		} else {
 			return new QTI12ItemStatisticsController(ureq, wControl, item, this, printMode);
 		}
@@ -184,6 +192,9 @@ public class QTIStatisticResourceResult implements StatisticResourceResult {
 			rootNode.addChild(sectionNode);
 			for (Item item : section.getItems()) {
 				GenericTreeNode itemNode = new ItemNode(item);
+				if(sectionNode.getDelegate() == null) {
+					sectionNode.setDelegate(itemNode);
+				}
 				itemNode.setUserObject(item);
 				sectionNode.addChild(itemNode);
 			}
diff --git a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsManager.java b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsManager.java
index 138211a91d86ce2807014a2464ad2af336493bd8..5ceb1493c9114f424e501c17259e1c8442bcbd59 100644
--- a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsManager.java
+++ b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsManager.java
@@ -23,15 +23,16 @@ package org.olat.ims.qti.statistics;
 import java.util.List;
 
 import org.olat.ims.qti.editor.beecom.objects.Item;
-import org.olat.ims.qti.statistics.model.StatisticsItem;
 import org.olat.ims.qti.statistics.model.QTIStatisticResult;
 import org.olat.ims.qti.statistics.model.QTIStatisticResultSet;
 import org.olat.ims.qti.statistics.model.StatisticAnswerOption;
 import org.olat.ims.qti.statistics.model.StatisticAssessment;
 import org.olat.ims.qti.statistics.model.StatisticChoiceOption;
+import org.olat.ims.qti.statistics.model.StatisticFIBOption;
 import org.olat.ims.qti.statistics.model.StatisticItem;
 import org.olat.ims.qti.statistics.model.StatisticKPrimOption;
 import org.olat.ims.qti.statistics.model.StatisticSurveyItem;
+import org.olat.ims.qti.statistics.model.StatisticsItem;
 
 /**
  * 
@@ -93,6 +94,8 @@ public interface QTIStatisticsManager {
 	 */
 	public List<StatisticAnswerOption> getStatisticAnswerOptionsOfItem(String itemIdent, QTIStatisticSearchParams searchParams);
 	
+	public List<StatisticFIBOption> getStatisticAnswerOptionsFIB(Item itemIdent, QTIStatisticSearchParams searchParams);
+	
 	/**
 	 * 
 	 * @param itemIdent
diff --git a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsResource.java b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..80f6004697c548487acef4aeb3e6ec760a4b104d
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticsResource.java
@@ -0,0 +1,146 @@
+/**
+ * <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.qti.statistics;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.olat.basesecurity.Group;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.Formatter;
+import org.olat.core.util.StringHelper;
+import org.olat.course.nodes.CourseNode;
+import org.olat.ims.qti.QTIResult;
+import org.olat.ims.qti.QTIResultManager;
+import org.olat.ims.qti.export.QTIArchiveWizardController;
+import org.olat.ims.qti.export.QTIExportFormatter;
+import org.olat.ims.qti.export.QTIExportFormatterCSVType1;
+import org.olat.ims.qti.export.QTIExportFormatterCSVType2;
+import org.olat.ims.qti.export.QTIExportManager;
+import org.olat.ims.qti.export.helper.QTIItemObject;
+import org.olat.ims.qti.export.helper.QTIObjectTreeBuilder;
+
+/**
+ * 
+ * Initial date: 17.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QTIStatisticsResource implements MediaResource {
+	
+	private static final OLog log = Tracing.createLoggerFor(QTIStatisticsResource.class);
+	
+	private final Locale locale;
+	private final String encoding = "UTF-8";
+	
+	private final QTIStatisticResourceResult resourceResult;
+	
+	public QTIStatisticsResource(QTIStatisticResourceResult resourceResult, Locale locale) {
+		this.resourceResult = resourceResult;
+		this.locale = locale;
+	}
+
+	@Override
+	public String getContentType() {
+		return "application/zip";
+	}
+
+	@Override
+	public Long getSize() {
+		return null;
+	}
+
+	@Override
+	public InputStream getInputStream() {
+		return null;
+	}
+
+	@Override
+	public Long getLastModified() {
+		return null;
+	}
+
+	@Override
+	public void prepare(HttpServletResponse hres) {
+		try {
+			hres.setCharacterEncoding(encoding);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+		CourseNode courseNode = resourceResult.getTestCourseNode();
+		String label = courseNode.getType() + "_"
+				+ StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName())
+				+ "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()))
+				+ ".csv";
+		String urlEncodedLabel = StringHelper.urlEncodeUTF8(label);
+		hres.setHeader("Content-Disposition","attachment; filename*=UTF-8''" + urlEncodedLabel);			
+		hres.setHeader("Content-Description", urlEncodedLabel);
+		
+		try {
+			String sep = "\\t"; // fields separated by
+			String emb = "\""; // fields embedded by
+			String esc = "\\"; // fields escaped by
+			String car = "\\r\\n"; // carriage return
+			
+			sep = QTIArchiveWizardController.convert2CtrlChars(sep);
+			car = QTIArchiveWizardController.convert2CtrlChars(car);
+			
+			int exportType = 1;
+			QTIExportFormatter formatter;
+			if (QTIType.test.equals(resourceResult.getType())){
+				exportType = 1;
+				formatter = new QTIExportFormatterCSVType1(locale, sep, emb, esc, car, true);
+		  	} else if (QTIType.survey.equals(resourceResult.getType())) {
+		  		exportType = 2;
+		  		formatter = new QTIExportFormatterCSVType2(locale, null, sep, emb, esc, car, true);
+		  	} else {
+		  		return;
+		  	}
+
+			Long qtiRepoEntryKey = resourceResult.getQTIRepositoryEntry().getKey();
+			List<QTIItemObject> itemList = new QTIObjectTreeBuilder().getQTIItemObjectList(resourceResult.getResolver());
+			formatter.setMapWithExportItemConfigs(QTIArchiveWizardController.getQTIItemConfigs(itemList));
+			
+			QTIResultManager qrm = QTIResultManager.getInstance();
+			
+			QTIStatisticSearchParams params = resourceResult.getSearchParams();
+			List<Group> limitToGroups = params.isMayViewAllUsersAssessments()
+					? null : params.getLimitToGroups();
+
+			List<QTIResult> results = qrm.selectResults(resourceResult.getCourseOres().getResourceableId(),
+					courseNode.getIdent(), qtiRepoEntryKey, limitToGroups, exportType);
+			
+			QTIExportManager.getInstance().exportResults(formatter, results, itemList, hres.getOutputStream());
+		} catch (Exception e) {
+			log.error("", e);
+		}
+	}
+
+	@Override
+	public void release() {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti/statistics/manager/QTIStatisticsManagerImpl.java b/src/main/java/org/olat/ims/qti/statistics/manager/QTIStatisticsManagerImpl.java
index 0be0e39061674f6ed14e88e635f549247e83fa9b..fb1bd3bae6bf8a587853646bc7c0d69c94ad8bfd 100644
--- a/src/main/java/org/olat/ims/qti/statistics/manager/QTIStatisticsManagerImpl.java
+++ b/src/main/java/org/olat/ims/qti/statistics/manager/QTIStatisticsManagerImpl.java
@@ -21,6 +21,7 @@
 package org.olat.ims.qti.statistics.manager;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -29,6 +30,8 @@ import javax.persistence.TypedQuery;
 
 import org.olat.core.commons.persistence.DB;
 import org.olat.course.assessment.AssessmentManager;
+import org.olat.ims.qti.QTIResultManager;
+import org.olat.ims.qti.editor.beecom.objects.FIBResponse;
 import org.olat.ims.qti.editor.beecom.objects.Item;
 import org.olat.ims.qti.editor.beecom.objects.Response;
 import org.olat.ims.qti.statistics.QTIStatisticSearchParams;
@@ -38,6 +41,7 @@ import org.olat.ims.qti.statistics.model.QTIStatisticResultSet;
 import org.olat.ims.qti.statistics.model.StatisticAnswerOption;
 import org.olat.ims.qti.statistics.model.StatisticAssessment;
 import org.olat.ims.qti.statistics.model.StatisticChoiceOption;
+import org.olat.ims.qti.statistics.model.StatisticFIBOption;
 import org.olat.ims.qti.statistics.model.StatisticItem;
 import org.olat.ims.qti.statistics.model.StatisticKPrimOption;
 import org.olat.ims.qti.statistics.model.StatisticSurveyItem;
@@ -373,7 +377,6 @@ public class QTIStatisticsManagerImpl implements QTIStatisticsManager {
 	@Override
 	public List<StatisticKPrimOption> getNumbersInKPrim(Item item, QTIStatisticSearchParams searchParams) {
 		List<StatisticAnswerOption> rawDatas = getStatisticAnswerOptionsOfItem(item.getIdent(), searchParams);
-		
 		List<Response> responses = item.getQuestion().getResponses();
 		List<StatisticKPrimOption> kprimPoints = new ArrayList<>();
 		for(Response response:responses) {
@@ -401,6 +404,74 @@ public class QTIStatisticsManagerImpl implements QTIStatisticsManager {
 		}
 		return kprimPoints;
 	}
+
+	@Override
+	public List<StatisticFIBOption> getStatisticAnswerOptionsFIB(Item item, QTIStatisticSearchParams searchParams) {
+
+		List<StatisticFIBOption> options = new ArrayList<>();
+		Map<String,StatisticFIBOption> optionMap = new HashMap<>();
+		
+		List<Response> responses = item.getQuestion().getResponses();
+		for(Response response:responses) {
+			if(response instanceof FIBResponse) {
+				FIBResponse fibResponse = (FIBResponse)response;
+				if(FIBResponse.TYPE_BLANK.equals(fibResponse.getType())) {
+					String ident = fibResponse.getIdent();
+					String[] correctFIBs = fibResponse.getCorrectBlank().split(";");
+					if(correctFIBs == null || correctFIBs.length == 0) {
+						continue;
+					}
+					
+					StatisticFIBOption option = new StatisticFIBOption();
+					option.setCorrectBlank(correctFIBs[0]);
+					option.setAlternatives(Arrays.asList(correctFIBs));
+					option.setCaseSensitive("Yes".equals(fibResponse.getCaseSensitive()));
+					option.setPoints(fibResponse.getPoints());
+					options.add(option);
+					optionMap.put(ident, option);
+				}
+			}
+		}
+		
+		
+		List<StatisticAnswerOption> answerOptions = getStatisticAnswerOptionsOfItem(item.getIdent(), searchParams);
+		
+		for(StatisticAnswerOption answerOption:answerOptions) {
+			long count = answerOption.getCount();
+			String concatenedAnswer = answerOption.getAnswer();
+			Map<String,String> parsedAnswerMap = QTIResultManager.parseResponseStrAnswers(concatenedAnswer);
+			for(Map.Entry<String, String> parsedAnswerEntry: parsedAnswerMap.entrySet()) {
+				String ident = parsedAnswerEntry.getKey();
+
+				StatisticFIBOption option = optionMap.get(ident);
+				if(option == null) {
+					continue;
+				}
+				
+				String text = parsedAnswerEntry.getValue();
+				boolean correct;
+				if(option.isCaseSensitive()) {
+					correct = option.getAlternatives().contains(text);
+				} else {
+					correct = false;
+					for(String alt:option.getAlternatives()) {
+						if(alt.equalsIgnoreCase(text)) {
+							correct = true;
+						}
+					}
+				}
+				
+				if(correct) {
+					option.setNumOfCorrect(option.getNumOfCorrect() + count);
+				} else {
+					option.setNumOfIncorrect(option.getNumOfIncorrect() + count);
+					option.getWrongAnswers().add(text);
+				}
+			}
+		}
+
+		return options;
+	}
 	
 	@Override
 	public List<StatisticAnswerOption> getStatisticAnswerOptionsOfItem(String itemIdent, QTIStatisticSearchParams searchParams) {
diff --git a/src/main/java/org/olat/ims/qti/statistics/model/StatisticFIBOption.java b/src/main/java/org/olat/ims/qti/statistics/model/StatisticFIBOption.java
new file mode 100644
index 0000000000000000000000000000000000000000..59c85226cf4a64deb058af4be4f32e80370dd140
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/model/StatisticFIBOption.java
@@ -0,0 +1,97 @@
+/**
+ * <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.qti.statistics.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 
+ * Initial date: 10.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class StatisticFIBOption {
+	
+	private long numOfCorrect = 0l;
+	private long numOfIncorrect = 0l;
+	private float points;
+	
+	private boolean caseSensitive;
+	private String correctBlank;
+	private List<String> alternatives;
+	private List<String> wrongAnswers = new ArrayList<>();
+
+	public long getNumOfCorrect() {
+		return numOfCorrect;
+	}
+
+	public void setNumOfCorrect(long numOfCorrect) {
+		this.numOfCorrect = numOfCorrect;
+	}
+
+	public long getNumOfIncorrect() {
+		return numOfIncorrect;
+	}
+
+	public void setNumOfIncorrect(long numOfIncorrect) {
+		this.numOfIncorrect = numOfIncorrect;
+	}
+
+	public float getPoints() {
+		return points;
+	}
+
+	public void setPoints(float points) {
+		this.points = points;
+	}
+
+	public boolean isCaseSensitive() {
+		return caseSensitive;
+	}
+
+	public void setCaseSensitive(boolean caseSensitive) {
+		this.caseSensitive = caseSensitive;
+	}
+
+	public String getCorrectBlank() {
+		return correctBlank;
+	}
+
+	public void setCorrectBlank(String correctBlank) {
+		this.correctBlank = correctBlank;
+	}
+
+	public List<String> getAlternatives() {
+		return alternatives;
+	}
+
+	public void setAlternatives(List<String> alternatives) {
+		this.alternatives = alternatives;
+	}
+
+	public List<String> getWrongAnswers() {
+		return wrongAnswers;
+	}
+
+	public void setWrongAnswers(List<String> wrongAnswers) {
+		this.wrongAnswers = wrongAnswers;
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/AbstractAssessmentStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/AbstractAssessmentStatisticsController.java
deleted file mode 100644
index 9a55a4f04f62bef9b306f589045edd5b07f2346f..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/AbstractAssessmentStatisticsController.java
+++ /dev/null
@@ -1,117 +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.ims.qti.statistics.ui;
-
-import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
-import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
-import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.chart.HistogramComponent;
-import org.olat.core.gui.components.chart.Scale;
-import org.olat.core.gui.components.velocity.VelocityContainer;
-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.creator.ControllerCreator;
-import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
-import org.olat.ims.qti.statistics.QTIStatisticsManager;
-import org.olat.ims.qti.statistics.model.StatisticAssessment;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class AbstractAssessmentStatisticsController extends BasicController  {
-	
-	protected final VelocityContainer mainVC;
-	protected final QTIStatisticResourceResult resourceResult;
-	
-	protected final QTIStatisticsManager qtiStatisticsManager;
-	
-	public AbstractAssessmentStatisticsController(UserRequest ureq, WindowControl wControl,
-			QTIStatisticResourceResult resourceResult, boolean printMode, String page) {
-		super(ureq, wControl);
-		
-		this.resourceResult = resourceResult;
-		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
-		
-		mainVC = createVelocityContainer(page);
-		mainVC.contextPut("printMode", new Boolean(printMode));
-		
-		initDurationHistogram(resourceResult.getQTIStatisticAssessment());
-		
-		putInitialPanel(mainVC);
-	}
-	
-	private void initDurationHistogram(StatisticAssessment stats) {
-		HistogramComponent scoreHistogram = new HistogramComponent("scoreHistogram");
-		scoreHistogram.setLongValues(stats.getDurations());
-		scoreHistogram.setYLegend(translate("chart.percent.participants"));
-		scoreHistogram.setXScale(Scale.hour);
-		mainVC.put("durationHistogram", scoreHistogram);
-	}
-	
-	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
-		if("print".equals(event.getCommand())){
-			printPages(ureq);
-		}
-	}
-
-	private void printPages(UserRequest ureq) {
-		ControllerCreator printControllerCreator = new ControllerCreator() {
-			public Controller createController(UserRequest lureq, WindowControl lwControl) {
-				Controller printCtr = new QTI12PrintController(lureq, lwControl, resourceResult);
-				Component view = printCtr.getInitialComponent();
-				LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(lureq, lwControl, null, null, view, null);
-				return layoutCtr;
-			}					
-		};
-		ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator);
-		openInNewBrowserWindow(ureq, layoutCtrlr);
-	}
-	
-	public static class ItemInfos {
-		
-		private final String label;
-		private final String text;
-		
-		public ItemInfos(String label, String text) {
-			this.label = label;
-			this.text = text;
-		}
-
-		public String getLabel() {
-			return label;
-		}
-
-		public String getText() {
-			return text;
-		}
-	}
-}
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/AbstractItemStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/AbstractItemStatisticsController.java
deleted file mode 100644
index c98f71380e63bda9a76fa78cb84152f7505d6edd..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/AbstractItemStatisticsController.java
+++ /dev/null
@@ -1,216 +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.ims.qti.statistics.ui;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.lang.StringUtils;
-import org.olat.core.CoreSpringFactory;
-import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.chart.BarChartComponent;
-import org.olat.core.gui.components.chart.BarSeries;
-import org.olat.core.gui.components.velocity.VelocityContainer;
-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.ims.qti.editor.beecom.objects.Item;
-import org.olat.ims.qti.editor.beecom.objects.Question;
-import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
-import org.olat.ims.qti.statistics.QTIStatisticSearchParams;
-import org.olat.ims.qti.statistics.QTIStatisticsManager;
-import org.olat.ims.qti.statistics.QTIType;
-import org.olat.ims.qti.statistics.model.StatisticsItem;
-import org.olat.ims.qti.statistics.model.StatisticAnswerOption;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-abstract class AbstractItemStatisticsController extends BasicController {
-	
-	
-	protected final VelocityContainer mainVC;
-	
-	protected final Item item;
-	protected final QTIType type;
-	protected final QTIStatisticSearchParams searchParams;
-	protected final QTIStatisticsManager qtiStatisticsManager;
-	
-	protected final int numOfParticipants;
-	protected final String mediaBaseURL;
-	
-	public AbstractItemStatisticsController(UserRequest ureq, WindowControl wControl,
-			Item item, QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl);
-		this.item = item;
-		numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
-		searchParams = resourceResult.getSearchParams();
-		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
-		
-		mediaBaseURL = resourceResult.getResolver().getStaticsBaseURI() + "/";
-		type = resourceResult.getType();
-
-		int questionType = item.getQuestion().getType();
-		switch(questionType) {
-			case Question.TYPE_SC:
-				mainVC = createVelocityContainer("statistics_item_" + type.name());
-				initSingleChoice();
-				initChoice();
-				break;
-			case Question.TYPE_MC:
-				mainVC = createVelocityContainer("statistics_item_" + type.name());
-				StatisticsItem itemstats = initChoice();
-				initMultipleChoice(itemstats);
-				break;
-			case Question.TYPE_KPRIM:
-				mainVC = createVelocityContainer("statistics_item_" + type.name());
-				initKPrim();
-				initChoice();
-				break;
-			case Question.TYPE_FIB:
-				mainVC = createVelocityContainer("statistics_item_" + type.name());
-				initFIB();
-				initChoice();
-				break;
-			case Question.TYPE_ESSAY:
-				mainVC = createVelocityContainer("statistics_essai");
-				initEssay();
-				break;
-			default:
-				mainVC = createVelocityContainer("statistics_item_" + type.name());
-				break;
-		}
-		
-		mainVC.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
-		mainVC.contextPut("questionType", questionType);
-		mainVC.contextPut("title", item.getTitle());
-		mainVC.contextPut("printMode", new Boolean(printMode));
-		putInitialPanel(mainVC);
-	}
-	
-	protected abstract void initSingleChoice();
-	
-	protected abstract void initMultipleChoice(StatisticsItem itemstats);
-	
-	protected abstract void initKPrim();
-	
-	protected void initFIB() {
-		List<StatisticAnswerOption> processedAnswers = qtiStatisticsManager
-				.getStatisticAnswerOptionsOfItem(item.getIdent(), searchParams);
-
-		BarSeries d1 = new BarSeries();
-		for (StatisticAnswerOption entry : processedAnswers) {
-			String answerString = getAllBlanksFromAnswer(entry.getAnswer());
-			d1.add(entry.getCount(), answerString);
-		}
-
-		BarChartComponent durationChart = new BarChartComponent("questionChart");
-		durationChart.addSeries(d1);
-		mainVC.put("questionChart", durationChart);
-	}
-	
-	protected static String getAllBlanksFromAnswer(String answerString) {
-		List<String> blanks = new ArrayList<String>();
-		Pattern p = Pattern.compile("\\[\\[([^\\[\\[,\\]]*)\\]\\]");
-		Matcher m = p.matcher(answerString);
-		while (m.find()) {
-			blanks.add(m.group().replace("]]", "").replace("[[", ""));
-		}
-		return StringUtils.join(blanks, ", ");
-	}
-	
-
-	
-	protected abstract StatisticsItem initChoice();
-	
-	protected void initEssay() {
-		mainVC.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
-		mainVC.contextPut("title", item.getTitle());
-		
-		List<String> answers = qtiStatisticsManager.getAnswers(item.getIdent(), searchParams);
-
-		List<String> cleanedAnswers = new ArrayList<String>();
-		for (String string : answers) {
-			cleanedAnswers.add(stripAnswerText(string));
-		}
-		mainVC.contextPut("studentAnswers", cleanedAnswers);
-	}
-		
-	private String stripAnswerText(String answerTextFromDB){
-		String result ="";
-		int start = answerTextFromDB.indexOf("[");
-		result = answerTextFromDB.substring(start+2);
-		result = result.substring(0, result.length()-2);
-		result = result.replaceAll("\\\\r\\\\n", "<br />");
-		return result;
-	}
-	
-	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
-		//
-	}
-
-	public static class ResponseInfos {
-		
-		private final String label;
-		private final String text;
-		private final float points;
-		private final boolean correct;
-		private final boolean survey;
-		
-		public ResponseInfos(String label, String text, float points, boolean correct, boolean survey) {
-			this.label = label;
-			this.text = text;
-			this.points = points;
-			this.survey = survey;
-			this.correct = correct;
-		}
-	
-		public String getLabel() {
-			return label;
-		}
-	
-		public String getText() {
-			return text;
-		}
-	
-		public float getPoints() {
-			return points;
-		}
-	
-		public boolean isSurvey() {
-			return survey;
-		}
-
-		public boolean isCorrect() {
-			return correct;
-		}
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12AssessmentStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12AssessmentStatisticsController.java
index 5e6dcc5b0090e05e39507c9b470d3b380654e12f..7378394aef895b09cc47a5ea15466c55c8b74ced 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12AssessmentStatisticsController.java
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12AssessmentStatisticsController.java
@@ -23,50 +23,78 @@ import static org.olat.ims.qti.statistics.ui.StatisticFormatter.duration;
 import static org.olat.ims.qti.statistics.ui.StatisticFormatter.format;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
+import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.chart.BarChartComponent;
+import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.chart.BarSeries;
-import org.olat.core.gui.components.chart.HistogramComponent;
-import org.olat.core.gui.components.chart.HorizontalBarChartComponent;
-import org.olat.core.gui.components.chart.Scale;
+import org.olat.core.gui.components.chart.BarSeries.Stringuified;
+import org.olat.core.gui.components.chart.StatisticsComponent;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+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.creator.ControllerCreator;
+import org.olat.core.gui.media.MediaResource;
 import org.olat.course.nodes.QTICourseNode;
 import org.olat.course.nodes.iq.IQEditController;
 import org.olat.ims.qti.editor.beecom.objects.Item;
 import org.olat.ims.qti.editor.beecom.objects.QTIDocument;
 import org.olat.ims.qti.editor.beecom.objects.Section;
 import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
+import org.olat.ims.qti.statistics.QTIStatisticsManager;
+import org.olat.ims.qti.statistics.QTIStatisticsResource;
+import org.olat.ims.qti.statistics.QTIType;
 import org.olat.ims.qti.statistics.model.StatisticAssessment;
 import org.olat.ims.qti.statistics.model.StatisticItem;
+import org.olat.ims.qti.statistics.model.StatisticSurveyItem;
 
 /**
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QTI12AssessmentStatisticsController extends AbstractAssessmentStatisticsController {
-	
+public class QTI12AssessmentStatisticsController extends BasicController {
+
+	private final QTIType type;
 	private final Float maxScore;
 	private final Float cutValue;
 	private final String mediaBaseURL;
+	
+	private final Link downloadRawLink;
+	private final VelocityContainer mainVC;
+	
+	private final SeriesFactory seriesfactory;
+	private final QTIStatisticResourceResult resourceResult;
+	private final QTIStatisticsManager qtiStatisticsManager;
 
 	public QTI12AssessmentStatisticsController(UserRequest ureq, WindowControl wControl,
 			QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl, resourceResult, printMode, "statistics_assessment");
+		super(ureq, wControl);
 		
-		mediaBaseURL = resourceResult.getResolver().getStaticsBaseURI() + "/";
+		type = resourceResult.getType();
+		this.resourceResult = resourceResult;
+		mediaBaseURL = resourceResult.getMediaBaseURL();
+		seriesfactory = new SeriesFactory(resourceResult);
+		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
+		
+		mainVC = createVelocityContainer("statistics_assessment");
+		mainVC.put("loadd3js", new StatisticsComponent("d3loader"));
+		mainVC.contextPut("printMode", new Boolean(printMode));
+		downloadRawLink = LinkFactory.createLink("download.raw.data", mainVC, this);
+		downloadRawLink.setCustomEnabledLinkCSS("b_content_download");
+		mainVC.put("download", downloadRawLink);
 
 		//cut value
 		QTICourseNode testNode = resourceResult.getTestCourseNode();
-		Object cutScoreObj = testNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_CUTVALUE);
-		if (cutScoreObj instanceof Float) {
-			cutValue = (Float)cutScoreObj;
-		} else {
-			cutValue = null;
-		}
-
+		
 		StatisticAssessment stats = resourceResult.getQTIStatisticAssessment();
 
 		List<Item> items = new ArrayList<>();
@@ -76,50 +104,96 @@ public class QTI12AssessmentStatisticsController extends AbstractAssessmentStati
 				items.add(item);
 			}
 		}
+		
+		cutValue = getCutValueSetting(testNode);
+		maxScore = getMaxScoreSetting(testNode, items);
 
-		Object maxScoreObj = testNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_MAXSCORE);
-		if (maxScoreObj instanceof Float) {
-			maxScore = (Float)maxScoreObj;
+		initCourseNodeInformation(stats);
+		initDurationHistogram(resourceResult.getQTIStatisticAssessment());
+		if(QTIType.test.equals(type)) {
+			initScoreHistogram(stats);
+			initScoreStatisticPerItem(items, stats.getNumOfParticipants());
 		} else {
-			// try to calculate max
-			float max = 0;
-			for (Item item: items) {
-				if(item.getQuestion() != null) {
-					max += item.getQuestion().getMaxValue();
+			initItemsOverview(items);
+		}
+		
+		putInitialPanel(mainVC);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	private Float getCutValueSetting(QTICourseNode testNode) {
+		Float cutValueSetting;
+		if(QTIType.test.equals(type)) {
+			Object cutScoreObj = testNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_CUTVALUE);
+			if (cutScoreObj instanceof Float) {
+				cutValueSetting = (Float)cutScoreObj;
+			} else {
+				cutValueSetting = null;
+			}
+		} else {
+			cutValueSetting = null;
+		}
+		return cutValueSetting;
+	}
+	
+	private Float getMaxScoreSetting(QTICourseNode testNode, List<Item> items) {
+		Float maxScoreSetting;
+		if(QTIType.test.equals(type)) {
+			Object maxScoreObj = testNode.getModuleConfiguration().get(IQEditController.CONFIG_KEY_MAXSCORE);
+			if (maxScoreObj instanceof Float) {
+				maxScoreSetting = (Float)maxScoreObj;
+			} else {
+				// try to calculate max
+				float max = 0;
+				for (Item item: items) {
+					if(item.getQuestion() != null) {
+						max += item.getQuestion().getMaxValue();
+					}
 				}
+				maxScoreSetting = max > 0 ? max : null;
 			}
-			maxScore = max > 0 ? max : null;
+		} else {
+			maxScoreSetting = null;
 		}
-
-		initCourseNodeInformation(stats);
-		initScoreHistogram(stats);
-		initScoreStatisticPerItem(items, stats.getNumOfParticipants());
+		return maxScoreSetting;
 	}
 	
 	private void initCourseNodeInformation(StatisticAssessment stats) {
 		mainVC.contextPut("numOfParticipants", stats.getNumOfParticipants());
-	
 		mainVC.contextPut("type", resourceResult.getType());
-		mainVC.contextPut("numOfPassed", stats.getNumOfPassed());
-		mainVC.contextPut("numOfFailed", stats.getNumOfFailed());
-
-		if (cutValue != null) {
-			mainVC.contextPut("cutScore", format(cutValue));
-		} else {
-			mainVC.contextPut("cutScore", "-");
+		
+		if(QTIType.test.equals(type)) {
+			mainVC.contextPut("numOfPassed", stats.getNumOfPassed());
+			mainVC.contextPut("numOfFailed", stats.getNumOfFailed());
+	
+			if (cutValue != null) {
+				mainVC.contextPut("cutScore", format(cutValue));
+			} else {
+				mainVC.contextPut("cutScore", "-");
+			}
+	
+			mainVC.contextPut("maxScore", format(maxScore));
+			mainVC.contextPut("average", format(stats.getAverage()));
+			mainVC.contextPut("range", format(stats.getRange()));
+			mainVC.contextPut("standardDeviation", format(stats.getStandardDeviation()));
+			mainVC.contextPut("mode", getModeString(stats.getMode()));
+			mainVC.contextPut("median", format(stats.getMedian()));
 		}
-
-		mainVC.contextPut("maxScore", format(maxScore));
-		mainVC.contextPut("average", format(stats.getAverage()));
-		mainVC.contextPut("range", format(stats.getRange()));
-		mainVC.contextPut("standardDeviation", format(stats.getStandardDeviation()));
-		mainVC.contextPut("mode", getModeString(stats.getMode()));
-		mainVC.contextPut("median", format(stats.getMedian()));
 		
 		String duration = duration(stats.getAverageDuration());
 		mainVC.contextPut("averageDuration", duration);
 	}
 	
+	private void initDurationHistogram(StatisticAssessment stats) {
+		VelocityContainer durationHistogramVC = createVelocityContainer("histogram_duration");
+		durationHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getDurations()));
+		mainVC.put("durationHistogram", durationHistogramVC);
+	}
+	
 	private String getModeString(List<Double> modes) {
 		StringBuilder sb = new StringBuilder();
 		for(Double mode:modes) {
@@ -140,12 +214,12 @@ public class QTI12AssessmentStatisticsController extends AbstractAssessmentStati
 		for (StatisticItem statisticItem: statisticItems) {
 			Item item = statisticItem.getItem();
 			
-			String label = StatisticFormatter.getLabel(i++);
+			String label = Integer.toString(++i);
 			String text = item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL);
 
 			d1.add(statisticItem.getAverageScore(), label);
 			double numOfRightAnswers = statisticItem.getNumOfCorrectAnswers();
-			double res = numOfRightAnswers / numOfParticipants;
+			double res = numOfRightAnswers;
 			d2.add(res, label);
 			
 			itemInfos.add(new ItemInfos(label, text));
@@ -153,29 +227,89 @@ public class QTI12AssessmentStatisticsController extends AbstractAssessmentStati
 		
 		mainVC.contextPut("itemInfoList", itemInfos);
 
-		HorizontalBarChartComponent averageScorePerItemChart = new HorizontalBarChartComponent("questionPoint");
-		averageScorePerItemChart.addSeries(d1);
-		averageScorePerItemChart.setXLegend(translate("chart.answer.averageScoreQuestions.y"));
-		mainVC.put("averageScorePerItemChart", averageScorePerItemChart);
+		VelocityContainer averageScorePeritemVC = createVelocityContainer("hbar_average_score_per_item");
+		Stringuified data1 = BarSeries.getDatasAndColors(Collections.singletonList(d1), "bar_default");
+		averageScorePeritemVC.contextPut("datas", data1);
+		mainVC.put("averageScorePerItemChart", averageScorePeritemVC);
 		
-		BarChartComponent percentRightAnswersPerItemChart = new BarChartComponent("correctQuestion");
-		percentRightAnswersPerItemChart.addSeries(d2);
-		percentRightAnswersPerItemChart.setDefaultBarClass("bar_green");
-		percentRightAnswersPerItemChart.setYScale(Scale.percent);
-		percentRightAnswersPerItemChart.setYLegend(translate("chart.percent.participants"));
-		mainVC.put("percentRightAnswersPerItemChart", percentRightAnswersPerItemChart);
+		VelocityContainer percentRightAnswersPerItemVC = createVelocityContainer("hbar_right_answer_per_item");
+		Stringuified data2 = BarSeries.getDatasAndColors(Collections.singletonList(d2), "bar_green");
+		percentRightAnswersPerItemVC.contextPut("datas", data2);
+		mainVC.put("percentRightAnswersPerItemChart", percentRightAnswersPerItemVC);
 	}
 
 	private void initScoreHistogram(StatisticAssessment stats) {
-		HistogramComponent scoreHistogram = new HistogramComponent("scoreHistogram");
-		scoreHistogram.setDoubleValues(stats.getScores());
-		scoreHistogram.setYLegend(translate("chart.percent.participants"));
-		if(maxScore != null) {
-			scoreHistogram.setMaxValue(maxScore.doubleValue());
+		VelocityContainer scoreHistogramVC = createVelocityContainer("histogram_score");
+		scoreHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getScores()));
+		scoreHistogramVC.contextPut("cutValue", cutValue);
+		mainVC.put("scoreHistogram", scoreHistogramVC);
+	}
+	
+	private void initItemsOverview(List<Item> items) {
+		List<StatisticSurveyItem> surveyItems = qtiStatisticsManager
+				.getStatisticAnswerOptions(resourceResult.getSearchParams(), items);
+
+		int count = 0;
+		List<String> overviewList = new ArrayList<>();
+		for(StatisticSurveyItem surveyItem:surveyItems) {
+			Item item = surveyItem.getItem();
+			Series series = seriesfactory.getSeries(item, null);
+			String name = "overview_" + count++;
+			VelocityContainer vc = createVelocityContainer(name, "hbar_item_overview");
+			vc.contextPut("series", series);
+			vc.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
+			vc.contextPut("questionType", item.getQuestion().getType());
+			vc.contextPut("title", item.getTitle());
+			mainVC.put(vc.getDispatchID(), vc);
+			overviewList.add(vc.getDispatchID());
 		}
-		if(cutValue != null) {
-			scoreHistogram.setCutValue("bar_red", cutValue.doubleValue(), "bar_green");
+		
+		mainVC.contextPut("overviewList", overviewList);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if("print".equals(event.getCommand())){
+			printPages(ureq);
+		} else if(downloadRawLink == source) {
+			doDownloadRawData(ureq);
+		}
+	}
+	
+	private void doDownloadRawData(UserRequest ureq) {
+		MediaResource resource = new QTIStatisticsResource(resourceResult, getLocale());
+		ureq.getDispatchResult().setResultingMediaResource(resource);
+	}
+
+	private void printPages(UserRequest ureq) {
+		ControllerCreator printControllerCreator = new ControllerCreator() {
+			public Controller createController(UserRequest lureq, WindowControl lwControl) {
+				Controller printCtr = new QTI12PrintController(lureq, lwControl, resourceResult);
+				Component view = printCtr.getInitialComponent();
+				LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(lureq, lwControl, null, null, view, null);
+				return layoutCtr;
+			}					
+		};
+		ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator);
+		openInNewBrowserWindow(ureq, layoutCtrlr);
+	}
+	
+	public static class ItemInfos {
+		
+		private final String label;
+		private final String text;
+		
+		public ItemInfos(String label, String text) {
+			this.label = label;
+			this.text = text;
+		}
+
+		public String getLabel() {
+			return label;
+		}
+
+		public String getText() {
+			return text;
 		}
-		mainVC.put("scoreHistogram", scoreHistogram);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12ItemStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12ItemStatisticsController.java
index 77c6ef3e11cda454f9d6d0d832d0dc42560d2128..7e399e6ddb066ad12da1ecfcbc0bc107d0144fd4 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12ItemStatisticsController.java
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12ItemStatisticsController.java
@@ -19,200 +19,139 @@
  */
 package org.olat.ims.qti.statistics.ui;
 
+import static org.olat.ims.qti.statistics.ui.StatisticFormatter.duration;
 import static org.olat.ims.qti.statistics.ui.StatisticFormatter.formatTwo;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.chart.BarChartComponent;
-import org.olat.core.gui.components.chart.BarSeries;
-import org.olat.core.gui.components.chart.Scale;
+import org.olat.core.gui.components.chart.StatisticsComponent;
+import org.olat.core.gui.components.velocity.VelocityContainer;
 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.ims.qti.editor.beecom.objects.Item;
-import org.olat.ims.qti.editor.beecom.objects.Response;
+import org.olat.ims.qti.editor.beecom.objects.Question;
 import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
+import org.olat.ims.qti.statistics.QTIStatisticSearchParams;
+import org.olat.ims.qti.statistics.QTIStatisticsManager;
 import org.olat.ims.qti.statistics.QTIType;
 import org.olat.ims.qti.statistics.model.StatisticsItem;
-import org.olat.ims.qti.statistics.model.StatisticAnswerOption;
-import org.olat.ims.qti.statistics.model.StatisticChoiceOption;
-import org.olat.ims.qti.statistics.model.StatisticKPrimOption;
 
 /**
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QTI12ItemStatisticsController extends AbstractItemStatisticsController {
+public class QTI12ItemStatisticsController extends BasicController {
 
-	public QTI12ItemStatisticsController(UserRequest ureq, WindowControl wControl,
-			Item item, QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl, item, resourceResult, printMode);
-	}
+	private final QTIStatisticSearchParams searchParams;
+	private final QTIStatisticResourceResult resourceResult;
+	private final QTIStatisticsManager qtiStatisticsManager;
+	private final VelocityContainer mainVC;
 	
-	@Override
-	protected StatisticsItem initChoice() {
-		double maxScore = item.getQuestion().getMaxValue();
-		StatisticsItem itemStats = qtiStatisticsManager
-				.getItemStatistics(item.getIdent(), maxScore, searchParams);
-
-		long rightAnswers = itemStats.getNumOfCorrectAnswers();
-		long wrongAnswers = itemStats.getNumOfIncorrectAnswers();
-		long notAnswered = numOfParticipants - rightAnswers - wrongAnswers;
+	private final int numOfParticipants;
+	private final String mediaBaseURL;
+	
+	private final Item item;
 	
+	private final SeriesFactory seriesfactory;
+	
+	public QTI12ItemStatisticsController(UserRequest ureq, WindowControl wControl,
+			Item item, QTIStatisticResourceResult resourceResult, boolean printMode) {
+		super(ureq, wControl);
+		this.item = item;
+		seriesfactory = new SeriesFactory(resourceResult);
+		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
+		
+		this.resourceResult = resourceResult;
+		searchParams = resourceResult.getSearchParams();
+		mediaBaseURL = resourceResult.getResolver().getStaticsBaseURI() + "/";
+		numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
+
+		int questionType = item.getQuestion().getType();
+		if(Question.TYPE_ESSAY == questionType) {
+			mainVC = createVelocityContainer("statistics_item_essai");
+			initEssay();
+		} else {
+			mainVC = createVelocityContainer("statistics_item");
+			StatisticsItem itemStats = initItemStatistics();
+			initItem(itemStats);
+		}
+		mainVC.put("d3loader", new StatisticsComponent("d3loader"));
 		mainVC.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
-		mainVC.contextPut("questionType", item.getQuestion().getType());
+		mainVC.contextPut("questionType", questionType);
 		mainVC.contextPut("title", item.getTitle());
-		mainVC.contextPut("maxScore", maxScore);
-		mainVC.contextPut("rightAnswers", rightAnswers);
-		mainVC.contextPut("wrongAnswers", wrongAnswers);
-		mainVC.contextPut("notAnswered", notAnswered);
-		mainVC.contextPut("itemDifficulty", formatTwo(itemStats.getDifficulty()));
-		mainVC.contextPut("averageScore", formatTwo(itemStats.getAverageScore()));
-		mainVC.contextPut("averageDuration", formatTwo(itemStats.getAverageDuration()));
-		return itemStats;
+		mainVC.contextPut("printMode", new Boolean(printMode));
+		putInitialPanel(mainVC);
 	}
 	
 	@Override
-	protected void initSingleChoice() {
-		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
-				.getNumOfAnswersPerSingleChoiceAnswerOption(item, searchParams);
-
-		int i = 0;
-		BarSeries d1 = new BarSeries();
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for (StatisticChoiceOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
-			double ans_count = statisticResponse.getCount();
-			double ans_count_percent = ans_count / numOfParticipants;
-			float points = response.getPoints();
-			String cssColor = response.isCorrect() ? "bar_green" : "bar_red";
-
-			String label = StatisticFormatter.getLabel(i++);;
-			d1.add(ans_count_percent, label, cssColor);
-
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, points, true, QTIType.survey.equals(type)));
-		}
-
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.addSeries(d1);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.setYScale(Scale.percent);
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
+	protected void doDispose() {
+		//
 	}
 
 	@Override
-	protected void initMultipleChoice(StatisticsItem itemStats) {
-		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
-				.getNumOfRightAnsweredMultipleChoice(item, searchParams);
-
-		BarSeries d1 = new BarSeries("bar_green");
-		BarSeries d2 = new BarSeries("bar_red");
-		BarSeries d3 = new BarSeries("bar_grey");
-		
-		double wrongFactor = itemStats.getNumOfResults() / (double)numOfParticipants;
-		
-		int i = 0;
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for(StatisticChoiceOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
 
-			float points = response.getPoints();
-			double answersPerAnswerOption = statisticResponse.getCount();
-			double percentageRight;
-			if (points > 0) {
-				percentageRight = answersPerAnswerOption / numOfParticipants;
-			} else {
-				percentageRight = 1.0d - answersPerAnswerOption / numOfParticipants;
-			}
-			
-			String label = StatisticFormatter.getLabel(i++);
-			
-			double rightA = percentageRight;
-			d1.add(rightA, label);
-			double wrongA = wrongFactor - percentageRight;
-			d2.add(wrongA, label);
-			
-			d3.add(1.0d - wrongFactor, label);
-			
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, points, true, QTIType.survey.equals(type)));
-		}
+	protected StatisticsItem initItemStatistics() {
+		boolean survey = QTIType.survey.equals(resourceResult.getType());
+		double maxScore = survey ? 1.0d : item.getQuestion().getMaxValue();
+		StatisticsItem itemStats = qtiStatisticsManager
+				.getItemStatistics(item.getIdent(), maxScore, searchParams);
 
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.setYScale(Scale.percent);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.addSeries(d1, d2);
-		if(wrongFactor < 1.0) {
-			chart.addSeries(d3);
+		mainVC.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
+		mainVC.contextPut("questionType", item.getQuestion().getType());
+		mainVC.contextPut("title", item.getTitle());
+		if(!survey) {
+			long rightAnswers = itemStats.getNumOfCorrectAnswers();
+			long wrongAnswers = itemStats.getNumOfIncorrectAnswers();
+			long notAnswered = numOfParticipants - rightAnswers - wrongAnswers;
+
+			mainVC.contextPut("maxScore", maxScore);
+			mainVC.contextPut("rightAnswers", rightAnswers);
+			mainVC.contextPut("wrongAnswers", wrongAnswers);
+			mainVC.contextPut("notAnswered", notAnswered);
+			mainVC.contextPut("itemDifficulty", formatTwo(itemStats.getDifficulty()));
+			mainVC.contextPut("averageScore", formatTwo(itemStats.getAverageScore()));
 		}
-
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
+		mainVC.contextPut("averageDuration", duration(itemStats.getAverageDuration()));
+		return itemStats;
 	}
+	
+	protected void initItem(StatisticsItem itemStats) {
+		Series series = seriesfactory.getSeries(item, itemStats);
 
-	@Override
-	protected void initKPrim() {
-		List<StatisticKPrimOption> statisticResponses = qtiStatisticsManager
-				.getNumbersInKPrim(item, searchParams);
-
-		int i = 0;
-		BarSeries d1 = new BarSeries("bar_green");
-		BarSeries d2 = new BarSeries("bar_red");
-		BarSeries d3 = new BarSeries("bar_grey");
-		
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for (StatisticKPrimOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
-			float points = response.getPoints();
-			double right = ((double)statisticResponse.getNumOfCorrect() / numOfParticipants);
-			double wrong = ((double)statisticResponse.getNumOfIncorrect() / numOfParticipants);
-			double notanswered = 1.0 - right - wrong;
-
-			String label = StatisticFormatter.getLabel(i++);
-			d1.add(right, label);
-			d2.add(wrong, label);
-			d3.add(notanswered, label);
-
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, points, true, QTIType.survey.equals(type)));
-		}
-		
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.setYScale(Scale.percent);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.addSeries(d1, d2, d3);
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
+		VelocityContainer vc = createVelocityContainer("hbar_item");
+		vc.contextPut("series", series);
+		mainVC.put("questionChart", vc);
+		mainVC.contextPut("series", series);
 	}
 	
-	@Override
-	protected void initFIB() {
-		List<StatisticAnswerOption> processedAnswers = qtiStatisticsManager
-				.getStatisticAnswerOptionsOfItem(item.getIdent(), searchParams);
+	protected void initEssay() {
+		mainVC.contextPut("question", item.getQuestion().getQuestion().renderAsHtml(mediaBaseURL));
+		mainVC.contextPut("title", item.getTitle());
+		
+		List<String> answers = qtiStatisticsManager.getAnswers(item.getIdent(), searchParams);
 
-		BarSeries d1 = new BarSeries();
-		for (StatisticAnswerOption entry : processedAnswers) {
-			String answerString = getAllBlanksFromAnswer(entry.getAnswer());
-			d1.add(entry.getCount(), answerString);
+		List<String> cleanedAnswers = new ArrayList<String>();
+		for (String string : answers) {
+			cleanedAnswers.add(stripAnswerText(string));
 		}
-
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.addSeries(d1);
-		mainVC.put("questionChart", chart);
+		mainVC.contextPut("studentAnswers", cleanedAnswers);
 	}
 	
-	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
-		//
+	private String stripAnswerText(String answerTextFromDB){
+		String result ="";
+		int start = answerTextFromDB.indexOf("[");
+		result = answerTextFromDB.substring(start+2);
+		result = result.substring(0, result.length()-2);
+		result = result.replaceAll("\\\\r\\\\n", "<br />");
+		return result;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java
index 072426b1a0cb26294df1664be73f7fa48b8bec45..1550ac5c6a50b643cfb97aa0f90220d77defef3e 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12StatisticsToolController.java
@@ -157,7 +157,7 @@ public class QTI12StatisticsToolController extends BasicController implements St
 
 	private void doLaunchStatistics(UserRequest ureq, WindowControl wControl) {
 		if(result == null) {
-			result = new QTIStatisticResourceResult(courseNode, searchParams);
+			result = new QTIStatisticResourceResult(courseRes, courseNode, searchParams);
 		}
 		
 		GenericTreeModel treeModel = new GenericTreeModel();
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyItemStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyItemStatisticsController.java
deleted file mode 100644
index a088a2ff8eb77980e73319da3aaf1be1331053d1..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyItemStatisticsController.java
+++ /dev/null
@@ -1,164 +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.ims.qti.statistics.ui;
-
-import static org.olat.ims.qti.statistics.ui.StatisticFormatter.formatTwo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.chart.BarChartComponent;
-import org.olat.core.gui.components.chart.BarSeries;
-import org.olat.core.gui.components.chart.Scale;
-import org.olat.core.gui.control.Event;
-import org.olat.core.gui.control.WindowControl;
-import org.olat.ims.qti.editor.beecom.objects.Item;
-import org.olat.ims.qti.editor.beecom.objects.Response;
-import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
-import org.olat.ims.qti.statistics.QTIType;
-import org.olat.ims.qti.statistics.model.StatisticsItem;
-import org.olat.ims.qti.statistics.model.StatisticChoiceOption;
-import org.olat.ims.qti.statistics.model.StatisticKPrimOption;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class QTI12SurveyItemStatisticsController extends AbstractItemStatisticsController {
-	
-	
-	
-	public QTI12SurveyItemStatisticsController(UserRequest ureq, WindowControl wControl,
-			Item item, QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl, item, resourceResult, printMode);
-	}
-	
-	@Override
-	protected void initSingleChoice() {
-		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
-				.getNumOfAnswersPerSingleChoiceAnswerOption(item, searchParams);
-
-		int i = 0;
-		BarSeries series = new BarSeries();
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for (StatisticChoiceOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
-			double ans_count = statisticResponse.getCount();
-			double ans_count_percent = ans_count / numOfParticipants;
-
-			String label = StatisticFormatter.getLabel(i++);
-			series.add(ans_count_percent, label);
-
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, 0.0f, true, QTIType.survey.equals(type)));
-		}
-
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.setYScale(Scale.percent);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.addSeries(series);
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
-	}
-	
-	@Override
-	protected void initMultipleChoice(StatisticsItem itemStats) {
-		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
-				.getNumOfRightAnsweredMultipleChoice(item, searchParams);
-
-		BarSeries series = new BarSeries();
-
-		int i = 0;
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for(StatisticChoiceOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
-
-			float points = response.getPoints();
-			double answersPerAnswerOption = statisticResponse.getCount();
-			double percentage = answersPerAnswerOption / numOfParticipants;
-			
-			String label = StatisticFormatter.getLabel(i++);
-			series.add(percentage, label);
-
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, points, true, QTIType.survey.equals(type)));
-		}
-
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.setYScale(Scale.percent);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.addSeries(series);
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
-	}
-	
-	@Override
-	protected void initKPrim() {
-		List<StatisticKPrimOption> statisticResponses = qtiStatisticsManager
-				.getNumbersInKPrim(item, searchParams);
-
-		int i = 0;
-		BarSeries d1 = new BarSeries("bar_default");
-		BarSeries d2 = new BarSeries("bar_default_darker");
-		
-		List<ResponseInfos> responseInfos = new ArrayList<>();
-		for (StatisticKPrimOption statisticResponse:statisticResponses) {
-			Response response = statisticResponse.getResponse();
-			double left = ((double)statisticResponse.getNumOfCorrect() / numOfParticipants);
-			double wrong = ((double)statisticResponse.getNumOfIncorrect() / numOfParticipants);
-	
-			String label = StatisticFormatter.getLabel(i++);
-			d1.add(left, label);
-			d2.add(wrong, label);
-
-			String text = response.getContent().renderAsHtml(mediaBaseURL);
-			responseInfos.add(new ResponseInfos(label, text, 0.0f, true, QTIType.survey.equals(type)));
-		}
-		
-		BarChartComponent chart = new BarChartComponent("questionChart");
-		chart.setYScale(Scale.percent);
-		chart.setYLegend(translate("chart.percent.participants"));
-		chart.addSeries(d1, d2);
-		mainVC.put("questionChart", chart);
-		mainVC.contextPut("responseInfos", responseInfos);
-	}
-
-	@Override
-	protected StatisticsItem initChoice() {
-		StatisticsItem itemStats = qtiStatisticsManager
-				.getItemStatistics(item.getIdent(), 1.0, searchParams);
-
-		mainVC.contextPut("averageDuration", formatTwo(itemStats.getAverageDuration()));
-		return itemStats;
-	}
-	
-	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
-		//
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyStatisticsController.java
deleted file mode 100644
index eaa5cfb5d391552be92ceee2da2d56f9d2166311..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI12SurveyStatisticsController.java
+++ /dev/null
@@ -1,94 +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.ims.qti.statistics.ui;
-
-import static org.olat.ims.qti.statistics.ui.StatisticFormatter.duration;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.chart.BarSeries;
-import org.olat.core.gui.components.chart.HorizontalBarChartComponent;
-import org.olat.core.gui.control.WindowControl;
-import org.olat.ims.qti.editor.beecom.objects.Item;
-import org.olat.ims.qti.editor.beecom.objects.QTIDocument;
-import org.olat.ims.qti.editor.beecom.objects.Section;
-import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
-import org.olat.ims.qti.statistics.model.StatisticAssessment;
-import org.olat.ims.qti.statistics.model.StatisticSurveyItem;
-import org.olat.ims.qti.statistics.model.StatisticSurveyItemResponse;
-
-/**
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class QTI12SurveyStatisticsController extends AbstractAssessmentStatisticsController {
-
-	public QTI12SurveyStatisticsController(UserRequest ureq, WindowControl wControl,
-			QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl, resourceResult, printMode, "statistics_survey");
-
-		StatisticAssessment stats = resourceResult.getQTIStatisticAssessment();
-		List<Item> items = new ArrayList<>();
-		QTIDocument qtiDocument = resourceResult.getQTIDocument();
-		for(Section section:qtiDocument.getAssessment().getSections()) {
-			for(Item item:section.getItems()) {
-				items.add(item);
-			}
-		}
-		initCourseNodeInformation(stats);
-		initItemsOverview(items);
-	}
-	
-	private void initCourseNodeInformation(StatisticAssessment stats) {
-		mainVC.contextPut("type", resourceResult.getType());
-		mainVC.contextPut("numOfParticipants", stats.getNumOfParticipants());
-		String duration = duration(stats.getAverageDuration());
-		mainVC.contextPut("averageDuration", duration);
-	}
-	
-	private void initItemsOverview(List<Item> items) {
-		List<StatisticSurveyItem> surveyItems = qtiStatisticsManager
-				.getStatisticAnswerOptions(resourceResult.getSearchParams(), items);
-		
-		BarSeries series = new BarSeries("bar_default");
-		for(StatisticSurveyItem surveyItem:surveyItems) {
-			Item item = surveyItem.getItem();
-			String atext = item.getTitle();
-			for(StatisticSurveyItemResponse response:surveyItem.getResponses()) {
-				long  value = response.getNumOfResponses();
-				String category;
-				if(response.getResponse() != null && response.getResponse().getContent() != null) {
-					String text = response.getResponse().getContent().renderAsText();
-					category = text;
-				} else {
-					category = response.getAnswer();
-				}
-				series.add(value, atext + ":" + category);
-			}
-		}
-
-		HorizontalBarChartComponent overviewSurvey = new HorizontalBarChartComponent("overviewSurvey");
-		overviewSurvey.addSeries(series);
-		mainVC.put("overviewSurveyBarChart", overviewSurvey);
-	}
-}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/QTI21OnyxAssessmentStatisticsController.java b/src/main/java/org/olat/ims/qti/statistics/ui/QTI21OnyxAssessmentStatisticsController.java
index 993d72b001245ca8361d3e0b70d8729e893025c3..9a6a191d6a0b4764623d48a77472c88e04f5b30b 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/QTI21OnyxAssessmentStatisticsController.java
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/QTI21OnyxAssessmentStatisticsController.java
@@ -25,8 +25,13 @@ import static org.olat.ims.qti.statistics.ui.StatisticFormatter.format;
 import java.util.List;
 
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.chart.HistogramComponent;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.chart.BarSeries;
+import org.olat.core.gui.components.chart.StatisticsComponent;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+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.ims.qti.statistics.QTIStatisticResourceResult;
 import org.olat.ims.qti.statistics.model.StatisticAssessment;
 
@@ -35,19 +40,34 @@ import org.olat.ims.qti.statistics.model.StatisticAssessment;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QTI21OnyxAssessmentStatisticsController extends AbstractAssessmentStatisticsController {
+public class QTI21OnyxAssessmentStatisticsController extends BasicController {
 	
-
+	private final VelocityContainer mainVC;
+	
+	private final QTIStatisticResourceResult resourceResult;
 
 	public QTI21OnyxAssessmentStatisticsController(UserRequest ureq, WindowControl wControl,
 			QTIStatisticResourceResult resourceResult, boolean printMode) {
-		super(ureq, wControl, resourceResult, printMode, "statistics_onyx");
+		super(ureq, wControl);
+		
+		this.resourceResult = resourceResult;
+
+		mainVC = createVelocityContainer("statistics_onyx");
+		mainVC.put("loadd3js", new StatisticsComponent("d3loader"));
+		mainVC.contextPut("printMode", new Boolean(printMode));
+		putInitialPanel(mainVC);
 
 		StatisticAssessment stats = resourceResult.getQTIStatisticAssessment();
 		initScoreHistogram(stats);
+		initDurationHistogram(stats);
 		initCourseNodeInformation(stats);
 	}
 	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
 	private void initCourseNodeInformation(StatisticAssessment stats) {
 		mainVC.contextPut("numOfParticipants", stats.getNumOfParticipants());
 	
@@ -75,9 +95,19 @@ public class QTI21OnyxAssessmentStatisticsController extends AbstractAssessmentS
 	}
 
 	private void initScoreHistogram(StatisticAssessment stats) {
-		HistogramComponent scoreHistogram = new HistogramComponent("scoreHistogram");
-		scoreHistogram.setDoubleValues(stats.getScores());
-		scoreHistogram.setYLegend(translate("chart.percent.participants"));
-		mainVC.put("scoreHistogram", scoreHistogram);
+		VelocityContainer scoreHistogramVC = createVelocityContainer("histogram_score");
+		scoreHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getScores()));
+		mainVC.put("scoreHistogram", scoreHistogramVC);
+	}
+	
+	private void initDurationHistogram(StatisticAssessment stats) {
+		VelocityContainer durationHistogramVC = createVelocityContainer("histogram_duration");
+		durationHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getDurations()));
+		mainVC.put("durationHistogram", durationHistogramVC);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/ResponseInfos.java b/src/main/java/org/olat/ims/qti/statistics/ui/ResponseInfos.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8a1b5f452c6dfc92af648e9639a61313033df4c
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/ResponseInfos.java
@@ -0,0 +1,108 @@
+/**
+ * <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.qti.statistics.ui;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.core.util.StringHelper;
+import org.olat.course.assessment.AssessmentHelper;
+
+/**
+ * 
+ * Initial date: 10.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ResponseInfos {
+	
+	private final String label;
+	private final String text;
+	private final Float points;
+	private final boolean correct;
+	private final boolean survey;
+	
+	private final List<String> wrongAnswers;
+	
+	public ResponseInfos(String label, String text, Float points, boolean correct, boolean survey) {
+		this(label, text, Collections.<String>emptyList(),  points, survey, correct);
+	}
+	
+	public ResponseInfos(String label, String text, List<String> wrongAnswers, Float points, boolean correct, boolean survey) {
+		this.label = label;
+		this.text = text;
+		this.points = points;
+		this.survey = survey;
+		this.correct = correct;
+		this.wrongAnswers = wrongAnswers;
+	}
+
+	public String getLabel() {
+		return label;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public Float getPoints() {
+		return points;
+	}
+	
+	public String getFormattedPoints() {
+		if(points == null) {
+			return "";
+		}
+		return AssessmentHelper.getRoundedScore(points);
+	}
+	
+	public boolean isWrongAnswersAvailable() {
+		return wrongAnswers != null && wrongAnswers.size() > 0;
+	}
+
+	public List<String> getWrongAnswers() {
+		return wrongAnswers;
+	}
+	
+	public String getFormattedWrongAnswers() {
+		if(wrongAnswers != null && wrongAnswers.size() > 0) {
+			StringBuilder sb = new StringBuilder();
+			for(String answer:wrongAnswers) {
+				if(sb.length() > 0) sb.append(", ");
+				if(StringHelper.containsNonWhitespace(answer)) {
+					sb.append(answer);
+				} else {
+					sb.append("\"\"");
+				}
+			}
+			
+			return sb.toString();
+		}
+		return "";
+	}
+
+	public boolean isSurvey() {
+		return survey;
+	}
+
+	public boolean isCorrect() {
+		return correct;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/Series.java b/src/main/java/org/olat/ims/qti/statistics/ui/Series.java
new file mode 100644
index 0000000000000000000000000000000000000000..d74ce8f2b57d9ac9d8c0123609f359c16437be76
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/Series.java
@@ -0,0 +1,89 @@
+/**
+ * <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.qti.statistics.ui;
+
+import java.util.List;
+
+import org.olat.core.gui.components.chart.BarSeries;
+import org.olat.core.gui.components.chart.BarSeries.Stringuified;
+
+/**
+ * 
+ * Initial date: 10.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class Series {
+	
+	private String itemCss;
+	private String chartType;
+	private final List<BarSeries> series;
+	private final List<ResponseInfos> responseInfos;
+	
+	private final int numOfParticipants;
+	private Stringuified datas;
+	
+	public Series(List<BarSeries> series, List<ResponseInfos> responseInfos, int numOfParticipants) {
+		this.series = series;
+		this.responseInfos = responseInfos;
+		this.numOfParticipants = numOfParticipants;
+	}
+
+	public String getItemCss() {
+		return itemCss;
+	}
+
+	public void setItemCss(String itemCss) {
+		this.itemCss = itemCss;
+	}
+
+	public String getChartType() {
+		return chartType;
+	}
+
+	public void setChartType(String chartType) {
+		this.chartType = chartType;
+	}
+
+	public int getNumOfParticipants() {
+		return numOfParticipants;
+	}
+
+	public List<BarSeries> getSeries() {
+		return series;
+	}
+	
+	public List<ResponseInfos> getResponseInfos() {
+		return responseInfos;
+	}
+	
+	public boolean isColorsCustom() {
+		Stringuified d = getDatas();
+		return d.getColors().length() > 3;
+	}
+
+	public Stringuified getDatas() {
+		if(datas == null) {
+			datas = BarSeries.getDatasAndColors(series, "bar_default");
+		}
+		return datas;
+	}
+
+}
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/SeriesFactory.java b/src/main/java/org/olat/ims/qti/statistics/ui/SeriesFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..39f6de006fb961a4aa8b9abe06b6474442c78d62
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/SeriesFactory.java
@@ -0,0 +1,257 @@
+/**
+ * <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.qti.statistics.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.gui.components.chart.BarSeries;
+import org.olat.ims.qti.editor.beecom.objects.Item;
+import org.olat.ims.qti.editor.beecom.objects.Question;
+import org.olat.ims.qti.editor.beecom.objects.Response;
+import org.olat.ims.qti.statistics.QTIStatisticResourceResult;
+import org.olat.ims.qti.statistics.QTIStatisticsManager;
+import org.olat.ims.qti.statistics.QTIType;
+import org.olat.ims.qti.statistics.model.StatisticChoiceOption;
+import org.olat.ims.qti.statistics.model.StatisticFIBOption;
+import org.olat.ims.qti.statistics.model.StatisticKPrimOption;
+import org.olat.ims.qti.statistics.model.StatisticsItem;
+
+/**
+ * 
+ * Initial date: 10.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SeriesFactory {
+	
+	private static final String BAR_CORRECT_WRONG_NOT = "horizontalBarMultipleChoice";
+	private static final String BAR_ANSWERED = "horizontalBarMultipleChoiceSurvey";
+	private static final String BAR_CORRECT = "horizontalBarSingleChoice";
+	
+	
+	private final QTIStatisticsManager qtiStatisticsManager;
+	private final QTIStatisticResourceResult resourceResult;
+	
+	public SeriesFactory(QTIStatisticResourceResult resourceResult) {
+		this.resourceResult = resourceResult;
+		qtiStatisticsManager = CoreSpringFactory.getImpl(QTIStatisticsManager.class);
+	}
+	
+	public static String getCssClass(Item item) {
+		int questionType = item.getQuestion().getType();
+		switch (questionType) {
+			case Question.TYPE_SC: return "o_mi_qtisc";
+			case Question.TYPE_MC: return "o_mi_qtimc";
+			case Question.TYPE_KPRIM: return "o_mi_qtikprim";
+			case Question.TYPE_FIB: return "o_mi_qtifib";
+			case Question.TYPE_ESSAY: return "o_mi_qtiessay";
+			default: return null;
+		}
+	}
+	
+	public Series getSeries(Item item, StatisticsItem itemStats) {
+		int questionType = item.getQuestion().getType();
+		switch(questionType) {
+			case Question.TYPE_SC: return getSingleChoice(item);
+			case Question.TYPE_MC: return getMultipleChoice(item, itemStats);
+			case Question.TYPE_KPRIM: return getKPrim(item);
+			case Question.TYPE_FIB: return getFIB(item);
+			default: return null;
+		}
+	}
+
+	public Series getSingleChoice(Item item) {
+		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
+				.getNumOfAnswersPerSingleChoiceAnswerOption(item, resourceResult.getSearchParams());
+		
+		String mediaBaseURL = resourceResult.getMediaBaseURL();
+		boolean survey = QTIType.survey.equals(resourceResult.getType());
+		int numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
+
+		int i = 0;
+		BarSeries d1 = new BarSeries();
+		List<ResponseInfos> responseInfos = new ArrayList<>();
+		for (StatisticChoiceOption statisticResponse:statisticResponses) {
+			Response response = statisticResponse.getResponse();
+			double ans_count = statisticResponse.getCount();
+
+			Float points;
+			String cssColor;
+			if(survey) {
+				points = null;
+				cssColor = "bar_default";
+			} else {
+				points = response.getPoints();
+				cssColor = response.isCorrect() ? "bar_green" : "bar_red";
+			}
+
+			String label = Integer.toString(++i);
+			d1.add(ans_count, label, cssColor);
+
+			String text = response.getContent().renderAsHtml(mediaBaseURL);
+			responseInfos.add(new ResponseInfos(label, text, points, true, survey));
+		}
+
+		List<BarSeries> serieList = Collections.singletonList(d1);
+		Series series = new Series(serieList, responseInfos, numOfParticipants);
+		series.setChartType(BAR_CORRECT);
+		series.setItemCss(getCssClass(item));
+		return series;
+	}
+	
+	public Series getMultipleChoice(Item item, StatisticsItem itemStats) {
+		List<StatisticChoiceOption> statisticResponses = qtiStatisticsManager
+				.getNumOfRightAnsweredMultipleChoice(item, resourceResult.getSearchParams());
+
+		BarSeries d1 = new BarSeries("bar_green");
+		BarSeries d2 = new BarSeries("bar_red");
+		BarSeries d3 = new BarSeries("bar_grey");
+
+		String mediaBaseURL = resourceResult.getMediaBaseURL();
+		boolean survey = QTIType.survey.equals(resourceResult.getType());
+		int numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
+		int notAnswered = numOfParticipants - (itemStats == null ? 0 : itemStats.getNumOfResults());
+		
+		int i = 0;
+		List<ResponseInfos> responseInfos = new ArrayList<>();
+		for(StatisticChoiceOption statisticResponse:statisticResponses) {
+			Response response = statisticResponse.getResponse();
+
+			float points = response.getPoints();
+			double answersPerAnswerOption = statisticResponse.getCount();
+
+			double rightA;
+			double wrongA;
+			if (points >= 0f) {
+				rightA = answersPerAnswerOption;
+				wrongA = numOfParticipants - notAnswered - answersPerAnswerOption;
+			} else {
+				//minus negative points are not answered right?
+				rightA = numOfParticipants - notAnswered - answersPerAnswerOption ;
+				wrongA = answersPerAnswerOption;
+			}
+			
+			String label = Integer.toString(++i);
+			d1.add(rightA, label);
+			d2.add(wrongA, label);
+			d3.add(notAnswered, label);
+			
+			String text = response.getContent().renderAsHtml(mediaBaseURL);
+			Float pointsObj = survey ? null : points;
+			responseInfos.add(new ResponseInfos(label, text, pointsObj, true, survey));
+		}
+
+		List<BarSeries> serieList = new ArrayList<>(3);
+		serieList.add(d1);
+		if(!survey) {
+			serieList.add(d2);
+			serieList.add(d3);
+		}
+		
+		Series series = new Series(serieList, responseInfos, numOfParticipants);
+		series.setChartType(survey ? BAR_ANSWERED : BAR_CORRECT_WRONG_NOT);
+		series.setItemCss(getCssClass(item));
+		return series;
+	}
+	
+	public Series getKPrim(Item item) {
+		List<StatisticKPrimOption> statisticResponses = qtiStatisticsManager
+				.getNumbersInKPrim(item, resourceResult.getSearchParams());
+
+		String mediaBaseURL = resourceResult.getMediaBaseURL();
+		boolean survey = QTIType.survey.equals(resourceResult.getType());
+		int numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
+		
+		int i = 0;
+		BarSeries d1 = new BarSeries("bar_green");
+		BarSeries d2 = new BarSeries("bar_red");
+		BarSeries d3 = new BarSeries("bar_grey");
+		
+		List<ResponseInfos> responseInfos = new ArrayList<>();
+		for (StatisticKPrimOption statisticResponse:statisticResponses) {
+			Response response = statisticResponse.getResponse();
+
+			double right = statisticResponse.getNumOfCorrect();
+			double wrong = statisticResponse.getNumOfIncorrect();
+			double notanswered = numOfParticipants - right - wrong;
+
+			String label = Integer.toString(++i);
+			d1.add(right, label);
+			d2.add(wrong, label);
+			d3.add(notanswered, label);
+
+			String text = response.getContent().renderAsHtml(mediaBaseURL);
+			responseInfos.add(new ResponseInfos(label, text, null, true, survey));
+		}
+		
+		List<BarSeries> serieList = new ArrayList<>(3);
+		serieList.add(d1);
+		serieList.add(d2);
+		serieList.add(d3);
+		Series series = new Series(serieList, responseInfos, numOfParticipants);
+		series.setChartType(survey ? BAR_ANSWERED : BAR_CORRECT_WRONG_NOT);
+		series.setItemCss(getCssClass(item));
+		return series;
+	}
+	
+	public Series getFIB(Item item) {
+		List<StatisticFIBOption> processedAnswers = qtiStatisticsManager
+				.getStatisticAnswerOptionsFIB(item, resourceResult.getSearchParams());
+		Collections.reverse(processedAnswers);
+
+		boolean survey = QTIType.survey.equals(resourceResult.getType());
+		boolean singleCorrectScore = item.getQuestion().getSingleCorrectScore() > 0.0f;
+		int numOfParticipants = resourceResult.getQTIStatisticAssessment().getNumOfParticipants();
+
+		int i = 0;
+		String cssColor = survey ? "bar_default" : "bar_green";
+		BarSeries d1 = new BarSeries();
+		List<ResponseInfos> responseInfos = new ArrayList<>();
+		for (StatisticFIBOption entry : processedAnswers) {
+
+			String label = Integer.toString(++i);
+			String answerString = entry.getCorrectBlank();
+			d1.add(entry.getNumOfCorrect(), label, cssColor);
+			
+			StringBuilder text = new StringBuilder();
+			text.append(answerString);
+			if(entry.getAlternatives().size() > 1) {
+				text.append(" [");
+				for(int j=1; j<entry.getAlternatives().size(); j++) {
+					if(j > 2) text.append(", ");
+					text.append(entry.getAlternatives().get(j));
+				}
+				text.append("]");
+			}
+			
+			Float score = singleCorrectScore ? null : entry.getPoints();
+			responseInfos.add(new ResponseInfos(label, text.toString(), entry.getWrongAnswers(), score, true, survey));
+		}
+		
+		List<BarSeries> serieList = Collections.singletonList(d1);
+		Series series = new Series(serieList, responseInfos, numOfParticipants);
+		series.setChartType(BAR_ANSWERED);
+		series.setItemCss(getCssClass(item));
+		return series;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_average_score_per_item.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_average_score_per_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..de3fcc5af865a90ef1951045d85b1d3841ab87eb
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_average_score_per_item.html
@@ -0,0 +1,12 @@
+<div id="$r.getId('d3div')"><div id="$r.getId('d3holder')" class='d3chart' style='width:600px;height:155px'></div>
+<script type='text/javascript'>
+/* <![CDATA[ */
+jQuery(function () {
+	jQuery('#$r.getId("d3holder")').qtiStatistics('averageScorePerItem', {
+		values: [$datas.data],
+		xBottomLegend: ' $r.translate("chart.answer.averageScoreQuestions.y")',
+		yLeftLegend: '$r.translate("chart.item")'
+	});
+});
+/* ]]> */</script>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..c6ac3aa65c0c0d517070bc1e0986060ce8126be8
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item.html
@@ -0,0 +1,15 @@
+<div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:600px;height:300px'></div>
+<script type='text/javascript'>
+/* <![CDATA[ */
+jQuery(function () {
+	jQuery('#$r.getId("d3holder")').qtiStatistics('$series.chartType', {
+		values: [$series.datas.data],
+		colors: #if($series.colorsCustom) [$series.datas.colors] #else null #end,
+		participants: $series.numOfParticipants,
+		xTopLegend: '$r.translate("chart.percent.participants.num")',
+		xBottomLegend: '$r.translate("chart.percent.participants")',
+		yLeftLegend: '$r.translate("chart.item")',
+	})
+});
+/* ]]> */</script>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item_overview.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item_overview.html
new file mode 100644
index 0000000000000000000000000000000000000000..d9cbe33313cd1ca813f8b10fe2d46425ee8422d6
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_item_overview.html
@@ -0,0 +1,37 @@
+<div class="o_qti_statistics">
+	<h4 class="b_with_small_icon_left $series.itemCss">$title</h4>
+	<p>$question</p>
+
+	<div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:600px;'></div>
+	<script type='text/javascript'>
+	/* <![CDATA[ */
+	jQuery(function () {
+		jQuery('#$r.getId("d3holder")').qtiStatistics('$series.chartType', {
+			values: [$series.datas.data],
+			colors: #if($series.colorsCustom) [$series.datas.colors] #else null #end,
+			participants: $series.numOfParticipants,
+			barHeight: 25,
+			xTopLegend: '$r.translate("chart.percent.participants.num")',
+			xBottomLegend: '$r.translate("chart.percent.participants")',
+			yLeftLegend: '$r.translate("chart.item")',
+		})
+	});
+	/* ]]> */</script>
+	</div>
+
+	<ul>
+	#foreach($responseInfo in $series.responseInfos)
+		#if($responseInfo.survey)
+			<li class="o_qti_statistics-survey-item qti-survey">
+		#elseif($responseInfo.correct)
+			<li class="o_qti_statistics-correct">
+		#else
+			<li class="o_qti_statistics-ncorrect">
+		#end
+		<strong>$responseInfo.label.</strong> $responseInfo.text
+		 #if($responseInfo.points) $r.translate("answer.points", $responseInfo.formattedPoints) #end
+		 #if($responseInfo.wrongAnswersAvailable) <br/>$r.translate("wrong.answer")  $responseInfo.formattedWrongAnswers #end
+		 </li>
+	#end
+	</ul>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_right_answer_per_item.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_right_answer_per_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..93c7ee9d4c2938a5878daab7aea717467f48b198
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/hbar_right_answer_per_item.html
@@ -0,0 +1,13 @@
+<div id="$r.getId('d3div')"><div id="$r.getId('d3holder')" class='d3chart' style='width:600px;height:155px'></div>
+<script type='text/javascript'>
+/* <![CDATA[ */
+jQuery(function () {
+	jQuery('#$r.getId("d3holder")').qtiStatistics('rightAnswerPerItem', {
+		values: [$datas.data],
+		xBottomLegend: '$r.translate("chart.percent.participants.num")',
+		xTopLegend: '$r.translate("chart.percent.participants")',
+		yLeftLegend: '$r.translate("chart.item")'
+	});
+});
+/* ]]> */</script>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_duration.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_duration.html
new file mode 100644
index 0000000000000000000000000000000000000000..64d061cd854a5fd934d5b36d2a446a63d2b12292
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_duration.html
@@ -0,0 +1,14 @@
+<h4>$r.translate("chart.duration.histogramm")</h4>
+<div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:600px;height:300px'></div>
+<script type='text/javascript'>
+/* <![CDATA[ */
+jQuery(function () {
+	jQuery('#$r.getId("d3holder")').qtiStatistics('histogramDuration', {
+		values: [$datas],
+        xBottomLegend: '$r.translate("chart.duration.histogramm.legend")',
+        yLeftLegend: '$r.translate("chart.percent.participants")',
+        yRightLegend:'$r.translate("chart.percent.participants.num")'
+	});
+});
+/* ]]> */</script>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_score.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_score.html
new file mode 100644
index 0000000000000000000000000000000000000000..6ffcfd014cd9a030c06becb22c526341508acbcb
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/histogram_score.html
@@ -0,0 +1,15 @@
+<h4>$r.translate("chart.score.histogramm")</h4>
+<div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:600px;height:300px'></div>
+<script type='text/javascript'>
+/* <![CDATA[ */
+jQuery(function () {
+	jQuery('#$r.getId("d3holder")').qtiStatistics('histogramScore', {
+		values: [$datas],
+        cut: #if($cutValue) $cutValue #else null #end,
+        xBottomLegend: '$r.translate("chart.points")',
+        yLeftLegend: '$r.translate("chart.percent.participants")',
+        yRightLegend:'$r.translate("chart.percent.participants.num")'
+	});
+});
+/* ]]> */</script>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_assessment.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_assessment.html
index 9037ca9ea9e9dd077464ce040fce15244ca165d6..beadbbfc40f769d767d2fc44b678b30c3713a9d3 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_assessment.html
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_assessment.html
@@ -11,68 +11,97 @@
 			}
 		}
 		</script>
-		<div id='$r.getId("print")'>
-			<a class="b_small_icon o_print_icon" href='javascript:$r.getId("print")()' title='$r.translateInAttribute("print")'><span>Print</span></a>	
-		</div>
+		<a class="b_with_small_icon_left o_print_icon" href='javascript:$r.getId("print")()' title='$r.translateInAttribute("print")'>
+			<span>$r.translate("print")</span>
+		</a>
+		$r.render("download")
 	</div>
 #end
+
 <div class="b_clearfix">
 	<div class="b_c33l">
-<h4>$r.translate("fig.title")</h4>
-$r.translate("fig.participants"): $numOfParticipants<br/>
-#if($numOfPassed)
-$r.translate("fig.passed"): $numOfPassed<br/>
-#end
-#if($numOfFailed)
-$r.translate("fig.failed"): $numOfFailed<br/>
-#end
-
-#if($maxScore)
-	$r.translate("chart.maxscore"): $maxScore<br/>
-#end
-#if($cutScore)
-	$r.translate("chart.cutscore"): $cutScore<br/>
-#end
-
-$r.translate("fig.avg"): $average<br/>
-$r.translate("fig.span"): $range<br/>
-$r.translate("fig.stddev"): $standardDeviation<br/>
-$r.translate("fig.mode"): $mode<br/>
-$r.translate("fig.median"): $median<br/>
-$r.translate("fig.averagedur"): $averageDuration<br/>
+		<h4>$r.translate("fig.title")</h4>
+		<table><tbody>
+			<tr><td>$r.translate("fig.participants")</td>
+				<td>$numOfParticipants</td></tr>
+			#if($numOfPassed)
+				<tr><td>$r.translate("fig.passed")</td>
+					<td>$numOfPassed</td></tr>
+			#end
+			#if($numOfFailed)
+				<tr><td>$r.translate("fig.failed")</td>
+					<td>$numOfFailed</td></tr>
+			#end
+			#if($maxScore)
+				<tr><td>$r.translate("chart.maxscore")</td>
+					<td>$maxScore</td></tr>
+			#end
+			#if($cutScore)
+			<tr><td>$r.translate("chart.cutscore")</td>
+				<td>$cutScore</td></tr>
+			#end
+			#if($average)
+			<tr><td>$r.translate("fig.avg")</td>
+				<td>$average</td></tr>
+			#end
+			#if($range)
+			<tr><td>$r.translate("fig.span")</td>
+				<td>$range</td></tr>
+			#end
+			#if($standardDeviation)
+			<tr><td>$r.translate("fig.stddev")</td>
+				<td>$standardDeviation</td></tr>
+			#end
+			#if($mode)
+			<tr><td>$r.translate("fig.mode")</td>
+				<td>$mode</td></tr>
+			#end
+			#if($median)
+			<tr><td>$r.translate("fig.median")</td>
+				<td>$median</td></tr>
+			#end
+			#if($averageDuration)
+			<tr><td>$r.translate("fig.averagedur")</td>
+				<td>$averageDuration</td></tr>
+			#end
+		</tbody></table>
 	</div>
 	<div class="b_c66r">
-	
-	<h4>$r.translate("chart.score.histogramm")</h4>
-	#if($r.available("scoreHistogram"))
-		$r.render("scoreHistogram")
-	#end
+		#if($r.available("scoreHistogram"))
+			$r.render("scoreHistogram")
+		#end
 	</div>
 </div>
 
-<h4>$r.translate("chart.duration.histogramm")</h4>
 $r.render("durationHistogram")
 
 #if($r.available("averageScorePerItemChart"))
 	<h4>$r.translate("chart.averagescore.peritem")</h4>
 	$r.render("averageScorePerItemChart")
+	
+	<ul>
+	#foreach($itemInfo in $itemInfoList)
+		<li><strong>$itemInfo.label.</strong> $itemInfo.text</li>
+	#end
+	</ul>
 #end
 
-<ul>
-#foreach($itemInfo in $itemInfoList)
-	<li><strong>$itemInfo.label.</strong> $itemInfo.text</li>
-#end
-</ul>
-
-
 #if($r.available("percentRightAnswersPerItemChart"))
 	<h4>$r.translate("chart.rightanswers.peritem")</h4>
 	$r.render("percentRightAnswersPerItemChart")
+	<ul>
+	#foreach($itemInfo in $itemInfoList)
+		<li><strong>$itemInfo.label.</strong> $itemInfo.text</li>
+	#end
+	</ul>
 #end
 
-<ul>
-#foreach($itemInfo in $itemInfoList)
-	<li><strong>$itemInfo.label.</strong> $itemInfo.text</li>
+#if($overviewList)
+	#foreach($overview in $overviewList)
+		$r.render($overview)
+	
+	#end
 #end
-</ul>
+
+
 </div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..380268731304f40df1a84ce84a037cb9da31e65b
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item.html
@@ -0,0 +1,56 @@
+<div class="o_qti_statistics">
+	<h4 class="b_with_small_icon_left $series.itemCss">$title</h4>
+	<p>$question</p>
+	<table><tbody>
+		#if($numOfParticipants)
+		<tr><td>$r.translate("fig.participants")</td>
+			<td>$numOfParticipants</td></tr>
+		#end
+		#if($maxScore)
+		<tr><td>$r.translate("chart.maxscore")</td>
+			<td>$maxScore</td></tr>
+		#end
+		#if($rightAnswers)
+		<tr><td>$r.translate("fig.correctanswers")</td>
+			<td>$rightAnswers</td></tr>
+		#end
+		#if($wrongAnswers)
+		<tr><td>$r.translate("fig.wronganswers")</td>
+			<td>$wrongAnswers</td></tr>
+		#end
+		#if($notAnswered)
+		<tr><td>$r.translate("fig.notanswered")</td>
+			<td>$notAnswered</td></tr>
+		#end
+		#if($itemDifficulty)
+		<tr><td>$r.translate("fig.itemdiff")</td>
+			<td>$itemDifficulty</td></tr>
+		#end
+		#if($averageScore)
+		<tr><td>$r.translate("fig.averagescore")</td>
+			<td>$averageScore</td></tr>
+		#end
+		#if($averageDuration)
+		<tr><td>$r.translate("fig.averagedur")</td>
+			<td>$averageDuration</td></tr>
+		#end
+	</tbody></table>
+
+	<h4>$r.translate("chart.responses")</h4>
+	$r.render("questionChart")
+	<ul>
+	#foreach($responseInfo in $series.responseInfos)
+		#if($responseInfo.survey)
+			<li class="o_qti_statistics-survey-item qti-survey">
+		#elseif($responseInfo.correct)
+			<li class="o_qti_statistics-correct">
+		#else
+			<li class="o_qti_statistics-ncorrect">
+		#end
+		<strong>$responseInfo.label.</strong> $responseInfo.text
+		 #if($responseInfo.points) $r.translate("answer.points", $responseInfo.formattedPoints) #end
+		 #if($responseInfo.wrongAnswersAvailable) <br/>$r.translate("wrong.answer")  $responseInfo.formattedWrongAnswers #end
+		 </li>
+	#end
+	</ul>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_essai.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_essai.html
similarity index 54%
rename from src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_essai.html
rename to src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_essai.html
index f029324fa37bb7e68b0cf2739f0686eea0a15f0c..096d3917c6fd3009f603016eddb5032529779349 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_essai.html
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_essai.html
@@ -1,8 +1,8 @@
 <div class="o_qti_statistics">
-<h4>$title</h4>
+<h4 class="b_with_small_icon_left $series.itemCss">$title</h4>
 <p>$question</p>
 
 #foreach($studentAnswer in $studentAnswers)
-<p>$studentAnswer</p>
+	<p>$studentAnswer</p>
 #end
 </div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_survey.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_survey.html
deleted file mode 100644
index bf19c81a2f5c9809d2a4bb8fd794661cc4e6e7d3..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_survey.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="o_qti_statistics">
-<h4>$title</h4>
-<p>$question</p>
-$r.translate("fig.averagedur"): $averageDuration<br/>
-
-<h4>$r.translate("chart.responses")</h4>
-$r.render("questionChart")
-<ul>
-#foreach($responseInfo in $responseInfos)
-	<li class="qti-survey"><strong>$responseInfo.label</strong> $responseInfo.text</li>
-#end
-</ul>
-</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_test.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_test.html
deleted file mode 100644
index 9ebf699501a4c5e02bf251f829c8fba54bc0b739..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_item_test.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<div class="o_qti_statistics">
-<h4>$title</h4>
-<p>$question</p>
-
-#if($maxScore)
-	$r.translate("chart.maxscore"): $maxScore<br/>
-#end
-#if($rightAnswers)
-	$r.translate("fig.correctanswers"): $rightAnswers<br/>
-#end
-#if($wrongAnswers)
-	$r.translate("fig.wronganswers"): $wrongAnswers<br/>
-#end
-#if($notAnswered)
-	$r.translate("fig.notanswered"): $notAnswered<br/>
-#end
-#if($itemDifficulty)
-	$r.translate("fig.itemdiff"): $itemDifficulty<br/>
-#end
-#if($averageScore)
-	$r.translate("fig.averagescore"): $averageScore<br/>
-#end
-#if($averageDuration)
-	$r.translate("fig.averagedur"): $averageDuration<br/>
-#end
-
-<h4>$r.translate("chart.responses")</h4>
-$r.render("questionChart")
-<ul>
-#foreach($responseInfo in $responseInfos)
-	#if($responseInfo.survey)
-		<li class="o_qti_statistics-survey-item">
-	#elseif($responseInfo.correct)
-		<li class="o_qti_statistics-correct">
-	#else
-		<li class="o_qti_statistics-ncorrect">
-	#end
-	<strong>$responseInfo.label.</strong> $responseInfo.text ($responseInfo.points)</li>
-#end
-</ul>
-</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_onyx.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_onyx.html
index 5270da16cb6917e6634de69875fae3467e8bcd22..f85fffe12a010320bcc488ead3030cbcb5c80a25 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_onyx.html
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_onyx.html
@@ -1,4 +1,5 @@
 <div class="o_qti_statistics">
+
 #if(!$printMode)
 	<div class="o_buttons_box_right">
 		<script type="text/javascript">
@@ -18,24 +19,32 @@
 #end
 <div class="b_clearfix">
 	<div class="b_c33l">
-<h4>$r.translate("fig.title")</h4>
-$r.translate("fig.participants"): $numOfParticipants<br/>
-$r.translate("fig.avg"): $average<br/>
-$r.translate("fig.span"): $range<br/>
-$r.translate("fig.stddev"): $standardDeviation<br/>
-$r.translate("fig.mode"): $mode<br/>
-$r.translate("fig.median"): $median<br/>
-$r.translate("fig.averagedur"): $averageDuration<br/>
+		<h4>$r.translate("fig.title")</h4>
+		<table><tbody>
+			<tr><td>$r.translate("fig.participants")</td>
+				<td>$numOfParticipants</td></tr>
+			<tr><td>$r.translate("fig.avg")</td>
+				<td>$average</td></tr>
+			<tr><td>$r.translate("fig.span")</td>
+				<td>$range</td></tr>
+			<tr><td>$r.translate("fig.stddev")</td>
+				<td>$standardDeviation</td></tr>
+			<tr><td>$r.translate("fig.mode")</td>
+				<td>$mode</td></tr>
+			<tr><td>$r.translate("fig.median")</td>
+				<td>$median</td></tr>
+			<tr><td>$r.translate("fig.averagedur")</td>
+				<td>$averageDuration</td></tr>
+		</tbody></table>
 	</div>
 	<div class="b_c66r">
-	
-	<h4>$r.translate("chart.score.histogramm")</h4>
-	#if($r.available("scoreHistogram"))
-		$r.render("scoreHistogram")
-	#end
+		#if($r.available("scoreHistogram"))
+			$r.render("scoreHistogram")
+		#end
 	</div>
 </div>
+<div class="b_clearfix">
+	$r.render("durationHistogram")
+</div>
 
-<h4>$r.translate("chart.duration.histogramm")</h4>
-$r.render("durationHistogram")
 </div>
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_survey.html b/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_survey.html
deleted file mode 100644
index e2a88bb059922814ea3de1085bdecc6420ac9261..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_content/statistics_survey.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<div class="o_qti_statistics">
-#if(!$printMode)
-	<div class="o_buttons_box_right">
-		<script type="text/javascript">
-		function $r.getId("print")() {
-			try {
-				var ww = window.open('$r.commandURI("print")', '$winid', 'height=400, width=800, left=0, top=0, location=no, menubar=no, resizable=yes, scrollbars=yes, toolbar=no');
-				ww.focus();
-			} catch(e) {
-				if(jQuery(document).ooLog().isDebugEnabled()) jQuery(document).ooLog('debug','Error when trying to print qti statistics' , "");
-			}
-		}
-		</script>
-		<div id='$r.getId("print")'>
-			<a class="b_small_icon o_print_icon" href='javascript:$r.getId("print")()' title='$r.translateInAttribute("print")'><span>Print</span></a>	
-		</div>
-	</div>
-#end
-
-<div class="b_clearfix">
-	<div class="b_c33l">
-		<h4>$r.translate("fig.title")</h4>
-		$r.translate("fig.participants"): $numOfParticipants<br/>
-		$r.translate("fig.averagedur"): $averageDuration<br/>
-	</div>
-	<div class="b_c66r">
-		<h4>$r.translate("chart.duration.histogramm")</h4>
-		$r.render("durationHistogram")
-	</div>
-</div>
-
-<h4>$r.translate("chart.survey.overview")</h4>
-$r.render("overviewSurveyBarChart")
-</div>
-
diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties
index acd4b53ba0181fa94cc4beb851678c94ecdd1fa2..b122af998cd5d4517a54242851c07112ae15df75 100644
--- a/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/ims/qti/statistics/ui/_i18n/LocalStrings_de.properties
@@ -7,14 +7,16 @@ splash.notenoughresults=F
 
 chart.answer.averageScoreQuestions.y=Average score
 chart.percent.participants=% Teilnehmer
-
+chart.percent.participants.num=# Teilnehmer
 
 chart.score.histogramm=Score histogram
-chart.duration.histogramm=Duration histogramm
+chart.duration.histogramm=Bearbeitungsdauer
+chart.duration.histogramm.legend=Bearbeitungsdauer (Min.)
 chart.averagescore.peritem=Average score per item
 chart.rightanswers.peritem=Percent of right answers per item
 chart.responses=Antworten
 chart.survey.overview=Ubersicht
+chart.item=Item
 
 chart.points=Punkte
 chart.tests=Anzahl Tests
@@ -26,7 +28,7 @@ chart.questions=Fragen
 chart.duration=Bearbeitungs-Dauer in Sekunden
 chart.percent=Prozent
 chart.maxscore=Maximale Punktzahl
-chart.cutscore=Cut-Score
+chart.cutscore=Notwendige Punktzahl für "bestanden"
 
 
 chart.title.ovpassedfailed=Bestanden / Nicht bestanden
@@ -53,7 +55,9 @@ answer.false=falsch
 answer.noanswer=keine Antwort
 answer.yes=Ja
 answer.no=Nein
-
+wrong.answer=Liste of falsche Antworten: 
+answer.points=({0} Punkte)
+download.raw.data=Raw Daten herunterladen
 fig.title=Kennzahlen
 fig.participants=Teilnehmer
 fig.passed=Bestanden
@@ -70,3 +74,4 @@ fig.stddev=Standardabweichung
 fig.avg=Mittelwert
 fig.span=Spannweite
 fig.median=Median
+print=Drücken
diff --git a/src/main/java/org/olat/instantMessaging/ui/ChatController.java b/src/main/java/org/olat/instantMessaging/ui/ChatController.java
index 5c86bba4d65bc6ce6ba4b0cf192478c674d99e7b..45c2dbee7586143947d3571f5163ae4a830cd7e4 100644
--- a/src/main/java/org/olat/instantMessaging/ui/ChatController.java
+++ b/src/main/java/org/olat/instantMessaging/ui/ChatController.java
@@ -85,6 +85,7 @@ public class ChatController extends BasicController implements GenericEventListe
 	private JSAndCSSComponent jsc;
 	private FloatingResizableDialogController chatPanelCtr;
 	
+	private Date today;
 	private List<String> allChats;
 	private final Formatter formatter;
 
@@ -109,6 +110,7 @@ public class ChatController extends BasicController implements GenericEventListe
 		this.ores = ores;
 		this.privateReceiverKey = privateReceiverKey;
 		this.vip = vip;
+		setToday();
 
 		avatarBaseURL = registerCacheableMapper(ureq, "avatars-members", new AvatarMapper());
 		
@@ -193,6 +195,15 @@ public class ChatController extends BasicController implements GenericEventListe
 		}
 	}
 	
+	private void setToday() {
+		Calendar cal = Calendar.getInstance();
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		today = cal.getTime();
+	}
+	
 	private Date getYesterday() {
 		Calendar cal = Calendar.getInstance();
 		cal.add(Calendar.DATE, -1);
@@ -254,6 +265,7 @@ public class ChatController extends BasicController implements GenericEventListe
 	}
 	
 	private void loadModel(Date from, int maxResults) {
+		setToday();
 		messageHistory.clear();
 		List<InstantMessage> lastMessages = imService.getMessages(getIdentity(), getOlatResourceable(), from, 0, maxResults, true);
 		for(int i=lastMessages.size(); i-->0; ) {
@@ -322,7 +334,15 @@ public class ChatController extends BasicController implements GenericEventListe
 		
 		String m = message.getBody().replaceAll("<br/>\n", "\r\n");
 		m = prepareMsgBody(m.replaceAll("<", "&lt;").replaceAll(">", "&gt;")).replaceAll("\r\n", "<br/>\n");
-		String creationDate = formatter.formatTime(message.getCreationDate());
+		
+		Date msgDate = message.getCreationDate();
+		String creationDate;
+		if(today.compareTo(msgDate) < 0) {
+			creationDate = formatter.formatTime(message.getCreationDate());
+		} else {
+			creationDate = formatter.formatDateAndTime(message.getCreationDate());
+		}
+
 		String from = message.getFromNickName();
 		
 		synchronized (messageHistory) {
diff --git a/src/main/java/org/olat/modules/scorm/assessment/ScormResultDetailsController.java b/src/main/java/org/olat/modules/scorm/assessment/ScormResultDetailsController.java
index 8150fa69f325d36b089a77f9d6c0e6c2a5059a97..1db98dc378454b73807749b0eb0d68889a222b3e 100644
--- a/src/main/java/org/olat/modules/scorm/assessment/ScormResultDetailsController.java
+++ b/src/main/java/org/olat/modules/scorm/assessment/ScormResultDetailsController.java
@@ -170,12 +170,12 @@ public class ScormResultDetailsController extends BasicController {
 				String username = userCourseEnvironment.getIdentityEnvironment().getIdentity().getName();
 				CourseEnvironment courseEnv = userCourseEnvironment.getCourseEnvironment();
 				ScormAssessmentManager.getInstance().deleteResults(username, courseEnv, node);
-				fireEvent(ureq, Event.CHANGED_EVENT);
+				fireEvent(ureq, Event.DONE_EVENT);
 			}
 		}
 	}
 	
-	public class CmiTableDataModel extends BaseTableDataModelWithoutFilter<CmiData> {
+	public static class CmiTableDataModel extends BaseTableDataModelWithoutFilter<CmiData> {
 		private final List<CmiData> datas;
 		private final Translator translator;
 		private final Pattern pattern = Pattern.compile("[0-9]");
@@ -242,7 +242,7 @@ public class ScormResultDetailsController extends BasicController {
 	 * Initial Date:  07.01.2010 <br>
 	 * @author thomasw
 	 */
-	public class SummaryTableDataModelMultiResults implements TableDataModel<List<CmiData>> {
+	public static class SummaryTableDataModelMultiResults implements TableDataModel<List<CmiData>> {
 		
 		private final Map<Date, List<CmiData>> objects;
 		
diff --git a/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java b/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
index 9aae9a47071145a90a757d20ebc34c3e021bd08c..02793e274f037a43f782efe4af37f4a4dcbee996 100755
--- a/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
+++ b/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
@@ -262,6 +262,7 @@ public class EPArtefactPoolRunController extends BasicController implements Acti
 			// some artefacts were added, refresh view
 			if (event.equals(Event.DONE_EVENT)) {
 				initTPAllView(ureq);
+				fireEvent(ureq, event);
 			}
 		} else if (event instanceof EPArtefactChoosenEvent) {
 			// an artefact was choosen, pass through the event until top
diff --git a/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java b/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
index 8f83a36c183b0808596a3a0a3550261341862890..211c0c576e7d2a861540d25ba4e7665ded1405d2 100644
--- a/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
+++ b/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
@@ -26,7 +26,6 @@ package org.olat.portfolio.ui.artefacts.view;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -65,6 +64,7 @@ import org.olat.portfolio.model.structel.PortfolioStructure;
 import org.olat.portfolio.ui.artefacts.collect.EPCollectStepForm00;
 import org.olat.portfolio.ui.artefacts.collect.EPCollectStepForm03;
 import org.olat.portfolio.ui.artefacts.collect.EPReflexionChangeEvent;
+import org.olat.resource.OLATResource;
 import org.olat.util.logging.activity.LoggingResourceable;
 
 /**
@@ -203,20 +203,15 @@ public class EPArtefactViewController extends FormBasicController {
 		// get maps wherein this artefact is linked and create links to them
 		List<PortfolioStructure> linkedMaps = ePFMgr.getReferencedMapsForArtefact(artefact);
 		if (linkedMaps != null && linkedMaps.size() != 0) {
-			StringBuilder buf = new StringBuilder();
-			for (Iterator<PortfolioStructure> iterator = linkedMaps.iterator(); iterator.hasNext();) {
-				PortfolioStructure ePMap = iterator.next();
-				if (viewOnlyMode || artefactChooseMode){
-					StringHelper.escapeHtml(ePMap.getTitle());
-					buf.append(", ");
-				} else {
-					buf.append("<a href=\"").append(createLinkToMap(ePMap)).append("\">");
-					StringHelper.escapeHtml(ePMap.getTitle());
-					buf.append("</a>, ");
-				}
+			List<FormLink> selectMapNames = new ArrayList<FormLink>(linkedMaps.size());
+			for (PortfolioStructure ePMap : linkedMaps) {
+				String title = StringHelper.escapeHtml(ePMap.getTitle());
+				FormLink selectMap = uifactory.addFormLink("map", "map", title, null, formLayout, Link.NONTRANSLATED);
+				selectMap.setUserObject(ePMap.getOlatResource());
+				selectMap.setEnabled(!viewOnlyMode && !artefactChooseMode);
+				selectMapNames.add(selectMap);
 			}
-			String mapLinks = buf.toString();
-			flc.contextPut("maps", mapLinks.substring(0, mapLinks.length() - 2));
+			flc.contextPut("maps", selectMapNames);
 		}
 
 		// build link to original source
@@ -276,13 +271,10 @@ public class EPArtefactViewController extends FormBasicController {
 		flc.contextPut("artAttribConfig", attribConfig);
 	}
 
-	private String createLinkToMap(PortfolioStructure ePMap) {
-		BusinessControlFactory bCF = BusinessControlFactory.getInstance();
-		ContextEntry mapCE = bCF.createContextEntry(ePMap.getOlatResource());
-		ArrayList<ContextEntry> cEList = new ArrayList<ContextEntry>();
-		cEList.add(mapCE);
-		String busLink = bCF.getAsURIString(cEList, true); 
-		return busLink;
+	private void doOpenLinkToMap(UserRequest ureq, OLATResource mapResource) {
+		String businessPath = "[" + mapResource.getResourceableTypeName() + ":"
+				+ mapResource.getResourceableId() + "]";
+		NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl());
 	}
 
 	private String createLinkToArtefactSource(UserRequest ureq, String businessPath){
@@ -317,6 +309,12 @@ public class EPArtefactViewController extends FormBasicController {
 		} else if(source == tblE){
 			List<String> actualTags = tblE.getValueList();
 			ePFMgr.setArtefactTags(getIdentity(), artefact, actualTags);
+		} else if(source instanceof FormLink) {
+			FormLink link = (FormLink)source;
+			if("map".equals(link.getCmd())) {
+				OLATResource map = (OLATResource)link.getUserObject();
+				doOpenLinkToMap(ureq, map);
+			}
 		}
 	}
 	
diff --git a/src/main/java/org/olat/portfolio/ui/artefacts/view/_content/singleArtefact.html b/src/main/java/org/olat/portfolio/ui/artefacts/view/_content/singleArtefact.html
index 3529f592a300866fb35181be6d593a185142b79a..83b5dfa21db57321c7c1a7bd60efdbc21f9a5fa5 100644
--- a/src/main/java/org/olat/portfolio/ui/artefacts/view/_content/singleArtefact.html
+++ b/src/main/java/org/olat/portfolio/ui/artefacts/view/_content/singleArtefact.html
@@ -59,7 +59,11 @@
 	#if ($artAttribConfig.get("artefact.used.in.maps"))
 	<div class="b_form_element_wrapper b_clearfix">
 		<div class="b_form_element_label">$r.translate("artefact.used.in.maps")</div>
-		<div class="b_form_element">$!maps</div>
+		<div class="b_form_element">
+			#foreach($map in $maps)
+				$r.render($map.component.componentName)
+			#end
+		</div>
 	</div>
 	#end
 	#if ($artAttribConfig.get("artefact.reflexion"))
diff --git a/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java b/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
index 3a8a3f1a381caa479406f8f5f75f4e74da861e61..57429a2c48e6e7c8108f1248fb354bac0b30e976 100644
--- a/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
+++ b/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
@@ -133,28 +133,32 @@ public class EPAddElementsController extends BasicController {
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
 		super.event(ureq, source, event);
-		if (source == artefactPoolCtrl && event instanceof EPArtefactChoosenEvent) {
-			// finally an artefact was choosen
-			EPArtefactChoosenEvent artCEv = (EPArtefactChoosenEvent) event;
-			artefactBox.deactivate();
-			AbstractArtefact choosenArtefact = artCEv.getArtefact();
-			// check for a yet existing link to this artefact
-			if (ePFMgr.isArtefactInStructure(choosenArtefact, portfolioStructure)) {
-				showWarning("artefact.already.in.structure");
-			} else {
-				boolean successfullLink = ePFMgr.addArtefactToStructure(getIdentity(), choosenArtefact, portfolioStructure);
-				if (successfullLink) {
-					getWindowControl().setInfo(
-							getTranslator().translate("artefact.choosen", new String[] { choosenArtefact.getTitle(), portfolioStructure.getTitle() }));
-					ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapPortfolioOres(choosenArtefact));
-					ThreadLocalUserActivityLogger.log(EPLoggingAction.EPORTFOLIO_ARTEFACT_SELECTED, getClass());
+		if (source == artefactPoolCtrl) {
+			if(event instanceof EPArtefactChoosenEvent) {
+				// finally an artefact was choosen
+				EPArtefactChoosenEvent artCEv = (EPArtefactChoosenEvent) event;
+				artefactBox.deactivate();
+				AbstractArtefact choosenArtefact = artCEv.getArtefact();
+				// check for a yet existing link to this artefact
+				if (ePFMgr.isArtefactInStructure(choosenArtefact, portfolioStructure)) {
+					showWarning("artefact.already.in.structure");
 				} else {
-					showError("restrictions.not.conform");
+					boolean successfullLink = ePFMgr.addArtefactToStructure(getIdentity(), choosenArtefact, portfolioStructure);
+					if (successfullLink) {
+						getWindowControl().setInfo(
+								getTranslator().translate("artefact.choosen", new String[] { choosenArtefact.getTitle(), portfolioStructure.getTitle() }));
+						ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapPortfolioOres(choosenArtefact));
+						ThreadLocalUserActivityLogger.log(EPLoggingAction.EPORTFOLIO_ARTEFACT_SELECTED, getClass());
+					} else {
+						showError("restrictions.not.conform");
+					}
+					fireEvent(ureq, new EPStructureChangeEvent(EPStructureChangeEvent.ADDED, portfolioStructure));
 				}
+			} else if(event == Event.DONE_EVENT) {
+				artefactBox.deactivate();
 				fireEvent(ureq, new EPStructureChangeEvent(EPStructureChangeEvent.ADDED, portfolioStructure));
 			}
-
-		}
+		} 
 	}
 
 	public void setShowLink(String... types) {
diff --git a/src/main/java/org/olat/repository/controllers/RepositoryEntryMarkCellRenderer.java b/src/main/java/org/olat/repository/controllers/RepositoryEntryMarkCellRenderer.java
deleted file mode 100644
index a251ea42ab6d22da7e7398cd1feea3cda36a2f64..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/repository/controllers/RepositoryEntryMarkCellRenderer.java
+++ /dev/null
@@ -1,75 +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.repository.controllers;
-
-import java.util.Locale;
-import java.util.UUID;
-
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.table.CustomCellRenderer;
-import org.olat.core.gui.components.velocity.VelocityContainer;
-import org.olat.core.gui.control.Controller;
-import org.olat.core.gui.render.RenderResult;
-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.repository.RepositoryEntry;
-
-/**
- * 
- * Render to mark a group
- * 
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- */
-public class RepositoryEntryMarkCellRenderer implements CustomCellRenderer {
-	
-	private final Translator translator;
-	private final VelocityContainer container;
-	private final Controller listeningController;
-	
-	public RepositoryEntryMarkCellRenderer(Controller listeningController, VelocityContainer container, Translator translator) {
-		this.listeningController = listeningController;
-		this.container = container;
-		this.translator = translator;
-	}
-
-	@Override
-	public void render(StringOutput sb, Renderer renderer, Object val, Locale locale, int alignment, String action) {
-		if(val instanceof RepositoryEntry && renderer != null) {
-			RepositoryEntry item = (RepositoryEntry)val;
-			sb.append("<div class='b_mark'>");
-			
-			Link link = LinkFactory.createLink("marked_" + UUID.randomUUID().toString(), container, listeningController);
-			link.setCustomDisplayText("&#160;&#160;&#160;");
-			if(false /*item.isMarked()*/) {
-				link.setCustomEnabledLinkCSS("b_mark_set");
-			} else {
-				link.setCustomEnabledLinkCSS("b_mark_not_set");
-			}
-			link.setUserObject(item);
-			URLBuilder ubu = renderer.getUrlBuilder().createCopyFor(link);
-			RenderResult renderResult = new RenderResult();
-			link.getHTMLRendererSingleton().render(renderer, sb, link, ubu, translator, renderResult, null);
-			sb.append("</div>");
-		}
-	}
-}
diff --git a/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java b/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
index d0ef095fba1d423263e59c66e454a923031925ad..3324306212e74df2b21c19d22eae03cfce152166 100644
--- a/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
+++ b/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
@@ -155,7 +155,7 @@ public class WizardCloseCourseController extends WizardController implements Wiz
 								+ ureq.getIdentity().getUser().getProperty(UserConstants.LASTNAME, null)
 				}));
 		if(mailNotificationCtr != null) mailNotificationCtr.dispose();
-		mailNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false);
+		mailNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false, true);
 		mailNotificationCtr.addControllerListener(this);
 		sendNotificationVC = createVelocityContainer("sendnotification");
 		sendNotificationVC.put("notificationForm", mailNotificationCtr.getInitialComponent());
diff --git a/src/main/java/org/olat/repository/delete/SelectionController.java b/src/main/java/org/olat/repository/delete/SelectionController.java
index c9466cbe6525cdaaee67abd41dbf8ca87f2525c0..acfe28491a2ed140f7421881eae28bafb0bd430c 100644
--- a/src/main/java/org/olat/repository/delete/SelectionController.java
+++ b/src/main/java/org/olat/repository/delete/SelectionController.java
@@ -90,7 +90,6 @@ public class SelectionController extends BasicController {
 
 	private final RepositoryService repositoryService;
 
-
 	/**
 	 * @param ureq
 	 * @param wControl
@@ -205,7 +204,7 @@ public class SelectionController extends BasicController {
 			deleteMailTemplate.addToContext("durationdeleteemail", Integer.toString(RepositoryDeletionManager.getInstance().getDeleteEmailDuration() ));
 	
 			removeAsListenerAndDispose(deleteRepositoryMailCtr);
-			deleteRepositoryMailCtr = new MailNotificationEditController(getWindowControl(), ureq, deleteMailTemplate, true, false);
+			deleteRepositoryMailCtr = new MailNotificationEditController(getWindowControl(), ureq, deleteMailTemplate, true, false, true);
 			listenTo(deleteRepositoryMailCtr);
 			
 			removeAsListenerAndDispose(cmc);
diff --git a/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.js b/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.js
new file mode 100644
index 0000000000000000000000000000000000000000..01daea1e31f8e76848e0769cd7cd64365ca35857
--- /dev/null
+++ b/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.js
@@ -0,0 +1,658 @@
+(function ( $ ) {
+    $.fn.qtiStatistics = function(type, options) {
+    	var settings = $.extend({
+            values: [],
+            colors: [],
+            cut: null,
+            participants: -1,
+            barHeight: 40,
+            xTopLegend: 'x Top',
+            xBottomLegend: 'x Bottom',
+            yLeftLegend: 'y Left',
+            yRightLegend: 'y Right'
+        }, options );
+    	
+    	try {
+    		if(type == 'histogramScore') {
+        		histogramScore(this, settings);
+        	} else if(type == 'histogramDuration') {
+        		histogramDuration(this, settings);
+        	} else if(type == 'horizontalBarSingleChoice') {
+        		horizontalBarSingleChoice(this, settings);
+        	} else if(type == 'rightAnswerPerItem') {
+        		rightAnswerPerItem(this, settings);
+        	} else if(type == 'averageScorePerItem') {
+        		averageScorePerItem(this, settings);
+        	} else if(type == 'horizontalBarMultipleChoice') {
+        		horizontalBarMultipleChoice(this, settings);
+        	} else if(type == 'horizontalBarMultipleChoiceSurvey') {
+        		horizontalBarMultipleChoiceSurvey(this, settings);
+        	}
+    	} catch(e) {
+    		if(console) console.log(e);
+    	}
+        return this;
+    };
+    
+    averageScorePerItem = function($obj, settings) {
+    	var placeholderheight = $obj.height();
+    	var placeholderwidth = $obj.width();
+    	var data = settings.values;
+    	
+    	var margin = {top: 10, right: 60, bottom: 40, left: 60},
+    	   width = placeholderwidth - margin.left - margin.right,
+    	   height = placeholderheight - margin.top - margin.bottom;
+    	
+    	var x = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d[1]; })])
+    	   .range([0, width]);
+    	var xAxis = d3.svg.axis()
+    	   .scale(x)
+    	   .orient('bottom')
+    	   .ticks(10);
+    	
+    	var y = d3.scale.ordinal()
+    	  .domain(data.map(function(d) { return d[0]; }))
+    	   .rangeRoundBands([height, 0]);
+    	var yAxis = d3.svg.axis()
+    	   .scale(y)
+    	   .orient('left');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	    .attr('width', width + margin.left + margin.right)
+    	    .attr('height', height + margin.top + margin.bottom)
+    	   .append('g')
+    	    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	svg.append('g')
+    	    .attr('class', 'x axis')
+    	    .attr('transform', 'translate(0,' + height + ')')
+    	   .call(xAxis)  .append('text')
+    	    .attr('y', (margin.bottom / 1.7))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xBottomLegend);
+    	
+    	svg.selectAll('.bar0')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar0 bar_default')
+    	    .attr('fill', 'bar_default')
+    	    .attr('x', 0)
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[1]); })
+    	    .attr('height', y.rangeBand() - 4);
+    	
+    	svg.append('g')
+    	    .attr('class', 'y axis')
+    	    .call(yAxis)
+    	   .append('text')
+    	    .attr('transform', 'rotate(-90)')
+    	    .attr('y', 0 - (margin.right / 1.7))
+    	    .attr('x', 0 - (height / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.yLeftLegend);
+    }
+    
+    rightAnswerPerItem = function($obj, settings) {
+    	var placeholderheight = $obj.height();
+    	var placeholderwidth = $obj.width();
+    	
+    	var data = settings.values;
+    	
+    	var margin = {top: 40, right: 60, bottom: 40, left: 60},
+    	   width = placeholderwidth - margin.left - margin.right,
+    	   height = placeholderheight - margin.top - margin.bottom;
+    	
+    	var sum = d3.sum(data, function(d) { return d[1]; });
+    	
+    	var x = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d[1]; })])
+    	   .range([0, width]);
+    	var xAxis = d3.svg.axis()
+    	   .scale(x)
+    	   .orient('top')
+    	   .ticks(10);
+    	
+    	var x2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d[1] / sum; })])
+    	   .range([0, width]);
+    	var x2Axis = d3.svg.axis()
+    	   .scale(x2)
+    	   .orient('bottom')
+    	   .ticks(10, '%');
+    	
+    	var y = d3.scale.ordinal()
+    	  .domain(data.map(function(d) { return d[0]; }))
+    	   .rangeRoundBands([height, 0]);
+    	var yAxis = d3.svg.axis()
+    	   .scale(y)
+    	   .orient('left');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	   .attr('width', width + margin.left + margin.right)
+    	   .attr('height', height + margin.top + margin.bottom)
+    	  .append('g')
+    	   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,0)')
+    	     .call(xAxis)  .append('text')
+    	    .attr('y', 0 - (margin.top / 1.1))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xBottomLegend);
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,' + height + ')')
+    	     .call(x2Axis)  .append('text')
+    	    .attr('y', (margin.bottom / 1.7))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xTopLegend);
+    	
+    	svg.selectAll('.bar0')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar0 bar_green')
+    	    .attr('fill', 'bar_green')
+    	    .attr('x', 0)
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[1]); })
+    	    .attr('height', y.rangeBand() - 4);
+    	
+    	svg.append('g')
+    	    .attr('class', 'y axis')
+    	   .call(yAxis)
+    	    .append('text')
+    	    .attr('transform', 'rotate(-90)')
+    	    .attr('y', 0 - (margin.right / 1.7))
+    	    .attr('x', 0 - (height / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.yLeftLegend);
+    };
+    
+    horizontalBarMultipleChoiceSurvey = function($obj, settings) {
+    	var placeholderwidth = $obj.width();
+    	
+    	var data = settings.values;
+    	var colors = settings.colors;
+    	
+    	var margin = {top: 40, right: 10, bottom: 40, left: 40};
+    	var height = data.length * settings.barHeight;
+    	$obj.height(height + margin.top + margin.bottom + 'px');
+    	var width = placeholderwidth - margin.left - margin.right;
+    	
+    	var sum =  settings.participants;
+    	var max = d3.max(data, function(d) { return d[1]; });
+
+    	var x = d3.scale.linear()
+    	  .domain([0, max])
+    	   .range([0, width]);
+    	var xAxis = d3.svg.axis()
+    	   .scale(x)
+    	   .orient('top')
+    	   .ticks(max);
+    	
+    	var x2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return (d[1]) / sum; })])
+    	   .range([0, width]);
+    	var x2Axis = d3.svg.axis()
+    	   .scale(x2)
+    	   .orient('bottom')
+    	   .ticks(10, '%');
+    	
+    	var y = d3.scale.ordinal()
+    	  .domain(data.map(function(d) { return d[0]; }))
+    	   .rangeRoundBands([height, 0]);
+    	var yAxis = d3.svg.axis()
+    	   .scale(y)
+    	   .orient('left');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	   .attr('width', width + margin.left + margin.right)
+    	   .attr('height', height + margin.top + margin.bottom)
+    	  .append('g')
+    	   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,0)')
+    	     .call(xAxis)  .append('text')
+    	    .attr('y', 0 - (margin.top / 1.1))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xTopLegend);
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,' + height + ')')
+    	     .call(x2Axis)  .append('text')
+    	    .attr('y', (margin.bottom / 1.7))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xBottomLegend);
+    	
+    	svg.append('g')
+    	    .attr('class', 'y axis')
+    	   .call(yAxis)
+    	    .append('text')
+    	    .attr('transform', 'rotate(-90)')
+    	    .attr('y', 0 - margin.left)
+    	    .attr('x', 0 - (height / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.yLeftLegend);
+
+    	svg.selectAll('.bar0')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar_default')
+    	    .attr('fill', 'bar_default')
+    	    .attr('x', 0)
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[1]); })
+    	    .attr('height', y.rangeBand() - 4);
+    };
+    
+    horizontalBarMultipleChoice = function($obj, settings) {
+    	var placeholderwidth = $obj.width();
+    	
+    	var data = settings.values;
+    	var colors = settings.colors;
+    	
+    	var margin = {top: 40, right: 10, bottom: 40, left: 40};
+    	var height = data.length * settings.barHeight;
+    	$obj.height(height + margin.top + margin.bottom + 'px');
+    	var width = placeholderwidth - margin.left - margin.right;
+    	
+    	var sum =  settings.participants;
+    	var max = d3.max(data, function(d) { return d[1] + d[2] + d[3]; });
+
+    	var x = d3.scale.linear()
+    	  .domain([0, max])
+    	   .range([0, width]);
+    	var xAxis = d3.svg.axis()
+    	   .scale(x)
+    	   .orient('top')
+    	   .ticks(max);
+    	
+    	var x2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return (d[1] + d[2] + d[3]) / sum; })])
+    	   .range([0, width]);
+    	var x2Axis = d3.svg.axis()
+    	   .scale(x2)
+    	   .orient('bottom')
+    	   .ticks(10, '%');
+    	
+    	var y = d3.scale.ordinal()
+    	  .domain(data.map(function(d) { return d[0]; }))
+    	   .rangeRoundBands([height, 0]);
+    	var yAxis = d3.svg.axis()
+    	   .scale(y)
+    	   .orient('left');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	   .attr('width', width + margin.left + margin.right)
+    	   .attr('height', height + margin.top + margin.bottom)
+    	  .append('g')
+    	   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,0)')
+    	     .call(xAxis)  .append('text')
+    	    .attr('y', 0 - (margin.top / 1.1))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xTopLegend);
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,' + height + ')')
+    	     .call(x2Axis)  .append('text')
+    	    .attr('y', (margin.bottom / 1.7))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xBottomlegend);
+    	
+    	svg.append('g')
+    	    .attr('class', 'y axis')
+    	   .call(yAxis)
+    	    .append('text')
+    	    .attr('transform', 'rotate(-90)')
+    	    .attr('y', 0 - margin.left)
+    	    .attr('x', 0 - (height / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.yLeftLegend);
+
+    	svg.selectAll('.bar0')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar_green')
+    	    .attr('fill', 'bar_green')
+    	    .attr('x', 0)
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[1]); })
+    	    .attr('height', y.rangeBand() - 4);
+    	
+    	svg.selectAll('.bar1')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar_red')
+    	    .attr('fill', 'bar_red')
+    	    .attr('x', function(d) { return x(d[1]); })
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[2]); })
+    	    .attr('height', y.rangeBand() - 4);
+    	
+    	svg.selectAll('.bar2')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', 'bar bar_grey')
+    	    .attr('fill', 'bar_grey')
+    	    .attr('x', function(d) { return x(d[1] + d[2]); })
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[3]); })
+    	    .attr('height', y.rangeBand() - 4);
+    };
+    
+    horizontalBarSingleChoice = function($obj, settings) {
+    	var placeholderwidth = $obj.width();
+    	
+    	var data = settings.values;
+    	var colors = settings.colors;
+    	
+    	var margin = {top: 40, right: 10, bottom: 40, left: 40};
+    	var height = data.length * settings.barHeight;
+    	$obj.height(height + margin.top + margin.bottom + 'px');
+    	var width = placeholderwidth - margin.left - margin.right;
+    	
+    	var sum = d3.sum(data, function(d) { return d[1]; });
+    	var max = d3.max(data, function(d) { return d[1]; });
+
+    	var x = d3.scale.linear()
+    	  .domain([0, max])
+    	   .range([0, width]);
+    	var xAxis = d3.svg.axis()
+    	   .scale(x)
+    	   .orient('top')
+    	   .ticks(max);
+    	
+    	var x2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d[1] / sum; })])
+    	   .range([0, width]);
+    	var x2Axis = d3.svg.axis()
+    	   .scale(x2)
+    	   .orient('bottom')
+    	   .ticks(10, '%');
+    	
+    	var y = d3.scale.ordinal()
+    	  .domain(data.map(function(d) { return d[0]; }))
+    	   .rangeRoundBands([height, 0]);
+    	var yAxis = d3.svg.axis()
+    	   .scale(y)
+    	   .orient('left');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	   .attr('width', width + margin.left + margin.right)
+    	   .attr('height', height + margin.top + margin.bottom)
+    	  .append('g')
+    	   .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,0)')
+    	    .call(xAxis)
+    	     .append('text')
+    	    .attr('y', 0 - (margin.top / 1.1))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xTopLegend);
+    	
+    	svg.append('g')
+    	     .attr('class', 'x axis')
+    	     .attr('transform', 'translate(0,' + height + ')')
+    	     .call(x2Axis)  .append('text')
+    	    .attr('y', (margin.bottom / 1.7))
+    	    .attr('x', (width / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.xBottomLegend);
+
+    	svg.selectAll('.bar0')
+    	    .data(data)
+    	  .enter().append('rect')
+    	    .attr('class', function(d, i){
+    	    	if(colors == null) return 'bar bar0 bar_default';
+    	    	else if(colors.length > i) return colors[i];
+    	    	else return 'bar bar0 bar_default';
+    	    })
+    	    .attr('fill', 'bar_green')
+    	    .attr('x', 0)
+    	    .attr('y', function(d) { return y(d[0]) + 2; })
+    	    .attr('width', function(d) { return x(d[1]); })
+    	    .attr('height', y.rangeBand() - 4);
+    	
+    	svg.append('g')
+    	    .attr('class', 'y axis')
+    	   .call(yAxis)
+    	    .append('text')
+    	    .attr('transform', 'rotate(-90)')
+    	    .attr('y', 0 - margin.left)
+    	    .attr('x', 0 - (height / 2))
+    	    .attr('dy', '1em')
+    	    .style('text-anchor', 'middle')
+    	    .text(settings.yLeftLegend);
+    };
+    
+    histogramDuration = function($obj, settings) {
+    	var placeholderheight = $obj.height();
+    	var placeholderwidth = $obj.width();
+    	
+    	var values = settings.values;
+    	var formatCount = d3.format(',.f'),
+    	  formatTime = d3.time.format('%H:%M'),
+    	  formatMinutes = function(d) { return formatTime(new Date(2012, 0, 1, 0, d)); };
+    	
+    	var margin = {top: 10, right: 60, bottom: 40, left: 60},
+    	  width = placeholderwidth - margin.left - margin.right,
+    	  height = placeholderheight - margin.top - margin.bottom;
+    	  
+    	var x = d3.scale.linear()
+    	  .domain([0, 4.0])
+    	  .range([0, width]);
+    	
+    	var data = d3.layout.histogram()
+    	  .bins(x.ticks(20))
+    	  (values);
+    	
+    	var sum = d3.sum(data, function(d) { return d.y; });
+    	var y = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d.y; })])
+    	  .range([height, 0]);
+    	
+    	var y2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d.y / sum; })])
+    	  .range([height, 0]);
+    	
+    	var xAxis = d3.svg.axis()
+    	  .scale(x)
+    	  .orient('bottom')
+    	  .tickFormat(formatMinutes);
+    	
+    	var yAxis = d3.svg.axis()
+    	  .scale(y)
+    	  .orient('right')
+    	  .ticks(y.domain()[1]).tickSubdivide(0);
+    	
+    	var y2Axis = d3.svg.axis()
+    	  .scale(y2)
+    	  .orient('left')
+    	  .ticks(10, '%');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	  .attr('width', width + margin.left + margin.right)
+    	  .attr('height', height + margin.top + margin.bottom)
+    	 .append('g')
+    	  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	var bar = svg.selectAll('.bar')
+    	  .data(data)
+    	 .enter().append('g')
+    	  .attr('class', 'bar bar_default')
+    	  .attr('transform', function(d) { return 'translate(' + x(d.x) + ',' + y(d.y) + ')'; })
+    	  .append('rect')
+    	  .attr('x', 2)
+    	  .attr('width', x(data[0].dx) - 4)
+    	  .attr('height', function(d) { return height - y(d.y); });
+    	
+    	svg.append('g')
+    	  .attr('class', 'y axis')
+    	  .call(y2Axis)
+    	 .append('text')
+    	  .attr('transform', 'rotate(-90)')
+    	  .attr('y', 0 - margin.left)
+    	  .attr('x', 0 - (height / 2))
+    	  .attr('dy', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.yLeftLegend);
+    	
+    	svg.append('g')
+    	  .attr('class', 'x axis')
+    	  .attr('transform', 'translate(0,' + height + ')')
+    	  .call(xAxis)
+    	 .append('text')
+    	  .attr('y', (margin.bottom / 1.1))
+    	  .attr('x', (width / 2))
+    	  .attr('dx', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.xBottomLegend);
+    	
+    	svg.append('g')
+    	  .attr('class', 'y axis')
+    	  .attr('transform', 'translate(' + width + ',0)')
+    	 .call(yAxis)
+    	 .append('text')
+    	  .attr('transform', 'rotate(90)')
+    	  .attr('y', 0 - (margin.right))
+    	  .attr('x', (height / 2))
+    	  .attr('dy', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.yLeftLegend);
+    };
+
+    histogramScore = function($obj, settings) {
+    	var placeholderheight = $obj.height();
+    	var placeholderwidth = $obj.width();
+    	var values = settings.values;
+
+    	var margin = {top: 10, right: 60, bottom: 40, left: 60},
+    	  width = placeholderwidth - margin.left - margin.right,
+    	  height = placeholderheight - margin.top - margin.bottom;
+    	
+    	var cut = settings.cut;
+    	
+    	var x = d3.scale.linear()
+    	  .domain([0, 3.0])
+    	  .range([0, width]);
+    	
+    	var data = d3.layout.histogram()
+    	  .bins(x.ticks(20))
+    	  (values);
+    	
+    	var sum = d3.sum(data, function(d) { return d.y; });
+    	
+    	var y = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d.y; })])
+    	  .range([height, 0]);
+    	
+    	var y2 = d3.scale.linear()
+    	  .domain([0, d3.max(data, function(d) { return d.y / sum; })])
+    	  .range([height, 0]);
+    	
+    	var xAxis = d3.svg.axis()
+    	  .scale(x)
+    	  .orient('bottom')
+    	  .tickFormat(d3.format('.01f'));
+    	
+    	var yAxis = d3.svg.axis()
+    	  .scale(y)
+    	  .orient('right')
+    	  .ticks(y.domain()[1]).tickSubdivide(0);
+    	
+    	var y2Axis = d3.svg.axis()
+    	  .scale(y2)
+    	  .orient('left')
+    	  .ticks(10, '%');
+    	
+    	var svg = d3.select('#' + $obj.attr('id')).append('svg')
+    	  .attr('width', width + margin.left + margin.right)
+    	  .attr('height', height + margin.top + margin.bottom)
+    	 .append('g')
+    	  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+    	
+    	var bar = svg.selectAll('.bar')
+    	  .data(data)
+    	 .enter().append('g')
+    	  .attr('class', function(d, i) {
+    		  if(cut == null) return 'bar bar_default';
+    		  else if(data[i].x < cut) return 'bar bar_red';
+    		  else return 'bar bar_green';
+    	  })
+    	  .attr('transform', function(d) { return 'translate(' + x(d.x) + ',' + y(d.y) + ')'; })
+    	  .append('rect')
+    	  .attr('x', 2)
+    	  .attr('width', x(data[0].dx) - 4)
+    	  .attr('height', function(d) { return height - y(d.y); });
+    	
+    	svg.append('g')
+    	  .attr('class', 'y axis')
+    	  .call(y2Axis)
+    	 .append('text')
+    	  .attr('transform', 'rotate(-90)')
+    	  .attr('y', 0 - margin.left)
+    	  .attr('x', 0 - (height / 2))
+    	  .attr('dy', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.yLeftLegend);
+    	
+    	svg.append('g')
+    	  .attr('class', 'x axis')
+    	  .attr('transform', 'translate(0,' + height + ')')
+    	  .call(xAxis)
+    	 .append('text')
+    	  .attr('y', (margin.bottom / 1.1))
+    	  .attr('x', (width / 2))
+    	  .attr('dx', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.xBottomLegend);
+    	
+    	svg.append('g')
+    	  .attr('class', 'y axis')
+    	  .attr('transform', 'translate(' + width + ',0)')
+    	 .call(yAxis)
+    	 .append('text')
+    	  .attr('transform', 'rotate(90)')
+    	  .attr('y', 0 - (margin.right))
+    	  .attr('x', (height / 2))
+    	  .attr('dy', '1em')
+    	  .style('text-anchor', 'middle')
+    	  .text(settings.yRightLegend);
+    }
+    
+
+}( jQuery ));
\ No newline at end of file
diff --git a/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.min.js b/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..2f443e233ecb393c2474ba00c5c0d90275f2e49f
--- /dev/null
+++ b/src/main/webapp/static/js/jquery/openolat/jquery.statistics.chart.min.js
@@ -0,0 +1 @@
+(function(a){a.fn.qtiStatistics=function(d,b){var c=a.extend({values:[],colors:[],cut:null,participants:-1,barHeight:40,xTopLegend:"x Top",xBottomLegend:"x Bottom",yLeftLegend:"y Left",yRightLegend:"y Right"},b);try{if(d=="histogramScore"){histogramScore(this,c)}else{if(d=="histogramDuration"){histogramDuration(this,c)}else{if(d=="horizontalBarSingleChoice"){horizontalBarSingleChoice(this,c)}else{if(d=="rightAnswerPerItem"){rightAnswerPerItem(this,c)}else{if(d=="averageScorePerItem"){averageScorePerItem(this,c)}else{if(d=="horizontalBarMultipleChoice"){horizontalBarMultipleChoice(this,c)}else{if(d=="horizontalBarMultipleChoiceSurvey"){horizontalBarMultipleChoiceSurvey(this,c)}}}}}}}}catch(f){if(console){console.log(f)}}return this};averageScorePerItem=function(b,f){var m=b.height();var j=b.width();var h=f.values;var g={top:10,right:60,bottom:40,left:60},d=j-g.left-g.right,n=m-g.top-g.bottom;var l=d3.scale.linear().domain([0,d3.max(h,function(o){return o[1]})]).range([0,d]);var e=d3.svg.axis().scale(l).orient("bottom").ticks(10);var k=d3.scale.ordinal().domain(h.map(function(o){return o[0]})).rangeRoundBands([n,0]);var c=d3.svg.axis().scale(k).orient("left");var i=d3.select("#"+b.attr("id")).append("svg").attr("width",d+g.left+g.right).attr("height",n+g.top+g.bottom).append("g").attr("transform","translate("+g.left+","+g.top+")");i.append("g").attr("class","x axis").attr("transform","translate(0,"+n+")").call(e).append("text").attr("y",(g.bottom/1.7)).attr("x",(d/2)).attr("dy","1em").style("text-anchor","middle").text(f.xBottomLegend);i.selectAll(".bar0").data(h).enter().append("rect").attr("class","bar bar0 bar_default").attr("fill","bar_default").attr("x",0).attr("y",function(o){return k(o[0])+2}).attr("width",function(o){return l(o[1])}).attr("height",k.rangeBand()-4);i.append("g").attr("class","y axis").call(c).append("text").attr("transform","rotate(-90)").attr("y",0-(g.right/1.7)).attr("x",0-(n/2)).attr("dy","1em").style("text-anchor","middle").text(f.yLeftLegend)};rightAnswerPerItem=function(c,h){var p=c.height();var l=c.width();var j=h.values;var i={top:40,right:60,bottom:40,left:60},f=l-i.left-i.right,q=p-i.top-i.bottom;var m=d3.sum(j,function(r){return r[1]});var o=d3.scale.linear().domain([0,d3.max(j,function(r){return r[1]})]).range([0,f]);var g=d3.svg.axis().scale(o).orient("top").ticks(10);var e=d3.scale.linear().domain([0,d3.max(j,function(r){return r[1]/m})]).range([0,f]);var b=d3.svg.axis().scale(e).orient("bottom").ticks(10,"%");var n=d3.scale.ordinal().domain(j.map(function(r){return r[0]})).rangeRoundBands([q,0]);var d=d3.svg.axis().scale(n).orient("left");var k=d3.select("#"+c.attr("id")).append("svg").attr("width",f+i.left+i.right).attr("height",q+i.top+i.bottom).append("g").attr("transform","translate("+i.left+","+i.top+")");k.append("g").attr("class","x axis").attr("transform","translate(0,0)").call(g).append("text").attr("y",0-(i.top/1.1)).attr("x",(f/2)).attr("dy","1em").style("text-anchor","middle").text(h.xBottomLegend);k.append("g").attr("class","x axis").attr("transform","translate(0,"+q+")").call(b).append("text").attr("y",(i.bottom/1.7)).attr("x",(f/2)).attr("dy","1em").style("text-anchor","middle").text(h.xTopLegend);k.selectAll(".bar0").data(j).enter().append("rect").attr("class","bar bar0 bar_green").attr("fill","bar_green").attr("x",0).attr("y",function(r){return n(r[0])+2}).attr("width",function(r){return o(r[1])}).attr("height",n.rangeBand()-4);k.append("g").attr("class","y axis").call(d).append("text").attr("transform","rotate(-90)").attr("y",0-(i.right/1.7)).attr("x",0-(q/2)).attr("dy","1em").style("text-anchor","middle").text(h.yLeftLegend)};horizontalBarMultipleChoiceSurvey=function(c,i){var m=c.width();var k=i.values;var d=i.colors;var j={top:40,right:10,bottom:40,left:40};var r=k.length*i.barHeight;c.height(r+j.top+j.bottom+"px");var g=m-j.left-j.right;var n=i.participants;var p=d3.max(k,function(s){return s[1]});var q=d3.scale.linear().domain([0,p]).range([0,g]);var h=d3.svg.axis().scale(q).orient("top").ticks(p);var f=d3.scale.linear().domain([0,d3.max(k,function(s){return(s[1])/n})]).range([0,g]);var b=d3.svg.axis().scale(f).orient("bottom").ticks(10,"%");var o=d3.scale.ordinal().domain(k.map(function(s){return s[0]})).rangeRoundBands([r,0]);var e=d3.svg.axis().scale(o).orient("left");var l=d3.select("#"+c.attr("id")).append("svg").attr("width",g+j.left+j.right).attr("height",r+j.top+j.bottom).append("g").attr("transform","translate("+j.left+","+j.top+")");l.append("g").attr("class","x axis").attr("transform","translate(0,0)").call(h).append("text").attr("y",0-(j.top/1.1)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xTopLegend);l.append("g").attr("class","x axis").attr("transform","translate(0,"+r+")").call(b).append("text").attr("y",(j.bottom/1.7)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xBottomLegend);l.append("g").attr("class","y axis").call(e).append("text").attr("transform","rotate(-90)").attr("y",0-j.left).attr("x",0-(r/2)).attr("dy","1em").style("text-anchor","middle").text(i.yLeftLegend);l.selectAll(".bar0").data(k).enter().append("rect").attr("class","bar bar_default").attr("fill","bar_default").attr("x",0).attr("y",function(s){return o(s[0])+2}).attr("width",function(s){return q(s[1])}).attr("height",o.rangeBand()-4)};horizontalBarMultipleChoice=function(c,i){var m=c.width();var k=i.values;var d=i.colors;var j={top:40,right:10,bottom:40,left:40};var r=k.length*i.barHeight;c.height(r+j.top+j.bottom+"px");var g=m-j.left-j.right;var n=i.participants;var p=d3.max(k,function(s){return s[1]+s[2]+s[3]});var q=d3.scale.linear().domain([0,p]).range([0,g]);var h=d3.svg.axis().scale(q).orient("top").ticks(p);var f=d3.scale.linear().domain([0,d3.max(k,function(s){return(s[1]+s[2]+s[3])/n})]).range([0,g]);var b=d3.svg.axis().scale(f).orient("bottom").ticks(10,"%");var o=d3.scale.ordinal().domain(k.map(function(s){return s[0]})).rangeRoundBands([r,0]);var e=d3.svg.axis().scale(o).orient("left");var l=d3.select("#"+c.attr("id")).append("svg").attr("width",g+j.left+j.right).attr("height",r+j.top+j.bottom).append("g").attr("transform","translate("+j.left+","+j.top+")");l.append("g").attr("class","x axis").attr("transform","translate(0,0)").call(h).append("text").attr("y",0-(j.top/1.1)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xTopLegend);l.append("g").attr("class","x axis").attr("transform","translate(0,"+r+")").call(b).append("text").attr("y",(j.bottom/1.7)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xBottomlegend);l.append("g").attr("class","y axis").call(e).append("text").attr("transform","rotate(-90)").attr("y",0-j.left).attr("x",0-(r/2)).attr("dy","1em").style("text-anchor","middle").text(i.yLeftLegend);l.selectAll(".bar0").data(k).enter().append("rect").attr("class","bar bar_green").attr("fill","bar_green").attr("x",0).attr("y",function(s){return o(s[0])+2}).attr("width",function(s){return q(s[1])}).attr("height",o.rangeBand()-4);l.selectAll(".bar1").data(k).enter().append("rect").attr("class","bar bar_red").attr("fill","bar_red").attr("x",function(s){return q(s[1])}).attr("y",function(s){return o(s[0])+2}).attr("width",function(s){return q(s[2])}).attr("height",o.rangeBand()-4);l.selectAll(".bar2").data(k).enter().append("rect").attr("class","bar bar_grey").attr("fill","bar_grey").attr("x",function(s){return q(s[1]+s[2])}).attr("y",function(s){return o(s[0])+2}).attr("width",function(s){return q(s[3])}).attr("height",o.rangeBand()-4)};horizontalBarSingleChoice=function(c,i){var m=c.width();var k=i.values;var d=i.colors;var j={top:40,right:10,bottom:40,left:40};var r=k.length*i.barHeight;c.height(r+j.top+j.bottom+"px");var g=m-j.left-j.right;var n=d3.sum(k,function(s){return s[1]});var p=d3.max(k,function(s){return s[1]});var q=d3.scale.linear().domain([0,p]).range([0,g]);var h=d3.svg.axis().scale(q).orient("top").ticks(p);var f=d3.scale.linear().domain([0,d3.max(k,function(s){return s[1]/n})]).range([0,g]);var b=d3.svg.axis().scale(f).orient("bottom").ticks(10,"%");var o=d3.scale.ordinal().domain(k.map(function(s){return s[0]})).rangeRoundBands([r,0]);var e=d3.svg.axis().scale(o).orient("left");var l=d3.select("#"+c.attr("id")).append("svg").attr("width",g+j.left+j.right).attr("height",r+j.top+j.bottom).append("g").attr("transform","translate("+j.left+","+j.top+")");l.append("g").attr("class","x axis").attr("transform","translate(0,0)").call(h).append("text").attr("y",0-(j.top/1.1)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xTopLegend);l.append("g").attr("class","x axis").attr("transform","translate(0,"+r+")").call(b).append("text").attr("y",(j.bottom/1.7)).attr("x",(g/2)).attr("dy","1em").style("text-anchor","middle").text(i.xBottomLegend);l.selectAll(".bar0").data(k).enter().append("rect").attr("class",function(t,s){if(d==null){return"bar bar0 bar_default"}else{if(d.length>s){return d[s]}else{return"bar bar0 bar_default"}}}).attr("fill","bar_green").attr("x",0).attr("y",function(s){return o(s[0])+2}).attr("width",function(s){return q(s[1])}).attr("height",o.rangeBand()-4);l.append("g").attr("class","y axis").call(e).append("text").attr("transform","rotate(-90)").attr("y",0-j.left).attr("x",0-(r/2)).attr("dy","1em").style("text-anchor","middle").text(i.yLeftLegend)};histogramDuration=function(s,r){var c=s.height();var o=s.width();var e=r.values;var t=d3.format(",.f"),u=d3.time.format("%H:%M"),j=function(w){return u(new Date(2012,0,1,0,w))};var l={top:10,right:60,bottom:40,left:60},p=o-l.left-l.right,n=c-l.top-l.bottom;var k=d3.scale.linear().domain([0,4]).range([0,p]);var v=d3.layout.histogram().bins(k.ticks(20))(e);var g=d3.sum(v,function(w){return w.y});var i=d3.scale.linear().domain([0,d3.max(v,function(w){return w.y})]).range([n,0]);var f=d3.scale.linear().domain([0,d3.max(v,function(w){return w.y/g})]).range([n,0]);var h=d3.svg.axis().scale(k).orient("bottom").tickFormat(j);var b=d3.svg.axis().scale(i).orient("right").ticks(i.domain()[1]).tickSubdivide(0);var d=d3.svg.axis().scale(f).orient("left").ticks(10,"%");var m=d3.select("#"+s.attr("id")).append("svg").attr("width",p+l.left+l.right).attr("height",n+l.top+l.bottom).append("g").attr("transform","translate("+l.left+","+l.top+")");var q=m.selectAll(".bar").data(v).enter().append("g").attr("class","bar bar_default").attr("transform",function(w){return"translate("+k(w.x)+","+i(w.y)+")"}).append("rect").attr("x",2).attr("width",k(v[0].dx)-4).attr("height",function(w){return n-i(w.y)});m.append("g").attr("class","y axis").call(d).append("text").attr("transform","rotate(-90)").attr("y",0-l.left).attr("x",0-(n/2)).attr("dy","1em").style("text-anchor","middle").text(r.yLeftLegend);m.append("g").attr("class","x axis").attr("transform","translate(0,"+n+")").call(h).append("text").attr("y",(l.bottom/1.1)).attr("x",(p/2)).attr("dx","1em").style("text-anchor","middle").text(r.xBottomLegend);m.append("g").attr("class","y axis").attr("transform","translate("+p+",0)").call(b).append("text").attr("transform","rotate(90)").attr("y",0-(l.right)).attr("x",(n/2)).attr("dy","1em").style("text-anchor","middle").text(r.yLeftLegend)};histogramScore=function(s,r){var c=s.height();var o=s.width();var d=r.values;var l={top:10,right:60,bottom:40,left:60},p=o-l.left-l.right,n=c-l.top-l.bottom;var f=r.cut;var k=d3.scale.linear().domain([0,3]).range([0,p]);var t=d3.layout.histogram().bins(k.ticks(20))(d);var h=d3.sum(t,function(u){return u.y});var j=d3.scale.linear().domain([0,d3.max(t,function(u){return u.y})]).range([n,0]);var g=d3.scale.linear().domain([0,d3.max(t,function(u){return u.y/h})]).range([n,0]);var i=d3.svg.axis().scale(k).orient("bottom").tickFormat(d3.format(".01f"));var b=d3.svg.axis().scale(j).orient("right").ticks(j.domain()[1]).tickSubdivide(0);var e=d3.svg.axis().scale(g).orient("left").ticks(10,"%");var m=d3.select("#"+s.attr("id")).append("svg").attr("width",p+l.left+l.right).attr("height",n+l.top+l.bottom).append("g").attr("transform","translate("+l.left+","+l.top+")");var q=m.selectAll(".bar").data(t).enter().append("g").attr("class",function(v,u){if(f==null){return"bar bar_default"}else{if(t[u].x<f){return"bar bar_red"}else{return"bar bar_green"}}}).attr("transform",function(u){return"translate("+k(u.x)+","+j(u.y)+")"}).append("rect").attr("x",2).attr("width",k(t[0].dx)-4).attr("height",function(u){return n-j(u.y)});m.append("g").attr("class","y axis").call(e).append("text").attr("transform","rotate(-90)").attr("y",0-l.left).attr("x",0-(n/2)).attr("dy","1em").style("text-anchor","middle").text(r.yLeftLegend);m.append("g").attr("class","x axis").attr("transform","translate(0,"+n+")").call(i).append("text").attr("y",(l.bottom/1.1)).attr("x",(p/2)).attr("dx","1em").style("text-anchor","middle").text(r.xBottomLegend);m.append("g").attr("class","y axis").attr("transform","translate("+p+",0)").call(b).append("text").attr("transform","rotate(90)").attr("y",0-(l.right)).attr("x",(n/2)).attr("dy","1em").style("text-anchor","middle").text(r.yRightLegend)}}(jQuery));
\ No newline at end of file
diff --git a/src/main/webapp/static/js/js.plugins.min.js b/src/main/webapp/static/js/js.plugins.min.js
index ab84cd7635a71ea4c5b62e75e4618df7f1802cfc..7eaec0c1833d2bfcffb47861af3e65a0293eb0a5 100644
--- a/src/main/webapp/static/js/js.plugins.min.js
+++ b/src/main/webapp/static/js/js.plugins.min.js
@@ -5,4 +5,4 @@ OPOL={};var o2c=0;var o3c=new Array();o_info.guibusy=false;o_info.linkbusy=false
  * Dual licensed under the MIT or GPL Version 2 licenses.
  *
  */
-jQuery.periodic=function(l,h){if(jQuery.isFunction(l)){h=l;l={}}var c=jQuery.extend({},jQuery.periodic.defaults,{ajax_complete:j,increment:g,reset:f,cancel:i},l);c.cur_period=c.period;c.tid=false;var e="";b();return c;function b(){i();c.tid=setTimeout(function(){h.call(c);g();if(c.tid){b()}},c.cur_period)}function j(n,m){if(m==="success"&&e!==n.responseText){e=n.responseText;f()}}function g(){c.cur_period*=c.decay;if(c.cur_period<c.period){f()}else{if(c.cur_period>c.max_period){c.cur_period=c.max_period;if(c.on_max!==undefined){c.on_max.call(c)}}}}function f(){c.cur_period=c.period;b()}function i(){clearTimeout(c.tid);c.tid=null}function k(){}function a(){}function d(){}};jQuery.periodic.defaults={period:4000,max_period:1800000,decay:1.5,on_max:undefined};!function(t){function e(){function e(t){"remove"===t&&this.each(function(t,e){var n=r(e);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(t,e){var n=tinymce.get(e.id.replace(/_parent$/,""));n&&n.remove()})}function i(t){var n,i=this;if(null!=t)e.call(i),i.each(function(e,n){var i;(i=tinymce.get(n.id))&&i.setContent(t)});else if(i.length>0&&(n=tinymce.get(i[0].id)))return n.getContent()}function r(t){var e=null;return t&&t.id&&a.tinymce&&(e=tinymce.get(t.id)),e}function c(t){return!!(t&&t.length&&a.tinymce&&t.is(":tinymce"))}var u={};t.each(["text","html","val"],function(e,a){var o=u[a]=t.fn[a],s="text"===a;t.fn[a]=function(e){var a=this;if(!c(a))return o.apply(a,arguments);if(e!==n)return i.call(a.filter(":tinymce"),e),o.apply(a.not(":tinymce"),arguments),a;var u="",l=arguments;return(s?a:a.eq(0)).each(function(e,n){var i=r(n);u+=i?s?i.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):i.getContent({save:!0}):o.apply(t(n),l)}),u}}),t.each(["append","prepend"],function(e,i){var a=u[i]=t.fn[i],o="prepend"===i;t.fn[i]=function(t){var e=this;return c(e)?t!==n?(e.filter(":tinymce").each(function(e,n){var i=r(n);i&&i.setContent(o?t+i.getContent():i.getContent()+t)}),a.apply(e.not(":tinymce"),arguments),e):void 0:a.apply(e,arguments)}}),t.each(["remove","replaceWith","replaceAll","empty"],function(n,i){var r=u[i]=t.fn[i];t.fn[i]=function(){return e.call(this,i),r.apply(this,arguments)}}),u.attr=t.fn.attr,t.fn.attr=function(e,a){var o=this,s=arguments;if(!e||"value"!==e||!c(o))return a!==n?u.attr.apply(o,s):u.attr.apply(o,s);if(a!==n)return i.call(o.filter(":tinymce"),a),u.attr.apply(o.not(":tinymce"),s),o;var l=o[0],p=r(l);return p?p.getContent({save:!0}):u.attr.apply(t(l),s)}}var n,i,r=[],a=window;t.fn.tinymce=function(n){function c(){var i=[],r=0;e&&(e(),e=null),l.each(function(t,e){var a,c=e.id,u=n.oninit;c||(e.id=c=tinymce.DOM.uniqueId()),tinymce.get(c)||(a=new tinymce.Editor(c,n,tinymce.EditorManager),i.push(a),a.on("init",function(){var t,e=u;l.css("visibility",""),u&&++r==i.length&&("string"==typeof e&&(t=-1===e.indexOf(".")?null:tinymce.resolve(e.replace(/\.\w+$/,"")),e=tinymce.resolve(e)),e.apply(t||tinymce,i))}))}),t.each(i,function(t,e){e.render()})}var u,o,s,l=this,p="";if(!l.length)return l;if(!n)return tinymce.get(l[0].id);if(l.css("visibility","hidden"),a.tinymce||i||!(u=n.script_url))1===i?r.push(c):c();else{i=1,o=u.substring(0,u.lastIndexOf("/")),-1!=u.indexOf(".min")&&(p=".min"),a.tinymce=a.tinyMCEPreInit||{base:o,suffix:p},-1!=u.indexOf("gzip")&&(s=n.language||"en",u=u+(/\?/.test(u)?"&":"?")+"js=true&core=true&suffix="+escape(p)+"&themes="+escape(n.theme||"")+"&plugins="+escape(n.plugins||"")+"&languages="+(s||""),a.tinyMCE_GZ||(a.tinyMCE_GZ={start:function(){function e(t){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(t))}e("langs/"+s+".js"),e("themes/"+n.theme+"/theme"+p+".js"),e("themes/"+n.theme+"/langs/"+s+".js"),t.each(n.plugins.split(","),function(t,n){n&&(e("plugins/"+n+"/plugin"+p+".js"),e("plugins/"+n+"/langs/"+s+".js"))})},end:function(){}}));var f=document.createElement("script");f.type="text/javascript",f.onload=f.onreadystatechange=function(e){e=e||event,("load"==e.type||/complete|loaded/.test(f.readyState))&&(tinymce.dom.Event.domLoaded=1,i=2,n.script_loaded&&n.script_loaded(),c(),t.each(r,function(t,e){e()}))},f.src=u,document.body.appendChild(f)}return l},t.extend(t.expr[":"],{tinymce:function(t){return!!(t.id&&"tinymce"in window&&tinymce.get(t.id))}})}(jQuery);
\ No newline at end of file
+jQuery.periodic=function(l,h){if(jQuery.isFunction(l)){h=l;l={}}var c=jQuery.extend({},jQuery.periodic.defaults,{ajax_complete:j,increment:g,reset:f,cancel:i},l);c.cur_period=c.period;c.tid=false;var e="";b();return c;function b(){i();c.tid=setTimeout(function(){h.call(c);g();if(c.tid){b()}},c.cur_period)}function j(n,m){if(m==="success"&&e!==n.responseText){e=n.responseText;f()}}function g(){c.cur_period*=c.decay;if(c.cur_period<c.period){f()}else{if(c.cur_period>c.max_period){c.cur_period=c.max_period;if(c.on_max!==undefined){c.on_max.call(c)}}}}function f(){c.cur_period=c.period;b()}function i(){clearTimeout(c.tid);c.tid=null}function k(){}function a(){}function d(){}};jQuery.periodic.defaults={period:4000,max_period:1800000,decay:1.5,on_max:undefined};!function(e){function t(){function t(e){"remove"===e&&this.each(function(e,t){var n=r(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=tinymce.get(t.id.replace(/_parent$/,""));n&&n.remove()})}function i(e){var n,i=this;if(null!=e)t.call(i),i.each(function(t,n){var i;(i=tinymce.get(n.id))&&i.setContent(e)});else if(i.length>0&&(n=tinymce.get(i[0].id)))return n.getContent()}function r(e){var t=null;return e&&e.id&&a.tinymce&&(t=tinymce.get(e.id)),t}function c(e){return!!(e&&e.length&&a.tinymce&&e.is(":tinymce"))}var u={};e.each(["text","html","val"],function(t,a){var o=u[a]=e.fn[a],s="text"===a;e.fn[a]=function(t){var a=this;if(!c(a))return o.apply(a,arguments);if(t!==n)return i.call(a.filter(":tinymce"),t),o.apply(a.not(":tinymce"),arguments),a;var u="",l=arguments;return(s?a:a.eq(0)).each(function(t,n){var i=r(n);u+=i?s?i.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):i.getContent({save:!0}):o.apply(e(n),l)}),u}}),e.each(["append","prepend"],function(t,i){var a=u[i]=e.fn[i],o="prepend"===i;e.fn[i]=function(e){var t=this;return c(t)?e!==n?(t.filter(":tinymce").each(function(t,n){var i=r(n);i&&i.setContent(o?e+i.getContent():i.getContent()+e)}),a.apply(t.not(":tinymce"),arguments),t):void 0:a.apply(t,arguments)}}),e.each(["remove","replaceWith","replaceAll","empty"],function(n,i){var r=u[i]=e.fn[i];e.fn[i]=function(){return t.call(this,i),r.apply(this,arguments)}}),u.attr=e.fn.attr,e.fn.attr=function(t,a){var o=this,s=arguments;if(!t||"value"!==t||!c(o))return a!==n?u.attr.apply(o,s):u.attr.apply(o,s);if(a!==n)return i.call(o.filter(":tinymce"),a),u.attr.apply(o.not(":tinymce"),s),o;var l=o[0],m=r(l);return m?m.getContent({save:!0}):u.attr.apply(e(l),s)}}var n,i,r=[],a=window;e.fn.tinymce=function(n){function c(){var i=[],r=0;t&&(t(),t=null),l.each(function(e,t){var a,c=t.id,u=n.oninit;c||(t.id=c=tinymce.DOM.uniqueId()),tinymce.get(c)||(a=new tinymce.Editor(c,n,tinymce.EditorManager),i.push(a),a.on("init",function(){var e,t=u;l.css("visibility",""),u&&++r==i.length&&("string"==typeof t&&(e=-1===t.indexOf(".")?null:tinymce.resolve(t.replace(/\.\w+$/,"")),t=tinymce.resolve(t)),t.apply(e||tinymce,i))}))}),e.each(i,function(e,t){t.render()})}var u,o,s,l=this,m="";if(!l.length)return l;if(!n)return tinymce.get(l[0].id);if(l.css("visibility","hidden"),a.tinymce||i||!(u=n.script_url))1===i?r.push(c):c();else{i=1,o=u.substring(0,u.lastIndexOf("/")),-1!=u.indexOf(".min")&&(m=".min"),a.tinymce=a.tinyMCEPreInit||{base:o,suffix:m},-1!=u.indexOf("gzip")&&(s=n.language||"en",u=u+(/\?/.test(u)?"&":"?")+"js=true&core=true&suffix="+escape(m)+"&themes="+escape(n.theme||"modern")+"&plugins="+escape(n.plugins||"")+"&languages="+(s||""),a.tinyMCE_GZ||(a.tinyMCE_GZ={start:function(){function t(e){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(e))}t("langs/"+s+".js"),t("themes/"+n.theme+"/theme"+m+".js"),t("themes/"+n.theme+"/langs/"+s+".js"),e.each(n.plugins.split(","),function(e,n){n&&(t("plugins/"+n+"/plugin"+m+".js"),t("plugins/"+n+"/langs/"+s+".js"))})},end:function(){}}));var p=document.createElement("script");p.type="text/javascript",p.onload=p.onreadystatechange=function(t){t=t||event,2===i||"load"!=t.type&&!/complete|loaded/.test(p.readyState)||(tinymce.dom.Event.domLoaded=1,i=2,n.script_loaded&&n.script_loaded(),c(),e.each(r,function(e,t){t()}))},p.src=u,document.body.appendChild(p)}return l},e.extend(e.expr[":"],{tinymce:function(e){return!!(e.id&&"tinymce"in window&&tinymce.get(e.id))}})}(jQuery);
\ No newline at end of file
diff --git a/src/main/webapp/static/themes/openolat/all/modules/_course.scss b/src/main/webapp/static/themes/openolat/all/modules/_course.scss
index 29cf4d6c8dabd68c4f1ec9cd3224444dbe8eab6f..56d3554f33510ed0af7072ddd41c5552104788f6 100644
--- a/src/main/webapp/static/themes/openolat/all/modules/_course.scss
+++ b/src/main/webapp/static/themes/openolat/all/modules/_course.scss
@@ -170,7 +170,9 @@ div.o_scorm div.o_scorm_content iframe { }
 .o_scorm_not_attempted { top: 6px; left: 6px; background-image: none; }
 
 /* Checklists */
-span.o_cl_duedate { color: red; }
+span.o_cl_duedate { color: green; }
+p.o_cl_duedate_passed span.o_cl_duedate { color: red; }
+
 
 
 	
\ No newline at end of file
diff --git a/src/main/webapp/static/themes/openolat/all/modules/_misc.scss b/src/main/webapp/static/themes/openolat/all/modules/_misc.scss
index 9839827d966fbf7b266540375c99db7f3e3c6b39..83f0c5169f9fc87865db628b16c96706215b142a 100644
--- a/src/main/webapp/static/themes/openolat/all/modules/_misc.scss
+++ b/src/main/webapp/static/themes/openolat/all/modules/_misc.scss
@@ -1,19 +1,14 @@
 /*  misc */
 
-/* charts */
+/* charts (crispEdges) */
 
 .d3chart {
 
 	.bar { shape-rendering: crispEdges; }
 
 	.bar_default_light { fill: $basecolor_ultra_light; }
-	.bar_default_light:hover { fill: $basecolor_light; }
-
 	.bar_default { fill: $basecolor_light; }
-	.bar_default:hover { fill: $basecolor; }
-	
 	.bar_default_dark { fill: $basecolor; }
-	.bar_default_dark:hover { fill: black; }
 
 	.axis { font: 12px sans-serif; }
 
@@ -22,8 +17,6 @@
 		stroke: #000;
 		shape-rendering: crispEdges;
 	}
-
-	.x.axis path { display: none; }
 }
 
 /* BUSINESS CARD &  Member site, members search  */
@@ -355,6 +348,7 @@ div.o_notifications_news_subscription {
 	margin: 1.5em 0 2em 0;
 	h4 { font-size: 110%; }
 	h4.o_returnbox_icon { background-image: url(../openolat/images/box_return.png) !important; }
+	h4.o_dropbox_icon { background-image: url(../openolat/images/box_drop.png) !important; }
 }
 div.o_notifications_news_context { color: #7D7D7D; font-size: 90%; }
 div.o_notifications_news_content {
diff --git a/src/main/webapp/static/themes/openolat/all/modules/_qti.scss b/src/main/webapp/static/themes/openolat/all/modules/_qti.scss
index 0c97ed0d341fd73d90f93297c3bd806b68b364e4..e9b2bf4fa2102a7d6cfb3fac80ea091182944b55 100644
--- a/src/main/webapp/static/themes/openolat/all/modules/_qti.scss
+++ b/src/main/webapp/static/themes/openolat/all/modules/_qti.scss
@@ -4,16 +4,11 @@ a.o_print_icon span { display:none; }
 
 .d3chart {
 	.bar_green { fill: #9dd53a; }
-	.bar_green:hover { fill: #7cbc0a; }
 	.bar_red { fill: #f85032; }
-	.bar_red:hover { fill: #e73827; }
 	.bar_grey { fill: lightgrey; }
-	.bar_grey:hover { fill: grey; }
 }
 
 div.o_qti_statistics {
-	padding-top: 20px; padding-left: 40px; padding-right: 5%;
-
 	ul { list-style-type: none; padding:0; margin:0; }
 	li { padding-left: 22px; }
 	
@@ -21,22 +16,27 @@ div.o_qti_statistics {
 	li.o_qti_statistics-ncorrect { background: url(../img/decorator_error.png) no-repeat 5px 4px; }
 	li.o_qti_statistics-correct { background: url(../img/decorator_ok.png) no-repeat 5px 2px; }
 
-
 	h4 {
-		padding-left:20px;
-		&.qti-qtype1 {background: url(../img/scItem.png) no-repeat;}
+		&.qti-qtype1 {padding-left:20px; background: url(../img/scItem.png) no-repeat;}
 
-		&.qti-qtype2 {background: url(../img/mcItem.png) no-repeat;}
+		&.qti-qtype2 {padding-left:20px; background: url(../img/mcItem.png) no-repeat;}
 	
-		&.qti-qtype3 {background: url(../img/fibItem.png) no-repeat;}
+		&.qti-qtype3 {padding-left:20px; background: url(../img/fibItem.png) no-repeat;}
 	
-		&.qti-qtype4 {background: url(../img/essayItem.png) no-repeat;}
+		&.qti-qtype4 {padding-left:20px; background: url(../img/essayItem.png) no-repeat;}
+	
+		&.qti-qtype5 {padding-left:20px; background: url(../img/kprimItem.png) no-repeat;}
+	}
 	
-		&.qti-qtype5 { background: url(../img/kprimItem.png) no-repeat;}
+	a.b_content_download {
+		display: inline;
+	}
+	
+	a.o_print_icon span {
+		display: inline;
 	}
 }
 
-
 /* QTI legacy styles */
 	#o_qti_run {}
 	#o_qti_run div.b_button_group { text-align: left;}
diff --git a/src/test/java/org/olat/ims/qti/QTIResultManagerTest.java b/src/test/java/org/olat/ims/qti/QTIResultManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c83cb2c1d380b3d94353e0c3da6e7b3c2e4677c
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti/QTIResultManagerTest.java
@@ -0,0 +1,381 @@
+/**
+ * <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.qti;
+
+
+import static org.olat.modules.iq.IQTestHelper.createRepository;
+import static org.olat.modules.iq.IQTestHelper.createResult;
+import static org.olat.modules.iq.IQTestHelper.createSet;
+import static org.olat.modules.iq.IQTestHelper.modDate;
+
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 18.03.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QTIResultManagerTest extends OlatTestCase {
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private QTIResultManager qtiResultManager;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+	@Autowired
+	private BaseSecurity securityManager;
+	
+	
+	@Test
+	public void hasResultSets() {
+		RepositoryEntry re = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-1");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-2");
+		dbInstance.commit();
+		
+		long assessmentId = 838l;
+		String resSubPath = "qtiResult34";
+		
+		//3 try for id1 and id2
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id1, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResultSet set1_2 = createSet(3.0f, assessmentId, id1, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResultSet set2_1 = createSet(5.0f, assessmentId, id2, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		dbInstance.commit();
+		Assert.assertNotNull(set1_1);
+		Assert.assertNotNull(set1_2);
+		Assert.assertNotNull(set2_1);
+		
+		boolean hasSet = qtiResultManager.hasResultSets(re.getOlatResource().getResourceableId(), resSubPath, re.getKey());
+		Assert.assertTrue(hasSet);
+	}
+	
+	@Test
+	public void hasResultSets_negativeTest() {
+		RepositoryEntry re = createRepository();
+		String resSubPath = "qtiResult35";
+		
+		boolean hasSet = qtiResultManager.hasResultSets(re.getOlatResource().getResourceableId(), resSubPath, re.getKey());
+		Assert.assertFalse(hasSet);
+	}
+	
+	@Test
+	public void getResultSets_withIdentity() {
+		RepositoryEntry re = createRepository();
+		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-3");
+		dbInstance.commit();
+		
+		long assessmentId = 839l;
+		String resSubPath = "qtiResult36";
+		
+		//3 try for id1
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResultSet set1_3 = createSet(3.0f, assessmentId, id, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResultSet set1_2 = createSet(5.0f, assessmentId, id, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		dbInstance.commit();
+		
+		List<QTIResultSet> set = qtiResultManager.getResultSets(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), id);
+		Assert.assertNotNull(set);
+		Assert.assertEquals(3, set.size());
+		Assert.assertTrue(set.contains(set1_1));
+		Assert.assertTrue(set.contains(set1_2));
+		Assert.assertTrue(set.contains(set1_3));
+	}
+	
+	@Test
+	public void getResultSets_withoutIdentity() {
+		RepositoryEntry re = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-4");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-5");
+		dbInstance.commit();
+		
+		long assessmentId = 840l;
+		String resSubPath = "qtiResult36";
+		
+		//3 try for id1
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id1, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResultSet set1_2 = createSet(3.0f, assessmentId, id1, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResultSet set2_1 = createSet(5.0f, assessmentId, id2, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		dbInstance.commit();
+		
+		List<QTIResultSet> set = qtiResultManager.getResultSets(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), null);
+		Assert.assertNotNull(set);
+		Assert.assertEquals(3, set.size());
+		Assert.assertTrue(set.contains(set1_1));
+		Assert.assertTrue(set.contains(set1_2));
+		Assert.assertTrue(set.contains(set2_1));
+	}
+	
+	@Test
+	public void selectResults() {
+		RepositoryEntry re = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-6");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-7");
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-8");
+		dbInstance.commit();
+
+		long assessmentId = 841l;
+		String resSubPath = "qtiResult37";
+		String itemIdent = "NES:PS4:849235789";
+		
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id1, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResult result1_1 = createResult(itemIdent, "Hello world", set1_1);
+		QTIResultSet set2_1 = createSet(3.0f, assessmentId, id2, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResult result2_1 = createResult(itemIdent, "Bonjour madame", set2_1);
+		QTIResultSet set3_1 = createSet(5.0f, assessmentId, id3, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		QTIResult result3_1 = createResult(itemIdent, "Tschuss", set3_1);
+		dbInstance.commit();
+		
+		//order by last name
+		List<QTIResult> resultsType1 =  qtiResultManager.selectResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), null, 1);
+		Assert.assertNotNull(resultsType1);
+		Assert.assertEquals(3, resultsType1.size());
+		Assert.assertTrue(resultsType1.contains(result1_1));
+		Assert.assertTrue(resultsType1.contains(result2_1));
+		Assert.assertTrue(resultsType1.contains(result3_1));
+
+		//order by last name
+		List<QTIResult> resultsType2 =  qtiResultManager.selectResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), null,  2);
+		Assert.assertNotNull(resultsType2);
+		Assert.assertEquals(3, resultsType2.size());
+		Assert.assertTrue(resultsType2.contains(result1_1));
+		Assert.assertTrue(resultsType2.contains(result2_1));
+		Assert.assertTrue(resultsType2.contains(result3_1));
+
+		//order by creation date
+		List<QTIResult> resultsType3 =  qtiResultManager.selectResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), null,  3);
+		Assert.assertNotNull(resultsType3);
+		Assert.assertEquals(3, resultsType3.size());
+		Assert.assertTrue(resultsType3.contains(result1_1));
+		Assert.assertTrue(resultsType3.contains(result2_1));
+		Assert.assertTrue(resultsType3.contains(result3_1));	
+	}
+	
+	@Test
+	public void selectResults_limitToSecurityGroup() {
+		RepositoryEntry re = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-16");
+		repositoryEntryRelationDao.addRole(id1, re, GroupRoles.participant.name());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-17");
+		dbInstance.commit();
+
+		long assessmentId = 841l;
+		String resSubPath = "qtiResult37";
+		String itemIdent1 = "NES:PS4:849235797";
+		String itemIdent2 = "NES:PS4:849235798";
+		
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id1, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResult result1_1 = createResult(itemIdent1, "Hello world", set1_1);
+		QTIResultSet set2_1 = createSet(3.0f, assessmentId, id2, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResult result2_1 = createResult(itemIdent1, "Bonjour madame", set2_1);
+		QTIResultSet set1_1b = createSet(5.0f, assessmentId, id1, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		QTIResult result1_1b = createResult(itemIdent1, "Tschuss", set1_1b);
+		QTIResult result1_1c = createResult(itemIdent2, "Tschuss", set1_1b);
+		dbInstance.commitAndCloseSession();
+		
+		List<Group> secGroups = Collections.singletonList(repositoryEntryRelationDao.getDefaultGroup(re));
+		List<QTIResult> resultsType1 =  qtiResultManager.selectResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey(), secGroups, 1);
+		Assert.assertNotNull(resultsType1);
+		Assert.assertEquals(3, resultsType1.size());
+		Assert.assertTrue(resultsType1.contains(result1_1));
+		Assert.assertTrue(resultsType1.contains(result1_1b));
+		Assert.assertTrue(resultsType1.contains(result1_1c));
+		//not a participant in the security group
+		Assert.assertFalse(resultsType1.contains(result2_1));
+	}
+	
+	@Test
+	public void hasResults() {
+		RepositoryEntry re = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-9");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-10");
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-11");
+		dbInstance.commit();
+
+		long assessmentId = 842l;
+		String resSubPath = "qtiResult38";
+		String itemIdent = "NES:PS4:849235790";
+		
+		QTIResultSet set1_1 = createSet(1.0f, assessmentId, id1, re, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResult result1_1 = createResult(itemIdent, "Hello world", set1_1);
+		QTIResultSet set2_1 = createSet(3.0f, assessmentId, id2, re, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResult result2_1 = createResult(itemIdent, "Bonjour madame", set2_1);
+		QTIResultSet set3_1 = createSet(5.0f, assessmentId, id3, re, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		QTIResult result3_1 = createResult(itemIdent, "Tschuss", set3_1);
+		dbInstance.commit();
+		Assert.assertNotNull(result1_1);
+		Assert.assertNotNull(result2_1);
+		Assert.assertNotNull(result3_1);
+		
+		int numOfResults =  qtiResultManager.countResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey());
+		Assert.assertEquals(3, numOfResults);
+	}
+	
+	@Test
+	public void hasResults_negativeTest() {
+		RepositoryEntry re = createRepository();
+		dbInstance.commit();
+		
+		String resSubPath = "qtiResult39";
+
+		int numOfResults =  qtiResultManager.countResults(re.getOlatResource().getResourceableId(), resSubPath, re.getKey());
+		Assert.assertEquals(0, numOfResults);
+	}
+	
+	@Test
+	public void findQtiResultSets() {
+		RepositoryEntry re1 = createRepository();
+		RepositoryEntry re2 = createRepository();
+		RepositoryEntry re3 = createRepository();
+		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-12");
+		dbInstance.commit();
+
+		QTIResultSet set1_1 = createSet(1.0f, 842l, id, re1, "qtiResult38", modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResultSet set1_2 = createSet(3.0f, 843l, id, re2, "qtiResult39", modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResultSet set1_3 = createSet(5.0f, 844l, id, re3, "qtiResult40", modDate(3, 10, 35), modDate(3, 10, 55));
+		dbInstance.commit();
+		
+		List<QTIResultSet> sets = qtiResultManager.findQtiResultSets(id);
+		Assert.assertNotNull(sets);
+		Assert.assertEquals(3, sets.size());
+		Assert.assertTrue(sets.contains(set1_1));
+		Assert.assertTrue(sets.contains(set1_2));
+		Assert.assertTrue(sets.contains(set1_3));
+	}
+	
+	@Test
+	public void deleteResultSet() {
+		RepositoryEntry re1 = createRepository();
+		RepositoryEntry re2 = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-17");
+		
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-18");
+		dbInstance.commit();
+		
+		String itemIdent1 = "NES:PS4:849235795";
+		String itemIdent2 = "NES:PS4:849235796";
+		String resSubPath = "qtiResult43";
+
+		//the set to delete
+		QTIResultSet set1_1 = createSet(1.0f, 842l, id1, re1, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResult result1_1a = createResult(itemIdent1, "Hello world", set1_1);
+		QTIResult result1_1b = createResult(itemIdent2, "Hello world", set1_1);
+		//two sets which stay on the database and use to check that the queries didn't delete too much rows
+		QTIResultSet set1_2 = createSet(3.0f, 843l, id1, re2, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResult result1_2 = createResult(itemIdent1, "Hello world", set1_2);
+		QTIResultSet set2_1 = createSet(5.0f, 844l, id2, re1, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		QTIResult result2_1 = createResult(itemIdent1, "Hello world", set2_1);
+		dbInstance.commitAndCloseSession();
+		
+		qtiResultManager.deleteResultSet(set1_1);
+		dbInstance.commit();
+		
+		//check sets on database
+		List<QTIResultSet> sets = qtiResultManager.getResultSets(re1.getOlatResource().getResourceableId(), resSubPath, re1.getKey(), null);
+		Assert.assertNotNull(sets);
+		Assert.assertEquals(1, sets.size());
+		Assert.assertTrue(sets.contains(set2_1));
+		
+		List<QTIResultSet> setRe2s = qtiResultManager.getResultSets(re2.getOlatResource().getResourceableId(), resSubPath, re2.getKey(), null);
+		Assert.assertNotNull(setRe2s);
+		Assert.assertEquals(1, setRe2s.size());
+		Assert.assertTrue(setRe2s.contains(set1_2));
+		
+		//check results
+		List<QTIResult> results =  qtiResultManager.selectResults(re1.getOlatResource().getResourceableId(), resSubPath, re1.getKey(), null,  3);
+		Assert.assertNotNull(results);
+		Assert.assertEquals(1, results.size());
+		Assert.assertTrue(results.contains(result2_1));
+		Assert.assertFalse(results.contains(result1_1a));
+		Assert.assertFalse(results.contains(result1_1b));
+		
+		List<QTIResult> resultsRe2 =  qtiResultManager.selectResults(re2.getOlatResource().getResourceableId(), resSubPath, re2.getKey(), null,  3);
+		Assert.assertNotNull(resultsRe2);
+		Assert.assertEquals(1, resultsRe2.size());
+		Assert.assertTrue(resultsRe2.contains(result1_2));
+	}
+	
+	@Test
+	public void deleteAllResults() {
+		RepositoryEntry re1 = createRepository();
+		RepositoryEntry re2 = createRepository();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-15");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("qti-result-mgr-16");
+		dbInstance.commit();
+		
+		String itemIdent1 = "NES:PS4:849235793";
+		String itemIdent2 = "NES:PS4:849235794";
+		String resSubPath = "qtiResult42";
+
+		//the set to delete
+		QTIResultSet set1_1 = createSet(1.0f, 842l, id1, re1, resSubPath, modDate(3, 8, 5), modDate(3, 8, 20));
+		QTIResult result1_1a = createResult(itemIdent1, "Hello world", set1_1);
+		QTIResult result1_1b = createResult(itemIdent2, "Hello world", set1_1);
+		//two sets which stay on the database and use to check that the queries didn't delete too much rows
+		QTIResultSet set1_2 = createSet(3.0f, 843l, id1, re2, resSubPath, modDate(3, 14, 8), modDate(3, 14, 32));
+		QTIResult result1_2 = createResult(itemIdent1, "Hello world", set1_2);
+		QTIResultSet set2_1 = createSet(5.0f, 844l, id2, re1, resSubPath, modDate(3, 10, 35), modDate(3, 10, 55));
+		QTIResult result2_1 = createResult(itemIdent1, "Hello world", set2_1);
+		dbInstance.commitAndCloseSession();
+		
+		//delete all results of re1
+		qtiResultManager.deleteAllResults(re1.getOlatResource().getResourceableId(), resSubPath, re1.getKey());
+		dbInstance.commit();
+		
+		//check sets on database
+		List<QTIResultSet> sets = qtiResultManager.getResultSets(re1.getOlatResource().getResourceableId(), resSubPath, re1.getKey(), null);
+		Assert.assertNotNull(sets);
+		Assert.assertEquals(0, sets.size());
+		Assert.assertFalse(sets.contains(result1_1a));
+		Assert.assertFalse(sets.contains(result1_1b));
+		Assert.assertFalse(sets.contains(result2_1));
+		
+		List<QTIResultSet> setRe2s = qtiResultManager.getResultSets(re2.getOlatResource().getResourceableId(), resSubPath, re2.getKey(), null);
+		Assert.assertNotNull(setRe2s);
+		Assert.assertEquals(1, setRe2s.size());
+		Assert.assertTrue(setRe2s.contains(set1_2));
+		
+		//check results
+		List<QTIResult> results =  qtiResultManager.selectResults(re1.getOlatResource().getResourceableId(), resSubPath, re1.getKey(), null, 3);
+		Assert.assertNotNull(results);
+		Assert.assertEquals(0, results.size());
+		
+		List<QTIResult> resultsRe2 =  qtiResultManager.selectResults(re2.getOlatResource().getResourceableId(), resSubPath, re2.getKey(), null, 3);
+		Assert.assertNotNull(resultsRe2);
+		Assert.assertEquals(1, resultsRe2.size());
+		Assert.assertTrue(resultsRe2.contains(result1_2));
+	}
+}
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index e4e2c6afddd41a37e3cea3d9db8b4b953ba01fe7..f4467b3f39909f0817624c3046c93658094e42ef 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -134,6 +134,7 @@ import org.junit.runners.Suite;
 	org.olat.core.commons.persistence.DBTest.class,
 	org.olat.modules.ims.cp.CPManagerTest.class,
 	org.olat.modules.ims.qti.fileresource.FileResourceValidatorTest.class,
+	org.olat.ims.qti.QTIResultManagerTest.class,
 	org.olat.ims.qti.qpool.QTIImportProcessorTest.class,
 	org.olat.ims.qti.qpool.QTIExportProcessorTest.class,
 	org.olat.ims.qti.statistics.manager.QTIStatisticsManagerLargeTest.class,