diff --git a/src/main/java/de/bps/course/assessment/AssessmentMainController.java b/src/main/java/de/bps/course/assessment/AssessmentMainController.java
index c919dcf6f2690f664d7ba270a2591a1e610039cd..0b90f817b78db3baffd6145642ecc4090e7452d5 100644
--- a/src/main/java/de/bps/course/assessment/AssessmentMainController.java
+++ b/src/main/java/de/bps/course/assessment/AssessmentMainController.java
@@ -27,6 +27,8 @@ package de.bps.course.assessment;
 
 import java.rmi.RemoteException;
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -79,6 +81,7 @@ import org.olat.core.logging.OLATSecurityException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.ActionType;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.resource.OresHelper;
@@ -95,7 +98,6 @@ import org.olat.course.assessment.GroupAndContextTableModel;
 import org.olat.course.assessment.IAssessmentCallback;
 import org.olat.course.assessment.IdentityAssessmentEditController;
 import org.olat.course.assessment.IndentedNodeRenderer;
-import de.bps.course.assessment.NodeTableDataModelOnyx;
 import org.olat.course.condition.Condition;
 import org.olat.course.condition.interpreter.ConditionExpression;
 import org.olat.course.condition.interpreter.OnlyGroupConditionInterpreter;
@@ -104,10 +106,12 @@ import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.STCourseNode;
+import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
 import org.olat.group.ui.context.BGContextTableModel;
+import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
@@ -169,7 +173,8 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 
 	// Hash map to keep references to already created user course environments
 	// Serves as a local cache to reduce database access - not shared by multiple threads
-	Map<Long, UserCourseEnvironment> localUserCourseEnvironmentCache; // package visibility for avoiding synthetic accessor method
+	final Map<Long, UserCourseEnvironment> localUserCourseEnvironmentCache; // package visibility for avoiding synthetic accessor method
+	final Map<Long, Date> initialLaunchDates;
 	// List of groups to which the user has access rights in this course
 	private List<BusinessGroup> coachedGroups;
 
@@ -224,6 +229,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 		this.ores = ores;
 		this.callback = assessmentCallback;
 		this.localUserCourseEnvironmentCache = new HashMap<Long, UserCourseEnvironment>();
+		this.initialLaunchDates = new HashMap<Long, Date>();
 
     //use the PropertyHandlerTranslator	as tableCtr translator
 		propertyHandlerTranslator = UserManager.getInstance().getPropertyHandlerTranslator(getTranslator());
@@ -330,7 +336,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			}
 
 			// select user
-			this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, this.localUserCourseEnvironmentCache, course, null);
+			assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 
 			UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();
 			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
@@ -510,8 +516,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 						// in user MODE_USERFOCUS, a simple identity table is used, no wrapped identites
 						UserTableDataModel userListModel = (UserTableDataModel) userListCtr.getTableDataModel();
 						Identity assessedIdentity = userListModel.getIdentityAt(rowid);
-						this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentity,
-								this.localUserCourseEnvironmentCache, course, null);
+						assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentity,
+								localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 					} else {
 						// all other cases where user can be choosen the assessed identity wrapper is used
 						AssessedIdentitiesTableDataModel userListModel = (AssessedIdentitiesTableDataModel) userListCtr.getTableDataModel();
@@ -578,8 +584,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 					if (aiwList.contains(this.assessedIdentityWrapper)) {
 						ICourse course = CourseFactory.loadCourse(ores);
 						aiwList.remove(this.assessedIdentityWrapper);
-						this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(this.assessedIdentityWrapper.getIdentity(),
-						this.localUserCourseEnvironmentCache, course, currentCourseNode);
+						assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentityWrapper.getIdentity(),
+								localUserCourseEnvironmentCache, initialLaunchDates, course, currentCourseNode);
 						aiwList.add(this.assessedIdentityWrapper);
 						userListCtr.modelChanged();
 					}
@@ -676,7 +682,14 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 					// 2.2) update wrapper object
 					if (wrappedIdFromModel != null) {
 						wrappers.remove(wrappedIdFromModel);
-						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), currentCourseNode);
+						
+						Date initialLaunchDate;
+						if(initialLaunchDates.containsKey(identityKeyFromEvent)) {
+							initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent);
+						} else {
+							initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment());
+						}
+						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode);
 						wrappers.add(wrappedIdFromModel);
 						userListCtr.modelChanged();
 					}
@@ -822,7 +835,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			Identity identity = (Identity) identities.get(i);
 			// if course node is null the wrapper will only contain the identity and no score information
 			AssessedIdentityWrapper aiw = AssessmentHelper.wrapIdentity(identity,
-			this.localUserCourseEnvironmentCache, course, courseNode);
+					localUserCourseEnvironmentCache, initialLaunchDates, course, courseNode);
 			wrappedIdentities.add(aiw);
 		}
 		// Add the wrapped identities to the table data model
@@ -1225,9 +1238,20 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			// 2) preload controller local user environment cache
 			start = System.currentTimeMillis();
 			List<Identity> identities = getAllIdentitisFromGroupmanagement();
-			for (Iterator<Identity> iter = identities.iterator(); iter.hasNext();) {
-				Identity identity = iter.next();
-				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, course, null);
+			
+			CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode();
+			CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager();
+			List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
+			Calendar cal = Calendar.getInstance();
+			for(Property property:firstTime) {
+				if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) {
+					cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
+					initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime());
+				}
+			}
+
+			for (Identity identity : identities) {
+				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 				if (Thread.interrupted()) break;
 			}
 			if (logDebug) {
diff --git a/src/main/java/org/olat/core/util/ExportUtil.java b/src/main/java/org/olat/core/util/ExportUtil.java
index a1b356b62aad6d766a2abfed0574e8652b803c66..6583716688e6767edbfe81d72424e06bc9306063 100644
--- a/src/main/java/org/olat/core/util/ExportUtil.java
+++ b/src/main/java/org/olat/core/util/ExportUtil.java
@@ -52,10 +52,11 @@ public class ExportUtil {
 	 * @param exportDirectory
 	 */
 
-	public static void writeContentToFile(String fileName, String content, File exportDirectory, String enc) {
+	public static File writeContentToFile(String fileName, String content, File exportDirectory, String enc) {
 		File f = new File(exportDirectory, fileName);
 		if (f.exists()) { throw new AssertException("File " + fileName + " already exists!"); }
 		FileUtils.save(f, content, enc);
+		return f;
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/archiver/ScoreAccountingArchiveController.java b/src/main/java/org/olat/course/archiver/ScoreAccountingArchiveController.java
index 6d7b8448709f184c55a7359b094fe690063e049b..d0f98a0022a23985fcc678f54a4402b376ca5ed6 100644
--- a/src/main/java/org/olat/course/archiver/ScoreAccountingArchiveController.java
+++ b/src/main/java/org/olat/course/archiver/ScoreAccountingArchiveController.java
@@ -37,8 +37,11 @@ import org.olat.core.gui.components.velocity.VelocityContainer;
 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.media.FileMediaResource;
+import org.olat.core.gui.media.MediaResource;
 import org.olat.core.gui.translator.PackageTranslator;
 import org.olat.core.gui.translator.Translator;
+import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.util.ExportUtil;
 import org.olat.core.util.Util;
@@ -56,14 +59,12 @@ public class ScoreAccountingArchiveController extends DefaultController {
 	private static final String PACKAGE = Util.getPackageName(ScoreAccountingArchiveController.class);
 	private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(PACKAGE);
 
-	private static final String CMD_START = "cmd.start";
-
 	private OLATResourceable ores;
 	private Panel myPanel;
 	private VelocityContainer myContent;
 	private VelocityContainer vcFeedback;
 	private Translator t;
-	private Link startButton;
+	private Link startButton, downloadButton;
 
 	/**
 	 * Constructor for the score accounting archive controller
@@ -93,7 +94,7 @@ public class ScoreAccountingArchiveController extends DefaultController {
 	public void event(UserRequest ureq, Component source, Event event) {
 		if (source == startButton) {
 			ICourse course = CourseFactory.loadCourse(ores);
-			List users = ScoreAccountingHelper.loadUsers(course.getCourseEnvironment());
+			List<Identity> users = ScoreAccountingHelper.loadUsers(course.getCourseEnvironment());
 			List nodes = ScoreAccountingHelper.loadAssessableNodes(course.getCourseEnvironment());
 			
 			String result = ScoreAccountingHelper.createCourseResultsOverviewTable(users, nodes, course, ureq.getLocale());
@@ -107,11 +108,19 @@ public class ScoreAccountingArchiveController extends DefaultController {
 			UserManager um = UserManager.getInstance();
 			String charset = um.getUserCharset(ureq.getIdentity());
 			
-			ExportUtil.writeContentToFile(fileName, result, exportDirectory, charset);
+			File downloadFile = ExportUtil.writeContentToFile(fileName, result, exportDirectory, charset);
 
 			vcFeedback = new VelocityContainer("feedback", VELOCITY_ROOT + "/feedback.html", t, this);
 			vcFeedback.contextPut("body", vcFeedback.getTranslator().translate("course.res.feedback", new String[] { fileName }));
+			downloadButton = LinkFactory.createButtonSmall("cmd.download", vcFeedback, this);
+			downloadButton.setUserObject(downloadFile);
 			myPanel.setContent(vcFeedback);
+		} else if(source == downloadButton) {
+			File file = (File)downloadButton.getUserObject();
+			if(file != null) {
+				MediaResource resource = new FileMediaResource(file);
+				ureq.getDispatchResult().setResultingMediaResource(resource);
+			}
 		}
 	}
 
diff --git a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
index 0cff13895f6bb8bcf61d6b026ca41e6c7aa7fa09..7f8119b944d2ac1520dfef6bab707e1e62b86c2f 100644
--- a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
+++ b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
@@ -26,18 +26,21 @@
 package org.olat.course.archiver;
 
 import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.SecurityGroup;
-import org.olat.core.gui.translator.PackageTranslator;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
+import org.olat.core.util.Formatter;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.resource.OresHelper;
@@ -49,11 +52,13 @@ import org.olat.course.groupsandrights.CourseGroupManager;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.STCourseNode;
+import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
+import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
@@ -64,8 +69,7 @@ import org.olat.user.propertyhandlers.UserPropertyHandler;
  * Comment: Provides functionality to get a course results overview.
  */
 public class ScoreAccountingHelper {
-    private static final String PACKAGE = Util.getPackageName(ScoreAccountingArchiveController.class);
-    
+ 
 	/**
 	 * The results from assessable nodes are written to one row per user into an excel-sheet. An
      * assessable node will only appear if it is producing at least one of the
@@ -77,9 +81,9 @@ public class ScoreAccountingHelper {
 	 * @param locale
 	 * @return String
 	 */
-	public static String createCourseResultsOverviewTable(List identities, List myNodes, ICourse course, Locale locale) {
-	    Translator t = new PackageTranslator(PACKAGE, locale);
-	    StringBuilder tableHeader1 = new StringBuilder();
+	public static String createCourseResultsOverviewTable(List<Identity> identities, List<AssessableCourseNode> myNodes, ICourse course, Locale locale) {
+	  Translator t = Util.createPackageTranslator(ScoreAccountingArchiveController.class, locale);
+	  StringBuilder tableHeader1 = new StringBuilder();
 		StringBuilder tableHeader2 = new StringBuilder();
 		StringBuilder tableContent = new StringBuilder();
 		StringBuilder table = new StringBuilder();
@@ -92,6 +96,8 @@ public class ScoreAccountingHelper {
 		String co = t.translate("column.header.comment");
 		String cco = t.translate("column.header.coachcomment");
 		String at = t.translate("column.header.attempts");
+		String il = t.translate("column.header.initialLaunchDate");
+		String slm = t.translate("column.header.scoreLastModified");
 		String na = t.translate("column.field.notavailable");
 		String mi = t.translate("column.field.missing");
 		String yes = t.translate("column.field.yes");
@@ -108,6 +114,10 @@ public class ScoreAccountingHelper {
 		// get user property handlers for this export, translate using the fallback
 		// translator configured in the property handler
 		
+			//Initial launch date
+		tableHeader1.append(il).append("\t");
+		tableHeader2.append("\t");
+		
 		List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(
 				ScoreAccountingHelper.class.getCanonicalName(), true);
 		t = UserManager.getInstance().getPropertyHandlerTranslator(t);
@@ -115,22 +125,44 @@ public class ScoreAccountingHelper {
 			tableHeader1.append(t.translate(propertyHandler.i18nColumnDescriptorLabelKey()));
 			tableHeader1.append("\t");			
 			tableHeader2.append("\t");
-		}				
+		}
 
 		// preload user properties cache
-		course.getCourseEnvironment().getAssessmentManager().preloadCache();
+		CourseEnvironment courseEnvironment = course.getCourseEnvironment();
+		AssessmentManager assessmentManager = courseEnvironment.getAssessmentManager();
+		assessmentManager.preloadCache();
 		
 		boolean firstIteration = true;
 		int rowNumber = 1;
-		Iterator iterIdentities = identities.iterator();
-		while (iterIdentities.hasNext()) {
-			Identity identity = (Identity) iterIdentities.next();
+
+		CourseNode node = courseEnvironment.getRunStructure().getRootNode();
+		CoursePropertyManager pm = courseEnvironment.getCoursePropertyManager();
+		List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
+		Map<Identity,Date> firstTimes = new HashMap<Identity,Date>((identities.size() * 2) + 1);
+		Calendar cal = Calendar.getInstance();
+		for(Property property:firstTime) {
+			if (StringHelper.containsNonWhitespace(property.getStringValue())) {
+				cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
+				firstTimes.put(property.getIdentity(), cal.getTime());
+			}
+		}
+		
+		Formatter formatter = Formatter.getInstance(locale);
+
+		for (Identity identity:identities) {
 			String uname = identity.getName();
 
 			tableContent.append(rowNumber);
 			tableContent.append("\t");
 			tableContent.append(uname);
 			tableContent.append("\t");
+
+			String initialLaunchDate = "";
+			if(firstTimes.containsKey(identity)) {
+				initialLaunchDate = formatter.formatDateAndTime(firstTimes.get(identity));
+			}
+			tableContent.append(initialLaunchDate).append("\t");
+
 			// add dynamic user properties
 			for (UserPropertyHandler propertyHandler : userPropertyHandlers) {
 				String value = propertyHandler.getUserProperty(identity.getUser(), t.getLocale());
@@ -145,9 +177,7 @@ public class ScoreAccountingHelper {
 			uce.getScoreAccounting().evaluateAll();
 			AssessmentManager am = course.getCourseEnvironment().getAssessmentManager();
 
-			Iterator iterNodes = myNodes.iterator();
-			while (iterNodes.hasNext()) {
-				AssessableCourseNode acnode = (AssessableCourseNode) iterNodes.next();
+			for (AssessableCourseNode acnode:myNodes) {
 				boolean scoreOk = acnode.hasScoreConfigured();
 				boolean passedOk = acnode.hasPassedConfigured();
 				boolean attemptsOk = acnode.hasAttemptsConfigured();
@@ -217,6 +247,20 @@ public class ScoreAccountingHelper {
 						tableContent.append("\t");
 					}
 
+					if (firstIteration) {
+						//last Modified
+						tableHeader2.append(slm);
+						tableHeader2.append("\t");
+					}
+
+					String scoreLastModified = "";
+					Date lastModified = am.getScoreLastModifiedDate(acnode, identity);
+					if(lastModified != null) {
+						scoreLastModified = formatter.formatDateAndTime(lastModified);
+					}
+					tableContent.append(scoreLastModified);
+					tableContent.append("\t");
+
 					if (commentOk) {
 					    // Comments for user
 						String comment = am.getNodeComment(acnode, identity);
@@ -282,9 +326,7 @@ public class ScoreAccountingHelper {
 		//fxdiff VCRP-4: assessment overview with max score
 		StringBuilder tableFooter = new StringBuilder();
 		tableFooter.append("\t\n").append("\t\n").append(t.translate("legend")).append("\t\n").append("\t\n");
-		Iterator iterNodes = myNodes.iterator();
-		while (iterNodes.hasNext()) {
-			AssessableCourseNode acnode = (AssessableCourseNode) iterNodes.next();
+		for (AssessableCourseNode acnode:myNodes) {
 			if (!acnode.hasScoreConfigured()) {
 				// only show min/max/cut legend when score configured
 				continue;
@@ -371,11 +413,10 @@ public class ScoreAccountingHelper {
 	 * @param courseEnv
 	 * @return The list of assessable nodes from this course
 	 */
-	public static List loadAssessableNodes(CourseEnvironment courseEnv) {
+	public static List<AssessableCourseNode> loadAssessableNodes(CourseEnvironment courseEnv) {
 		CourseNode rootNode = courseEnv.getRunStructure().getRootNode();
-		List nodeList = new ArrayList();
+		List<AssessableCourseNode> nodeList = new ArrayList<AssessableCourseNode>();
 		collectAssessableCourseNodes(rootNode, nodeList);
-
 		return nodeList;
 	}
 
@@ -385,9 +426,9 @@ public class ScoreAccountingHelper {
 	 * @param node
 	 * @param nodeList
 	 */
-	private static void collectAssessableCourseNodes(CourseNode node, List nodeList) {
+	private static void collectAssessableCourseNodes(CourseNode node, List<AssessableCourseNode> nodeList) {
 		if (node instanceof AssessableCourseNode) {
-			nodeList.add(node);
+			nodeList.add((AssessableCourseNode)node);
 		}
 		int count = node.getChildCount();
 		for (int i = 0; i < count; i++) {
diff --git a/src/main/java/org/olat/course/archiver/_content/feedback.html b/src/main/java/org/olat/course/archiver/_content/feedback.html
index 343baeadd3dddaea308cfe8f5389bdab95b3d0ca..b18e2be1519c192fffe332a3a3335536730911ad 100644
--- a/src/main/java/org/olat/course/archiver/_content/feedback.html
+++ b/src/main/java/org/olat/course/archiver/_content/feedback.html
@@ -2,5 +2,6 @@
 <p>
 	$body
 </p>
+<p>$r.render("cmd.download")</p>
 
 
diff --git a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties
index 5dcc34c8957ec9b294858a84ef87ffbca9f572a8..1ae1e1079ff881b008bdc3c30bd6ecf18f959bce 100644
--- a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties
@@ -47,6 +47,7 @@ chelp.title=Titel
 chelp.uid=Benutzeridentifikation
 chelp.ziel=Ziel
 cmd.start=Start
+cmd.download=Herunterladen
 column.field.missing=-
 column.field.no=nein
 column.field.notavailable=k. A.
@@ -58,6 +59,8 @@ column.header.login=Benutzername
 column.header.passed=Bestanden
 column.header.score=Punkte
 column.header.seqnum=Nr.
+column.header.initialLaunchDate=Erster Kursstart
+column.header.scoreLastModified=Letzte Aktualisierung
 command.change.charset=Zeichensatz
 command.closearchiver=Schliessen
 course.logs.error=Sie haben kein Logfile ausgew\u00E4hlt.
diff --git a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties
index 95dbbe1e9252dd666b07216d9a73627c64972d8d..42b77c689cc17d123b590cd5cb99c1c7262e2f75 100644
--- a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties
@@ -47,6 +47,7 @@ chelp.title=Title
 chelp.uid=User identification
 chelp.ziel=Target
 cmd.start=Start
+cmd.download=Download
 column.field.missing=-
 column.field.no=No
 column.field.notavailable=n/a
@@ -58,6 +59,8 @@ column.header.login=User name
 column.header.passed=Passed
 column.header.score=Score
 column.header.seqnum=No.
+column.header.initialLaunchDate=Initial course launch
+column.header.scoreLastModified=Last update
 command.change.charset=Character set
 command.closearchiver=Close
 course.logs.error=You did not select any log files.
diff --git a/src/main/java/org/olat/course/assessment/AssessedIdentitiesTableDataModel.java b/src/main/java/org/olat/course/assessment/AssessedIdentitiesTableDataModel.java
index d8e46bd05a03be71041b6e834477a8c9e6792da1..ccebd9556e33e97060a3d7db5b9fa8fe1cba90b0 100644
--- a/src/main/java/org/olat/course/assessment/AssessedIdentitiesTableDataModel.java
+++ b/src/main/java/org/olat/course/assessment/AssessedIdentitiesTableDataModel.java
@@ -68,6 +68,8 @@ public class AssessedIdentitiesTableDataModel extends DefaultTableDataModel {
 	private static final String COL_MAXSCORE = "maxScore";
 	private static final String COL_PASSED = "passed";
 	private static final String COL_STATUS = "status";
+	private static final String COL_INITIAL_LAUNCH = "initialLaunch";
+	private static final String COL_LAST_SCORE_DATE = "lastScoreDate";
 
 	private List<String> colMapping;	
 	private List<String> userPropertyNameList;
@@ -127,6 +129,8 @@ public class AssessedIdentitiesTableDataModel extends DefaultTableDataModel {
 			if (courseNode.hasPassedConfigured()) {
 				colMapping.add(colCount++, COL_PASSED);			
 			}
+			colMapping.add(colCount++, COL_INITIAL_LAUNCH);		
+			colMapping.add(colCount++, COL_LAST_SCORE_DATE);		
 		}
 	}
 
@@ -187,10 +191,14 @@ public class AssessedIdentitiesTableDataModel extends DefaultTableDataModel {
 			return "";
   	}	else if (colName.equals(COL_STATUS)) {
 			return getStatusFor(courseNode, wrappedIdentity);
-		}else if (colName.equals(COL_PASSED)) {
+		} else if (colName.equals(COL_PASSED)) {
 			ScoreEvaluation scoreEval = wrappedIdentity.getUserCourseEnvironment().getScoreAccounting().evalCourseNode(courseNode);
 			if (scoreEval == null) scoreEval = new ScoreEvaluation(null, null);
 			return scoreEval.getPassed();
+		} else if (colName.equals(COL_INITIAL_LAUNCH)) {
+			return wrappedIdentity.getInitialLaunchDate();
+		} else if (colName.equals(COL_LAST_SCORE_DATE)) {
+			return wrappedIdentity.getLastModified();
 		} else return "error";
 	}
 
@@ -262,6 +270,8 @@ public class AssessedIdentitiesTableDataModel extends DefaultTableDataModel {
 				userListCtr.addColumnDescriptor(new BooleanColumnDescriptor("table.header.passed", colCount++, translator.translate("passed.true"), 
 						translator.translate("passed.false")));
 			}
+			userListCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.header.initialLaunchDate", colCount++, null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT));
+			userListCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.header.lastScoreDate", colCount++, null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT));
 		}
 	}
 
diff --git a/src/main/java/org/olat/course/assessment/AssessedIdentityWrapper.java b/src/main/java/org/olat/course/assessment/AssessedIdentityWrapper.java
index 2f5a86e029de0f3bfc0fa3be12a7438e2b3c0009..ac967307b4de7391c63161e8c493e4653f606314 100644
--- a/src/main/java/org/olat/course/assessment/AssessedIdentityWrapper.java
+++ b/src/main/java/org/olat/course/assessment/AssessedIdentityWrapper.java
@@ -25,6 +25,8 @@
 
 package org.olat.course.assessment;
 
+import java.util.Date;
+
 import org.olat.core.id.Identity;
 import org.olat.course.run.userview.UserCourseEnvironment;
 
@@ -37,9 +39,11 @@ import org.olat.course.run.userview.UserCourseEnvironment;
  * variables that should be displayed in the user list table.
  */
 public class AssessedIdentityWrapper {
-    private UserCourseEnvironment userCourseEnvironment;
-    private Integer nodeAttempts;
-    private String detailsListView;
+    private final UserCourseEnvironment userCourseEnvironment;
+    private final Integer nodeAttempts;
+    private final String detailsListView;
+    private final Date initialLaunchDate;
+    private final Date lastModified;
 
     /**
      * Constructor for this identity wrapper object. Wraps an identity with 
@@ -48,11 +52,14 @@ public class AssessedIdentityWrapper {
      * @param nodeAttempts the users node attempts for the current node
      * @param detailsListView the users details for this node
      */
-    public AssessedIdentityWrapper(UserCourseEnvironment userCourseEnvironment, Integer nodeAttempts, String detailsListView) {
+    public AssessedIdentityWrapper(UserCourseEnvironment userCourseEnvironment, Integer nodeAttempts, String detailsListView,
+    		Date initialLaunchDate, Date lastModified) {
         super();
         this.userCourseEnvironment = userCourseEnvironment;
         this.nodeAttempts = nodeAttempts;
         this.detailsListView = detailsListView;
+        this.initialLaunchDate = initialLaunchDate;
+        this.lastModified = lastModified;
     }
 
     /**
@@ -83,4 +90,12 @@ public class AssessedIdentityWrapper {
     public Integer getNodeAttempts() {
         return nodeAttempts;
     }
+    
+    public Date getInitialLaunchDate() {
+    	return initialLaunchDate;
+    }
+
+		public Date getLastModified() {
+			return lastModified;
+		}
 }
diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
index 64fb18b9e2febe13d1fb491f7b1ff494249f30c1..499d3921e64777527e74229255165b62f106f985 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
@@ -28,6 +28,7 @@ package org.olat.course.assessment;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -36,7 +37,9 @@ import java.util.Map;
 
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
+import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.nodes.INode;
 import org.olat.core.util.tree.TreeVisitor;
 import org.olat.core.util.tree.Visitor;
@@ -47,12 +50,14 @@ import org.olat.course.nodes.ProjectBrokerCourseNode;
 import org.olat.course.nodes.STCourseNode;
 import org.olat.course.nodes.ScormCourseNode;
 import org.olat.course.nodes.iq.IQEditController;
+import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.course.tree.CourseEditorTreeModel;
 import org.olat.course.tree.CourseEditorTreeNode;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.properties.Property;
 
 /**
  * Description:<br>
@@ -62,6 +67,8 @@ import org.olat.modules.ModuleConfiguration;
  * @author gnaegi
  */
 public class AssessmentHelper {
+	
+	private static final OLog log = Tracing.createLoggerFor(AssessmentHelper.class);
 
 	/**
 	 * String to symbolize 'not available' or 'not assigned' in assessments
@@ -87,25 +94,25 @@ public class AssessmentHelper {
 	 *          attempts must be fetched
 	 * @return a wrapped identity
 	 */
-	public static AssessedIdentityWrapper wrapIdentity(Identity identity, Map<Long,UserCourseEnvironment> localUserCourseEnvironmentCache, ICourse course,
-			AssessableCourseNode courseNode) {
+	public static AssessedIdentityWrapper wrapIdentity(Identity identity, Map<Long,UserCourseEnvironment> localUserCourseEnvironmentCache,
+			Map<Long, Date> initialLaunchDates, ICourse course, AssessableCourseNode courseNode) {
 		// Try to get user course environment from local hash map cache. If not
 		// successful
 		// create the environment and add it to the map for later performance
 		// optimization
-		//synchronized (localUserCourseEnvironmentCache) { //o_clusterOK by:ld - no need to synchronized - only local variables
 			UserCourseEnvironment uce = localUserCourseEnvironmentCache.get(identity.getKey());
 			if (uce == null) {
 				uce = createAndInitUserCourseEnvironment(identity, course);
 				// add to cache for later usage
 				localUserCourseEnvironmentCache.put(identity.getKey(), uce);
-				if (Tracing.isDebugEnabled(AssessmentHelper.class)){
-					Tracing.logDebug("localUserCourseEnvironmentCache hit failed, adding course environment for user::"
-						+ identity.getName(), AssessmentHelper.class);
+				if (log.isDebug()){
+					log.debug("localUserCourseEnvironmentCache hit failed, adding course environment for user::"
+						+ identity.getName());
 				}
 			}
-			return wrapIdentity(uce, courseNode);
-		//}
+			
+			Date initialLaunchDate = initialLaunchDates.get(identity.getKey());
+			return wrapIdentity(uce, initialLaunchDate, courseNode);
 	}
 
 	/**
@@ -118,7 +125,7 @@ public class AssessmentHelper {
 	 *          attempts must be fetched
 	 * @return a wrapped identity
 	 */
-	public static AssessedIdentityWrapper wrapIdentity(UserCourseEnvironment uce, AssessableCourseNode courseNode) {
+	public static AssessedIdentityWrapper wrapIdentity(UserCourseEnvironment uce, Date initialLaunchDate, AssessableCourseNode courseNode) {
 		// Fetch attempts and details for this node if available
 		Integer attempts = null;
 		String details = null;
@@ -131,9 +138,27 @@ public class AssessmentHelper {
 				if (details == null) details = DETAILS_NA_VALUE;
 			}
 		}
-		AssessedIdentityWrapper aiw = new AssessedIdentityWrapper(uce, attempts, details);
+
+		Identity identity = uce.getIdentityEnvironment().getIdentity();
+		Date lastModified = uce.getCourseEnvironment().getAssessmentManager().getScoreLastModifiedDate(courseNode, identity);
+		AssessedIdentityWrapper aiw = new AssessedIdentityWrapper(uce, attempts, details, initialLaunchDate, lastModified);
 		return aiw;
 	}
+	
+	public static Date getInitialLaunchDate(UserCourseEnvironment uce) {
+		CourseNode node = uce.getCourseEnvironment().getRunStructure().getRootNode();
+		CoursePropertyManager pm = uce.getCourseEnvironment().getCoursePropertyManager();
+		Identity identity = uce.getIdentityEnvironment().getIdentity();
+		Property firstTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
+		
+		Date initialLaunchDate = null;
+		if (firstTime != null && StringHelper.containsNonWhitespace(firstTime.getStringValue())) {
+			Calendar cal = Calendar.getInstance();
+			cal.setTimeInMillis(Long.parseLong(firstTime.getStringValue()));
+			initialLaunchDate = cal.getTime();
+		}
+		return initialLaunchDate;
+	}
 
 	/**
 	 * Create a user course environment for the given user and course. After
diff --git a/src/main/java/org/olat/course/assessment/AssessmentMainController.java b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
index 4b799adcae0705a926e4934499f7aec55c15be9c..ac7d812267dc9fbb466607b58440b32929448166 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentMainController.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
@@ -26,6 +26,8 @@
 package org.olat.course.assessment;
 
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -78,6 +80,7 @@ import org.olat.core.logging.OLATSecurityException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.ActionType;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.resource.OresHelper;
@@ -92,10 +95,12 @@ import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.ProjectBrokerCourseNode;
 import org.olat.course.nodes.STCourseNode;
+import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
 import org.olat.group.ui.context.BGContextTableModel;
+import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
@@ -146,7 +151,8 @@ public class AssessmentMainController extends MainLayoutBasicController implemen
 	
 	// Hash map to keep references to already created user course environments
 	// Serves as a local cache to reduce database access - not shared by multiple threads
-	Map<Long, UserCourseEnvironment> localUserCourseEnvironmentCache; // package visibility for avoiding synthetic accessor method
+	final Map<Long, UserCourseEnvironment> localUserCourseEnvironmentCache; // package visibility for avoiding synthetic accessor method
+	final Map<Long, Date> initialLaunchDates;
 	// List of groups to which the user has access rights in this course
 	private List<BusinessGroup> coachedGroups;
 	//Is tutor from the security group of repository entry
@@ -188,7 +194,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 		getUserActivityLogger().setStickyActionType(ActionType.admin);
 		this.ores = ores;
 		this.callback = assessmentCallback;
-		this.localUserCourseEnvironmentCache = new HashMap<Long, UserCourseEnvironment>();
+		localUserCourseEnvironmentCache = new HashMap<Long, UserCourseEnvironment>();
+		initialLaunchDates = new HashMap<Long,Date>();
 		
     //use the PropertyHandlerTranslator	as tableCtr translator
 		propertyHandlerTranslator = UserManager.getInstance().getPropertyHandlerTranslator(getTranslator());
@@ -290,7 +297,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			}
 			
 			// select user
-			this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, this.localUserCourseEnvironmentCache, course, null);
+			assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 			
 			UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();		
 			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
@@ -419,8 +426,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 						// in user MODE_USERFOCUS, a simple identity table is used, no wrapped identites
 						UserTableDataModel userListModel = (UserTableDataModel) userListCtr.getTableDataModel();
 						Identity assessedIdentity = userListModel.getIdentityAt(rowid);
-						this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentity, 
-								this.localUserCourseEnvironmentCache, course, null);
+						assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedIdentity, 
+								localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 					} else {
 						// all other cases where user can be choosen the assessed identity wrapper is used
 						AssessedIdentitiesTableDataModel userListModel = (AssessedIdentitiesTableDataModel) userListCtr.getTableDataModel();
@@ -480,7 +487,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 						ICourse course = CourseFactory.loadCourse(ores);
 						aiwList.remove(this.assessedIdentityWrapper);
 						this.assessedIdentityWrapper = AssessmentHelper.wrapIdentity(this.assessedIdentityWrapper.getIdentity(),
-						this.localUserCourseEnvironmentCache, course, currentCourseNode);
+						this.localUserCourseEnvironmentCache, initialLaunchDates, course, currentCourseNode);
 						aiwList.add(this.assessedIdentityWrapper);
 						userListCtr.modelChanged();
 					}
@@ -563,7 +570,14 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 					// 2.2) update wrapper object
 					if (wrappedIdFromModel != null) {
 						wrappers.remove(wrappedIdFromModel);
-						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), currentCourseNode);
+						
+						Date initialLaunchDate;
+						if(initialLaunchDates.containsKey(identityKeyFromEvent)) {
+							initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent);
+						} else {
+							initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment());
+						}
+						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode);
 						wrappers.add(wrappedIdFromModel);
 						userListCtr.modelChanged();								
 					}
@@ -750,7 +764,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			Identity identity = identities.get(i);
 			// if course node is null the wrapper will only contain the identity and no score information
 			AssessedIdentityWrapper aiw = AssessmentHelper.wrapIdentity(identity,
-			this.localUserCourseEnvironmentCache, course, courseNode);
+					localUserCourseEnvironmentCache, initialLaunchDates, course, courseNode);
 			wrappedIdentities.add(aiw);
 		}
 		// Add the wrapped identities to the table data model
@@ -1161,9 +1175,20 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			// 2) preload controller local user environment cache
 			start = System.currentTimeMillis();
 			List<Identity> identities = getAllIdentitisFromGroupmanagement();
-			for (Iterator<Identity> iter = identities.iterator(); iter.hasNext();) {
-				Identity identity = iter.next();
-				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, course, null);
+			
+			CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode();
+			CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager();
+			List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
+			Calendar cal = Calendar.getInstance();
+			for(Property property:firstTime) {
+				if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) {
+					cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
+					initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime());
+				}
+			}
+			
+			for (Identity identity : identities) {
+				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 				if (Thread.interrupted()) break;
 			}
 			if (logDebug) {
diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java
index 95c7db85fb006ab94af8cd50c6a6ad8f48a785a2..4f422eb77cb94165c7b17a2516b8491a1b84ffb8 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentManager.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java
@@ -25,6 +25,8 @@
 
 package org.olat.course.assessment;
 
+import java.util.Date;
+
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.util.event.GenericEventListener;
@@ -170,6 +172,14 @@ public interface AssessmentManager {
 	 */
 	public Long getAssessmentID(CourseNode courseNode, Identity identity);
 	
+	/**
+	 * 
+	 * @param courseNode
+	 * @param identity
+	 * @return
+	 */
+	public Date getScoreLastModifiedDate(CourseNode courseNode, Identity identity);
+	
 	/**
 	 * Save the users achieved ScoreEvaluation for this node. If there is already a score property available, it will be
 	 * overwritten with the new value <p>
diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
index 4afb5ae1343c1dbee186f1a9011b2db9429c80e4..f7be1e5e1a93b895f012edab0d48c366ca7665b9 100644
--- a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
+++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
@@ -25,6 +25,8 @@
 
 package org.olat.course.assessment;
 
+import java.util.Date;
+
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
@@ -156,7 +158,8 @@ public class IdentityAssessmentEditController extends BasicController {
 	private void doEditNodeAssessment(UserRequest ureq, AssessableCourseNode courseNode){
 		if (mayEdit) {
 			ICourse course = CourseFactory.loadCourse(ores);
-			AssessedIdentityWrapper assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedUserCourseEnvironment, courseNode);
+			Date initialLaunchDate = AssessmentHelper.getInitialLaunchDate(assessedUserCourseEnvironment);
+			AssessedIdentityWrapper assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedUserCourseEnvironment, initialLaunchDate, courseNode);
 			assessmentEditCtr = new AssessmentEditController(ureq, getWindowControl(), course, courseNode, assessedIdentityWrapper);			
 			listenTo(assessmentEditCtr);
 			main.setContent(assessmentEditCtr.getInitialComponent());
diff --git a/src/main/java/org/olat/course/assessment/NewCachePersistingAssessmentManager.java b/src/main/java/org/olat/course/assessment/NewCachePersistingAssessmentManager.java
index 02f672b9f8597cc878bef6a0f15a39631df7b86e..fefb470d7ddbaec51366177394a186c593203b7e 100644
--- a/src/main/java/org/olat/course/assessment/NewCachePersistingAssessmentManager.java
+++ b/src/main/java/org/olat/course/assessment/NewCachePersistingAssessmentManager.java
@@ -26,8 +26,9 @@
 package org.olat.course.assessment;
 
 import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -107,6 +108,8 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 	 * (otherwise we cannot know whether a null value means expiration of cache or no-such-property-yet-for-user)
 	 */
 	private static final String FULLUSERSET = "FULLUSERSET";
+	private static final String LAST_MODIFIED = "LAST_MODIFIED";
+	
 	
 	// Float and Integer are immutable objects, we can reuse them.
 	private static final Float FLOAT_ZERO = new Float(0);
@@ -148,7 +151,7 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 	 * are loaded.
 	 * @return
 	 */
-	private List loadPropertiesFor(Identity identity) {
+	private List<Property> loadPropertiesFor(Identity identity) {
 		ICourse course = CourseFactory.loadCourse(ores);
 		StringBuilder sb = new StringBuilder();
 		sb.append("from org.olat.properties.Property as p");
@@ -171,7 +174,7 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 		if (identity != null) {
 			query.setEntity("id", identity);
 		}
-		List properties = query.list();
+		List<Property> properties = query.list();
 		return properties;		
 	}
 	
@@ -211,9 +214,8 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 				// or has been invalidated (in cluster mode when puts occurred from an other node for the same cache)
 				m = new HashMap<String, Serializable>();
 				// load data
-				List properties = loadPropertiesFor(identity);
-				for (Iterator iter = properties.iterator(); iter.hasNext();) {
-					Property property = (Property) iter.next();
+				List<Property> properties = loadPropertiesFor(identity);
+				for (Property property:properties) {
 					addPropertyToCache(m, property);
 				}
 				// we use a putSilent here (no invalidation notifications to other cluster nodes), since
@@ -288,10 +290,17 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 			throw new AssertionError("property in list that is not of type attempts, score, passed or ASSESSMENT_ID, COMMENT and COACH_COMMENT :: " + propertyName);
 		}
 		
+		Date lastModified = property.getLastModified();
 		// put in cache, maybe overriding old values		
 		String cacheKey = getPropertyCacheKey(property);		
 		synchronized(acache) {//cluster_ok acache is an element from the cacher
 			acache.put(cacheKey, value);
+			
+			String lmCacheKey = getLastModifiedCacheKey(property);
+			Long currentLastModifiedDate = (Long)acache.get(lmCacheKey);
+			if(currentLastModifiedDate == null || currentLastModifiedDate.longValue() < lastModified.getTime()) {
+				acache.put(lmCacheKey, new Long(lastModified.getTime()));
+			}
 		}
 	}
 	
@@ -689,6 +698,13 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 		return cacheKey;
 	}
 	
+	private String getLastModifiedCacheKey(Property property) {;
+		String propertyCategory = property.getCategory();
+		String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2);
+		String cacheKey = getCacheKey(nodeIdent, LAST_MODIFIED);
+		return cacheKey;
+	}
+	
 	/**
 	 * @see org.olat.course.assessment.AssessmentManager#registerForAssessmentChangeEvents(org.olat.core.util.event.GenericEventListener,
 	 *      org.olat.core.id.Identity)
@@ -759,6 +775,25 @@ public class NewCachePersistingAssessmentManager extends BasicManager implements
 		}				
 	}
 	
+	@Override
+	public Date getScoreLastModifiedDate(CourseNode courseNode, Identity identity) {
+		if (courseNode == null) {
+			return null; // return default value
+		}
+		
+		String cacheKey = getCacheKey(courseNode, LAST_MODIFIED);
+		Map<String, Serializable> m = getOrLoadScorePassedAttemptsMap(identity, false);		
+		synchronized(m) {//o_clusterOK by:fj is per vm only
+			Long lastModified = (Long) m.get(cacheKey);
+			if(lastModified != null) {
+				Calendar cal = Calendar.getInstance();
+				cal.setTimeInMillis(lastModified.longValue());
+				return cal.getTime();
+			}
+		}
+		return null;
+	}
+
 	/**
 	 * 
 	 * @see org.olat.course.assessment.AssessmentManager#saveScoreEvaluation(org.olat.course.nodes.CourseNode, org.olat.core.id.Identity, org.olat.core.id.Identity, org.olat.course.run.scoring.ScoreEvaluation)
diff --git a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
index 53a906bd837b6df5c9866736a6b12c55da74067b..14c06179a8009a906d873adb1272f3a4aa270da3 100644
--- a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
@@ -141,6 +141,8 @@ table.header.score=Punkte
 table.header.show=Leistungsnachweis
 table.header.status=Status
 table.header.type=Typ
+table.header.initialLaunchDate=Erster Kursstart
+table.header.lastScoreDate=Letzte Aktualisierung
 notifications.title=Kurs "{0}" 
 notifications.header=Neue Testresultate in Kurs "{0}"
 notifications.entry={3}: "{0}" ausgef\u00FChrt von {1} mit {2} Punkten
diff --git a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_en.properties
index 787b552c0d1af90a53d0a8fdf4f05464a99aae94..68f1d52ec8135870af21b32096bf4922e3e6a03e 100644
--- a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_en.properties
@@ -149,6 +149,8 @@ table.header.score=Score
 table.header.show=Evidence of achievement
 table.header.status=Status
 table.header.type=Type
+table.header.initialLaunchDate=Initial course launch
+table.header.lastScoreDate=Last update
 title.infocoach=Information on assessment
 tool.name=Assessment tool
 userchoose.nousers=No users found. Either there are no persons assigned to those groups or you do not have any coaching rights.
diff --git a/src/main/java/org/olat/course/properties/CoursePropertyManager.java b/src/main/java/org/olat/course/properties/CoursePropertyManager.java
index 32a50cef94ad33d3251dd75b24e73ca2e773d540..91ac73aa9112de8fe50bb415decc30013f216e9b 100644
--- a/src/main/java/org/olat/course/properties/CoursePropertyManager.java
+++ b/src/main/java/org/olat/course/properties/CoursePropertyManager.java
@@ -103,6 +103,17 @@ public interface CoursePropertyManager extends IdentityAnonymizerCallback {
 	 */
 	public Property findCourseNodeProperty(CourseNode node, Identity identity, BusinessGroup grp, String name);
 	
+
+	/**
+	 * Find a specific course node property (exact match. I.e. null values are taken into account)
+	 * @param node
+	 * @param identity
+	 * @param grp
+	 * @param name
+	 * @return matching course node property
+	 */
+	public List<Property> findCourseNodeProperties(CourseNode node, List<Identity> identities, String name);
+	
 	/**
 	 * Delete all node properties for a given course node and a category.
 	 * @param courseNode The course node. Must not be null.
diff --git a/src/main/java/org/olat/course/properties/PersistingCoursePropertyManager.java b/src/main/java/org/olat/course/properties/PersistingCoursePropertyManager.java
index c3bd1d2e99cb998ae3bbc55e47c80e871b733490..d2cda015cc9cf688af9801fe6dfa4448a838d825 100644
--- a/src/main/java/org/olat/course/properties/PersistingCoursePropertyManager.java
+++ b/src/main/java/org/olat/course/properties/PersistingCoursePropertyManager.java
@@ -142,6 +142,12 @@ public class PersistingCoursePropertyManager extends BasicManager implements Cou
 		return pm.findProperty(identity, grp, myCategory, name);
 	}
 
+	@Override
+	public List<Property> findCourseNodeProperties(CourseNode node, List<Identity> identities, String name) {
+		String myCategory = buildCourseNodePropertyCategory(node);
+		return pm.findProperties(identities, myCategory, name);
+	}
+
 	/**
 	 * @see org.olat.course.properties.CoursePropertyManager#deleteNodeProperties(org.olat.course.nodes.CourseNode,
 	 *      java.lang.String)
diff --git a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
index fdedee2298463c2523337add357b62891a419daf..481509411c17ca6d7d570ffb8b1b7d65f7c31633 100644
--- a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
+++ b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
@@ -25,6 +25,7 @@
 
 package org.olat.course.run.preview;
 
+import java.util.Date;
 import java.util.HashMap;
 
 import org.olat.core.id.Identity;
@@ -185,6 +186,11 @@ final class PreviewAssessmentManager extends BasicManager implements AssessmentM
 		return (Long)nodeAssessmentID.get(courseNode.getIdent());
 	}
 
+	@Override
+	public Date getScoreLastModifiedDate(CourseNode courseNode, Identity identity) {
+		return null;
+	}
+
 	/**
 	 * 
 	 * @see org.olat.course.assessment.AssessmentManager#saveScoreEvaluation(org.olat.course.nodes.CourseNode, org.olat.core.id.Identity, org.olat.core.id.Identity, org.olat.course.run.scoring.ScoreEvaluation)
diff --git a/src/main/java/org/olat/course/run/preview/PreviewCoursePropertyManager.java b/src/main/java/org/olat/course/run/preview/PreviewCoursePropertyManager.java
index 1031fc67b09bf92cd44260887190204be207e017..1f5a7ef4ec0f4454a71950e60a2fbbf5bb46ef17 100644
--- a/src/main/java/org/olat/course/run/preview/PreviewCoursePropertyManager.java
+++ b/src/main/java/org/olat/course/run/preview/PreviewCoursePropertyManager.java
@@ -26,6 +26,7 @@
 package org.olat.course.run.preview;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -134,6 +135,11 @@ final class PreviewCoursePropertyManager extends BasicManager implements CourseP
 		return (Property)propertyList.get(0);
 	}
 
+	@Override
+	public List<Property> findCourseNodeProperties(CourseNode node, List<Identity> identities, String name) {
+		return Collections.emptyList();
+	}
+
 	/**
 	 * @see org.olat.course.properties.CoursePropertyManager#deleteNodeProperties(org.olat.course.nodes.CourseNode, java.lang.String)
 	 */
diff --git a/src/main/java/org/olat/properties/NarrowedPropertyManager.java b/src/main/java/org/olat/properties/NarrowedPropertyManager.java
index 1e01c33072eb9766e93af8c75f6c63fdfdc51b94..eca30e220339a253bff4eb7820c9921feeb94200 100644
--- a/src/main/java/org/olat/properties/NarrowedPropertyManager.java
+++ b/src/main/java/org/olat/properties/NarrowedPropertyManager.java
@@ -146,6 +146,20 @@ public class NarrowedPropertyManager {
 		return pm.findProperty(identity, grp, resourceable, category, name);
 	}
 	
+	/**
+	 * Find method for a batch of people
+	 * 
+	 * @param identity
+	 * @param grp
+	 * @param category
+	 * @param name
+	 * @return The property or null if no property found
+	 * @throws AssertException if more than one property matches.
+	 */
+	public List<Property> findProperties(List<Identity> identities, String category, String name) {
+		return pm.findProperties(identities, resourceable, category, name);
+	}
+	
 	/**
 	 * deletes all properties of this resourceable
 	 *
diff --git a/src/main/java/org/olat/properties/PropertyManager.java b/src/main/java/org/olat/properties/PropertyManager.java
index 643c3d21d4d0c03b2cec6f572962f326a7b3ad4d..758d02486ed77f10177fbf7e658ed6de796838f9 100644
--- a/src/main/java/org/olat/properties/PropertyManager.java
+++ b/src/main/java/org/olat/properties/PropertyManager.java
@@ -26,6 +26,7 @@
 package org.olat.properties;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -34,6 +35,7 @@ import org.hibernate.Hibernate;
 import org.hibernate.type.Type;
 import org.olat.admin.user.delete.service.UserDeletionManager;
 import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.commons.persistence.DBQuery;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.logging.AssertException;
@@ -499,6 +501,42 @@ public class PropertyManager extends BasicManager implements UserDataDeletable {
 		return (Property)props.get(0);
 	}
 	
+	public List<Property> findProperties(List<Identity> identities, OLATResourceable resourceable, String category, String name) {
+		if(identities == null || identities.isEmpty()) {
+			return Collections.emptyList();
+		}
+
+		StringBuilder query = new StringBuilder();
+		query.append("select p from ").append(Property.class.getName()).append(" as p")
+			.append(" where p.identity in (:identities)");
+		if (resourceable != null) {
+			query.append(" and p.resourceTypeName=:resourceTypeName and p.resourceTypeId=:resourceableId");
+		}
+		if (category != null) {
+			query.append(" and p.category=:category");
+		}
+		if (name != null) {
+			query.append(" and p.name=:name");
+		}
+		
+		DBQuery dbQuery = DBFactory.getInstance().createQuery(query.toString());
+		if (resourceable != null) {
+			dbQuery.setString("resourceTypeName", resourceable.getResourceableTypeName());
+			dbQuery.setLong("resourceableId", resourceable.getResourceableId());
+		}
+		if (category != null) {
+			dbQuery.setString("category", category);
+		}
+		if (name != null) {
+			dbQuery.setString("name", name);
+		}
+		dbQuery.setParameterList("identities", identities);
+		
+		List<Property> props = dbQuery.list();
+		return props;
+	}
+	
+	
 	/**
 	 * @return a list of all available resource type names
 	 */