diff --git a/.gitignore b/.gitignore
index 571cf011ab01e0ba9c2f0c1dcc704f7f53e307d7..4e879538ecaaf3590de0b1aea7da36ef8ec6bff2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@ src/test/java/org/olat/selenium/SeleniumTest.java
 
 **/*/.sass-cache
 
+/.sts4-cache
diff --git a/src/main/java/org/olat/core/util/i18n/ui/SingleKeyTranslatorController.java b/src/main/java/org/olat/core/util/i18n/ui/SingleKeyTranslatorController.java
index 6fc7e7a408e56a13ecedb924978e72d7167d3324..ddee744d569728282dbf0db4eb8e718048e81b4f 100644
--- a/src/main/java/org/olat/core/util/i18n/ui/SingleKeyTranslatorController.java
+++ b/src/main/java/org/olat/core/util/i18n/ui/SingleKeyTranslatorController.java
@@ -62,14 +62,34 @@ public class SingleKeyTranslatorController extends FormBasicController {
 	private static final String LBL_NAME_PREFIX = "lbl.";
 	private Map<String, TextElement> textElements;
 	private Object uobject;
+	private boolean textArea;
 
 	@Autowired
 	private I18nManager i18nMng;
 	@Autowired
 	private I18nModule i18nModule;
 
+	/**
+	 * 
+	 * @param ureq The user request
+	 * @param wControl The window control
+	 * @param keyToTranslate The key to translate
+	 * @param translatorBaseClass The package to translate
+	 */
 	public SingleKeyTranslatorController(UserRequest ureq, WindowControl wControl, String keyToTranslate, Class<?> translatorBaseClass) {
-		this(ureq,wControl,new String[]{keyToTranslate},translatorBaseClass);
+		this(ureq, wControl, new String[]{keyToTranslate}, translatorBaseClass, false);
+	}
+	
+	/**
+	 * 
+	 * @param ureq The user request
+	 * @param wControl The window control
+	 * @param keyToTranslate The key to translate
+	 * @param translatorBaseClass The package to translate
+	 * @param textArea true if the edit field is a text area, false for a simple text field
+	 */
+	public SingleKeyTranslatorController(UserRequest ureq, WindowControl wControl, String keyToTranslate, Class<?> translatorBaseClass, boolean textArea) {
+		this(ureq, wControl, new String[]{keyToTranslate}, translatorBaseClass, textArea);
 	}
 
 	/**
@@ -84,14 +104,17 @@ public class SingleKeyTranslatorController extends FormBasicController {
 
 	/**
 	 * 
-	 * @param ureq
-	 * @param wControl
+	 * @param ureq The user request
+	 * @param wControl The window control
 	 * @param keysToTranslate array of keys to translate (each key will have the
 	 *          same value, translation is only done once (for each language) !)
-	 * @param translatorBaseClass
+	 * @param translatorBaseClass The package to translate
+	 * @param textArea true if the edit field is a text area, false for a simple text field
 	 */
-	public SingleKeyTranslatorController(UserRequest ureq, WindowControl wControl, String[] keysToTranslate, Class<?> translatorBaseClass) {
+	public SingleKeyTranslatorController(UserRequest ureq, WindowControl wControl, String[] keysToTranslate,
+			Class<?> translatorBaseClass, boolean textArea) {
 		super(ureq, wControl, FormBasicController.LAYOUT_VERTICAL);
+		this.textArea = textArea;
 		i18nItemKeys = keysToTranslate;
 		this.translatorBaseClass = translatorBaseClass;
 		initForm(ureq);
@@ -119,18 +142,24 @@ public class SingleKeyTranslatorController extends FormBasicController {
 		// build the form
 		textElements = new HashMap<>();
 		for (I18nRowBundle i18nRowBundle : bundles) {
-			uifactory.addStaticTextElement(LBL_NAME_PREFIX + i18nRowBundle.getLanguageKey(), null, i18nRowBundle.getKeyTranslator().getLocale()
-					.getDisplayLanguage(getLocale()), formLayout);
+			String labelId = LBL_NAME_PREFIX + i18nRowBundle.getLanguageKey();
+			String label = i18nRowBundle.getKeyTranslator().getLocale().getDisplayLanguage(getLocale());
+			uifactory.addStaticTextElement(labelId, null, label, formLayout);
 
 			String value = "";
 			if (i18nRowBundle.hasTranslationForValue(i18nItemKeys[0])) {
 				value = i18nRowBundle.getKeyTranslator().translate(i18nItemKeys[0]);
 			}
-			TextElement te = uifactory.addTextElement(TXT_NAME_PREFIX + i18nRowBundle.getLanguageKey(), null, 255, value, formLayout);
+			String textId = TXT_NAME_PREFIX + i18nRowBundle.getLanguageKey();
+			TextElement te;
+			if(textArea) {
+				te = uifactory.addTextAreaElement(textId, null, -1, 8, 60, false, false, value, formLayout);
+			} else {
+				te = uifactory.addTextElement(textId, null, null, 255, value, formLayout);
+				te.setDisplaySize(60);
+			}
 			te.setMandatory(true);
-			te.setDisplaySize(60);
 			textElements.put(i18nRowBundle.getLanguageKey(), te);
-
 		}
 
 		FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("ok_cancel", getTranslator());
@@ -190,11 +219,11 @@ public class SingleKeyTranslatorController extends FormBasicController {
 	 * 
 	 * @author strentini
 	 */
-	class I18nRowBundle {
-		private Locale overlayLocale;
-		private Locale locale;
-		private String languageKey;
-		private Translator keyTranslator;
+	private class I18nRowBundle {
+		private final Locale overlayLocale;
+		private final Locale locale;
+		private final String languageKey;
+		private final Translator keyTranslator;
 
 		/**
 		 * 
@@ -217,10 +246,6 @@ public class SingleKeyTranslatorController extends FormBasicController {
 			return overlayLocale;
 		}
 
-		public Locale getLocale() {
-			return locale;
-		}
-
 		public String getLanguageKey() {
 			return languageKey;
 		}
@@ -228,7 +253,5 @@ public class SingleKeyTranslatorController extends FormBasicController {
 		public Translator getKeyTranslator() {
 			return keyTranslator;
 		}
-
 	}
-
 }
diff --git a/src/main/java/org/olat/course/assessment/manager/AssessmentModeManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/AssessmentModeManagerImpl.java
index a9b1534b440d6068a573dcce080d8c10afbad1ad..c4c6fdeb72b13942e4485ae97f989b631594062d 100644
--- a/src/main/java/org/olat/course/assessment/manager/AssessmentModeManagerImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/AssessmentModeManagerImpl.java
@@ -380,12 +380,8 @@ public class AssessmentModeManagerImpl implements AssessmentModeManager {
 			assessedKeys.addAll(courseMemberKeys);
 		}
 		
-		if(targetAudience == Target.curriculumEls || targetAudience == Target.courseAndGroups) {
-			List<Long> courseMemberKeys = assessmentMode.isApplySettingsForCoach()
-					? repositoryEntryRelationDao.getMemberKeys(re, RepositoryEntryRelationType.entryAndCurriculums, GroupRoles.coach.name(), GroupRoles.participant.name())
-					: repositoryEntryRelationDao.getMemberKeys(re, RepositoryEntryRelationType.entryAndCurriculums, GroupRoles.participant.name());
-			assessedKeys.addAll(courseMemberKeys);
-			
+		// For courseAndGroups, the curriculums are retrieved by the relation type.
+		if(targetAudience == Target.curriculumEls) {
 			List<CurriculumElementRef> curriculumElements = new ArrayList<>();
 			Set<AssessmentModeToCurriculumElement> modeTocurriculumElements  = assessmentMode.getCurriculumElements();
 			for(AssessmentModeToCurriculumElement modeTocurriculumElement:modeTocurriculumElements) {
diff --git a/src/main/java/org/olat/course/reminder/rule/AttemptsRuleSPI.java b/src/main/java/org/olat/course/reminder/rule/AttemptsRuleSPI.java
index 5e1d63059c2bb69ec991fa2dadeb11882469911c..cc25c520f0ea5d4a848ee60c119e9ad2b5ad3901 100644
--- a/src/main/java/org/olat/course/reminder/rule/AttemptsRuleSPI.java
+++ b/src/main/java/org/olat/course/reminder/rule/AttemptsRuleSPI.java
@@ -87,7 +87,7 @@ public class AttemptsRuleSPI implements FilterRuleSPI {
 			CourseNode courseNode = course.getRunStructure().getNode(nodeIdent);
 			if (courseNode == null) {
 				identities.clear();
-				log.error("Attempts rule in course " + entry.getKey() + " (" + entry.getDisplayname() + ") is missing a course element");
+				log.warn("Attempts rule in course {} ({}) is missing a course element", entry.getKey(), entry.getDisplayname());
 				return;
 			}
 
diff --git a/src/main/java/org/olat/ims/qti21/AssessmentTestHelper.java b/src/main/java/org/olat/ims/qti21/AssessmentTestHelper.java
index 876423142d66ad3bdbd6ef25116031559bbd9158..4addfebcbbc08aa724d2a33f39eae3ad49a72a8f 100644
--- a/src/main/java/org/olat/ims/qti21/AssessmentTestHelper.java
+++ b/src/main/java/org/olat/ims/qti21/AssessmentTestHelper.java
@@ -66,7 +66,9 @@ public class AssessmentTestHelper {
 					.getItemRefsBySystemIdMap().get(currentItem.getItemSystemId());
 			
 			AssessmentItemRef itemRef = null;
-			if(itemRefs.size() == 1) {
+			if(itemRefs == null) {
+				// itemRef stay null
+			} else if(itemRefs.size() == 1) {
 				itemRef = itemRefs.get(0);
 			} else {
 				Identifier itemId = itemKey.getIdentifier();
diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
index 53ca58e8b30cfe438a9766f9dae01c02be8e5310..d8c73c8413beb067bb4b4f035b3aa29392a0d30f 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
@@ -369,7 +369,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 						initNewAssessmentTestSession(ureq, assessmentEntry, authorMode);
 					}
 				} catch(Exception e) {
-					logError("Cannot resume session as author", e);
+					logWarn("Cannot resume session as author", e);
 					initNewAssessmentTestSession(ureq, assessmentEntry, authorMode);
 				}
 			} else {
@@ -414,12 +414,15 @@ public class AssessmentTestDisplayController extends BasicController implements
 		if(subIdent == null && !authorMode) {
 			resourcesList.deregisterResourceable(entry, subIdent, getWindow());
 		}
-		
-		suspendAssessmentTest(new Date());
-		if(candidateSession != null) {
-			OLATResourceable sessionOres = OresHelper
-					.createOLATResourceableInstance(AssessmentTestSession.class, candidateSession.getKey());
-			CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, sessionOres);
+		try {
+			suspendAssessmentTest(new Date());
+			if(candidateSession != null) {
+				OLATResourceable sessionOres = OresHelper
+						.createOLATResourceableInstance(AssessmentTestSession.class, candidateSession.getKey());
+				CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, sessionOres);
+			}
+		} catch (Exception e) {
+			logError("", e);
 		}
 	}
 	
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java
index f4b2dd4fe57f3fc405c7873d3a3bd20eca3bdb5a..7d1ccbc8117f6d03a6f7456f9238e14fbc3fbcb9 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java
@@ -92,7 +92,7 @@ import uk.ac.ed.ph.jqtiplus.state.TestSessionState;
 
 /**
  * A table with the list of assessment items of the test
- * with statistics about the users wo answered (or not answered)
+ * with statistics about the users who answered (or not answered)
  * every assessment item.
  * 
  * 
@@ -472,7 +472,7 @@ public class CorrectionAssessmentItemListController extends FormBasicController
 				boolean readOnly = model.isReadOnly(assessedIdentity);
 				identityItemCtrl = new CorrectionIdentityAssessmentItemNavigationController(ureq, getWindowControl(),
 						model.getTestEntry(), model.getResolvedAssessmentTest(), itemCorrection, listEntry,
-						selectedItemSessions, model, null, readOnly);
+						selectedItemSessions, model, null, readOnly, true);
 				listenTo(identityItemCtrl);
 				updatePreviousNext();
 				
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java
index 86ea3fb3ab264935ce97d2efcd748e0800df1274..b94cc51effcbf48a9991ebe0807ad45dc96f298e 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java
@@ -88,6 +88,7 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl
 	private final ResourcesMapper resourcesMapper;
 	
 	private final boolean readOnly;
+	private final boolean pageIdentity;
 	private CorrectionOverviewModel model;
 	private final RepositoryEntry testEntry;
 	private AssessmentItemCorrection itemCorrection;
@@ -114,7 +115,7 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl
 			RepositoryEntry testEntry, ResolvedAssessmentTest resolvedAssessmentTest,
 			AssessmentItemCorrection itemCorrection, AssessmentItemListEntry assessmentEntry,
 			List<? extends AssessmentItemListEntry> assessmentEntryList, CorrectionOverviewModel model,
-			GradingTimeRecordRef gradingTimeRecord, boolean readOnly) {
+			GradingTimeRecordRef gradingTimeRecord, boolean readOnly, boolean pageIdentity) {
 		super(ureq, wControl, "correction_identity_assessment_item");
 		this.readOnly = readOnly;
 		this.gradingTimeRecord = gradingTimeRecord;
@@ -126,6 +127,7 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl
 		
 		this.model = model;
 		this.testEntry = testEntry;
+		this.pageIdentity = pageIdentity;
 		this.itemCorrection = itemCorrection;
 		this.assessmentEntry = assessmentEntry;
 		this.assessmentEntryList = assessmentEntryList;
@@ -166,12 +168,15 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl
 		formLayout.add("interactions", identityInteractionsCtrl.getInitialFormItem());
 		
 		uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl());
+
 		if(readOnly) {
-			nextQuestionButton = uifactory.addFormLink("next.item", formLayout, Link.BUTTON);
+			String nextI18n = pageIdentity ? "next.user" : "next.item";
+			nextQuestionButton = uifactory.addFormLink("next.item", nextI18n, null, formLayout, Link.BUTTON);
 			backOverviewButton = uifactory.addFormLink("back.overview", formLayout, Link.BUTTON);
 		} else {
 			uifactory.addFormSubmitButton("save", formLayout);
-			saveNextQuestionButton = uifactory.addFormLink("save.next", formLayout, Link.BUTTON);
+			String saveNextI18n = pageIdentity ? "save.next.identity" : "save.next";
+			saveNextQuestionButton = uifactory.addFormLink("save.next", saveNextI18n, null, formLayout, Link.BUTTON);
 			saveBackOverviewButton = uifactory.addFormLink("save.back", formLayout, Link.BUTTON);
 		}
 	}
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java
index 7554969282ff4a1fb81276adebbbdd3ae69ee4ab..3d60ff2d2898e35398fc52d65db80d33f2581e91 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java
@@ -408,7 +408,7 @@ public class CorrectionIdentityAssessmentItemListController extends FormBasicCon
 				AssessmentItem assessmentItem = resolvedAssessmentItem.getRootNodeLookup().extractIfSuccessful();
 				identityItemCtrl = new CorrectionIdentityAssessmentItemNavigationController(ureq, getWindowControl(),
 						model.getTestEntry(), model.getResolvedAssessmentTest(), itemCorrection, row,
-						tableModel.getObjects(), model, gradingTimeRecord, readOnly);
+						tableModel.getObjects(), model, gradingTimeRecord, readOnly, false);
 				listenTo(identityItemCtrl);
 				stackPanel.pushController(assessmentItem.getTitle(), identityItemCtrl);
 				updatePreviousNext();
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java
index ddbd698bcf3485f5220d41f95d5e6f9d7f6a9868..5b197d8e77cc63d7e1e77e28642c1dac759cdc18 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java
@@ -64,12 +64,12 @@ public class CorrectionIdentityAssessmentItemNavigationController extends BasicC
 			RepositoryEntry testEntry, ResolvedAssessmentTest resolvedAssessmentTest,
 			AssessmentItemCorrection itemCorrection, AssessmentItemListEntry assessmentEntry,
 			List<? extends AssessmentItemListEntry> assessmentEntryList, CorrectionOverviewModel model,
-			GradingTimeRecordRef gradingTimeRecord, boolean readOnly) {
+			GradingTimeRecordRef gradingTimeRecord, boolean readOnly, boolean pageIdentity) {
 		super(ureq, wControl);
 		mainVC = createVelocityContainer("corrections_navigation");
 		
 		itemCtrl = new CorrectionIdentityAssessmentItemController(ureq, wControl, testEntry, resolvedAssessmentTest,
-				itemCorrection, assessmentEntry, assessmentEntryList, model, gradingTimeRecord, readOnly);
+				itemCorrection, assessmentEntry, assessmentEntryList, model, gradingTimeRecord, readOnly, pageIdentity);
 		listenTo(itemCtrl);
 		mainVC.put("items", itemCtrl.getInitialComponent());
 		
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_de.properties
index 017fb110e7c01e52999e923473df2a84c84bbf50..72fc0bb3dcb865a0dc495109ea44f0d23613f317 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_de.properties
@@ -28,10 +28,11 @@ previous.item=Vorherige Frage
 previous.user=Vorheriger Benutzer
 save.back=Speichern und zur \u00DCbersicht
 save.next=Speichern und n\u00E4chste Frage
+save.next.identity=Speichern und n\u00E4chster Teilnehmer
 save.tests=Als endg\u00FCltiges Resultat speichern
 score=Punkte
 show.rubric=Beschreibung anzeigen
-show.rubric.with.title=Beschreibung f\u00FCr "<b>{0}</b>" anzeigen
+show.rubric.with.title=Beschreibung f\u00FCr "<strong>{0}</strong>" anzeigen
 status=Status
 table.header.action=<i class='o_icon o_icon_actions o_icon-lg'> </i>
 table.header.answered=Beantwortet
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_en.properties
index a27b4b521427c2e13b20914ffdc9a8efb56f700d..524ddb4f1e4df2d52ab33ed3b2a44941eee606a3 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_en.properties
@@ -28,10 +28,11 @@ previous.item=Previous question
 previous.user=Previous user
 save.back=Save and back to overview
 save.next=Save and next question
+save.next.identity=Save and next participant
 save.tests=Save results as completed
 score=Score
 show.rubric=Show description
-show.rubric.with.title=Show description for "<b>{0}</b>"
+show.rubric.with.title=Show description for "<strong>{0}</strong>"
 status=Status
 table.header.action=<i class\='o_icon o_icon_actions o_icon-lg'> </i>
 table.header.answered=Answered
diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_fr.properties
index 09386334c17266c5013ba2a800b3c6d425332950..d41a0314c2f66f966f9e8519456fa56e23b3a9c1 100644
--- a/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/assessment/_i18n/LocalStrings_fr.properties
@@ -28,6 +28,7 @@ previous.item=Question pr\u00E9c\u00E9dente
 previous.user=Utilisateur pr\u00E9c\u00E9dent
 save.back=Sauver et retourner \u00E0 l'aper\u00E7u
 save.next=Sauver et question suivante
+save.next.identity=Sauver et participant suivant
 save.tests=Sauver les r\u00E9sultats d\u00E9finitifs
 score=R\u00E9sultats
 show.rubric=Montrer la description
diff --git a/src/main/java/org/olat/modules/fo/Message.java b/src/main/java/org/olat/modules/fo/Message.java
index e452453420194721eb5cd9b81051802d2e766f6b..c2db05cd9807c0b4933f0b75271f7fd21355b9e1 100644
--- a/src/main/java/org/olat/modules/fo/Message.java
+++ b/src/main/java/org/olat/modules/fo/Message.java
@@ -25,6 +25,8 @@
 
 package org.olat.modules.fo;
 
+import java.util.Date;
+
 import org.olat.core.id.CreateInfo;
 import org.olat.core.id.Identity;
 import org.olat.core.id.ModifiedInfo;
@@ -57,6 +59,13 @@ public interface Message extends MessageLight, CreateInfo, ModifiedInfo,  Persis
 	
 	public void setModifier(Identity identity);
 	
+	/**
+	 * @return The date the modifier makes a change
+	 */
+	public Date getModificationDate();
+	
+	public void setModificationDate(Date date);
+	
 	public Message getParent();
 	
 	public void setParent(Message message);
diff --git a/src/main/java/org/olat/modules/fo/MessageLight.java b/src/main/java/org/olat/modules/fo/MessageLight.java
index b81bad0ad9b4f51f0752bd37aea793f914d9cd9b..0170e47618982937cb13f952939853d435f6cd34 100644
--- a/src/main/java/org/olat/modules/fo/MessageLight.java
+++ b/src/main/java/org/olat/modules/fo/MessageLight.java
@@ -43,6 +43,11 @@ public interface MessageLight extends MessageRef {
 	
 	public Identity getModifier();
 	
+	/**
+	 * @return The date the modifier makes a change
+	 */
+	public Date getModificationDate();
+	
 	public Date getCreationDate();
 	
 	public Date getLastModified();
diff --git a/src/main/java/org/olat/modules/fo/archiver/MessageNode.java b/src/main/java/org/olat/modules/fo/archiver/MessageNode.java
index e3865c00f3bc2e8b9fc7b2f71cacedaf590d61c7..80fca14b639b0e287ff388ad50cddbabad4f732d 100644
--- a/src/main/java/org/olat/modules/fo/archiver/MessageNode.java
+++ b/src/main/java/org/olat/modules/fo/archiver/MessageNode.java
@@ -66,7 +66,11 @@ public class MessageNode extends GenericNode implements Serializable {
 		creator = message.getCreator();
 		creationDate = message.getCreationDate();
 		modifier = message.getModifier();
-		modifiedDate = message.getLastModified();
+		if(message.getModificationDate() != null) {
+			modifiedDate = message.getModificationDate();
+		} else {
+			modifiedDate = message.getLastModified();
+		}
 		guest = message.isGuest();
 		pseudonym = message.getPseudonym();
 		if(message.getParent()==null) {
diff --git a/src/main/java/org/olat/modules/fo/manager/ForumManager.java b/src/main/java/org/olat/modules/fo/manager/ForumManager.java
index 1b2e4d042f0470fbad0280b82dae1292837622d6..9feeb02040cecb09e23b06e8de94234af3bc23bf 100644
--- a/src/main/java/org/olat/modules/fo/manager/ForumManager.java
+++ b/src/main/java/org/olat/modules/fo/manager/ForumManager.java
@@ -1231,6 +1231,7 @@ public class ForumManager {
 		((MessageImpl)message).setCreationDate(oldMessage.getCreationDate());
 		message.setLastModified(oldMessage.getLastModified());
 		message.setModifier(oldMessage.getModifier());
+		message.setModificationDate(oldMessage.getModificationDate());
 		message.setTitle(oldMessage.getTitle());
 		message.setBody(oldMessage.getBody());
 		message.setPseudonym(oldMessage.getPseudonym());
diff --git a/src/main/java/org/olat/modules/fo/model/MessageImpl.java b/src/main/java/org/olat/modules/fo/model/MessageImpl.java
index 9bb1255adbed67fba7d1e0662554a56ad6e01d98..355b591057b24ca91f34b900461aab2d9db1c594 100644
--- a/src/main/java/org/olat/modules/fo/model/MessageImpl.java
+++ b/src/main/java/org/olat/modules/fo/model/MessageImpl.java
@@ -90,6 +90,9 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 	@Column(name="numofwords", nullable=true, insertable=true, updatable=true)
 	private Integer numOfWords;
 	
+	@Column(name="modification_date", nullable=true, insertable=true, updatable=true)
+	private Date modificationDate;
+	
 	@ManyToOne(targetEntity=MessageImpl.class,fetch=FetchType.LAZY,optional=true)
 	@JoinColumn(name="parent_id", nullable=true, insertable=true, updatable=true)
 	private Message parent;
@@ -130,18 +133,12 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 		this.creationDate = creationDate;
 	}
 
-
-	/**
-	 * @return
-	 */
+	@Override
 	public String getBody() {
 		return body;
 	}
 
-
-	/**
-	 * @return
-	 */
+	@Override
 	public Identity getCreator() {
 		return creator;
 	}
@@ -153,14 +150,17 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 		creator = identity;
 	}
 
+	@Override
 	public String getPseudonym() {
 		return pseudonym;
 	}
 
+	@Override
 	public void setPseudonym(String pseudonym) {
 		this.pseudonym = pseudonym;
 	}
 
+	@Override
 	public boolean isGuest() {
 		return guest;
 	}
@@ -169,9 +169,7 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 		this.guest = guest;
 	}
 
-	/**
-	 * @return
-	 */
+	@Override
 	public Forum getForum() {
 		return forum;
 	}
@@ -180,16 +178,12 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 		this.forum = forum;
 	}
 
-	/**
-	 * @return
-	 */
+	@Override
 	public Identity getModifier() {
 		return modifier;
 	}
 
-	/**
-	 * @return
-	 */
+	@Override
 	public Message getParent() {
 		return parent;
 	}
@@ -199,90 +193,86 @@ public class MessageImpl implements CreateInfo, Persistable, Message {
 		return parent == null ? null : parent.getKey();
 	}
 
-	/**
-	 * @return
-	 */
+	@Override
 	public Message getThreadtop() {
 		return threadtop;
 	}
 
-	/**
-	 * @return
-	 */
+	@Override
 	public String getTitle() {
 		return title;
 	}
 
-	/**
-	 * @param string
-	 */
+	@Override
 	public void setBody(String string) {
 		body = string;
 	}
 
-	/**
-	 * @param identity
-	 */
+	@Override
 	public void setModifier(Identity identity) {
 		modifier = identity;
 	}
 
-	/**
-	 * @param message
-	 */
+	@Override
 	public void setParent(Message message) {
 		parent = message;
 	}
 
-	/**
-	 * @param message
-	 */
+	@Override
 	public void setThreadtop(Message message) {
 		threadtop = message;
 	}
 
-	/**
-	 * @param string
-	 */
+	@Override
 	public void setTitle(String string) {
 		title = string;
 	}
-	
+
+	@Override
 	public int getStatusCode() {
 		return statusCode;
 	}
+	
+	@Override
 	public void setStatusCode(int statusCode) {
 		this.statusCode = statusCode;
 	}
 
-	/**
-	 * 
-	 * @see org.olat.core.id.ModifiedInfo#getLastModified()
-	 */
+	@Override
 	public Date getLastModified() {
 		return lastModified;
 	}
 
-	/**
-	 * 
-	 * @see org.olat.core.id.ModifiedInfo#setLastModified(java.util.Date)
-	 */
+	@Override
 	public void setLastModified(Date date) {
 		this.lastModified = date;
 	}
 
+	@Override
+	public Date getModificationDate() {
+		return modificationDate;
+	}
+
+	public void setModificationDate(Date modificationDate) {
+		this.modificationDate = modificationDate;
+	}
+
+	@Override
 	public Integer getNumOfCharacters() {
 		return numOfCharacters;
 	}
 
+	@Override
 	public void setNumOfCharacters(Integer numOfCharacters) {
 		this.numOfCharacters = numOfCharacters;
 	}
 
+	@Override
 	public Integer getNumOfWords() {
 		return numOfWords;
 	}
 
+	@Override
 	public void setNumOfWords(Integer numOfWords) {
 		this.numOfWords = numOfWords;
 	}
diff --git a/src/main/java/org/olat/modules/fo/model/MessageLightImpl.java b/src/main/java/org/olat/modules/fo/model/MessageLightImpl.java
index 54fd2e06ed4d3bdf2220103a5cf0078412db65da..50eff107ceeb497669ea8adf932b327063b0bbc0 100644
--- a/src/main/java/org/olat/modules/fo/model/MessageLightImpl.java
+++ b/src/main/java/org/olat/modules/fo/model/MessageLightImpl.java
@@ -82,6 +82,9 @@ public class MessageLightImpl implements MessageLight, CreateInfo, Persistable,
 	private String title;
 	@Column(name="body", nullable=false, insertable=false, updatable=false)
 	private String body;
+	
+	@Column(name="modification_date", nullable=true, insertable=true, updatable=true)
+	private Date modificationDate;
 
 	@Column(name="parent_id", nullable=true, insertable=false, updatable=false)
 	private Long parentKey;
@@ -112,27 +115,33 @@ public class MessageLightImpl implements MessageLight, CreateInfo, Persistable,
 	public Date getCreationDate() {
 		return creationDate;
 	}
-	
+
+	@Override
 	public int getStatusCode() {
 		return statusCode;
 	}
-	
+
+	@Override
 	public String getTitle() {
 		return title;
 	}
 
+	@Override
 	public String getBody() {
 		return body;
 	}
 
+	@Override
 	public Identity getCreator() {
 		return creator;
 	}
 
+	@Override
 	public String getPseudonym() {
 		return pseudonym;
 	}
 
+	@Override
 	public boolean isGuest() {
 		return guest;
 	}
@@ -141,21 +150,30 @@ public class MessageLightImpl implements MessageLight, CreateInfo, Persistable,
 		return forumKey;
 	}
 
+	@Override
 	public Identity getModifier() {
 		return modifier;
 	}
 
+	@Override
 	public Long getParentKey() {
 		return parentKey;
 	}
 
+	@Override
 	public MessageRef getThreadtop() {
 		return threadtop;
 	}
 
+	@Override
 	public Date getLastModified() {
 		return lastModified;
 	}
+	
+	@Override
+	public Date getModificationDate() {
+		return modificationDate;
+	}
 
 	@Override
 	public int hashCode() {
diff --git a/src/main/java/org/olat/modules/fo/restapi/MessageVO.java b/src/main/java/org/olat/modules/fo/restapi/MessageVO.java
index 81b0399f74209f9f7b5790d7af079692f3b309b7..0652ff9df4983e81336be7461e21729cc033b93f 100644
--- a/src/main/java/org/olat/modules/fo/restapi/MessageVO.java
+++ b/src/main/java/org/olat/modules/fo/restapi/MessageVO.java
@@ -89,7 +89,7 @@ public class MessageVO {
 		body = message.getBody();
 		
 		Status messageStatus = Status.getStatus(message.getStatusCode());
-		sticky = new Boolean(messageStatus.isSticky());
+		sticky = Boolean.valueOf(messageStatus.isSticky());
 	}
 
 	public Long getKey() {
diff --git a/src/main/java/org/olat/modules/fo/ui/MessageEditController.java b/src/main/java/org/olat/modules/fo/ui/MessageEditController.java
index c2714266c793a80d0994f34f76cbb86c95e0aaae..c6e8abfe19ef777b8eb3997af1b2b706f753fb09 100644
--- a/src/main/java/org/olat/modules/fo/ui/MessageEditController.java
+++ b/src/main/java/org/olat/modules/fo/ui/MessageEditController.java
@@ -24,6 +24,7 @@ import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.List;
 
 import javax.persistence.PersistenceException;
@@ -559,7 +560,8 @@ public class MessageEditController extends FormBasicController {
 	private void commitEditMode() {
 		boolean children = fm.countMessageChildren(message.getKey()) > 0;
 		if (foCallback.mayEditMessageAsModerator() || (userIsMsgCreator && !children)) {
-			message.setModifier(getIdentity());	
+			message.setModifier(getIdentity());
+			message.setModificationDate(new Date());
 			message = fm.updateMessage(message, true);
 			persistTempUploadedFiles(message);
 			notifiySubscription();
diff --git a/src/main/java/org/olat/modules/fo/ui/MessageListController.java b/src/main/java/org/olat/modules/fo/ui/MessageListController.java
index 7d05902624a9ac97dca26fcbcae16d5dcb376f5c..d459573b058ebde3c0c2d9ab1d5e0e3bea12d0ad 100644
--- a/src/main/java/org/olat/modules/fo/ui/MessageListController.java
+++ b/src/main/java/org/olat/modules/fo/ui/MessageListController.java
@@ -600,6 +600,12 @@ public class MessageListController extends BasicController implements GenericEve
 				messageView.setModifierFirstName(modifier.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()));
 				messageView.setModifierLastName(modifier.getUser().getProperty(UserConstants.LASTNAME, getLocale()));
 			}
+			
+			if(m.getModificationDate() != null) {
+				messageView.setFormattedModificationDate(formatter.formatDateAndTime(m.getModificationDate()));
+			} else {
+				messageView.setFormattedModificationDate(messageView.getFormattedLastModified());
+			}	
 		} else {
 			messageView.setModified(false);
 		}
diff --git a/src/main/java/org/olat/modules/fo/ui/MessageView.java b/src/main/java/org/olat/modules/fo/ui/MessageView.java
index c92e46ad79b18c295f05c5bb4fab3405e679ddd9..2a419eef6456ed93c8ea47ac9ff6e7c87f81bd91 100644
--- a/src/main/java/org/olat/modules/fo/ui/MessageView.java
+++ b/src/main/java/org/olat/modules/fo/ui/MessageView.java
@@ -45,6 +45,7 @@ public class MessageView extends MessageLightView {
 	private String modifierFirstName;
 	private String modifierLastName;
 	private String modifierPseudonym;
+	private String formattedModificationDate;
 	
 	private String creatorFirstname;
 	private String creatorLastname;
@@ -140,6 +141,14 @@ public class MessageView extends MessageLightView {
 		this.modifierPseudonym = modifierPseudonym;
 	}
 
+	public String getFormattedModificationDate() {
+		return formattedModificationDate;
+	}
+
+	public void setFormattedModificationDate(String formattedModificationDate) {
+		this.formattedModificationDate = formattedModificationDate;
+	}
+
 	public boolean isAuthor() {
 		return author;
 	}
diff --git a/src/main/java/org/olat/modules/fo/ui/_content/threadview.html b/src/main/java/org/olat/modules/fo/ui/_content/threadview.html
index c56798979da302640d2e6064e352ed798c06c017..0516c20957da370a157d08bcd3e2e4ec34b6b67c 100644
--- a/src/main/java/org/olat/modules/fo/ui/_content/threadview.html
+++ b/src/main/java/org/olat/modules/fo/ui/_content/threadview.html
@@ -112,7 +112,7 @@
 					#else
 		   				$r.escapeHtml($message.modifierFirstName) $r.escapeHtml($message.modifierLastName)
 		   			#end
-		   			$message.formattedLastModified
+		   			$message.formattedModificationDate
 		   		#end
 		   		#if($message.moved)
 		   			$r.translate("msg.moved")
diff --git a/src/main/java/org/olat/modules/grading/ui/GradingAdminTemplatesController.java b/src/main/java/org/olat/modules/grading/ui/GradingAdminTemplatesController.java
index 2e099272351750dab0eabba318e93d6b6f1b6c74..d4f95b8f9529c36eadd9bcb90fbdf256b7312312 100644
--- a/src/main/java/org/olat/modules/grading/ui/GradingAdminTemplatesController.java
+++ b/src/main/java/org/olat/modules/grading/ui/GradingAdminTemplatesController.java
@@ -31,6 +31,7 @@ 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.util.Formatter;
 import org.olat.core.util.i18n.ui.SingleKeyTranslatorController;
 
 /**
@@ -55,30 +56,34 @@ public class GradingAdminTemplatesController extends FormBasicController {
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		// new grader
-		initForm("mail.to.grader.subject", "mail.grader.to.entry.subject", formLayout);
-		initForm("mail.to.grader.body", "mail.grader.to.entry.body", formLayout);
+		initForm("mail.to.grader.subject", "mail.grader.to.entry.subject", false, formLayout);
+		initForm("mail.to.grader.body", "mail.grader.to.entry.body", true, formLayout);
 		uifactory.addSpacerElement("spacer-new-grader", formLayout, false);
 		
 		// notifications
-		initForm("notification.subject", "mail.notification.subject", formLayout);
-		initForm("notification.body", "mail.notification.body", formLayout);
+		initForm("notification.subject", "mail.notification.subject", false, formLayout);
+		initForm("notification.body", "mail.notification.body", true, formLayout);
 		uifactory.addSpacerElement("spacer-notification", formLayout, false);
 		
 		// reminder 1
-		initForm("reminder.1.subject", "mail.reminder1.subject", formLayout);
-		initForm("reminder.1.body", "mail.reminder1.body", formLayout);
+		initForm("reminder.1.subject", "mail.reminder1.subject", false, formLayout);
+		initForm("reminder.1.body", "mail.reminder1.body", true, formLayout);
 		uifactory.addSpacerElement("spacer-1-reminder", formLayout, false);
 		
 		// reminder 2
-		initForm("reminder.2.subject", "mail.reminder2.subject", formLayout);
-		initForm("reminder.2.body", "mail.reminder2.body", formLayout);
+		initForm("reminder.2.subject", "mail.reminder2.subject", false, formLayout);
+		initForm("reminder.2.body", "mail.reminder2.body", true, formLayout);
 	}
 
-	private void initForm(String labelI18nKey, String textI18nKey, FormItemContainer formLayout) {
+	private void initForm(String labelI18nKey, String textI18nKey, boolean multiLines, FormItemContainer formLayout) {
 		String text = translate(textI18nKey);
+		if(multiLines) {
+			text = Formatter.escWithBR(text).toString();
+		}
+		
 		StaticTextElement viewEl = uifactory.addStaticTextElement("view." + counter++, labelI18nKey, text, formLayout);
 		FormLink translationLink = uifactory.addFormLink("translate." + counter++, "translate", null, formLayout, Link.LINK);
-		translationLink.setUserObject(new TranslationBundle(textI18nKey, labelI18nKey, viewEl));
+		translationLink.setUserObject(new TranslationBundle(textI18nKey, labelI18nKey, multiLines, viewEl));
 	}
 
 	@Override
@@ -124,7 +129,7 @@ public class GradingAdminTemplatesController extends FormBasicController {
 		if(guardModalController(translatorCtrl)) return;
 		
 		translatorCtrl = new SingleKeyTranslatorController(ureq, getWindowControl(), bundle.getI18nKey(),
-				GradingAdminTemplatesController.class);
+				GradingAdminTemplatesController.class, bundle.isMultiLines());
 		translatorCtrl.setUserObject(bundle);
 		listenTo(translatorCtrl);
 
@@ -135,18 +140,24 @@ public class GradingAdminTemplatesController extends FormBasicController {
 	}
 	
 	private void doUpdate(TranslationBundle bundle) {
-		bundle.getViewEl().setValue(translate(bundle.getI18nKey()));
+		String text = translate(bundle.getI18nKey());
+		if(bundle.isMultiLines()) {
+			text = Formatter.escWithBR(text).toString();
+		}
+		bundle.getViewEl().setValue(text);
 	}
 	
 	private static class TranslationBundle {
 		
 		private final String i18nKey;
+		private final boolean multiLines;
 		private final String labelI18nKey;
 		private final StaticTextElement viewEl;
 		
-		public TranslationBundle(String i18nKey, String labelI18nKey, StaticTextElement viewEl) {
+		public TranslationBundle(String i18nKey, String labelI18nKey, boolean multiLines, StaticTextElement viewEl) {
 			this.i18nKey = i18nKey;
 			this.viewEl = viewEl;
+			this.multiLines = multiLines;
 			this.labelI18nKey = labelI18nKey;
 		}
 
@@ -161,5 +172,9 @@ public class GradingAdminTemplatesController extends FormBasicController {
 		public String getLabelI18nKey() {
 			return labelI18nKey;
 		}
+
+		public boolean isMultiLines() {
+			return multiLines;
+		}
 	}
 }
diff --git a/src/main/java/org/olat/restapi/system/BigBlueButtonWebService.java b/src/main/java/org/olat/restapi/system/BigBlueButtonWebService.java
new file mode 100644
index 0000000000000000000000000000000000000000..68d185ab3e897a29a663607fdf91fba28af769f5
--- /dev/null
+++ b/src/main/java/org/olat/restapi/system/BigBlueButtonWebService.java
@@ -0,0 +1,80 @@
+package org.olat.restapi.system;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.modules.bigbluebutton.BigBlueButtonManager;
+import org.olat.modules.bigbluebutton.BigBlueButtonModule;
+import org.olat.modules.bigbluebutton.model.BigBlueButtonMeetingInfos;
+import org.olat.modules.bigbluebutton.model.BigBlueButtonServerInfos;
+import org.olat.restapi.system.vo.BigBlueButtonStatisticsVO;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+/**
+ * Initial Date:  2 jul. 2020 <br>
+ * @author mjenny, moritz.jenny@frentix.com, http://www.frentix.com
+ */
+
+public class BigBlueButtonWebService {
+	
+	/**
+	 * Return the statistics about Big Blue Button
+	 * 
+	 * @return The statistics about Big Blue Button
+	 */
+	@GET
+	@Operation(summary = "Return the statistics about Big Blue Button", description = "Return the statistics about Big Blue Button")
+	@ApiResponse(responseCode = "200", description = "Statistics about the Big Blue Button", content = {
+			@Content(mediaType = "application/json", schema = @Schema(implementation = BigBlueButtonStatisticsVO.class)),
+			@Content(mediaType = "application/xml", schema = @Schema(implementation = BigBlueButtonStatisticsVO.class)) })
+	@ApiResponse(responseCode = "401", description = "The roles of the authenticated user are not sufficient")
+	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+	
+	public Response getStatistics() {
+		BigBlueButtonStatisticsVO stats = getBigBlueButtonStatistics();
+		return Response.ok(stats).build();
+	}		
+
+	private BigBlueButtonStatisticsVO getBigBlueButtonStatistics() {
+		BigBlueButtonStatisticsVO stats = new BigBlueButtonStatisticsVO();
+		BigBlueButtonModule bbbModule = CoreSpringFactory.getImpl(BigBlueButtonModule.class);		
+		if(bbbModule.isEnabled()) {
+			BigBlueButtonManager bbbManager = CoreSpringFactory.getImpl(BigBlueButtonManager.class);			
+			long sumAttendees = 0;
+			long sumMeetings = 0;	
+			long recordingCount = 0;
+			long videoCount = 0;
+			long capacity = 0;
+			List<BigBlueButtonServerInfos> bbbServerInfos = bbbManager.filterServersInfos(bbbManager.getServersInfos());
+			for (int i = 0; i < bbbServerInfos.size(); i++) {
+				List<BigBlueButtonMeetingInfos> bbbMeetingInfos = bbbServerInfos.get(i).getMeetingsInfos();			
+				for(int j = 0; j < bbbMeetingInfos.size(); j++) {
+					if(bbbMeetingInfos.get(j).isRunning()) {
+						sumMeetings += 1;	
+						videoCount += bbbMeetingInfos.get(j).getVideoCount();
+						capacity += bbbMeetingInfos.get(j).getMaxUsers();
+						sumAttendees += bbbMeetingInfos.get(j).getParticipantCount();
+						if(bbbMeetingInfos.get(j).isRecording()){
+							recordingCount += 1;
+						}						
+					}
+				}				
+			}					
+			stats.setMeetingsCount(sumMeetings);
+			stats.setAttendeesCount(sumAttendees);
+			stats.setRecordingCount(recordingCount);
+			stats.setVideoCount(videoCount);
+			stats.setCapacity(capacity);
+		}		
+		return stats;
+	}
+}
diff --git a/src/main/java/org/olat/restapi/system/MonitoringWebService.java b/src/main/java/org/olat/restapi/system/MonitoringWebService.java
index 6cff96611e49849d0fd42e2d9f7e9038bfb0b67a..72ee48bca1cc5d715cd9e218363af7c79db0f552 100644
--- a/src/main/java/org/olat/restapi/system/MonitoringWebService.java
+++ b/src/main/java/org/olat/restapi/system/MonitoringWebService.java
@@ -51,6 +51,7 @@ public class MonitoringWebService {
 	private static final ThreadsWebService threadsWebService = new ThreadsWebService();
 	private static final OpenOLATStatisticsWebService ooStatsWebService = new OpenOLATStatisticsWebService();
 	private static final VFSStatsWebService vfsStatsWebService = new VFSStatsWebService();
+	private static final BigBlueButtonWebService bigBlueButtonWebService = new BigBlueButtonWebService();
 	
 	public MonitoringWebService() {
 		//make Spring happy
@@ -91,6 +92,11 @@ public class MonitoringWebService {
 		return vfsStatsWebService;
 	}
 	
+	@Path("bigbluebutton")
+	public BigBlueButtonWebService getBigBlueButtonStatistics() {
+		return bigBlueButtonWebService;
+	}
+	
 	
 	
 	/**
diff --git a/src/main/java/org/olat/restapi/system/vo/BigBlueButtonStatisticsVO.java b/src/main/java/org/olat/restapi/system/vo/BigBlueButtonStatisticsVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..2af61a6c7748767bb1726277c28c91e039ac129a
--- /dev/null
+++ b/src/main/java/org/olat/restapi/system/vo/BigBlueButtonStatisticsVO.java
@@ -0,0 +1,57 @@
+package org.olat.restapi.system.vo;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "bigBlueButtonStatisticsVO")
+public class BigBlueButtonStatisticsVO {
+	
+	private long attendeeCount;
+	private long meetingCount;
+	private long recordingCount;
+	private long videoCount;
+	private long maximalUserCount;
+	
+	
+	public long getAttendeesCount() {
+		return attendeeCount;
+	}
+
+	public void setAttendeesCount(long attendees) {
+		this.attendeeCount = attendees;
+	}
+	
+	public long getMeetingsCount() {
+		return meetingCount;
+	}
+
+	public void setMeetingsCount(long meetings) {
+		this.meetingCount = meetings;
+	}
+	
+	public long getRecordingCount() {
+		return recordingCount;
+	}
+
+	public void setRecordingCount(long recordingCount) {
+		this.recordingCount = recordingCount;
+	}
+	
+	public long getVideoCount() {
+		return videoCount;
+	}
+	
+	public void setVideoCount(long videoCount) {
+		this.videoCount = videoCount;
+	}
+	
+	public long getCapacity() {
+		return maximalUserCount;
+	}
+	
+	public void setCapacity(long capacity) {
+		this.maximalUserCount = capacity;
+	}
+}
diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
index a6905f231cd6743dcfdcabb7958f3ce8f90ee0f2..2ade21fdc7e4888020f748cddfae0d101d8e509e 100644
--- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
+++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
@@ -228,6 +228,10 @@
 					<constructor-arg index="0" value="OLAT_14.2.13" />
 					<property name="alterDbStatements" value="alter_14_2_x_to_14_2_13.sql" />
 				</bean>
+				<bean id="database_upgrade_14_2_16" class="org.olat.upgrade.DatabaseUpgrade">
+					<constructor-arg index="0" value="OLAT_14.2.16" />
+					<property name="alterDbStatements" value="alter_14_2_x_to_14_2_16.sql" />
+				</bean>
 				<bean id="database_upgrade_15_pre_0" class="org.olat.upgrade.DatabaseUpgrade">
 					<constructor-arg index="0" value="OLAT_15.pre.0" />
 					<property name="alterDbStatements" value="alter_14_2_x_to_15_pre_0.sql" />
diff --git a/src/main/java/org/olat/user/propertyhandlers/ui/UsrPropCfgTableController.java b/src/main/java/org/olat/user/propertyhandlers/ui/UsrPropCfgTableController.java
index a64e4f74bd3877c82a8962678b60811473130dd6..6471ddda91b8dd7e62c12a5d167576176097c46a 100644
--- a/src/main/java/org/olat/user/propertyhandlers/ui/UsrPropCfgTableController.java
+++ b/src/main/java/org/olat/user/propertyhandlers/ui/UsrPropCfgTableController.java
@@ -216,7 +216,7 @@ public class UsrPropCfgTableController extends FormBasicController {
 				String key2Translate2 = handler.i18nColumnDescriptorLabelKey();
 
 				String[] keys2Translate = { key2Translate1, key2Translate2 };
-				singleKeyTrnsCtrl = new SingleKeyTranslatorController(ureq, getWindowControl(), keys2Translate, UserPropertyHandler.class);
+				singleKeyTrnsCtrl = new SingleKeyTranslatorController(ureq, getWindowControl(), keys2Translate, UserPropertyHandler.class, false);
 				listenTo(singleKeyTrnsCtrl);
 				removeAsListenerAndDispose(translatorCallout);
 				translatorCallout = new CloseableCalloutWindowController(ureq, getWindowControl(), singleKeyTrnsCtrl.getInitialComponent(),
diff --git a/src/main/resources/database/mysql/alter_14_2_x_to_14_2_16.sql b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_16.sql
new file mode 100644
index 0000000000000000000000000000000000000000..653a4f486bedee86c8bc15089722c9553a662969
--- /dev/null
+++ b/src/main/resources/database/mysql/alter_14_2_x_to_14_2_16.sql
@@ -0,0 +1,3 @@
+-- Forum
+alter table o_message add column modification_date datetime;
+
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index 9bfb12beabfff6678776124b8e0fb511529ecc56..29174514bbb946619838e65bb5425ffdf5ea5e21 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -418,6 +418,7 @@ create table if not exists o_message (
    topthread_id bigint,
    creator_id bigint,
    modifier_id bigint,
+   modification_date datetime,
    forum_fk bigint,
    statuscode integer,
    numofwords integer,
diff --git a/src/main/resources/database/oracle/alter_14_2_x_to_14_2_16.sql b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_16.sql
new file mode 100644
index 0000000000000000000000000000000000000000..97b1f33e661210eee5299a077450552c549342a0
--- /dev/null
+++ b/src/main/resources/database/oracle/alter_14_2_x_to_14_2_16.sql
@@ -0,0 +1,3 @@
+-- Forum
+alter table o_message add modification_date date;
+
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index 5f911680e7fca659a629c14b48a404807e76656f..ac8aaed042648df1c43faddd23c79ade34bdced0 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -456,6 +456,7 @@ CREATE TABLE o_message (
   topthread_id number(20),
   creator_id number(20),
   modifier_id number(20),
+  modification_date date,
   forum_fk number(20),
   statuscode number(11),
   numofwords number(11),
diff --git a/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_16.sql b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_16.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9b1f658350230fa7de3b8ab00c02ea608db1ef88
--- /dev/null
+++ b/src/main/resources/database/postgresql/alter_14_2_x_to_14_2_16.sql
@@ -0,0 +1,3 @@
+-- Forum
+alter table o_message add column modification_date timestamp;
+
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index e386e52daadd02cac208a6ef8b96c5caa26ac29d..1064d3b87f426294062fc2f0bc8596dda9241948 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -416,6 +416,7 @@ create table o_message (
    topthread_id int8,
    creator_id int8,
    modifier_id int8,
+   modification_date timestamp,
    forum_fk int8,
    statuscode int4,
    numofwords int4,
diff --git a/src/test/java/org/olat/course/assessment/manager/AssessmentModeManagerTest.java b/src/test/java/org/olat/course/assessment/manager/AssessmentModeManagerTest.java
index e102c9d950776d481803c369f13e11ad0e04a7b8..f2fa98477781eaf41a01de760cce6347ae20bdf7 100644
--- a/src/test/java/org/olat/course/assessment/manager/AssessmentModeManagerTest.java
+++ b/src/test/java/org/olat/course/assessment/manager/AssessmentModeManagerTest.java
@@ -994,8 +994,54 @@ public class AssessmentModeManagerTest extends OlatTestCase {
 		Assert.assertTrue(assessedIdentityKeys.contains(participant1.getKey()));
 		Assert.assertTrue(assessedIdentityKeys.contains(coach2.getKey()));
 		Assert.assertTrue(assessedIdentityKeys.contains(participant2.getKey()));
-	}	
+	}
 	
+	@Test
+	public void getAssessedIdentities_course_curriculumElements() {
+		Identity author = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-39");
+		RepositoryEntry entry = JunitTestHelper.deployBasicCourse(author);
+		Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-40");
+		Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-41");
+		Identity coach1 = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-42");
+		
+		Curriculum curriculum = curriculumService.createCurriculum("cur-as-mode-4", "Curriculum for assessment", "Curriculum", null);
+		CurriculumElement element1 = curriculumService.createCurriculumElement("Element-for-rel-1", "Element for assessment",  CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		CurriculumElement element2 = curriculumService.createCurriculumElement("Element-for-rel-2", "Element for assessment",  CurriculumElementStatus.active,
+				null, null, null, null, CurriculumCalendars.disabled, CurriculumLectures.disabled, CurriculumLearningProgress.disabled, curriculum);
+		
+		dbInstance.commit();
+		curriculumService.addRepositoryEntry(element1, entry, false);
+		curriculumService.addRepositoryEntry(element2, entry, false);
+		curriculumService.addMember(element1, participant1, CurriculumRoles.participant);
+		curriculumService.addMember(element2, participant2, CurriculumRoles.participant);
+		curriculumService.addMember(element1, coach1, CurriculumRoles.coach);
+		curriculumService.addMember(element2, coach1, CurriculumRoles.coach);
+		dbInstance.commitAndCloseSession();
+		
+		Identity participant3 = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-23");
+		Identity coach2 = JunitTestHelper.createAndPersistIdentityAsRndUser("as-mode-24");
+		repositoryEntryRelationDao.addRole(participant3, entry, GroupRoles.participant.name());
+		repositoryEntryRelationDao.addRole(coach2, entry, GroupRoles.coach.name());
+		repositoryEntryRelationDao.addRole(author, entry, GroupRoles.owner.name());
+		
+		AssessmentMode mode = createMinimalAssessmentmode(entry);
+		mode.setTargetAudience(AssessmentMode.Target.curriculumEls);
+		mode.setApplySettingsForCoach(true);
+		mode = assessmentModeMgr.persist(mode);
+
+		AssessmentModeToCurriculumElement modeToElement = assessmentModeMgr.createAssessmentModeToCurriculumElement(mode, element1);
+		mode.getCurriculumElements().add(modeToElement);
+		mode = assessmentModeMgr.merge(mode, true);
+		dbInstance.commitAndCloseSession();
+		Assert.assertNotNull(mode);
+
+		Set<Long> assessedIdentityKeys = assessmentModeMgr.getAssessedIdentityKeys(mode);
+		Assert.assertNotNull(assessedIdentityKeys);
+		Assert.assertEquals(2, assessedIdentityKeys.size());
+		Assert.assertTrue(assessedIdentityKeys.contains(coach1.getKey()));
+		Assert.assertTrue(assessedIdentityKeys.contains(participant1.getKey()));
+	}
 	
 	@Test
 	public void isIpAllowed_exactMatch() {