diff --git a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java
index 03ed08d5c9eb18e14d12ec95cbaa324fa2c7c65a..47782ba9b0e7388d25f54586716ea91e18e2e8dd 100644
--- a/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java
+++ b/src/main/java/org/olat/core/util/openxml/HTMLToOpenXMLHandler.java
@@ -62,6 +62,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler {
 	private Table currentTable;
 	private Element currentParagraph;
 	private ListParagraph currentListParagraph;
+	private boolean pNeedNewParagraph = true;
 	
 	public HTMLToOpenXMLHandler(OpenXMLDocument document, Element paragraph) {
 		this.factory = document;
@@ -153,7 +154,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler {
 			}
 		} else {
 			Element currentRun = getCurrentRun();
-			String text = textBuffer.toString();
+			String text = textBuffer.toString().replace("\n", "").replace("\r", "");
 			if(text.length() > 0 && Character.isSpaceChar(text.charAt(0))) {
 				currentRun.appendChild(factory.createPreserveSpaceEl());
 			}
@@ -318,7 +319,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler {
 	public void startElement(String uri, String localName, String qName, Attributes attributes) {
 		String tag = localName.toLowerCase();
 		if("p".equalsIgnoreCase(tag)) {
-			getCurrentParagraph(true);
+			getCurrentParagraph(pNeedNewParagraph);
 		} else if("span".equalsIgnoreCase(tag)) {
 			flushText();
 
@@ -361,6 +362,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler {
 			Style[] styles = setTextPreferences(Style.italic);
 			styleStack.add(new StyleStatus(tag, true, styles));
 			appendParagraph(new Spacing(90, 0));
+			pNeedNewParagraph = false;
 		} else if("div".equals(tag)) {
 			String cl = attributes.getValue("class");
 			if(StringHelper.containsNonWhitespace(cl)) {
@@ -368,6 +370,7 @@ public class HTMLToOpenXMLHandler extends DefaultHandler {
 					Style[] styles = setTextPreferences(Style.italic);
 					styleStack.add(new StyleStatus(tag, true, styles));
 					appendParagraph(new Spacing(120, 0));
+					pNeedNewParagraph = false;
 				} else {
 					styleStack.add(new StyleStatus(tag, new Style[0]));
 				}
diff --git a/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java b/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
index 71fe27d81280a1ad21ae665b9de534cb491bb937..4c9ed78bc6643109a5387306c7d43dd6d0c03ddf 100644
--- a/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
+++ b/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
@@ -99,7 +99,11 @@ public class ForumNotificationsHandler extends LogDelegator implements Notificat
 					
 					String name;
 					if(modifier != null) {
-						name = NotificationHelper.getFormatedName(modifier);
+						if(modifier.equals(creator) && StringHelper.containsNonWhitespace(mInfo.getPseudonym())) {
+							name = mInfo.getPseudonym();
+						} else {
+							name = NotificationHelper.getFormatedName(modifier);
+						}
 					} else if(StringHelper.containsNonWhitespace(mInfo.getPseudonym())) {
 						name = mInfo.getPseudonym();
 					} else if(mInfo.isGuest()) {
diff --git a/src/main/java/org/olat/modules/fo/archiver/formatters/ForumOpenXMLFormatter.java b/src/main/java/org/olat/modules/fo/archiver/formatters/ForumOpenXMLFormatter.java
index 820f60cdf6c46a7ff9ad2e4a96f0e7bc4b549bba..d89aff44d36a62b68bf8f218b76a8bfbaa7b61a0 100644
--- a/src/main/java/org/olat/modules/fo/archiver/formatters/ForumOpenXMLFormatter.java
+++ b/src/main/java/org/olat/modules/fo/archiver/formatters/ForumOpenXMLFormatter.java
@@ -98,6 +98,7 @@ public class ForumOpenXMLFormatter extends ForumFormatter {
 		MessageNode m = (MessageNode) node;
 		
 		StringBuilder creatorAndDate = new StringBuilder();
+		Identity creator = m.getCreator();
 		if(StringHelper.containsNonWhitespace(m.getPseudonym())) {
 			creatorAndDate.append(m.getPseudonym())
 			  .append(" ");
@@ -108,17 +109,19 @@ public class ForumOpenXMLFormatter extends ForumFormatter {
 			}
 		} else if(m.isGuest()) {
 			creatorAndDate.append(translator.translate("guest"));
-		} else {
-			creatorAndDate.append(m.getCreator().getUser().getProperty(UserConstants.FIRSTNAME, null));
+		} else if(creator != null) {
+			creatorAndDate.append(creator.getUser().getProperty(UserConstants.FIRSTNAME, null));
 			creatorAndDate.append(" ");
-			creatorAndDate.append(m.getCreator().getUser().getProperty(UserConstants.LASTNAME, null));
+			creatorAndDate.append(creator.getUser().getProperty(UserConstants.LASTNAME, null));
+		} else {
+			creatorAndDate.append("???");
 		}
 		creatorAndDate.append(" ");
 		creatorAndDate.append(formatter.formatDateAndTime(m.getCreationDate()));
 
 		if (isTopThread) {
 			document.appendHeading1(m.getTitle(), creatorAndDate.toString());
-			this.isTopThread = false;
+			isTopThread = false;
 		} else {
 			document.appendHeading2(m.getTitle(), creatorAndDate.toString());
 		}
@@ -126,16 +129,29 @@ public class ForumOpenXMLFormatter extends ForumFormatter {
 		Identity modifier = m.getModifier();
 		if (modifier != null) {
 			StringBuilder modSb = new StringBuilder();
-			modSb.append(translator.translate("msg.modified")).append(": ")
-			     .append(modifier.getUser().getProperty(UserConstants.FIRSTNAME, null))
-			     .append(" ")
-			     .append(modifier.getUser().getProperty(UserConstants.LASTNAME,  null))
-			     .append(" ")
-			     .append(formatter.formatDateAndTime(m.getModifiedDate()));
+			if(modifier.equals(creator) && StringHelper.containsNonWhitespace(m.getPseudonym())) {
+				modSb.append(m.getPseudonym())
+				  .append(" ");
+				if(m.isGuest()) {
+					modSb.append(translator.translate("guest.suffix"));
+				} else {
+					modSb.append(translator.translate("pseudonym.suffix"));
+				}
+			} else {
+				modSb.append(translator.translate("msg.modified")).append(": ")
+				     .append(modifier.getUser().getProperty(UserConstants.FIRSTNAME, null))
+				     .append(" ")
+				     .append(modifier.getUser().getProperty(UserConstants.LASTNAME,  null))
+				     .append(" ")
+				     .append(formatter.formatDateAndTime(m.getModifiedDate()));
+			}
 			document.appendSubtitle(modSb.toString());
 		}
 		
 		String body = m.getBody();
+		if(body != null) {
+			body = body.replace("<p>&nbsp;", "<p>");
+		}
 		document.appendHtmlText(body, new Spacing(180, 0));
 		
 		// message attachments
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 8edf6fd3ef7949744ef21825db233deae6681ec6..854a129a27d7a6d9db399667f6a9d321f2fadb08 100644
--- a/src/main/java/org/olat/modules/fo/manager/ForumManager.java
+++ b/src/main/java/org/olat/modules/fo/manager/ForumManager.java
@@ -170,20 +170,7 @@ public class ForumManager {
 	 */
 	public List<Message> getMessagesByForum(Forum forum){
 		if (forum == null) return new ArrayList<Message>(0); // fxdiff: while indexing it can somehow occur, that forum is null!
-		return getMessagesByForumID(forum.getKey(),  0, -1, null, true);
-	}
-	
-	/**
-	 * 
-	 * @param forum_id
-	 * @param start
-	 * @param limit
-	 * @param orderBy
-	 * @param asc
-	 * @return
-	 */
-	public List<Message> getMessagesByForumID(Long forum_id, int firstResult, int maxResults, Message.OrderBy orderBy, boolean asc) {
-		return getMessagesByForumID(forum_id, firstResult, maxResults, false, orderBy, asc);
+		return getMessagesByForumID(forum.getKey(),  0, -1, false, null, true);
 	}
 	
 	/**
@@ -286,12 +273,13 @@ public class ForumManager {
 		  .append(" ) as numOfMessages")
 		  .append(" , (select max(replies.lastModified) from fomessage as replies")
 		  .append("  where replies.threadtop.key=msg.key and replies.forum.key=:forumKey")
-		  .append(" ) as lastModified")
-		  .append(" , (select count(read.key) from foreadmessage as read, fomessage as posts")
-		  .append("  where (posts.threadtop.key=msg.key or posts.key=msg.key) and read.message.key=posts.key and read.identity.key=:identityKey")
-		  .append(" ) as numOfReadMessages");
+		  .append(" ) as lastModified");
+
 		if(identity != null) {
-			sb.append(" ,(select count(mark.key) from ").append(MarkImpl.class.getName()).append(" as mark, fomessage as mposts ")
+			sb.append(" , (select count(read.key) from foreadmessage as read, fomessage as posts")
+			  .append("  where (posts.threadtop.key=msg.key or posts.key=msg.key) and read.message.key=posts.key and read.identity.key=:identityKey")
+			  .append(" ) as numOfReadMessages")
+			  .append(" ,(select count(mark.key) from ").append(MarkImpl.class.getName()).append(" as mark, fomessage as mposts ")
 			  .append("   where mark.creator.key=:identityKey and mark.resId=:forumKey and (mposts.threadtop.key=msg.key or mposts.key=msg.key)")
 			  .append("    and mposts.key=cast(mark.resSubPath as long) and mark.resName='Forum'")
 			  .append(" ) as marks");
@@ -303,10 +291,11 @@ public class ForumManager {
 
 		TypedQuery<Object[]> objectsQuery = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Object[].class)
-				.setParameter("forumKey", forum.getKey())
-				.setParameter("identityKey", identity.getKey());
+				.setParameter("forumKey", forum.getKey());
+		if(identity != null) {
+			objectsQuery.setParameter("identityKey", identity.getKey());
+		}
 
-		
 		List<Object[]> objects = objectsQuery.getResultList();
 		List<ForumThread> threadList = new ArrayList<>(objects.size());
 		for(Object[] object:objects) {
@@ -317,11 +306,11 @@ public class ForumManager {
 			String creator = userManager.getUserDisplayName(msg.getCreator());
 			ForumThread thread = new ForumThread(msg, creator, lastModifed, numOfMessages);
 
-			Number readMessages = (Number)object[3];
-			int numOfReadMessages = readMessages == null ? 0 : readMessages.intValue();
-			thread.setNewMessages(numOfMessages - numOfReadMessages);
-			
 			if(identity != null) {
+				Number readMessages = (Number)object[3];
+				int numOfReadMessages = readMessages == null ? 0 : readMessages.intValue();
+				thread.setNewMessages(numOfMessages - numOfReadMessages);
+
 				Number numOfMarkedMessagesLong = (Number)object[4];
 				int numOfMarkedMessages = numOfMarkedMessagesLong == null ? 0 : numOfMarkedMessagesLong.intValue();
 				thread.setMarkedMessages(numOfMarkedMessages);
@@ -851,31 +840,23 @@ public class ForumManager {
 		} else {	
 			//it only make sense to split a thread if the current message is not a threadtop message.	
 			List<Message> threadList = getThread(msg.getThreadtop().getKey());
-			List<Message> subthreadList = new ArrayList<Message>();
-			subthreadList.add(msg);
+			List<Message> subthreadList = new ArrayList<>();
 			getSubthread(msg, threadList, subthreadList);
 
-			Iterator<Message> messageIterator = subthreadList.iterator();
-			Message firstMessage = null;
-			if (messageIterator.hasNext()) {
-				firstMessage = messageIterator.next();
-				firstMessage = getMessageById(firstMessage.getKey());
-				firstMessage.setParent(null);
-				firstMessage.setThreadtop(null);
-				updateMessage(firstMessage, false);
-				newTopMessage = firstMessage;
-			}
-			while (firstMessage != null && messageIterator.hasNext()) {
-				Message message = messageIterator.next();
-				message = getMessageById(firstMessage.getKey());
-				message.setThreadtop(firstMessage);
-				updateMessage(message, false);
+			newTopMessage = getMessageById(msg.getKey());
+			newTopMessage.setParent(null);
+			newTopMessage.setThreadtop(null);
+			newTopMessage = dbInstance.getCurrentEntityManager().merge(newTopMessage);
+
+			for(Message message : subthreadList) {
+				message.setThreadtop(newTopMessage);
+				message = dbInstance.getCurrentEntityManager().merge(message);
 			}
 
 			dbInstance.commit();// before sending async event
 			ForumChangedEvent event = new ForumChangedEvent(ForumChangedEvent.SPLIT, newTopMessage.getKey(), null, null);
 			CoordinatorManager.getInstance().getCoordinator().getEventBus()
-				.fireEventToListenersOf(event, firstMessage.getForum());
+				.fireEventToListenersOf(event, newTopMessage.getForum());
 		}		
 		return newTopMessage;
 	}
diff --git a/src/main/java/org/olat/modules/fo/ui/ForumController.java b/src/main/java/org/olat/modules/fo/ui/ForumController.java
index 67d9b37c25e5c197475bb61f4174ceb4f2a9d3bd..e32bc7198a3ed57c56d57977fdcee568c6db7026 100644
--- a/src/main/java/org/olat/modules/fo/ui/ForumController.java
+++ b/src/main/java/org/olat/modules/fo/ui/ForumController.java
@@ -49,6 +49,7 @@ import org.olat.modules.fo.Forum;
 import org.olat.modules.fo.ForumCallback;
 import org.olat.modules.fo.ForumChangedEvent;
 import org.olat.modules.fo.Message;
+import org.olat.modules.fo.Status;
 import org.olat.modules.fo.manager.ForumManager;
 import org.olat.modules.fo.ui.events.SelectMessageEvent;
 import org.olat.modules.fo.ui.events.SelectUserEvent;
@@ -159,18 +160,19 @@ public class ForumController extends BasicController implements GenericEventList
 					if (message != null) {
 						doThreadList(ureq);
 						Message thread = message.getThreadtop() == null ? message : message.getThreadtop();
-
-						String subType = null;
-						if(entries.size() > 1) {
-							subType = entries.get(1).getOLATResourceable().getResourceableTypeName();
-						}
-						
-						if("Marked".equalsIgnoreCase(subType)) {
-							doMarkedView(ureq, thread, message);
-						} else if("New".equalsIgnoreCase(subType)) {
-							doMarkedView(ureq, thread, message);
-						} else {
-							doThreadView(ureq, thread, message);
+						if(focallback.mayEditMessageAsModerator() || !Status.getStatus(thread.getStatusCode()).isHidden()) {
+							String subType = null;
+							if(entries.size() > 1) {
+								subType = entries.get(1).getOLATResourceable().getResourceableTypeName();
+							}
+							
+							if("Marked".equalsIgnoreCase(subType)) {
+								doMarkedView(ureq, thread, message);
+							} else if("New".equalsIgnoreCase(subType)) {
+								doMarkedView(ureq, thread, message);
+							} else {
+								doThreadView(ureq, thread, message);
+							}
 						}
 					}
 				}
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 74f853c2f7e19ddedd29fb9945684e0b1b344f38..79edbd2479750f21fc530e7a7c764793cdca6063 100644
--- a/src/main/java/org/olat/modules/fo/ui/MessageEditController.java
+++ b/src/main/java/org/olat/modules/fo/ui/MessageEditController.java
@@ -370,11 +370,7 @@ public class MessageEditController extends FormBasicController {
 		// set values from form to message
 		message.setTitle(titleEl.getValue());
 		String body = bodyEl.getValue();
-		
-		String tinyBlanc = "<p>&nbsp;";
-		if(body.startsWith(tinyBlanc)) {
-			body = "<p>" + body.substring(tinyBlanc.length(), body.length());
-		}
+		body = body.replace("<p>&nbsp;", "<p>");
 
 		message.setBody(body.trim());
 		if(usePseudonymEl != null && (usePseudonymEl.isAtLeastSelected(1) || guestOnly)) {
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 cec16e96eb57ae56d5997f0056061d3fc4109897..31f377a30d8e9fea658734c787a04dafbedf8a77 100644
--- a/src/main/java/org/olat/modules/fo/ui/MessageListController.java
+++ b/src/main/java/org/olat/modules/fo/ui/MessageListController.java
@@ -490,17 +490,21 @@ public class MessageListController extends BasicController implements GenericEve
 		// add some data now
 		messageView.setFormattedCreationDate(formatter.formatDateAndTime(m.getCreationDate()));
 		messageView.setFormattedLastModified(formatter.formatDateAndTime(m.getLastModified()));
-		
+
+		Identity creator = m.getCreator();
 		Identity modifier = m.getModifier();
 		if (modifier != null) {
 			messageView.setModified(true);
-			messageView.setModifierFirstName(modifier.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()));
-			messageView.setModifierLastName(modifier.getUser().getProperty(UserConstants.LASTNAME, getLocale()));
+			if(modifier.equals(creator) && StringHelper.containsNonWhitespace(m.getPseudonym())) {
+				messageView.setModifierPseudonym(m.getPseudonym());
+			} else {
+				messageView.setModifierFirstName(modifier.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()));
+				messageView.setModifierLastName(modifier.getUser().getProperty(UserConstants.LASTNAME, getLocale()));
+			}
 		} else {
 			messageView.setModified(false);
 		}
 		
-		Identity creator = m.getCreator();
 		boolean userIsMsgCreator = false;
 		//keeps the first 15 chars
 		if(creator != null) {
@@ -535,9 +539,9 @@ public class MessageListController extends BasicController implements GenericEve
 		}
 		messageView.setClosed(isThreadClosed);
 		
-		if(!guestOnly && !m.isGuest() && !StringHelper.containsNonWhitespace(m.getPseudonym())) {
+		if(!guestOnly && !m.isGuest() && creator != null && !StringHelper.containsNonWhitespace(m.getPseudonym())) {
 			// add portrait to map for later disposal and key for rendering in velocity
-			DisplayPortraitController portrait = new DisplayPortraitController(ureq, getWindowControl(), m.getCreator(), true, true, false, true);
+			DisplayPortraitController portrait = new DisplayPortraitController(ureq, getWindowControl(), creator, true, true, false, true);
 			messageView.setPortrait(portrait);
 			mainVC.put("portrait_".concat(keyString), portrait.getInitialComponent());
 		  
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 1c2ee21dcc7f220fb858ac7ff2963319a7191a15..55d307d386996a8aac7135b65498f98d50f8e9c6 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 boolean modified;
 	private String modifierFirstName;
 	private String modifierLastName;
+	private String modifierPseudonym;
 	
 	private String creatorFirstname;
 	private String creatorLastname;
@@ -152,6 +153,14 @@ public class MessageView extends MessageLightView {
 		this.modifierLastName = modifierLastName;
 	}
 
+	public String getModifierPseudonym() {
+		return modifierPseudonym;
+	}
+
+	public void setModifierPseudonym(String modifierPseudonym) {
+		this.modifierPseudonym = modifierPseudonym;
+	}
+
 	public boolean isAuthor() {
 		return author;
 	}
diff --git a/src/main/java/org/olat/modules/fo/ui/StatusTypeCellRenderer.java b/src/main/java/org/olat/modules/fo/ui/StatusTypeCellRenderer.java
index a64d6e8f1c056d1822e9b5b9c52e9f17bd9d51c4..972a40f153714038b1e831f03b870137524033b4 100644
--- a/src/main/java/org/olat/modules/fo/ui/StatusTypeCellRenderer.java
+++ b/src/main/java/org/olat/modules/fo/ui/StatusTypeCellRenderer.java
@@ -43,17 +43,18 @@ public class StatusTypeCellRenderer implements FlexiCellRenderer {
 			int status = ((Number)cellValue).intValue();
 			
 			Status messageStatus = Status.getStatus(status);
-			boolean isSticky = messageStatus.isSticky(); 
-			boolean isClosed = messageStatus.isClosed(); 
+			boolean isSticky = messageStatus.isSticky();
+			boolean isClosed = messageStatus.isClosed();
 
 			target.append("<i class='o_icon o_forum_");
-
 			if(isSticky && isClosed) {
 				target.append("status_sticky_closed");
-			} else if (isSticky) {
+			} else if(isSticky) {
 				target.append("status_sticky");
-			} else if (isClosed) {
+			} else if(isClosed) {
 				target.append("status_closed");
+			} else if(messageStatus.isHidden()) {
+				target.append("status_hidden");
 			} else {
 				target.append("status_thread");
 			}
diff --git a/src/main/java/org/olat/modules/fo/ui/ThreadListController.java b/src/main/java/org/olat/modules/fo/ui/ThreadListController.java
index b89ee13a7006995ef07647dbf3434845f23198cd..c8f29e55f3d0f031618bc2cb2934a5d6579179e0 100644
--- a/src/main/java/org/olat/modules/fo/ui/ThreadListController.java
+++ b/src/main/java/org/olat/modules/fo/ui/ThreadListController.java
@@ -19,6 +19,7 @@
  */
 package org.olat.modules.fo.ui;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.olat.basesecurity.BaseSecurityModule;
@@ -53,6 +54,7 @@ import org.olat.modules.fo.Forum;
 import org.olat.modules.fo.ForumCallback;
 import org.olat.modules.fo.Message;
 import org.olat.modules.fo.MessageRef;
+import org.olat.modules.fo.Status;
 import org.olat.modules.fo.archiver.formatters.ForumDownloadResource;
 import org.olat.modules.fo.manager.ForumManager;
 import org.olat.modules.fo.model.ForumThread;
@@ -158,6 +160,14 @@ public class ThreadListController extends FormBasicController {
 	public void loadModel() {
 		Identity identity = guestOnly ? null : getIdentity();
 		List<ForumThread> threads = forumManager.getForumThreads(forum, identity);
+		if(!foCallback.mayEditMessageAsModerator()) {
+			for(Iterator<ForumThread> threadIt=threads.iterator(); threadIt.hasNext(); ) {
+				if(Status.getStatus(threadIt.next().getStatusCode()).isHidden()) {
+					threadIt.remove();
+				}
+			}	
+		}
+		
 		threadTableModel.setObjects(threads);
 		threadTableModel.sort(new SortKey(ThreadListCols.thread.name(), true));
 		threadTable.reloadData();
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 7c3cf9c85100ad70c2b059fdf1c13cfdfec14d7a..4e3246fb2f519b66b8b8cb993184a2db4d96e380 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
@@ -105,7 +105,13 @@
 			#if($message.modified || $message.moved)
 			<div class="o_modified">
 				#if($message.modified)
-		   			$r.translate("msg.modified"): $r.escapeHtml($message.modifierFirstName) $r.escapeHtml($message.modifierLastName) $message.formattedLastModified
+					$r.translate("msg.modified"):
+					#if($message.modifierPseudonym && !${message.modifierPseudonym.isEmpty()})
+						$r.escapeHtml($message.modifierPseudonym)
+					#else
+		   				$r.escapeHtml($message.modifierFirstName) $r.escapeHtml($message.modifierLastName)
+		   			#end
+		   			$message.formattedLastModified
 		   		#end
 		   		#if($message.moved)
 		   			$r.translate("msg.moved")
diff --git a/src/test/java/org/olat/modules/fo/ForumManagerTest.java b/src/test/java/org/olat/modules/fo/ForumManagerTest.java
index 762ea205abe032933856f5b49507c7929a5ad00a..1d9a09babb2ffa041d87b84d1ebf27eac43ad17c 100644
--- a/src/test/java/org/olat/modules/fo/ForumManagerTest.java
+++ b/src/test/java/org/olat/modules/fo/ForumManagerTest.java
@@ -711,6 +711,261 @@ public class ForumManagerTest extends OlatTestCase {
 		Assert.assertEquals(2, olderLastMessages.size());
 	}
 	
+	@Test
+	public void moveMessage() {
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("fo-1");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("fo-2");
+		Identity guest3 =  securityManager.getAndUpdateAnonymousUserForLanguage(Locale.ENGLISH);
+		Forum fo = forumManager.addAForum();
+		dbInstance.commit();
+		
+		// thread
+		// -> message
+		// -> -> message to move
+		// -> -> -> message child 1
+		// -> -> -> -> message child 1.1
+		// -> -> -> message child 2
+		// -> -> message staying
+
+		Message topMessage = forumManager.createMessage(fo, id1, false);
+		topMessage.setTitle("Thread move message");
+		topMessage.setBody("Thread move message");
+		forumManager.addTopMessage(topMessage);
+		dbInstance.commit();
+
+		Message message = forumManager.createMessage(fo, id2, false);
+		message.setTitle("Re: Thread move message");
+		message.setBody("Thread move message");
+		forumManager.replyToMessage(message, topMessage);
+		dbInstance.commit();
+		
+		Message messageToMove = forumManager.createMessage(fo, id2, false);
+		messageToMove.setTitle("Message to move");
+		messageToMove.setBody("Thread move message");
+		forumManager.replyToMessage(messageToMove, message);
+		dbInstance.commit();
+		
+		Message messageToMove_1 = forumManager.createMessage(fo, id2, false);
+		messageToMove_1.setTitle("Re: Message to move 1");
+		messageToMove_1.setBody("Thread move message");
+		forumManager.replyToMessage(messageToMove_1, messageToMove);
+		dbInstance.commit();
+		
+		Message messageToMove_1_1 = forumManager.createMessage(fo, guest3, true);
+		messageToMove_1_1.setTitle("Re: Message to move 1");
+		messageToMove_1_1.setBody("Thread move message");
+		forumManager.replyToMessage(messageToMove_1_1, messageToMove_1);
+		dbInstance.commit();
+		
+		Message messageToMove_2 = forumManager.createMessage(fo, id2, false);
+		messageToMove_2.setTitle("Re: Message to move 2");
+		messageToMove_2.setBody("Thread move message");
+		forumManager.replyToMessage(messageToMove_2, messageToMove);
+		dbInstance.commit();
+		
+		Message messageToStay = forumManager.createMessage(fo, id2, false);
+		messageToStay.setTitle("Message to stay");
+		messageToStay.setBody("Thread move message");
+		forumManager.replyToMessage(messageToStay, message);
+		dbInstance.commit();
+			
+		Message targetThread = forumManager.createMessage(fo, id2, false);
+		targetThread.setTitle("Target thread");
+		targetThread.setBody("Target thread");
+		forumManager.addTopMessage(targetThread);
+		dbInstance.commit();
+		
+		Message targetMessage = forumManager.createMessage(fo, id2, false);
+		targetMessage.setTitle("Message to stay");
+		targetMessage.setBody("Thread move message");
+		forumManager.replyToMessage(targetMessage, targetThread);
+		dbInstance.commit();
+		
+		//move the message
+		Message movedMessage = forumManager.moveMessage(messageToMove, targetMessage);
+		dbInstance.commitAndCloseSession();
+		
+		//check target thread
+		List<Message> targetMessages = forumManager.getThread(targetThread.getKey());
+		Assert.assertEquals(3, targetMessages.size());
+		Assert.assertTrue(targetMessages.contains(targetThread));
+		Assert.assertTrue(targetMessages.contains(targetMessage));
+		Assert.assertTrue(targetMessages.contains(movedMessage));
+		
+		//check thread and parent of the target thread
+		Message reloadedTargetThread = forumManager.getMessageById(targetThread.getKey());
+		Assert.assertNull(reloadedTargetThread.getThreadtop());
+		Assert.assertNull(reloadedTargetThread.getParent());
+
+		Message reloadedTargetMessage = forumManager.getMessageById(targetMessage.getKey());
+		Assert.assertEquals(targetThread, reloadedTargetMessage.getThreadtop());
+		Assert.assertEquals(targetThread, reloadedTargetMessage.getParent());
+
+		Message reloadedMovedMessage = forumManager.getMessageById(movedMessage.getKey());
+		Assert.assertEquals(targetThread, reloadedMovedMessage.getThreadtop());
+		Assert.assertEquals(targetMessage, reloadedMovedMessage.getParent());
+		
+		//check original thread
+		List<Message> originMessages = forumManager.getThread(topMessage.getKey());
+		Assert.assertEquals(6, originMessages.size());
+		Assert.assertTrue(originMessages.contains(topMessage));
+		Assert.assertTrue(originMessages.contains(message));
+		Assert.assertTrue(originMessages.contains(messageToStay));
+		Assert.assertTrue(originMessages.contains(messageToMove_1));
+		Assert.assertTrue(originMessages.contains(messageToMove_1_1));
+		Assert.assertTrue(originMessages.contains(messageToMove_2));
+		Assert.assertFalse(originMessages.contains(movedMessage));
+		Assert.assertFalse(originMessages.contains(messageToMove));
+		
+		// thread
+		// -> message
+		// -> -> message child 1
+		// -> -> -> message child 1.1
+		// -> -> message child 2
+		// -> -> message staying
+		
+		//check thread and parent of the target thread
+		Message reloadedTopMessage = forumManager.getMessageById(topMessage.getKey());
+		Assert.assertNull(reloadedTopMessage.getThreadtop());
+		Assert.assertNull(reloadedTopMessage.getParent());
+		
+		Message reloadedMessage = forumManager.getMessageById(message.getKey());
+		Assert.assertEquals(topMessage, reloadedMessage.getThreadtop());
+		Assert.assertEquals(topMessage, reloadedMessage.getParent());
+
+		Message reloadedMessageToMove_1 = forumManager.getMessageById(messageToMove_1.getKey());
+		Assert.assertEquals(topMessage, reloadedMessageToMove_1.getThreadtop());
+		Assert.assertEquals(message, reloadedMessageToMove_1.getParent());
+		
+		Message reloadedMessageToMove_1_1 = forumManager.getMessageById(messageToMove_1_1.getKey());
+		Assert.assertEquals(topMessage, reloadedMessageToMove_1_1.getThreadtop());
+		Assert.assertEquals(messageToMove_1, reloadedMessageToMove_1_1.getParent());
+		
+		Message reloadedMessageToMove_2 = forumManager.getMessageById(messageToMove_1.getKey());
+		Assert.assertEquals(topMessage, reloadedMessageToMove_2.getThreadtop());
+		Assert.assertEquals(message, reloadedMessageToMove_2.getParent());
+		
+		Message reloadedMessageToStay = forumManager.getMessageById(messageToStay.getKey());
+		Assert.assertEquals(topMessage, reloadedMessageToStay.getThreadtop());
+		Assert.assertEquals(message, reloadedMessageToStay.getParent());
+	}
+	
+	@Test
+	public void splitMessage() {
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("fo-1");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("fo-2");
+		Identity guest3 =  securityManager.getAndUpdateAnonymousUserForLanguage(Locale.ENGLISH);
+		Forum fo = forumManager.addAForum();
+		dbInstance.commit();
+		
+		// thread
+		// -> message
+		// -> -> message to split
+		// -> -> -> message child 1
+		// -> -> -> -> message child 1.1
+		// -> -> -> message child 2
+		// -> -> message staying
+
+		Message topMessage = forumManager.createMessage(fo, id1, false);
+		topMessage.setTitle("Thread split message");
+		topMessage.setBody("Thread split message");
+		forumManager.addTopMessage(topMessage);
+		dbInstance.commit();
+
+		Message message = forumManager.createMessage(fo, id2, false);
+		message.setTitle("Re: Thread split message");
+		message.setBody("Thread split message");
+		forumManager.replyToMessage(message, topMessage);
+		dbInstance.commit();
+		
+		Message messageToSplit = forumManager.createMessage(fo, id2, false);
+		messageToSplit.setTitle("Message to split");
+		messageToSplit.setBody("Thread split message");
+		forumManager.replyToMessage(messageToSplit, message);
+		dbInstance.commit();
+		
+		Message messageToSplit_1 = forumManager.createMessage(fo, id2, false);
+		messageToSplit_1.setTitle("Re: Message to split 1");
+		messageToSplit_1.setBody("Thread split message");
+		forumManager.replyToMessage(messageToSplit_1, messageToSplit);
+		dbInstance.commit();
+		
+		Message messageToSplit_1_1 = forumManager.createMessage(fo, guest3, true);
+		messageToSplit_1_1.setTitle("Re: Re: Message to split 1");
+		messageToSplit_1_1.setBody("Thread split message");
+		forumManager.replyToMessage(messageToSplit_1_1, messageToSplit_1);
+		dbInstance.commit();
+		
+		Message messageToSplit_2 = forumManager.createMessage(fo, id2, false);
+		messageToSplit_2.setTitle("Re: Message to split 2");
+		messageToSplit_2.setBody("Thread split message");
+		forumManager.replyToMessage(messageToSplit_2, messageToSplit);
+		dbInstance.commit();
+		
+		Message messageToStay = forumManager.createMessage(fo, id2, false);
+		messageToStay.setTitle("Message to stay");
+		messageToStay.setBody("Thread split message");
+		forumManager.replyToMessage(messageToStay, message);
+		dbInstance.commit();
+		
+		//move the message
+		Message splitedMessage = forumManager.splitThread(messageToSplit);
+		dbInstance.commitAndCloseSession();
+		
+		//check the original thread
+		// thread
+		// -> message
+		// -> -> message staying
+		List<Message> originalMessages = forumManager.getThread(topMessage.getKey());
+		Assert.assertEquals(3, originalMessages.size());
+		Assert.assertTrue(originalMessages.contains(topMessage));
+		Assert.assertTrue(originalMessages.contains(message));
+		Assert.assertTrue(originalMessages.contains(messageToStay));
+		
+		//check thread and parent of the target thread
+		Message reloadedTopMessage = forumManager.getMessageById(topMessage.getKey());
+		Assert.assertNull(reloadedTopMessage.getThreadtop());
+		Assert.assertNull(reloadedTopMessage.getParent());
+
+		Message reloadedMessage = forumManager.getMessageById(message.getKey());
+		Assert.assertEquals(topMessage, reloadedMessage.getThreadtop());
+		Assert.assertEquals(topMessage, reloadedMessage.getParent());
+
+		Message reloadedMessageToStay = forumManager.getMessageById(messageToStay.getKey());
+		Assert.assertEquals(topMessage, reloadedMessageToStay.getThreadtop());
+		Assert.assertEquals(message, reloadedMessageToStay.getParent());
+		
+		//check original thread
+		// message to split
+		// -> message child 1
+		// -> -> message child 1.1
+		// -> message child 2
+		List<Message> splitedMessages = forumManager.getThread(splitedMessage.getKey());
+		Assert.assertEquals(4, splitedMessages.size());
+		Assert.assertTrue(splitedMessages.contains(splitedMessage));
+		Assert.assertTrue(splitedMessages.contains(messageToSplit_1));
+		Assert.assertTrue(splitedMessages.contains(messageToSplit_1_1));
+		Assert.assertTrue(splitedMessages.contains(messageToSplit_2));
+
+		//check thread and parent of the splited thread
+		
+		Message reloadedmessageToSplit = forumManager.getMessageById(messageToSplit.getKey());
+		Assert.assertNull(reloadedmessageToSplit.getThreadtop());
+		Assert.assertNull(reloadedmessageToSplit.getParent());
+
+		Message reloadedMessageToSplit_1 = forumManager.getMessageById(messageToSplit_1.getKey());
+		Assert.assertEquals(messageToSplit, reloadedMessageToSplit_1.getThreadtop());
+		Assert.assertEquals(messageToSplit, reloadedMessageToSplit_1.getParent());
+		
+		Message reloadedMessageToSplit_1_1 = forumManager.getMessageById(messageToSplit_1_1.getKey());
+		Assert.assertEquals(messageToSplit, reloadedMessageToSplit_1_1.getThreadtop());
+		Assert.assertEquals(messageToSplit_1, reloadedMessageToSplit_1_1.getParent());
+		
+		Message reloadedMessageToSplit_2 = forumManager.getMessageById(messageToSplit_1.getKey());
+		Assert.assertEquals(messageToSplit, reloadedMessageToSplit_2.getThreadtop());
+		Assert.assertEquals(messageToSplit, reloadedMessageToSplit_2.getParent());
+	}
+	
 	@Test
 	public void deleteMessageTree() {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("fo-5");
diff --git a/src/test/java/org/olat/selenium/CourseTest.java b/src/test/java/org/olat/selenium/CourseTest.java
index 38041c268daa79a453c969c256d220fbf2cbb03c..db823a6002d84c772f50f2ff96748f6a2f4faa84 100644
--- a/src/test/java/org/olat/selenium/CourseTest.java
+++ b/src/test/java/org/olat/selenium/CourseTest.java
@@ -1146,7 +1146,7 @@ public class CourseTest {
 	 */
 	@Test
 	@RunAsClient
-	public void concurrentForum(@InitialPage LoginPage loginPage,
+	public void forum_concurrent(@InitialPage LoginPage loginPage,
 			@Drone @Participant WebDriver kanuBrowser,
 			@Drone @Student WebDriver reiBrowser)
 	throws IOException, URISyntaxException {