diff --git a/pom.xml b/pom.xml
index d4703f9331616dbcff4f9552074e4e8681bdb518..5201533c76152436f178aef46aef74399b6d5282 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1315,6 +1315,13 @@
 							<artifactId>xercesImpl</artifactId>
 							<version>2.9.1</version>
 						</dependency>
+						<dependency>
+							<groupId>com.sun</groupId>
+							<artifactId>tools</artifactId>
+							<version>1.7</version>
+							<scope>system</scope>
+							<systemPath>${java.home}/../lib/tools.jar</systemPath>
+						</dependency>
 					</dependencies>
 				</plugin>
 
diff --git a/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java b/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java
index f1c6cc5f0df45adc6f335c878ba9d81cae890172..323acecd87c74433da030b6e419f45b92b8f9abc 100644
--- a/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java
+++ b/src/main/java/org/olat/admin/user/SystemRolesAndRightsController.java
@@ -37,9 +37,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.controller.BasicController;
-import org.olat.core.gui.translator.PackageTranslator;
 import org.olat.core.id.Identity;
-import org.olat.core.util.Util;
 
 /**
  * Initial Date:  Jan 27, 2006
@@ -129,12 +127,12 @@ public class SystemRolesAndRightsController extends BasicController {
 			SecurityGroup anonymousGroup = secMgr.findSecurityGroupByName(Constants.GROUP_ANONYMOUS);
 			boolean hasBeenAnonymous = secMgr.isIdentityInSecurityGroup(myIdentity, anonymousGroup);
 			isAnonymous = form.isAnonymous();
-			updateSecurityGroup(myIdentity, secMgr, anonymousGroup, hasBeenAnonymous, isAnonymous);
+			updateSecurityGroup(myIdentity, secMgr, anonymousGroup, hasBeenAnonymous, isAnonymous, Constants.GROUP_ANONYMOUS);
 			// system users - oposite of anonymous users
 			SecurityGroup usersGroup = secMgr.findSecurityGroupByName(Constants.GROUP_OLATUSERS);
 			boolean hasBeenUser = secMgr.isIdentityInSecurityGroup(myIdentity, usersGroup);
 			boolean isUser = !form.isAnonymous();
-			updateSecurityGroup(myIdentity, secMgr, usersGroup, hasBeenUser, isUser);
+			updateSecurityGroup(myIdentity, secMgr, usersGroup, hasBeenUser, isUser,Constants.GROUP_OLATUSERS);
 		}
 		// 2) system roles
 		// group manager
@@ -143,7 +141,7 @@ public class SystemRolesAndRightsController extends BasicController {
 			SecurityGroup groupManagerGroup = secMgr.findSecurityGroupByName(Constants.GROUP_GROUPMANAGERS);
 			boolean hasBeenGroupManager = secMgr.isIdentityInSecurityGroup(myIdentity, groupManagerGroup);
 			boolean isGroupManager = form.isGroupmanager();
-			updateSecurityGroup(myIdentity, secMgr, groupManagerGroup, hasBeenGroupManager, isGroupManager);
+			updateSecurityGroup(myIdentity, secMgr, groupManagerGroup, hasBeenGroupManager, isGroupManager, Constants.GROUP_GROUPMANAGERS);
 		}
 		// pool manager
 		Boolean canPoolmanagerByConfig =BaseSecurityModule.USERMANAGER_CAN_MANAGE_POOLMANAGERS;	
@@ -151,7 +149,7 @@ public class SystemRolesAndRightsController extends BasicController {
 			SecurityGroup poolManagerGroup = secMgr.findSecurityGroupByName(Constants.GROUP_POOL_MANAGER);
 			boolean hasBeenPoolManager = secMgr.isIdentityInSecurityGroup(myIdentity, poolManagerGroup);
 			boolean isPoolManager = form.isPoolmanager();
-			updateSecurityGroup(myIdentity, secMgr, poolManagerGroup, hasBeenPoolManager, isPoolManager);
+			updateSecurityGroup(myIdentity, secMgr, poolManagerGroup, hasBeenPoolManager, isPoolManager, Constants.GROUP_AUTHORS);
 		}
 		// author
 		Boolean canAuthorByConfig = BaseSecurityModule.USERMANAGER_CAN_MANAGE_AUTHORS;	
@@ -159,31 +157,44 @@ public class SystemRolesAndRightsController extends BasicController {
 			SecurityGroup authorGroup = secMgr.findSecurityGroupByName(Constants.GROUP_AUTHORS);
 			boolean hasBeenAuthor = secMgr.isIdentityInSecurityGroup(myIdentity, authorGroup);
 			boolean isAuthor = form.isAuthor() || form.isInstitutionalResourceManager();
-			updateSecurityGroup(myIdentity, secMgr, authorGroup, hasBeenAuthor, isAuthor);
+			updateSecurityGroup(myIdentity, secMgr, authorGroup, hasBeenAuthor, isAuthor, Constants.GROUP_AUTHORS);
 		}
 		// user manager, only allowed by admin
 		if (iAmOlatAdmin) {
 			SecurityGroup userManagerGroup = secMgr.findSecurityGroupByName(Constants.GROUP_USERMANAGERS);
 			boolean hasBeenUserManager = secMgr.isIdentityInSecurityGroup(myIdentity, userManagerGroup);
 			boolean isUserManager = form.isUsermanager();
-			updateSecurityGroup(myIdentity, secMgr, userManagerGroup, hasBeenUserManager, isUserManager);
+			updateSecurityGroup(myIdentity, secMgr, userManagerGroup, hasBeenUserManager, isUserManager, Constants.GROUP_USERMANAGERS);
 		}
 	 	// institutional resource manager, only allowed by admin
 		if (iAmUserManager || iAmOlatAdmin) {
 			SecurityGroup institutionalResourceManagerGroup = secMgr.findSecurityGroupByName(Constants.GROUP_INST_ORES_MANAGER);
 			boolean hasBeenInstitutionalResourceManager = secMgr.isIdentityInSecurityGroup(myIdentity, institutionalResourceManagerGroup);
 			boolean isInstitutionalResourceManager = form.isInstitutionalResourceManager();
-			updateSecurityGroup(myIdentity, secMgr, institutionalResourceManagerGroup, hasBeenInstitutionalResourceManager, isInstitutionalResourceManager);
+			updateSecurityGroup(myIdentity, secMgr, institutionalResourceManagerGroup, hasBeenInstitutionalResourceManager, isInstitutionalResourceManager, Constants.GROUP_INST_ORES_MANAGER);
 		}
 		// system administrator, only allowed by admin
 		if (iAmOlatAdmin) {
 			SecurityGroup adminGroup = secMgr.findSecurityGroupByName(Constants.GROUP_ADMIN);
 			boolean hasBeenAdmin = secMgr.isIdentityInSecurityGroup(myIdentity, adminGroup);
 			boolean isAdmin = form.isAdmin();
-			updateSecurityGroup(myIdentity, secMgr, adminGroup, hasBeenAdmin, isAdmin);		
+			updateSecurityGroup(myIdentity, secMgr, adminGroup, hasBeenAdmin, isAdmin, Constants.GROUP_ADMIN);		
 		}
-		if (iAmOlatAdmin &&  !myIdentity.getStatus().equals(form.getStatus()) ) {
-			identity = secMgr.saveIdentityStatus(myIdentity, form.getStatus());
+		if (iAmOlatAdmin &&  !myIdentity.getStatus().equals(form.getStatus()) ) {			
+			int oldStatus = myIdentity.getStatus();
+			String oldStatusText = (oldStatus == Identity.STATUS_PERMANENT ? "permanent"
+					: (oldStatus == Identity.STATUS_ACTIV ? "active"
+							: (oldStatus == Identity.STATUS_LOGIN_DENIED ? "login_denied"
+									: (oldStatus == Identity.STATUS_DELETED ? "deleted"
+											: "unknown"))));
+			int newStatus = form.getStatus();
+			String newStatusText = (newStatus == Identity.STATUS_PERMANENT ? "permanent"
+					: (newStatus == Identity.STATUS_ACTIV ? "active"
+							: (newStatus == Identity.STATUS_LOGIN_DENIED ? "login_denied"
+									: (newStatus == Identity.STATUS_DELETED ? "deleted"
+											: "unknown"))));
+			identity = secMgr.saveIdentityStatus(myIdentity, newStatus);
+			logAudit("User::" + getIdentity().getName() + " changed accout status for user::" + myIdentity.getName() + " from::" + oldStatusText + " to::" + newStatusText, null);
 		}
 	}
 
@@ -195,13 +206,15 @@ public class SystemRolesAndRightsController extends BasicController {
 	 * @param hasBeenInGroup
 	 * @param isNowInGroup
 	 */
-	private void updateSecurityGroup(Identity myIdentity, BaseSecurity secMgr, SecurityGroup securityGroup, boolean hasBeenInGroup, boolean isNowInGroup) {
+	private void updateSecurityGroup(Identity myIdentity, BaseSecurity secMgr, SecurityGroup securityGroup, boolean hasBeenInGroup, boolean isNowInGroup, String role) {
 		if (!hasBeenInGroup && isNowInGroup) {
 			// user not yet in security group, add him
 			secMgr.addIdentityToSecurityGroup(myIdentity, securityGroup);
+			logAudit("User::" + getIdentity().getName() + " added system role::" + role + " to user::" + myIdentity.getName(), null);
 		} else if (hasBeenInGroup && !isNowInGroup) {
 			// user not anymore in security group, remove him
 			secMgr.removeIdentityFromSecurityGroup(myIdentity, securityGroup);
+			logAudit("User::" + getIdentity().getName() + " removed system role::" + role + " from user::" + myIdentity.getName(), null);
 		}
 	}
 	
diff --git a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
index 327ca480f81038dedf26a8feee64ac5f3dab075f..7d3e3421413c9b5031c0910cbeb3fc8afe0bdebe 100644
--- a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
+++ b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
@@ -181,9 +181,15 @@ public class UserBulkChangeManager extends BasicManager {
 				if (roleChangeMap.containsKey(securityGroup)) {
 					thisRoleAction = roleChangeMap.get(securityGroup);
 					// user not anymore in security group, remove him
-					if (isInGroup && thisRoleAction.equals("remove")) secMgr.removeIdentityFromSecurityGroup(identity, secGroup);
+					if (isInGroup && thisRoleAction.equals("remove")) {
+						secMgr.removeIdentityFromSecurityGroup(identity, secGroup);
+						logAudit("User::" + addingIdentity.getName() + " removed system role::" + securityGroup + " from user::" + identity.getName(), null);
+					}
 					// user not yet in security group, add him
-					if (!isInGroup && thisRoleAction.equals("add")) secMgr.addIdentityToSecurityGroup(identity, secGroup);
+					if (!isInGroup && thisRoleAction.equals("add")) {
+						secMgr.addIdentityToSecurityGroup(identity, secGroup);
+						logAudit("User::" + addingIdentity.getName() + " added system role::" + securityGroup + " to user::" + identity.getName(), null);
+					}
 				}
 			}
 			
@@ -191,7 +197,20 @@ public class UserBulkChangeManager extends BasicManager {
 			// set status
 			if (roleChangeMap.containsKey("Status")) {
 				Integer status = Integer.parseInt(roleChangeMap.get("Status"));
+
+				int oldStatus = identity.getStatus();
+				String oldStatusText = (oldStatus == Identity.STATUS_PERMANENT ? "permanent"
+						: (oldStatus == Identity.STATUS_ACTIV ? "active"
+								: (oldStatus == Identity.STATUS_LOGIN_DENIED ? "login_denied"
+										: (oldStatus == Identity.STATUS_DELETED ? "deleted"
+												: "unknown"))));
+				String newStatusText = (status == Identity.STATUS_PERMANENT ? "permanent"
+						: (status == Identity.STATUS_ACTIV ? "active"
+								: (status == Identity.STATUS_LOGIN_DENIED ? "login_denied"
+										: (status == Identity.STATUS_DELETED ? "deleted"
+												: "unknown"))));
 				identity = secMgr.saveIdentityStatus(identity, status);
+				logAudit("User::" + addingIdentity.getName() + " changed accout status for user::" + identity.getName() + " from::" + oldStatusText + " to::" + newStatusText, null);
 			}
 
 			// persist changes:
@@ -202,7 +221,7 @@ public class UserBulkChangeManager extends BasicManager {
 			} else {
 				um.updateUserFromIdentity(identity);
 				changedIdentities.add(identity);
-				log.audit("user successfully changed during bulk-change: " + identity.getName());
+				logAudit("User::" + addingIdentity.getName() + " successfully changed account data for user::" + identity.getName() + " in bulk change", null);
 			}
 
 			// commit changes for this user
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java
index 2e19b38d70a8e5736f810d32814e313823e71aee..60b9a76bc8b0e46ec50392c43cb99a240cfd18e3 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurity.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java
@@ -88,9 +88,11 @@ public interface BaseSecurity {
 	
 	/**
 	 * Update the roles
-	 * @param identity
+	 * @param actingIdentity The identity who is performing the change
+	 * @param updatedIdentity The identity that is changed
+	 * @param roles The new roles to set on updatedIdentity
 	 */
-	public void updateRoles(Identity identity, Roles roles);
+	public void updateRoles(Identity actingIdentity, Identity updatedIdentity, Roles roles);
 
 	/**
 	 * @param identity
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index 664d8d63328e632e95eeab20c5b92eb4c0d5d462..bcbe84ec9d04040d1695c7f40a405ffe0bcfe4e5 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -419,63 +419,65 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 	}
 
 	@Override
-	public void updateRoles(Identity identity, Roles roles) {
+	public void updateRoles(Identity actingIdentity, Identity updatedIdentity, Roles roles) {
 		SecurityGroup anonymousGroup = findSecurityGroupByName(Constants.GROUP_ANONYMOUS);
-		boolean hasBeenAnonymous = isIdentityInSecurityGroup(identity, anonymousGroup);
-		updateRolesInSecurityGroup(identity, anonymousGroup, hasBeenAnonymous, roles.isGuestOnly());
+		boolean hasBeenAnonymous = isIdentityInSecurityGroup(updatedIdentity, anonymousGroup);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, anonymousGroup, hasBeenAnonymous, roles.isGuestOnly(), Constants.GROUP_ANONYMOUS);
 		
 		// system users - opposite of anonymous users
 		SecurityGroup usersGroup = findSecurityGroupByName(Constants.GROUP_OLATUSERS);
-		boolean hasBeenUser = isIdentityInSecurityGroup(identity, usersGroup);
-		updateRolesInSecurityGroup(identity,  usersGroup, hasBeenUser, !roles.isGuestOnly());
+		boolean hasBeenUser = isIdentityInSecurityGroup(updatedIdentity, usersGroup);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity,  usersGroup, hasBeenUser, !roles.isGuestOnly(), Constants.GROUP_OLATUSERS);
 
 		SecurityGroup groupManagerGroup = findSecurityGroupByName(Constants.GROUP_GROUPMANAGERS);
-		boolean hasBeenGroupManager = isIdentityInSecurityGroup(identity, groupManagerGroup);
+		boolean hasBeenGroupManager = isIdentityInSecurityGroup(updatedIdentity, groupManagerGroup);
 		boolean groupManager = roles.isGroupManager()
 				&& !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity, groupManagerGroup, hasBeenGroupManager, groupManager);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, groupManagerGroup, hasBeenGroupManager, groupManager, Constants.GROUP_GROUPMANAGERS);
 
-	// author
+		// author
 		SecurityGroup authorGroup = findSecurityGroupByName(Constants.GROUP_AUTHORS);
-		boolean hasBeenAuthor = isIdentityInSecurityGroup(identity, authorGroup);
+		boolean hasBeenAuthor = isIdentityInSecurityGroup(updatedIdentity, authorGroup);
 		boolean isAuthor = (roles.isAuthor() || roles.isInstitutionalResourceManager())
 				&& !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity, authorGroup, hasBeenAuthor, isAuthor);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, authorGroup, hasBeenAuthor, isAuthor, Constants.GROUP_AUTHORS);
 
 		// user manager, only allowed by admin
 		SecurityGroup userManagerGroup = findSecurityGroupByName(Constants.GROUP_USERMANAGERS);
-		boolean hasBeenUserManager = isIdentityInSecurityGroup(identity, userManagerGroup);
+		boolean hasBeenUserManager = isIdentityInSecurityGroup(updatedIdentity, userManagerGroup);
 		boolean userManager = roles.isUserManager()
 				&& !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity,  userManagerGroup, hasBeenUserManager, userManager);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity,  userManagerGroup, hasBeenUserManager, userManager, Constants.GROUP_USERMANAGERS);
 
  		// institutional resource manager
 		SecurityGroup institutionalResourceManagerGroup = findSecurityGroupByName(Constants.GROUP_INST_ORES_MANAGER);
-		boolean hasBeenInstitutionalResourceManager = isIdentityInSecurityGroup(identity, institutionalResourceManagerGroup);
+		boolean hasBeenInstitutionalResourceManager = isIdentityInSecurityGroup(updatedIdentity, institutionalResourceManagerGroup);
 		boolean institutionalResourceManager = roles.isInstitutionalResourceManager()
 				&& !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity, institutionalResourceManagerGroup, hasBeenInstitutionalResourceManager, institutionalResourceManager);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, institutionalResourceManagerGroup, hasBeenInstitutionalResourceManager, institutionalResourceManager, Constants.GROUP_INST_ORES_MANAGER);
 
 		// institutional resource manager
 		SecurityGroup poolManagerGroup = findSecurityGroupByName(Constants.GROUP_POOL_MANAGER);
-		boolean hasBeenPoolManager = isIdentityInSecurityGroup(identity, poolManagerGroup);
+		boolean hasBeenPoolManager = isIdentityInSecurityGroup(updatedIdentity, poolManagerGroup);
 		boolean poolManager = roles.isPoolAdmin()	&& !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity, poolManagerGroup, hasBeenPoolManager, poolManager);
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, poolManagerGroup, hasBeenPoolManager, poolManager, Constants.GROUP_POOL_MANAGER);
 
 		// system administrator
 		SecurityGroup adminGroup = findSecurityGroupByName(Constants.GROUP_ADMIN);
-		boolean hasBeenAdmin = isIdentityInSecurityGroup(identity, adminGroup);
+		boolean hasBeenAdmin = isIdentityInSecurityGroup(updatedIdentity, adminGroup);
 		boolean isOLATAdmin = roles.isOLATAdmin() && !roles.isGuestOnly() && !roles.isInvitee();
-		updateRolesInSecurityGroup(identity, adminGroup, hasBeenAdmin, isOLATAdmin);		
+		updateRolesInSecurityGroup(actingIdentity, updatedIdentity, adminGroup, hasBeenAdmin, isOLATAdmin, Constants.GROUP_ADMIN);		
 	}
 	
-	private void updateRolesInSecurityGroup(Identity identity, SecurityGroup securityGroup, boolean hasBeenInGroup, boolean isNowInGroup) {
+	private void updateRolesInSecurityGroup(Identity actingIdentity, Identity updatedIdentity, SecurityGroup securityGroup, boolean hasBeenInGroup, boolean isNowInGroup, String groupName) {
 		if (!hasBeenInGroup && isNowInGroup) {
 			// user not yet in security group, add him
-			addIdentityToSecurityGroup(identity, securityGroup);
+			addIdentityToSecurityGroup(updatedIdentity, securityGroup);
+			logAudit("User::" + actingIdentity.getName() + " added system role::" + groupName + " to user::" + updatedIdentity.getName(), null);
 		} else if (hasBeenInGroup && !isNowInGroup) {
 			// user not anymore in security group, remove him
-			removeIdentityFromSecurityGroup(identity, securityGroup);
+			removeIdentityFromSecurityGroup(updatedIdentity, securityGroup);
+			logAudit("User::" + actingIdentity.getName() + " removed system role::" + groupName + " from user::" + updatedIdentity.getName(), null);
 		}
 	}
 
diff --git a/src/main/java/org/olat/core/util/image/spi/ImageMagickHelper.java b/src/main/java/org/olat/core/util/image/spi/ImageMagickHelper.java
index 9d8cd3dd21af08575e8568ab39d41f82347d220e..2e4e4dae7dc29b6bd67014360a9dafc021aa9ce6 100644
--- a/src/main/java/org/olat/core/util/image/spi/ImageMagickHelper.java
+++ b/src/main/java/org/olat/core/util/image/spi/ImageMagickHelper.java
@@ -199,7 +199,9 @@ public class ImageMagickHelper extends BasicManager implements ImageHelperSPI {
 		} catch (InterruptedException e) {
 			//
 		}
-		logWarn("Could not generate thumbnail: "+thumbnailFile, null);
+		if(rv == null) {
+			logWarn("Could not generate thumbnail: "+thumbnailFile, null);
+		}
 		return rv;
 	}
 	/**
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index e403233a19d1709c496af8b91db1dfa92bd3e671..c8066b2d4659b5f9ef003d6e697af0e005bdaf2c 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -829,7 +829,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				response.getIdentitiesWithoutPermission().add(identity);
 			} else if(addOwner(ureqIdentity, ureqRoles, identity, group, mailing)) {
 				response.getAddedIdentities().add(identity);
-				log.audit("added identity '" + identity.getName() + "' to securitygroup with key " + group.getOwnerGroup().getKey());
 			} else {
 				response.getIdentitiesAlreadyInGroup().add(identity);
 			}
@@ -858,10 +857,12 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 							reservationDao.createReservation(identityToAdd, "group_coach", expiration, group.getResource());
 					if(reservation != null) {
 						BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addCoach, mailing, mailer);
+						// logging
+						log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to securitygroup with key " + group.getOwnerGroup().getKey());
 					}
 				}
 			} else {
-				internalAddCoach(identityToAdd, group);
+				internalAddCoach(ureqIdentity, identityToAdd, group);
 				BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addCoach, mailing, mailer);
 			}
 			return true;
@@ -869,12 +870,13 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		return false;
 	}
 	
-	private void internalAddCoach(Identity identityToAdd, BusinessGroup group) {
+	private void internalAddCoach(Identity ureqIdentity, Identity identityToAdd, BusinessGroup group) {
 		securityManager.addIdentityToSecurityGroup(identityToAdd, group.getOwnerGroup());
 		// notify currently active users of this business group
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identityToAdd);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_ADDED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToAdd));
+		log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to securitygroup with key " + group.getOwnerGroup().getKey());
 	}
 	
 	private boolean addParticipant(Identity ureqIdentity, Roles ureqRoles, Identity identityToAdd, BusinessGroup group,
@@ -903,7 +905,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 					}
 				}
 			} else {
-				internalAddParticipant(identityToAdd, group);
+				internalAddParticipant(ureqIdentity, identityToAdd, group);
 				BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addParticipant, mailing, mailer);
 			}
 			return true;
@@ -919,13 +921,14 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	 * @param group
 	 * @param syncIM
 	 */
-	private void internalAddParticipant(Identity identityToAdd, BusinessGroup group) {
+	private void internalAddParticipant(Identity ureqIdentity, Identity identityToAdd, BusinessGroup group) {
 		securityManager.addIdentityToSecurityGroup(identityToAdd, group.getPartipiciantGroup());
 
 		// notify currently active users of this business group
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identityToAdd);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_ADDED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToAdd));
+		log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to securitygroup with key " + group.getPartipiciantGroup().getKey());
 		// send notification mail in your controller!
 	}
 
@@ -940,7 +943,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				response.getIdentitiesWithoutPermission().add(identity);
 			} else if(addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, mailing)) {
 				response.getAddedIdentities().add(identity);
-				log.audit("added identity '" + identity.getName() + "' to securitygroup with key " + currBusinessGroup.getPartipiciantGroup().getKey());
 			} else {
 				response.getIdentitiesAlreadyInGroup().add(identity);
 			}
@@ -969,11 +971,11 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				String type = reservation.getType();
 				if("group_coach".equals(type)) {
 					if(!securityManager.isIdentityInSecurityGroup(reservationOwner, group.getOwnerGroup())) {
-						internalAddCoach(reservationOwner, group);
+						internalAddCoach(ureqIdentity, reservationOwner, group);
 					}
 				} else if("group_participant".equals(type)) {
 					if(!securityManager.isIdentityInSecurityGroup(reservationOwner, group.getPartipiciantGroup())) {
-						internalAddParticipant(reservationOwner, group);
+						internalAddParticipant(ureqIdentity, reservationOwner, group);
 					}
 				}
 			}
@@ -993,6 +995,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
 			// do logging
 			ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_REMOVED, getClass(), LoggingResourceable.wrap(identity), LoggingResourceable.wrap(group));
+			log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName() + "' from securitygroup with key " + group.getPartipiciantGroup().getKey());
 			// Check if a waiting-list with auto-close-ranks is configurated
 			if ( group.getWaitingListEnabled().booleanValue() && group.getAutoCloseRanksEnabled().booleanValue() ) {
 				// even when doOnlyPostRemovingStuff is set to true we really transfer the first Identity here
@@ -1008,7 +1011,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		group = businessGroupDAO.loadForUpdate(group.getKey());
 		for (Identity identity : identities) {
 		  removeParticipant(ureqIdentity, identity, group, mailing);
-		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + group.getPartipiciantGroup().getKey());
 		}
 		dbInstance.commit();
 	}
@@ -1115,6 +1117,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identity);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_TO_WAITING_LIST_ADDED, getClass(), LoggingResourceable.wrap(identity));
+		log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " added identity '" + identity.getName() + "' to securitygroup with key " + group.getPartipiciantGroup().getKey());
 		// send mail
 		BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.addToWaitingList, mailing, mailer);
 	}
@@ -1138,7 +1141,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				// identity has permission and is not already in group => add it
 				addToWaitingList(ureqIdentity, identity, currBusinessGroup, mailing);
 				response.getAddedIdentities().add(identity);
-				log.audit("added identity '" + identity.getName() + "' to securitygroup with key " + currBusinessGroup.getPartipiciantGroup().getKey());
 			}
 		}
 		dbInstance.commit();
@@ -1151,6 +1153,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_FROM_WAITING_LIST_REMOVED, getClass(), LoggingResourceable.wrap(identity));
+		log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName() + "' from securitygroup with key " + group.getOwnerGroup().getKey());
 		// send mail
 		BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.removeToWaitingList, mailing, mailer);
 	}
@@ -1160,7 +1163,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		businessGroup = businessGroupDAO.loadForUpdate(businessGroup.getKey());
 		for (Identity identity : identities) {
 		  removeFromWaitingList(ureqIdentity, identity, businessGroup, mailing);
-		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + businessGroup.getOwnerGroup().getKey());
 		}
 		dbInstance.commit();
 	}
@@ -1209,7 +1211,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	public void removeAndFireEvent(Identity ureqIdentity, List<Identity> identities, SecurityGroup secGroup) {
 		for (Identity identity : identities) {
 			securityManager.removeIdentityFromSecurityGroup(identity, secGroup);
-		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + secGroup.getKey());
+		  log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName() + "' from securitygroup with key " + secGroup.getKey());
 		}
 	}
 	
@@ -1332,6 +1334,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
   		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identityToRemove);
 		}
 		// do logging
+		log.audit("Idenitity(.key):" + ureqIdentity.getKey() + " removed identiy '" + identityToRemove.getName() + "' from securitygroup with key " + group.getOwnerGroup().getKey());
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToRemove));
 	}
 	
diff --git a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
index 2f37bd35c36b09a841a21edd856e62de0787770f..d251501d7c0d956d77e37eb770cd61d2eb292dd9 100644
--- a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
+++ b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
@@ -42,6 +42,8 @@ import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
 import org.olat.core.id.Identity;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
@@ -65,9 +67,6 @@ import org.olat.registration.PwChangeController;
 import org.olat.registration.RegistrationManager;
 import org.olat.user.UserModule;
 
-import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
-import org.olat.core.gui.control.generic.dtabs.Activateable2;
-
 public class LDAPAuthenticationController extends AuthenticationController implements Activateable2 {
 	public static final String PROVIDER_LDAP = "LDAP";
 	
@@ -148,6 +147,12 @@ protected void event(UserRequest ureq, Component source, Event event) {
 			String login = loginForm.getLogin();
 			String pass = loginForm.getPass();
 
+			if (LoginModule.isLoginBlocked(login)) {
+				// do not proceed when already blocked
+				showError("login.blocked", LoginModule.getAttackPreventionTimeoutMin().toString());
+				getLogger().audit("Login attempt on already blocked login for " + login + ". IP::" + ureq.getHttpReq().getRemoteAddr(), null);
+				return;
+			}
 			authenticatedIdentity= authenticate(login, pass, ldapError);
 
 			if(!ldapError.isEmpty()) {
@@ -177,7 +182,7 @@ protected void event(UserRequest ureq, Component source, Event event) {
 			// Still not found? register for hacking attempts
 			if (authenticatedIdentity == null) {
 				if (LoginModule.registerFailedLoginAttempt(login)) {
-					logAudit("Too many failed login attempts for " + login + ". Login blocked.", null);
+					logAudit("Too many failed login attempts for " + login + ". Login blocked. IP::" + ureq.getHttpReq().getRemoteAddr(), null);
 					showError("login.blocked", LoginModule.getAttackPreventionTimeoutMin().toString());
 					return;
 				} else {
diff --git a/src/main/java/org/olat/login/LoginModule.java b/src/main/java/org/olat/login/LoginModule.java
index ca6fa2449ebd5dfb4023e25b8410a9836c68c9f0..3e1187e5ffb3d4bab5fd48c0a0b673d519944a9b 100644
--- a/src/main/java/org/olat/login/LoginModule.java
+++ b/src/main/java/org/olat/login/LoginModule.java
@@ -151,14 +151,11 @@ public class LoginModule extends AbstractOLATModule {
 		
 		if (numAttempts == null) { // create new entry
 			numAttempts = new Integer(1);
+			failedLoginCache.put(login, numAttempts);
 		} else { // update entry
 			numAttempts = new Integer(numAttempts.intValue() + 1);
-		}
-		// do not use putSilent(...) here, since failed login attempts should propagate to all cluster nodes 
-		// o_clusterREVIEW todo: this is fine, however loading the data (numAttempts == null) ... should be via db e.g properties table, 
-		// otherwise it cannot be clustersafe
-		failedLoginCache.update(login, numAttempts);
-		
+			failedLoginCache.update(login, numAttempts);
+		}		
 		return (numAttempts.intValue() > attackPreventionMaxAttempts);
 	}
 	
@@ -172,7 +169,7 @@ public class LoginModule extends AbstractOLATModule {
 	}
 	
 	/**
-	 * Tells wether a login is blocked to prevent brute force attacks or not.
+	 * Tells whether a login is blocked to prevent brute force attacks or not.
 	 * @param login
 	 * @return True if login is blocked by attack prevention mechanism
 	 */
@@ -185,8 +182,8 @@ public class LoginModule extends AbstractOLATModule {
 	}
 	
 	/**
-	 * @return True if guest login kinks must be shown on login screen, false
-	 *         otherwhise
+	 * @return True if guest login links must be shown on login screen, false
+	 *         otherwise
 	 */
 	public static final boolean isGuestLoginLinksEnabled() {
 		return guestLoginLinksEnabled;
diff --git a/src/main/java/org/olat/login/OLATAuthenticationController.java b/src/main/java/org/olat/login/OLATAuthenticationController.java
index b7fd129ec5bddca16361f59d377d0579e12e43c1..2cc24fa1e6201cb981374dc254fe99f8dde1d5d9 100644
--- a/src/main/java/org/olat/login/OLATAuthenticationController.java
+++ b/src/main/java/org/olat/login/OLATAuthenticationController.java
@@ -30,7 +30,6 @@ import java.util.List;
 import java.util.Locale;
 
 import org.olat.basesecurity.AuthHelper;
-import org.olat.basesecurity.Authentication;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.BaseSecurityModule;
 import org.olat.core.CoreSpringFactory;
@@ -197,11 +196,17 @@ public class OLATAuthenticationController extends AuthenticationController imple
 		
 		if (source == loginForm && event == Event.DONE_EVENT) {
 			String login = loginForm.getLogin();
-			String pass = loginForm.getPass();
+			String pass = loginForm.getPass();	
+			if (LoginModule.isLoginBlocked(login)) {
+				// do not proceed when blocked
+				showError("login.blocked", LoginModule.getAttackPreventionTimeoutMin().toString());
+				getLogger().audit("Login attempt on already blocked login for " + login + ". IP::" + ureq.getHttpReq().getRemoteAddr(), null);
+				return;
+			}
 			authenticatedIdentity = authenticate(login, pass);
 			if (authenticatedIdentity == null) {
 				if (LoginModule.registerFailedLoginAttempt(login)) {
-					getLogger().audit("Too many failed login attempts for " + login + ". Login blocked.", null);
+					getLogger().audit("Too many failed login attempts for " + login + ". Login blocked. IP::" + ureq.getHttpReq().getRemoteAddr(), null);
 					showError("login.blocked", LoginModule.getAttackPreventionTimeoutMin().toString());
 					return;
 				} else {
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 2fca7e875477a863a9c01a1dafadc3f61f6029d0..41659f7e096ee4cc700c6ca8d1b85e1b4eef375e 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -1952,6 +1952,20 @@ public class RepositoryManager extends BasicManager {
 		}
 
 		boolean allOk = securityManager.removeIdentityFromSecurityGroups(members, secGroups);
+		if (allOk) {
+			// do logging - not optimal but 
+			StringBuffer sb = new StringBuffer();
+			sb.append("Idenitity(.key):").append(ureqIdentity.getKey()).append("removed multiple identities from security groups. Identities:: " );
+			for (Identity member : members) {
+				sb.append(member.getName()).append(", ");
+			}
+			sb.append(" SecurityGroups:: ");
+			for (SecurityGroup group : secGroups) {
+				sb.append(group.getKey()).append(", ");
+			}
+			logAudit(sb.toString());					
+		}
+		
 		for(Identity identity:members) {
 			RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing, mailer);
 		}
diff --git a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
index 609045735e18e2fb21d31be954d9a2df55ae5cbf..893c5780cee90639d814a3e79cff8271ae1ff9bf 100644
--- a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
+++ b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
@@ -587,6 +587,7 @@ public class CourseWebService {
 		if(!hasBeenAuthor) {
 			//not an author already, add this identity to the security group "authors"
 			securityManager.addIdentityToSecurityGroup(author, authorGroup);
+			log.audit("User::" + identity.getName() + " added system role::" + Constants.GROUP_AUTHORS + " to user::" + author.getName() + " via addAuthor method in course REST API", null);
 		}
 		
 		//add the author as owner of the course
diff --git a/src/main/java/org/olat/user/restapi/UserWebService.java b/src/main/java/org/olat/user/restapi/UserWebService.java
index a3e9f07a99b87e6f8391407e4c837bce0e3424ab..f9a6cea0580a9b7a22f091014ae966cab4a80d05 100644
--- a/src/main/java/org/olat/user/restapi/UserWebService.java
+++ b/src/main/java/org/olat/user/restapi/UserWebService.java
@@ -293,7 +293,8 @@ public class UserWebService {
 				return Response.serverError().status(Status.NOT_FOUND).build();
 			}
 			Roles modRoles = roles.toRoles();
-			BaseSecurityManager.getInstance().updateRoles(identity, modRoles);
+			Identity actingIdentity = getIdentity(request);
+			BaseSecurityManager.getInstance().updateRoles(actingIdentity, identity, modRoles);
 			return Response.ok(new RolesVO(modRoles)).build();
 		} catch (Throwable e) {
 			throw new WebApplicationException(e);
diff --git a/src/main/resources/infinispan-config.xml b/src/main/resources/infinispan-config.xml
index 17692b431e0c88075146eb6e21917e9381a48f49..5a08dcfed1d4113aba097a9e358137a09cffc6f9 100644
--- a/src/main/resources/infinispan-config.xml
+++ b/src/main/resources/infinispan-config.xml
@@ -59,6 +59,14 @@
 		<expiration maxIdle="2700000" wakeUpInterval="15000"/>
 		<transaction transactionMode="NON_TRANSACTIONAL" />
 	</namedCache>
+		
+	<namedCache name="LoginModule@blockafterfailedattempts">
+		<locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000" lockAcquisitionTimeout="15000" useLockStriping="false"/>
+		<eviction maxEntries="10000" strategy="LRU"/>
+		<expiration maxIdle="300000" lifespan="300000" wakeUpInterval="5000"/>
+		<transaction transactionMode="NON_TRANSACTIONAL" />
+	</namedCache>
+	
 
 	<!-- 
 	Hibernate cache
diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
index e46abe3ec513c38f679f50e09e179fcb06f87b38..479b27c95a0c68d293931e7c5883b230ccd4b450 100644
--- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
+++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
@@ -264,13 +264,14 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 	@Test
 	public void testUpdateRoles_giveAllRights() {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser( "roles-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser( "roles-" + UUID.randomUUID().toString());
 		Roles roles = securityManager.getRoles(id1);
 		Assert.assertNotNull(roles);
 		dbInstance.commitAndCloseSession();
 
 		//update roles
 		Roles modifiedRoles = new Roles(true, true, true, true, false, true, true, false);
-		securityManager.updateRoles(id1, modifiedRoles);
+		securityManager.updateRoles(id2, id1, modifiedRoles);
 		dbInstance.commitAndCloseSession();
 		
 		//check roles
@@ -292,13 +293,14 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 	@Test
 	public void testUpdateRoles_someRights() {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser( "roles-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser( "roles-" + UUID.randomUUID().toString());
 		Roles roles = securityManager.getRoles(id1);
 		Assert.assertNotNull(roles);
 		dbInstance.commitAndCloseSession();
 
 		//update roles
 		Roles modifiedRoles = new Roles(false, true, false, true, false, false, false, false);
-		securityManager.updateRoles(id1, modifiedRoles);
+		securityManager.updateRoles(id2, id1, modifiedRoles);
 		dbInstance.commitAndCloseSession();
 		
 		//check roles
@@ -320,13 +322,14 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 	@Test
 	public void testUpdateRoles_guest() {
 		Identity invitee = JunitTestHelper.createAndPersistIdentityAsUser("invitee-" + UUID.randomUUID().toString());
+		Identity user = JunitTestHelper.createAndPersistIdentityAsUser("invitee-" + UUID.randomUUID().toString());
 		Roles roles = securityManager.getRoles(invitee);
 		Assert.assertNotNull(roles);
 		dbInstance.commitAndCloseSession();
 
 		//update roles
 		Roles modifiedRoles = new Roles(true, true, true, true, true, true, false);
-		securityManager.updateRoles(invitee, modifiedRoles);
+		securityManager.updateRoles(user, invitee, modifiedRoles);
 		dbInstance.commitAndCloseSession();
 
 		//check roles
diff --git a/src/test/java/org/olat/instantMessaging/FunctionalInstantMessagingTest.java b/src/test/java/org/olat/instantMessaging/FunctionalInstantMessagingTest.java
index ffd71c853ea5b384f37e12cec0e18157c2f0118e..b4fef0574900c5458c86d3fb70125fa0c4e8e8be 100644
--- a/src/test/java/org/olat/instantMessaging/FunctionalInstantMessagingTest.java
+++ b/src/test/java/org/olat/instantMessaging/FunctionalInstantMessagingTest.java
@@ -22,6 +22,11 @@ package org.olat.instantMessaging;
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Assert;
 
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.container.test.api.RunAsClient;
@@ -33,8 +38,18 @@ import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.olat.test.ArquillianDeployments;
+import org.olat.user.restapi.UserVO;
+import org.olat.collaboration.CollaborationTools;
+import org.olat.restapi.support.vo.GroupVO;
+import org.olat.util.FunctionalInstantMessagingUtil;
 import org.olat.util.FunctionalUtil;
 import org.olat.util.FunctionalVOUtil;
+import org.olat.util.browser.Student1;
+import org.olat.util.browser.Student2;
+import org.olat.util.browser.Student3;
+import org.olat.util.browser.Tutor1;
+
+import com.thoughtworks.selenium.Selenium;
 
 /**
  * 
@@ -44,6 +59,19 @@ import org.olat.util.FunctionalVOUtil;
 @RunWith(Arquillian.class)
 public class FunctionalInstantMessagingTest {
 	
+	public final static String GROUP_CHAT_TUTOR0 = "tutor0";
+	public final static String GROUP_CHAT_PARTICIPANT0 = "participant0";
+	public final static String GROUP_CHAT_PARTICIPANT1 = "participant1";
+	public final static String GROUP_CHAT_PARTICIPANT2 = "participant2";
+	public final static Map<String,String> GROUP_CHAT_DIALOG = new HashMap<String,String>();
+	
+	static{
+		int count = 0;
+		
+		GROUP_CHAT_DIALOG.put(GROUP_CHAT_PARTICIPANT0 + "#" + count++, "hello world!");
+		GROUP_CHAT_DIALOG.put(GROUP_CHAT_PARTICIPANT2 + "#" + count++, "clear sky.");
+	}
+	
 	@Deployment(testable = false)
 	public static WebArchive createDeployment() {
 		return ArquillianDeployments.createDeployment();
@@ -53,6 +81,7 @@ public class FunctionalInstantMessagingTest {
 	URL deploymentUrl;
 
 	static FunctionalUtil functionalUtil;
+	static FunctionalInstantMessagingUtil functionalInstantMessagingUtil;
 
 	static FunctionalVOUtil functionalVOUtil;
 	
@@ -63,6 +92,8 @@ public class FunctionalInstantMessagingTest {
 		if(!initialized){
 			functionalUtil = new FunctionalUtil();
 			functionalUtil.setDeploymentUrl(deploymentUrl.toString());
+			functionalInstantMessagingUtil = new FunctionalInstantMessagingUtil();
+			
 			functionalVOUtil = new FunctionalVOUtil(functionalUtil.getUsername(), functionalUtil.getPassword());
 			
 			initialized = true;
@@ -72,7 +103,57 @@ public class FunctionalInstantMessagingTest {
 	@Ignore
 	@Test
 	@RunAsClient
-	public void checkChat(){
-		//TODO:JK: implement me
+	public void checkGroupChat(@Tutor1 Selenium tutor0, @Student1 Selenium student0, @Student2 Selenium student1, @Student3 Selenium student2)
+			throws IOException, URISyntaxException
+	{
+		/*
+		 * Prerequisites
+		 */
+		/* create users and group */
+		List<UserVO> tutor = functionalVOUtil.createTestAuthors(deploymentUrl, 1);
+		List<UserVO> user = functionalVOUtil.createTestUsers(deploymentUrl, 3);
+		
+		List<GroupVO> group = functionalVOUtil.createTestCourseGroups(deploymentUrl, 1);
+		
+		/* set visibility */
+		functionalVOUtil.setGroupConfiguration(deploymentUrl, group.get(0),
+				new String[]{CollaborationTools.TOOL_CHAT},
+				false, false,
+				false, false,
+				false, false);
+		
+		/* add users to group */
+		functionalVOUtil.addOwnerToGroup(deploymentUrl, group.get(0), tutor.get(0));
+		
+		for(int i = 0; i < 2; i++){
+			functionalVOUtil.addParticipantToGroup(deploymentUrl, group.get(0), user.get(i));
+		}
+		
+		/*
+		 * Content
+		 */
+		/* login */
+		Assert.assertTrue(functionalUtil.login(tutor0, tutor.get(0).getLogin(), tutor.get(0).getPassword(), true));
+		
+		Assert.assertTrue(functionalUtil.login(student0, user.get(0).getLogin(), user.get(0).getPassword(), true));
+		Assert.assertTrue(functionalUtil.login(student1, user.get(1).getLogin(), user.get(1).getPassword(), true));
+		Assert.assertTrue(functionalUtil.login(student2, user.get(2).getLogin(), user.get(2).getPassword(), true));
+		
+		/* dialog */
+		String[] keys = (String[]) GROUP_CHAT_DIALOG.keySet().toArray();
+		
+		for(int i = 0; i < GROUP_CHAT_DIALOG.size(); i++){
+			String current = GROUP_CHAT_DIALOG.get(keys[i]);
+			
+			if(keys[i].startsWith(GROUP_CHAT_TUTOR0)){
+				//nothing to be done, here.
+			}else if(keys[i].startsWith(GROUP_CHAT_PARTICIPANT0)){
+				functionalInstantMessagingUtil.sendMessageToGroup(student0, group.get(0).getName(), current);
+			}else if(keys[i].startsWith(GROUP_CHAT_PARTICIPANT1)){
+				functionalInstantMessagingUtil.sendMessageToGroup(student1, group.get(0).getName(), current);
+			}else if(keys[i].startsWith(GROUP_CHAT_PARTICIPANT2)){
+				//nothing to be done, here.
+			}
+		}
 	}
 }
diff --git a/src/test/java/org/olat/util/CatalogTreeEntryPosition.vm b/src/test/java/org/olat/util/CatalogTreeEntryPosition.vm
index 80b524827e9a9cb05e509da699117fe2a104200f..81343c8485caae1b455d58fb6871b5a4828abf5c 100644
--- a/src/test/java/org/olat/util/CatalogTreeEntryPosition.vm
+++ b/src/test/java/org/olat/util/CatalogTreeEntryPosition.vm
@@ -18,7 +18,7 @@ if(document.querySelector){
         
         if(current != null){
         	var itemSelector = "(" + selector + ")[" + (i) + "]//div[contains(@class, 'b_selectiontree_content') and text()='" + item[j] + "']";
-        	var itemSelection = window.document.evaluate(itemSelector, window.document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+        	var itemSelection = window.document.evaluate(itemSelector, window.document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
         
         	if(itemSelection != null && itemSelection.snapshotLength > 0){
         		j++;
diff --git a/src/test/java/org/olat/util/FunctionalInstantMessagingUtil.java b/src/test/java/org/olat/util/FunctionalInstantMessagingUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..0388fd06c99f7f6dca55928b517e1dd6696a1541
--- /dev/null
+++ b/src/test/java/org/olat/util/FunctionalInstantMessagingUtil.java
@@ -0,0 +1,54 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+
+package org.olat.util;
+
+import com.thoughtworks.selenium.Selenium;
+
+public class FunctionalInstantMessagingUtil {
+	
+	public boolean openUserChat(Selenium browser, String userName){
+
+		//TODO:JK: implement me
+		
+		return(false);
+	}
+	
+	public boolean openGroupChat(Selenium browser, String groupName){
+		
+		//TODO:JK: implement me
+		
+		return(false);
+	}
+	
+	public boolean sendMessageToUser(Selenium browser, String userName, String message){
+
+		//TODO:JK: implement me
+		
+		return(false);
+	}
+	
+	public boolean sendMessageToGroup(Selenium browser, String groupName, String message){
+		
+		//TODO:JK: implement me
+		
+		return(false);	
+	}
+}
diff --git a/src/test/java/org/olat/util/FunctionalVOUtil.java b/src/test/java/org/olat/util/FunctionalVOUtil.java
index 6c5e902f5dddda5ffe90b10e5b571e4eccf321ea..58a403a362f502be6a071c735779c4f74c9d7863 100644
--- a/src/test/java/org/olat/util/FunctionalVOUtil.java
+++ b/src/test/java/org/olat/util/FunctionalVOUtil.java
@@ -282,6 +282,34 @@ public class FunctionalVOUtil {
 		restConnection.shutdown();
 	}
 	
+	/**
+	 * Adds participant to group.
+	 * 
+	 * @param deploymentUrl
+	 * @param group
+	 * @param owner
+	 * @throws IOException
+	 * @throws URISyntaxException
+	 */
+	public void addParticipantToGroup(URL deploymentUrl, GroupVO group, UserVO participant) throws IOException, URISyntaxException{
+		//add an owner
+		RestConnection restConnection = new RestConnection(deploymentUrl);
+		assertTrue(restConnection.login(getUsername(), getPassword()));
+
+		URI request = UriBuilder.fromUri(deploymentUrl.toURI())
+				.path("restapi")
+				.path("groups").path(group.getKey().toString())
+				.path("participants").path(participant.getKey().toString())
+				.build();
+
+		HttpPut method = restConnection.createPut(request, MediaType.APPLICATION_JSON, true);
+		HttpResponse response = restConnection.execute(method);
+		assertEquals(200, response.getStatusLine().getStatusCode());
+		EntityUtils.consume(response.getEntity());
+
+		restConnection.shutdown();
+	}
+	
 	public void setGroupConfiguration(URL deploymentUrl, GroupVO group,
 			String[] tools,
 			boolean ownersPublic, boolean ownersVisible,
diff --git a/src/test/java/org/olat/util/browser/Student3.java b/src/test/java/org/olat/util/browser/Student3.java
new file mode 100644
index 0000000000000000000000000000000000000000..a08f315f448939c7ba6118b05183cde820d13f92
--- /dev/null
+++ b/src/test/java/org/olat/util/browser/Student3.java
@@ -0,0 +1,38 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.util.browser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+ 
+import org.jboss.arquillian.drone.api.annotation.Qualifier;
+
+/**
+ * 
+ * @author jkraehemann, joel.kraehemann@frentix.com, frentix.com
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.PARAMETER })
+@Qualifier
+public @interface Student3 {
+
+}