diff --git a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java
index 6b434b8585e07a01cf491f4d7c59d3584ef5e58d..d302b09cf4e2eeb305f1d4995549a7554479982e 100644
--- a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java
+++ b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarManager.java
@@ -354,7 +354,7 @@ public class ICalFileCalendarManager implements CalendarManager, InitializingBea
 	@Override
 	public boolean persistCalendar(Kalendar kalendar) {
 		Calendar calendar = buildCalendar(kalendar);
-		boolean success = writeCalendarFile(calendar,kalendar.getType(), kalendar.getCalendarID());
+		boolean success = writeCalendarFile(calendar, kalendar.getType(), kalendar.getCalendarID());
 		calendarCache.update(getKeyFor(kalendar.getType(), kalendar.getCalendarID()), kalendar);
 		return success;
 	}
@@ -1204,7 +1204,9 @@ public class ICalFileCalendarManager implements CalendarManager, InitializingBea
 		}
 
 		reloadedCal.removeEvent(kalendarEvent); // remove old event
-		reloadedCal.addEvent(kalendarEvent); // add changed event
+		// clone the event to initialize the immutable date used to control moving events
+		KalendarEvent clonedEvent = getKalendarEvent(getVEvent(kalendarEvent));
+		reloadedCal.addEvent(clonedEvent); // add changed event
 
 		boolean successfullyPersist = persistCalendar(reloadedCal);
 		// inform all controller about calendar change for reload
diff --git a/src/main/java/org/olat/core/util/StringHelper.java b/src/main/java/org/olat/core/util/StringHelper.java
index 7be94fd659c807a33f444186cbeebc83a70fc644..2cc3cedf3227d7e6f817f2f6aaa250cc5dd9d5a4 100644
--- a/src/main/java/org/olat/core/util/StringHelper.java
+++ b/src/main/java/org/olat/core/util/StringHelper.java
@@ -35,6 +35,7 @@ import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -42,7 +43,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -542,67 +542,44 @@ public class StringHelper {
 	}
 	
 	/**
-	 * set of strings to one string comma separated.<br>
+	 * Collection of strings to one string comma separated. This method doesn't do any escaping
+	 * and will never do escaping to be backwards compatible.<br>
 	 * e.g. ["a","b","c","s"] -> "a,b,c,s" 
 	 * @param selection
 	 * @return
 	 */
-	public static String formatAsCSVString(Set<String> entries) {
-		boolean isFirst = true;
-		String csvStr = null;
-		for (Iterator<String> iter = entries.iterator(); iter.hasNext();) {
-			String group = iter.next();
-			if (isFirst) {
-				csvStr = group;
-				isFirst = false;
-			} else {
-				csvStr += ", " + group;
+	public static String formatAsCSVString(Collection<String> entries) {
+		StringBuilder csv = new StringBuilder(256);
+		for (String group:entries) {
+			if (csv.length() > 0) {
+				csv.append(",");
 			}
+			csv.append(group);
 		}
-		return csvStr;
+		return csv.toString();
 	}
 	
 	/**
-	 * list of strings to one string comma separated.<br>
-	 * e.g. ["a","b","c","s"] -> "a,b,c,s" 
+	 * Collection of strings to one string comma separated. The method escaped
+	 * " and ,<br>
+	 * e.g. ["a","b,1","c","s"] -> "a,"b,1",c,s" 
 	 * @param selection
 	 * @return
 	 */
-	public static String formatAsCSVString(List<String> entries) {
-		boolean isFirst = true;
-		String csvStr = null;
-		for (Iterator<String> iter = entries.iterator(); iter.hasNext();) {
-			String group = iter.next();
-			if (isFirst) {
-				csvStr = group;
-				isFirst = false;
-			} else {
-				csvStr += ", " + group;
+	public static String formatAsEscapedCSVString(Collection<String> entries) {
+		StringBuilder csv = new StringBuilder(256);
+		for (String entry:entries) {
+			entry = entry.replace("\"", "\"\"");
+			if (entry.contains(",")) {
+				entry = "\"" + entry + "\"";  
 			}
+			
+			if (csv.length() > 0) {
+				csv.append(",");
+			}
+			csv.append(entry);
 		}
-		return csvStr;
-	}
-	
-	/**
-	 * list of strings to one string comma separated.<br>
-	 * e.g. ["z","a","b","c","s","a"] -> "a, b, c, s, z"
-	 * No duplicates, alphabetically sorted
-	 * @param selection
-	 * @return
-	 */
-	public static String formatAsSortUniqCSVString(List<String> s) {
-		
-		Map <String,String>u = new HashMap<String,String>();
-		for (Iterator <String> si = s.iterator(); si.hasNext();) {
-			u.put(si.next().trim(), null);
-		}
-		
-		List <String>rv = new ArrayList<String>();
-		rv.addAll(u.keySet());
-		rv.remove("");
-		Collections.sort(rv);
-		
-		return formatAsCSVString (rv);
+		return csv.toString();
 	}
 	
 	/**
@@ -612,18 +589,17 @@ public class StringHelper {
 	 * @param selection
 	 * @return
 	 */
-	public static String formatAsSortUniqCSVString(Set<String> s) {
-		
-		Map <String,String>u = new HashMap<String,String>();
-		for (Iterator <String> si = s.iterator(); si.hasNext();) {
+	public static String formatAsSortUniqCSVString(Collection<String> s) {
+		Map<String,String> u = new HashMap<>();
+		for (Iterator<String> si = s.iterator(); si.hasNext();) {
 			u.put(si.next().trim(), null);
 		}
 		
-		List <String>rv = new ArrayList<String>();
+		List <String>rv = new ArrayList<>(u.size());
 		rv.addAll(u.keySet());
 		rv.remove("");
 		Collections.sort(rv);
 		
-		return formatAsCSVString (rv);
+		return formatAsCSVString(rv);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/statistic/export/LogLineConverter.java b/src/main/java/org/olat/course/statistic/export/LogLineConverter.java
index 9a5ca06e7d6393084438fdadce4997bcad723dc3..bba51ebe2608baecbf9394934e8d52ec106f5603 100644
--- a/src/main/java/org/olat/course/statistic/export/LogLineConverter.java
+++ b/src/main/java/org/olat/course/statistic/export/LogLineConverter.java
@@ -125,7 +125,7 @@ public class LogLineConverter {
 			PropertyDescriptor pd = it.next();
 			propertyNames.add(pd.getName());
 		}
-		return StringHelper.formatAsCSVString(propertyNames);
+		return StringHelper.formatAsEscapedCSVString(propertyNames);
 	}
 
 	/**
@@ -160,7 +160,7 @@ public class LogLineConverter {
 			loggingObjectList.add(strValue);
 		}
 		
-		return StringHelper.formatAsCSVString(loggingObjectList);
+		return StringHelper.formatAsEscapedCSVString(loggingObjectList);
 	}
 	
 	/**
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index dbd5d5219fe71b5917ac9e4909fd5f025950e8bf..c778a89db8b41385740b21e43b322a92b411adbd 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -1623,7 +1623,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				List<Identity> ownerList = getMembers(groups, GroupRoles.participant.name());
 				repoTutorList.retainAll(ownerList);
 				if(!dryRun) {
-					repositoryManager.removeTutors(ureqIdentity, repoTutorList, entry);
+					repositoryManager.removeTutors(ureqIdentity, repoTutorList, entry, new MailPackage(false));
 				}
 				count += repoTutorList.size();
 			}
diff --git a/src/main/java/org/olat/repository/RepositoryMailing.java b/src/main/java/org/olat/repository/RepositoryMailing.java
index 85ef024fb528192c3dced73a70b90812b6877195..c8d9edfa14745703501f989238de84e4ce9584d0 100644
--- a/src/main/java/org/olat/repository/RepositoryMailing.java
+++ b/src/main/java/org/olat/repository/RepositoryMailing.java
@@ -106,7 +106,7 @@ public class RepositoryMailing {
 	 * @param actor
 	 * @return the generated MailTemplate
 	 */
-	private static MailTemplate createRemoveParticipantMailTemplate(RepositoryEntry re, Identity actor) {
+	private static MailTemplate createRemoveMailTemplate(RepositoryEntry re, Identity actor) {
 		String subjectKey = "notification.mail.removed.subject";
 		String bodyKey = "notification.mail.removed.body";
 		return createMailTemplate(re, actor, subjectKey, bodyKey);
@@ -118,12 +118,14 @@ public class RepositoryMailing {
 		switch(type) {
 			case addParticipant:
 				return createAddParticipantMailTemplate(re, ureqIdentity);
-			case removeParticipant:
-				return createRemoveParticipantMailTemplate(re, ureqIdentity);
 			case addTutor:
 				return createAddTutorMailTemplate(re, ureqIdentity);
 			case addOwner:
 				return createAddOwnerMailTemplate(re, ureqIdentity);
+			case removeParticipant:
+			case removeTutor:
+			case removeOwner:
+				return createRemoveMailTemplate(re, ureqIdentity);
 		}
 		return null;
 	}
@@ -176,7 +178,9 @@ public class RepositoryMailing {
 		addParticipant,
 		removeParticipant,
 		addTutor,
-		addOwner
+		removeTutor,
+		addOwner,
+		removeOwner
 	}
 	
 	private static MailTemplate createMailTemplate(RepositoryEntry re, Identity actor, String subjectKey, String bodyKey) {
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 2e1f16c9d85b4a6608bbe80ab71d9f79433f23be..d66e275bc245e03999e66a32a13e6125204417fc 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -1561,11 +1561,11 @@ public class RepositoryManager {
 	 * @param re
 	 * @param logger
 	 */
-	public void removeOwners(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re){
+	public void removeOwners(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re, MailPackage mailing) {
 		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
 
 		for (Identity identity : removeIdentities) {
-			removeOwner(ureqIdentity, identity, re);
+			removeOwner(ureqIdentity, identity, re, mailing);
 			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
 		}
 
@@ -1581,9 +1581,10 @@ public class RepositoryManager {
 		}
 	}
 
-	private void removeOwner(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
+	private void removeOwner(Identity ureqIdentity, Identity identity, RepositoryEntry re, MailPackage mailing) {
 		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.owner.name());
 
+		RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeTutor, mailing);
 
 		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
@@ -1687,19 +1688,21 @@ public class RepositoryManager {
 	 * @param re
 	 * @param logger
 	 */
-	public void removeTutors(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re) {
+	public void removeTutors(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re, MailPackage mailing) {
 		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
 		for (Identity identity : removeIdentities) {
-			removeTutor(ureqIdentity, identity, re);
+			removeTutor(ureqIdentity, identity, re, mailing);
 			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
 		}
 		dbInstance.commit();
 		sendDeferredEvents(deferredEvents, re);
 	}
 
-	private void removeTutor(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
+	private void removeTutor(Identity ureqIdentity, Identity identity, RepositoryEntry re, MailPackage mailing) {
 		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.coach.name());
 
+		RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeTutor, mailing);
+		
 		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
 		try{
@@ -2325,7 +2328,7 @@ public class RepositoryManager {
 			if(changes.getRepoOwner().booleanValue()) {
 				addOwners(ureqIdentity, new IdentitiesAddEvent(changes.getMember()), re, mailing);
 			} else {
-				removeOwner(ureqIdentity, changes.getMember(), re);
+				removeOwner(ureqIdentity, changes.getMember(), re, mailing);
 				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
 		}
@@ -2334,7 +2337,7 @@ public class RepositoryManager {
 			if(changes.getRepoTutor().booleanValue()) {
 				addTutors(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
 			} else {
-				removeTutor(ureqIdentity, changes.getMember(), re);
+				removeTutor(ureqIdentity, changes.getMember(), re, mailing);
 				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
 		}
diff --git a/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java b/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
index 83ecb074a4446e061a7ad1a6c4e600dfbc725aac..c63f92bda5bcd6b90062ffe1fc6d2c9436c88d84 100644
--- a/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
+++ b/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
@@ -286,7 +286,7 @@ public class RepositoryEntryResource {
 			}
 
 			final UserRequest ureq = RestSecurityHelper.getUserRequest(request);
-			repositoryManager.removeOwners(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry);
+			repositoryManager.removeOwners(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry, new MailPackage(false));
 			return Response.ok().build();
 		} catch (Exception e) {
 			log.error("Trying to remove an owner to a repository entry", e);
@@ -406,7 +406,7 @@ public class RepositoryEntryResource {
 			}
 
 			final UserRequest ureq = RestSecurityHelper.getUserRequest(request);
-			repositoryManager.removeTutors(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry);
+			repositoryManager.removeTutors(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry, new MailPackage(false));
 			return Response.ok().build();
 		} catch (Exception e) {
 			log.error("Trying to remove a coach from a repository entry", e);
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 92e9519186260c115805c6d0dc34448ad212ce06..ba0c26fdf5c04a88eaa159afb45a8b502ee57c39 100644
--- a/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
+++ b/src/main/java/org/olat/restapi/repository/course/CourseWebService.java
@@ -788,7 +788,7 @@ public class CourseWebService {
 		RepositoryManager rm = RepositoryManager.getInstance();
 		RepositoryEntry repositoryEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
 		List<Identity> authors = Collections.singletonList(author);
-		rm.removeOwners(identity, authors, repositoryEntry);
+		rm.removeOwners(identity, authors, repositoryEntry, new MailPackage(false));
 		return Response.ok().build();
 	}
 	
diff --git a/src/test/java/org/olat/core/util/StringHelperTest.java b/src/test/java/org/olat/core/util/StringHelperTest.java
index f554c6b3cabbfe8e28d434faf315fec9044afe9b..5f71eddda9668371c82f452eec410ec122be2f03 100644
--- a/src/test/java/org/olat/core/util/StringHelperTest.java
+++ b/src/test/java/org/olat/core/util/StringHelperTest.java
@@ -22,6 +22,9 @@ package org.olat.core.util;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -176,4 +179,25 @@ public class StringHelperTest {
 		Assert.assertTrue(StringHelper.isHtml("<ul><li>Hello<li>world</ul>"));
 		Assert.assertTrue(StringHelper.isHtml("Hello<br>world"));
 	}
+	
+	
+	@Test
+	public void formatAsCSVString() {
+		List<String> entries = new ArrayList<>();
+		entries.add("Hell\"o\"");
+		entries.add("Test,dru,");
+		entries.add("Final");
+		String csv = StringHelper.formatAsCSVString(entries);
+		Assert.assertEquals("Hell\"o\",Test,dru,,Final", csv);
+	}
+	
+	@Test
+	public void formatAsEscapedCSVString() {
+		List<String> entries = new ArrayList<>();
+		entries.add("Hell\"o\"");
+		entries.add("Test,dru,");
+		entries.add("Final");
+		String csv = StringHelper.formatAsEscapedCSVString(entries);
+		Assert.assertEquals("Hell\"\"o\"\",\"Test,dru,\",Final", csv);
+	}
 }
diff --git a/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java b/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java
index 642567fc9912283ea20c2e62be68067ec50ab98b..d868776ab0abbb6a878653a66cdb514d68976b7c 100644
--- a/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java
+++ b/src/test/java/org/olat/repository/manager/RepositoryEntryMembershipProcessorTest.java
@@ -141,7 +141,7 @@ public class RepositoryEntryMembershipProcessorTest extends OlatTestCase {
 		List<Identity> removeIdentities = new ArrayList<>(2);
 		removeIdentities.add(member);
 		removeIdentities.add(coach);
-		repositoryManager.removeTutors(owner, removeIdentities, re);
+		repositoryManager.removeTutors(owner, removeIdentities, re, new MailPackage(false));
 
 		//wait for the remove of subscription
 		waitForCondition(new CheckUnsubscription(member, context, dbInstance, notificationManager), 5000);