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 cbcca16c4c2a20468a6843f3c64921e8eb0bb250..f2a648837d76cf5f04d8e0165e35f1be7ec73946 100644
--- a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
+++ b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
@@ -22,7 +22,6 @@ package org.olat.admin.user.bulkChange;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -52,13 +51,9 @@ import org.olat.core.logging.Tracing;
 import org.olat.core.manager.BasicManager;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
-import org.olat.core.util.mail.MailTemplate;
-import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
-import org.olat.group.BusinessGroup;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.group.BusinessGroupService;
 import org.olat.group.model.BusinessGroupMembershipChange;
-import org.olat.group.ui.BGMailHelper;
 import org.olat.login.auth.OLATAuthManager;
 import org.olat.user.UserManager;
 import org.olat.user.propertyhandlers.UserPropertyHandler;
@@ -237,13 +232,16 @@ public class UserBulkChangeManager extends BasicManager {
 			}
 
 			BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
-			bgs.updateMemberships(addingIdentity, changes);
+			MailPackage mailing = new MailPackage();
+			bgs.updateMemberships(addingIdentity, changes, mailing);//TODO memail
 			DBFactory.getInstance().commit();
 			
-			if(mailGroups != null && !mailGroups.isEmpty()) {
+			//TODO memail not needed anymore
+			/*if(mailGroups != null && !mailGroups.isEmpty()) {
 				List<BusinessGroup> notifGroups = bgs.loadBusinessGroups(mailGroups);
 				for (BusinessGroup group : notifGroups) {
 					for(Identity selIdentity:selIdentities) {
+
 						MailTemplate mailTemplate = BGMailHelper.createAddParticipantMailTemplate(group, addingIdentity);
 						MailerWithTemplate mailer = MailerWithTemplate.getInstance();
 						MailerResult mailerResult = mailer.sendMailAsSeparateMails(null, Collections.singletonList(selIdentity), null, mailTemplate, null);
@@ -253,7 +251,7 @@ public class UserBulkChangeManager extends BasicManager {
 						}
 					}
 				}
-			}
+			}*/
 		}
 	}
 
diff --git a/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java b/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
index 648a491d52428a5fa81926af2280bc114d85b337..0837b6c77bef2e3ed5623294295442690074f010 100644
--- a/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
+++ b/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
@@ -52,16 +52,13 @@ import org.olat.core.gui.control.generic.closablewrapper.CloseableModalControlle
 import org.olat.core.id.Identity;
 import org.olat.core.util.Util;
 import org.olat.core.util.mail.MailHelper;
-import org.olat.core.util.mail.MailTemplate;
-import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupMembership;
 import org.olat.group.BusinessGroupService;
 import org.olat.group.model.AddToGroupsEvent;
 import org.olat.group.model.BusinessGroupMembershipChange;
 import org.olat.group.model.SearchBusinessGroupParams;
-import org.olat.group.ui.BGMailHelper;
 import org.olat.group.ui.main.BGRoleCellRenderer;
 import org.olat.group.ui.main.BGTableItem;
 import org.olat.group.ui.main.BusinessGroupTableModelWithType;
@@ -269,10 +266,12 @@ public class GroupOverviewController extends BasicController {
 				changes.add(change);
 			}
 		}
-		businessGroupService.updateMemberships(getIdentity(), changes);
+		
+		MailPackage mailing = new MailPackage();//TODO memail
+		businessGroupService.updateMemberships(getIdentity(), changes, mailing);
 		DBFactory.getInstance().commit();
 		
-		if(e.getMailForGroupsList() != null && !e.getMailForGroupsList().isEmpty()) {
+		/*if(e.getMailForGroupsList() != null && !e.getMailForGroupsList().isEmpty()) {
 			List<BusinessGroup> notifGroups = businessGroupService.loadBusinessGroups(e.getMailForGroupsList());
 			for (BusinessGroup group : notifGroups) {
 				MailTemplate mailTemplate = BGMailHelper.createAddParticipantMailTemplate(group, getIdentity());
@@ -283,7 +282,7 @@ public class GroupOverviewController extends BasicController {
 							+ group.getName() + " key: " + group.getKey() + " mailerresult: " + mailerResult.getReturnCode(), null);
 				}
 			}
-		}
+		}*/
 	}
 	
 	private void doLeave(UserRequest ureq, List<BusinessGroup> groupsToLeave) {
@@ -324,16 +323,18 @@ public class GroupOverviewController extends BasicController {
 				if (securityManager.isIdentityInSecurityGroup(identity, group.getOwnerGroup())) {
 					businessGroupService.removeOwners(ureq.getIdentity(), Collections.singletonList(identity), group);
 				}
+				MailPackage mailing = new MailPackage(doSendMail);
 				// 2) remove as participant
-				businessGroupService.removeParticipants(getIdentity(), Collections.singletonList(identity), group);
+				businessGroupService.removeParticipants(getIdentity(), Collections.singletonList(identity), group, mailing);
+				MailHelper.printErrorsAndWarnings(mailing.getResult(), getWindowControl(), getLocale());
 	
 				// 3) notify user about this action:
-				if(doSendMail){
+				/*if(doSendMail){
 					MailTemplate mailTemplate = BGMailHelper.createRemoveParticipantMailTemplate(group, getIdentity());
 					MailerWithTemplate mailer = MailerWithTemplate.getInstance();
 					MailerResult mailerResult = mailer.sendMailAsSeparateMails(null, Collections.singletonList(identity), null, mailTemplate, null);
-					MailHelper.printErrorsAndWarnings(mailerResult, getWindowControl(), getLocale());
-				}
+					
+				}*/
 			}
 		}
 
diff --git a/src/main/java/org/olat/admin/user/imp/UserImportController.java b/src/main/java/org/olat/admin/user/imp/UserImportController.java
index 0a713c00f4197caa8cfd6c14f972eed665c4a460..c7922d4fc28102ca2969011c2b48590120e0d71b 100644
--- a/src/main/java/org/olat/admin/user/imp/UserImportController.java
+++ b/src/main/java/org/olat/admin/user/imp/UserImportController.java
@@ -249,7 +249,7 @@ public class UserImportController extends BasicController {
 				}
 			}
 		}
-		businessGroupService.updateMemberships(getIdentity(), changes);
+		businessGroupService.updateMemberships(getIdentity(), changes, null);//TODO memail
 		DBFactory.getInstance().commit();
 		
 		if(mailGroups != null && !mailGroups.isEmpty()) {
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index e8a91fe0455445c513ade7467de0a14d3c3b8dad..0779f3ab3061895f5d33b501376ae2aace6fb517 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -315,7 +315,26 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		// if the olatResourceable is not persisted as OLATResource, then the answer
 		// is false,
 		// therefore we can use the query assuming there is an OLATResource
-
+		
+		
+		
+		TypedQuery<Number> query;
+		if(checkTypeRight) {
+			query = DBFactory.getInstance().getCurrentEntityManager().createNamedQuery("isIdentityPermittedOnResourceableCheckType", Number.class);
+		} else {
+			query = DBFactory.getInstance().getCurrentEntityManager().createNamedQuery("isIdentityPermittedOnResourceable", Number.class);
+		}
+		
+		Number count = query.setParameter("identitykey", iimpl.getKey())
+				.setParameter("permission", permission)
+				.setParameter("resid", oresid)
+				.setParameter("resname", oresName)
+				.getSingleResult();
+		return count.longValue() > 0;
+		
+		
+		
+/*
 		String queryString;
 		if (checkTypeRight) {
 			queryString = 
@@ -336,6 +355,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 			+ " and poi.permission = :permission and poi.olatResource = ori"
 			+ " and (ori.resId = :resid) and ori.resName = :resname";
 		}
+		
 		DBQuery query = DBFactory.getInstance().createQuery(queryString);
 		query.setLong("identitykey", iimpl.getKey());
 		query.setString("permission", permission);		
@@ -346,6 +366,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		Long cntL = (Long) res.get(0);
 		return (cntL.longValue() > 0); // can be > 1 if identity is in more the one group having
 		// the permission on the olatresourceable
+		*/
 	}
 
 	/**
diff --git a/src/main/java/org/olat/basesecurity/PolicyImpl.hbm.xml b/src/main/java/org/olat/basesecurity/PolicyImpl.hbm.xml
index 55c2f4db35eaaaf5595fce10bf753d2a50fd5b8e..d4ce888b5daa73178ac987c84d2af5769f447f4d 100644
--- a/src/main/java/org/olat/basesecurity/PolicyImpl.hbm.xml
+++ b/src/main/java/org/olat/basesecurity/PolicyImpl.hbm.xml
@@ -34,5 +34,15 @@
 		cascade="none"/> 
 	-->
 	</class>
+	
+	
+	
+	<query name="isIdentityPermittedOnResourceableCheckType">
+		<![CDATA[select count(poi) from org.olat.basesecurity.SecurityGroupMembershipImpl as sgmsi, org.olat.basesecurity.PolicyImpl as poi, org.olat.resource.OLATResourceImpl as ori where sgmsi.identity.key = :identitykey and sgmsi.securityGroup =  poi.securityGroup and poi.permission = :permission and poi.olatResource = ori and (ori.resId = :resid or ori.resId = 0) and ori.resName = :resname]]>
+	</query>
+	<query name="isIdentityPermittedOnResourceable">
+		<![CDATA[select count(poi) from org.olat.basesecurity.SecurityGroupMembershipImpl as sgmsi, org.olat.basesecurity.PolicyImpl as poi, org.olat.resource.OLATResourceImpl as ori where sgmsi.identity.key = :identitykey and sgmsi.securityGroup =  poi.securityGroup and poi.permission = :permission and poi.olatResource = ori and (ori.resId = :resid) and ori.resName = :resname]]>
+	</query>
+	
 </hibernate-mapping>
 
diff --git a/src/main/java/org/olat/core/util/mail/MailPackage.java b/src/main/java/org/olat/core/util/mail/MailPackage.java
new file mode 100644
index 0000000000000000000000000000000000000000..a848ebc105096fb463b02099be4dfaf23ea8ee3b
--- /dev/null
+++ b/src/main/java/org/olat/core/util/mail/MailPackage.java
@@ -0,0 +1,86 @@
+package org.olat.core.util.mail;
+
+/**
+ * A mail package is the sum of the template, the context and it's result.
+ * All or part of thesse can be null.
+ * 
+ * 
+ * @author srosse
+ *
+ */
+public class MailPackage {
+	
+	private final boolean sendEmail;
+	private final MailTemplate template;
+	private final MailContext context;
+	private final MailerResult result;
+	
+	public MailPackage() {
+		this.sendEmail = true;
+		this.template = null;
+		this.context = null;
+		this.result = new MailerResult();
+	}
+	
+	public MailPackage(boolean sendMail) {
+		this.sendEmail = sendMail;
+		this.template = null;
+		this.context = null;
+		this.result = new MailerResult();
+	}
+	
+	public MailPackage(MailTemplate template, MailContext context) {
+		this.sendEmail = true;
+		this.template = template;
+		this.context = context;
+		this.result = new MailerResult();
+	}
+	
+	public MailPackage(MailTemplate template, String businessPath, boolean sendMail) {
+		this.sendEmail = true;
+		this.template = template;
+		this.context = new MailContextImpl(null, null, businessPath);
+		this.result = new MailerResult();
+	}
+	
+	public MailPackage(MailerResult result, String businessPath, boolean sendMail) {
+		this.sendEmail = true;
+		this.template = null;
+		this.context = new MailContextImpl(null, null, businessPath);
+		this.result = result;
+	}
+	
+	public MailPackage(MailTemplate template, MailerResult result, String businessPath, boolean sendMail) {
+		this.sendEmail = true;
+		this.template = template;
+		this.context = new MailContextImpl(null, null, businessPath);
+		this.result = result;
+	}
+
+
+	/**
+	 * Default is true, you want to send mails. But in rare case, this flag
+	 * give you the possibility to skip the mails.
+	 * @return
+	 */
+	public boolean isSendEmail() {
+		return sendEmail;
+	}
+
+	public MailTemplate getTemplate() {
+		return template;
+	}
+	
+	public MailContext getContext() {
+		return context;
+	}
+	
+	public MailerResult getResult() {
+		return result;
+	}
+
+	
+	public void appendResult(MailerResult result) {
+		
+	}
+}
diff --git a/src/main/java/org/olat/core/util/mail/MailerResult.java b/src/main/java/org/olat/core/util/mail/MailerResult.java
index feba3219a40d40a30f0242ceea5b2e795a7f4625..bb88ac32752c5530cb239ee8aa986e9b737664f2 100644
--- a/src/main/java/org/olat/core/util/mail/MailerResult.java
+++ b/src/main/java/org/olat/core/util/mail/MailerResult.java
@@ -85,5 +85,16 @@ public class MailerResult {
 	public void setReturnCode(int returnCode) {
 		this.returnCode = returnCode;
 	}
-
+	
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("mailerResult[");
+		if(returnCode == 0) {
+			sb.append("OK");
+		} else {
+			sb.append("Problems");
+		}
+		return sb.append("]").toString();
+	}
 }
diff --git a/src/main/java/org/olat/core/util/mail/MailerWithTemplate.java b/src/main/java/org/olat/core/util/mail/MailerWithTemplate.java
index ea8088fadd5f71a39bf4f372eb104c77ecbcee70..87aeec6e72ef8d8f27f8bd549fd125a7678c0d27 100644
--- a/src/main/java/org/olat/core/util/mail/MailerWithTemplate.java
+++ b/src/main/java/org/olat/core/util/mail/MailerWithTemplate.java
@@ -47,6 +47,7 @@ import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.i18n.I18nManager;
 import org.olat.core.util.mail.manager.MailManager;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * Description:<br>
@@ -62,12 +63,28 @@ public class MailerWithTemplate {
 	
 	private static final OLog log = Tracing.createLoggerFor(MailerWithTemplate.class);
 	private static MailerWithTemplate INSTANCE = new MailerWithTemplate();
+	
+	@Autowired
+	private MailManager mailManager;
 
 	/**
 	 * Singleton constructor
 	 */
 	private MailerWithTemplate() {
-		super();
+		INSTANCE = this;
+	}
+	
+	/**
+	 * @return MailerWithTemplate returns the singleton instance
+	 */
+	public static MailerWithTemplate getInstance() {
+		return INSTANCE;
+	}
+	
+	/**
+	 * [used by Spring]
+	 */
+	public void init() {
 		// init velocity engine
 		Properties p = null;
 		try {
@@ -81,12 +98,7 @@ public class MailerWithTemplate {
 		}
 	}
 
-	/**
-	 * @return MailerWithTemplate returns the singleton instance
-	 */
-	public static MailerWithTemplate getInstance() {
-		return INSTANCE;
-	}
+
 
 	/**
 	 * Send a mail to the given identity and the other recipients (CC and BCC)
@@ -114,7 +126,7 @@ public class MailerWithTemplate {
 			return result;
 		}
 
-		MailManager.getInstance().sendExternMessage(null, null, recipientTO, null, null, null, null, msg.getSubject(), msg.getBody(), null, result);
+		mailManager.sendExternMessage(null, null, recipientTO, null, null, null, null, msg.getSubject(), msg.getBody(), null, result);
 		return result;
 	}
 
@@ -278,7 +290,7 @@ public class MailerWithTemplate {
 				}
 			}
 
-			MailerResult mgrResult = MailManager.getInstance().sendMessage(mCtxt, sender, null, recipientTO, null, null, null, null,
+			MailerResult mgrResult = mailManager.sendMessage(mCtxt, sender, null, recipientTO, null, null, null, null,
 					metaId, msg.getSubject(), msg.getBody(), attachmentList);
 			
 			if(mgrResult.getReturnCode() != MailerResult.OK) {
diff --git a/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml b/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml
index efbf7e6b37dfd84c30cf968559540339a5a5da8e..c9ace44fec135339010320b43c60d906d5e7b478 100644
--- a/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml
+++ b/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml
@@ -54,6 +54,10 @@
 		<property name="parentTreeNodeIdentifier" value="sysconfigParent" /> 
 	</bean> 
 	
+	<bean id="mailerWithTemplate" class="org.olat.core.util.mail.MailerWithTemplate" init-method="init">
+	
+	</bean>
+	
 	<bean id="mailManager" class="org.olat.core.util.mail.manager.MailManager" init-method="init">
 		<constructor-arg index="0" ref="mailModule" />
 		<property name="dbInstance" ref="database"/>
diff --git a/src/main/java/org/olat/course/member/MembersOverviewController.java b/src/main/java/org/olat/course/member/MembersOverviewController.java
index 7daa3e5c6f38056e83f42e1c00c63ab4e0602f64..00c834f8023ece910e4c991738a74babc5cd2141 100644
--- a/src/main/java/org/olat/course/member/MembersOverviewController.java
+++ b/src/main/java/org/olat/course/member/MembersOverviewController.java
@@ -46,12 +46,10 @@ import org.olat.core.id.context.BusinessControlFactory;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
-import org.olat.core.util.mail.MailContext;
-import org.olat.core.util.mail.MailContextImpl;
 import org.olat.core.util.mail.MailHelper;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
 import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.course.member.wizard.ImportMember_1a_LoginListStep;
 import org.olat.course.member.wizard.ImportMember_1b_ChooseMemberStep;
@@ -249,21 +247,21 @@ public class MembersOverviewController extends BasicController implements Activa
 		List<Identity> members = (List<Identity>)runContext.get("members");
 		
 		MemberPermissionChangeEvent changes = (MemberPermissionChangeEvent)runContext.get("permissions");
+		
+		MailTemplate template = (MailTemplate)runContext.get("mailTemplate");
 		//commit changes to the repository entry
+		MailerResult result = new MailerResult();
+		MailPackage reMailing = new MailPackage(result, getWindowControl().getBusinessControl().getAsString(), template != null);
+		
 		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges);
+		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
-		businessGroupService.updateMemberships(getIdentity(), allModifications);
 		
-		MailTemplate template = (MailTemplate)runContext.get("mailTemplate");
-		if (template != null && !members.isEmpty()) {
-			MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-			MailContext context = new MailContextImpl(null, null, getWindowControl().getBusinessControl().getAsString());
-			MailerResult mailerResult = mailer.sendMailAsSeparateMails(context, members, null, template, getIdentity());
-			MailHelper.printErrorsAndWarnings(mailerResult, getWindowControl(), getLocale());
-		}
+		MailPackage mailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), template != null);
+		businessGroupService.updateMemberships(getIdentity(), allModifications, mailing);
+		MailHelper.printErrorsAndWarnings(result, getWindowControl(), getLocale());
 		
 		switchToAllMembers(ureq);
 	}
diff --git a/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java b/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java
index f3e80ed0a270e408196864f5491fc0cd94a374c6..3f06d0113c62959560129a9b64ac1dc8f70a8575 100644
--- a/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java
+++ b/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java
@@ -33,10 +33,13 @@ import org.olat.core.gui.control.generic.wizard.StepsEvent;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
 import org.olat.core.util.mail.MailTemplate;
-import org.olat.core.util.mail.MailTemplateForm;
 import org.olat.group.BusinessGroupModule;
+import org.olat.group.BusinessGroupShort;
+import org.olat.group.manager.BusinessGroupMailing;
+import org.olat.group.manager.BusinessGroupMailing.MailType;
 import org.olat.group.model.BusinessGroupMembershipChange;
 import org.olat.group.ui.main.MemberPermissionChangeEvent;
+import org.olat.group.ui.wizard.BGMailTemplateController;
 
 /**
  * 
@@ -44,23 +47,30 @@ import org.olat.group.ui.main.MemberPermissionChangeEvent;
  */
 public class ImportMemberMailController extends StepFormBasicController {
 	
-	private final MailTemplate mailTemplate;
-	private final MailTemplateForm mailTemplateForm;
+	private MailTemplate mailTemplate;
+	private final BGMailTemplateController mailTemplateForm;
 
 	public ImportMemberMailController(UserRequest ureq, WindowControl wControl, Form rootForm, StepsRunContext runContext) {
 		super(ureq, wControl, rootForm, runContext, LAYOUT_CUSTOM, "mail_template");
-		
+
+		MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)runContext.get("permissions");
 		boolean mandatoryEmail = CoreSpringFactory.getImpl(BusinessGroupModule.class).isMandatoryEnrolmentEmail(ureq.getUserSession().getRoles());
 		if(mandatoryEmail) {
-			MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)runContext.get("permissions");
 			boolean includeParticipantsRights = hasParticipantRightsChanges(e);
 			if(!includeParticipantsRights) {
 				mandatoryEmail = false;//only mandatory for participants
 			}
 		}
 		
-		mailTemplate = new TestMailTemplate();
-		mailTemplateForm = new MailTemplateForm(ureq, wControl, mailTemplate, mandatoryEmail, rootForm);
+		boolean customizing = e.size() == 1;
+		MailType defaultType = BusinessGroupMailing.getDefaultTemplateType(e);
+		if(defaultType != null && e.getGroups().size() == 1) {
+			BusinessGroupShort group = e.getGroups().get(0);
+			mailTemplate = BusinessGroupMailing.getDefaultTemplate(defaultType, group, getIdentity());
+		} else {
+			mailTemplate = new TestMailTemplate();
+		}
+		mailTemplateForm = new BGMailTemplateController(ureq, wControl, mailTemplate, false, customizing, false, mandatoryEmail, rootForm);
 		
 		initForm (ureq);
 	}
@@ -98,7 +108,9 @@ public class ImportMemberMailController extends StepFormBasicController {
 	@Override
 	protected void formOK(UserRequest ureq) {
 		if(mailTemplateForm.sendMailSwitchEnabled()) {
-			mailTemplateForm.updateTemplateFromForm(mailTemplate);
+			if(!mailTemplateForm.isDefaultTemplate()) {
+				mailTemplateForm.updateTemplateFromForm(mailTemplate);
+			}
 			addToRunContext("mailTemplate", mailTemplate);
 		} else {
 			addToRunContext("mailTemplate", null);
diff --git a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java
index 49fa4ab6baf3c8796901a8441984f948219fc29f..2010d66ae206565411e0c5a4de7f3904671cf2c0 100644
--- a/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java
+++ b/src/main/java/org/olat/course/nodes/en/EnrollmentManager.java
@@ -93,7 +93,7 @@ public class EnrollmentManager extends BasicManager {
 			// and: why can't we just have a group here and a max participants count and an identity to enrol?
 			// the group was chosen, so why do we need the groupNames and areaNames here???
 
-			EnrollState state =businessGroupService.enroll(identity, roles, identity, group);
+			EnrollState state =businessGroupService.enroll(identity, roles, identity, group, null);//TODO memail
 			if(state.isFailed()) {
 				enrollStatus.setErrorMessage(trans.translate(state.getI18nErrorMessage()));
 			} else {
@@ -116,7 +116,7 @@ public class EnrollmentManager extends BasicManager {
 		// 1. Remove group membership, fire events, do loggin etc.
 		// Remove participant. This will also check if a waiting-list with auto-close-ranks is configurated
 		// and move the users accordingly
-		businessGroupService.removeParticipants(identity, Collections.singletonList(identity), enrolledGroup);
+		businessGroupService.removeParticipants(identity, Collections.singletonList(identity), enrolledGroup, null);//TODO memail
 		logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName());
 
 		logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName());
@@ -139,7 +139,7 @@ public class EnrollmentManager extends BasicManager {
 	public void doCancelEnrollmentInWaitingList(final Identity identity, final BusinessGroup enrolledWaitingListGroup, final ENCourseNode enNode,
 			final CoursePropertyManager coursePropertyManager, WindowControl wControl, Translator trans) {
 		// 1. Remove group membership, fire events, do loggin etc.
-		businessGroupService.removeFromWaitingList(identity, Collections.singletonList(identity), enrolledWaitingListGroup);
+		businessGroupService.removeFromWaitingList(identity, Collections.singletonList(identity), enrolledWaitingListGroup, null);
 		
 		// 2. Remove enrollmentdate property
 		// only remove last time date, not firsttime
diff --git a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java
index 987dc0ff848842d1d29b4b966eb53cf10afe1d02..63f31ab63241f5f30bcf19fc745cb9f175e04a6a 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java
@@ -301,14 +301,15 @@ public class ProjectBrokerCourseEditorController extends ActivateableTabbableDef
 		} else if (source == accountManagerGroupController) {
 			if (event instanceof IdentitiesAddEvent) {
 				IdentitiesAddEvent identitiesAddedEvent = (IdentitiesAddEvent)event;
-				BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(), identitiesAddedEvent.getAddIdentities(), accountManagerGroup);
+				BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(),
+						identitiesAddedEvent.getAddIdentities(), accountManagerGroup, null);//TODO memail
 				identitiesAddedEvent.setIdentitiesAddedEvent(response.getAddedIdentities());
 				identitiesAddedEvent.setIdentitiesWithoutPermission(response.getIdentitiesWithoutPermission());
 				identitiesAddedEvent.setIdentitiesAlreadyInGroup(response.getIdentitiesAlreadyInGroup());
 				getLogger().info("Add users as account-managers");
 				fireEvent(urequest, Event.CHANGED_EVENT );			
 			} else if (event instanceof IdentitiesRemoveEvent) {
-				businessGroupService.removeParticipants(urequest.getIdentity(), ((IdentitiesRemoveEvent) event).getRemovedIdentities(), accountManagerGroup);
+				businessGroupService.removeParticipants(urequest.getIdentity(), ((IdentitiesRemoveEvent) event).getRemovedIdentities(), accountManagerGroup, null);
 				getLogger().info("Remove users as account-managers");
 				fireEvent(urequest, Event.CHANGED_EVENT );
 			}
diff --git a/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java b/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java
index 2aae5698ea6a89b12b44f077a3da56960307ac38..adb0306903bf93f3ec38e3bece4ad538830f226f 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java
@@ -185,14 +185,15 @@ public class ProjectGroupController extends BasicController {
 	private void handleProjectMemberGroupEvent(UserRequest urequest, Event event) {
 		if (event instanceof IdentitiesAddEvent) {
 			IdentitiesAddEvent identitiesAddedEvent = (IdentitiesAddEvent)event;
-			BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(), identitiesAddedEvent.getAddIdentities(), project.getProjectGroup());
+			BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(),
+					identitiesAddedEvent.getAddIdentities(), project.getProjectGroup(), null);//TODO memail
 			identitiesAddedEvent.setIdentitiesAddedEvent(response.getAddedIdentities());
 			identitiesAddedEvent.setIdentitiesWithoutPermission(response.getIdentitiesWithoutPermission());
 			identitiesAddedEvent.setIdentitiesAlreadyInGroup(response.getIdentitiesAlreadyInGroup());
 			getLogger().info("Add users as project-members");
 			fireEvent(urequest, Event.CHANGED_EVENT );			
 		} else if (event instanceof IdentitiesRemoveEvent) {
-			businessGroupService.removeParticipants(urequest.getIdentity(), ((IdentitiesRemoveEvent) event).getRemovedIdentities(), project.getProjectGroup());
+			businessGroupService.removeParticipants(urequest.getIdentity(), ((IdentitiesRemoveEvent) event).getRemovedIdentities(), project.getProjectGroup(), null);
 			getLogger().info("Remove users as account-managers");
 			fireEvent(urequest, Event.CHANGED_EVENT );
 		}
diff --git a/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectGroupManagerImpl.java b/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectGroupManagerImpl.java
index 734dd672bd9d97878d5d25735a383a8a6eb0bf54..137ed01e6a54348d0e3b57723ad5bba395d4971e 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectGroupManagerImpl.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectGroupManagerImpl.java
@@ -248,7 +248,7 @@ public class ProjectGroupManagerImpl extends BasicManager implements ProjectGrou
 		final BusinessGroupAddResponse response = new BusinessGroupAddResponse();
 		final BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
 		//TODO memail
-		BusinessGroupAddResponse state = bgs.addParticipants(actionIdentity, null, identities, reloadedProject.getProjectGroup());
+		BusinessGroupAddResponse state = bgs.addParticipants(actionIdentity, null, identities, reloadedProject.getProjectGroup(), null);//TODO memail
 		response.getAddedIdentities().addAll(state.getAddedIdentities());
 		response.getIdentitiesAlreadyInGroup().addAll(state.getAddedIdentities());
 		
diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java
index 5157dcf444029d519a36d04086cab6dd9c206550..9bae08c5b90553b2c13236fa6760dff3175b7139 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -27,6 +27,7 @@ import java.util.Locale;
 import org.olat.basesecurity.SecurityGroup;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailerResult;
 import org.olat.group.area.BGArea;
 import org.olat.group.model.BGRepositoryEntryRelation;
@@ -195,14 +196,16 @@ public interface BusinessGroupService {
 	 * @param groupsToMerge
 	 * @return
 	 */
-	public BusinessGroup mergeBusinessGroups(Identity ureqIdentity, BusinessGroup targetGroup, List<BusinessGroup> groupsToMerge);
+	public BusinessGroup mergeBusinessGroups(Identity ureqIdentity, BusinessGroup targetGroup,
+			List<BusinessGroup> groupsToMerge, MailPackage mailing);
 	
 	/**
 	 * Update the members of a list of business groups
 	 * @param membersMod
 	 * @param groups
 	 */
-	public void updateMembership(Identity identity, MembershipModification modifications, List<BusinessGroup> groups);
+	public void updateMembership(Identity identity, MembershipModification modifications,
+			List<BusinessGroup> groups, MailPackage mailing);
 	
 	/**
 	 * Very fine tuned membership changes on several groups
@@ -210,7 +213,8 @@ public interface BusinessGroupService {
 	 * @param ureqIdentity
 	 * @param changes
 	 */
-	public void updateMemberships(Identity ureqIdentity, List<BusinessGroupMembershipChange> changes);
+	public void updateMemberships(Identity ureqIdentity, List<BusinessGroupMembershipChange> changes,
+			MailPackage mailing);
 	
 	//search methods
 	/**
@@ -350,7 +354,8 @@ public interface BusinessGroupService {
 	 * @param identity
 	 * @return
 	 */
-	public EnrollState enroll(Identity ureqIdentity, Roles ureqRoles, Identity identityToEnroll, BusinessGroup group);
+	public EnrollState enroll(Identity ureqIdentity, Roles ureqRoles, Identity identityToEnroll, BusinessGroup group,
+			MailPackage mailing);
 	
 	/**
 	 * Adds a list of users to a group as participant and does all the magic that needs to
@@ -368,7 +373,8 @@ public interface BusinessGroupService {
 	 * @param flags
 	 * @return
 	 */
-	public BusinessGroupAddResponse addParticipants(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities, BusinessGroup currBusinessGroup);
+	public BusinessGroupAddResponse addParticipants(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities,
+			BusinessGroup currBusinessGroup, MailPackage mailing);
 	
 	/**
 	 * 
@@ -392,7 +398,7 @@ public interface BusinessGroupService {
 	 * @param group
 	 * @param flags
 	 */
-	public void removeParticipants(Identity ureqIdentity, List<Identity> identities, BusinessGroup group);
+	public void removeParticipants(Identity ureqIdentity, List<Identity> identities, BusinessGroup group, MailPackage mailing);
 	
 	/**
 	 * Remove the members (tutors and participants) from all business groups connected
@@ -403,7 +409,7 @@ public interface BusinessGroupService {
 	 * @param identities
 	 * @param group
 	 */
-	public void removeMembers(Identity ureqIdentity, List<Identity> identities, OLATResource resource);
+	public void removeMembers(Identity ureqIdentity, List<Identity> identities, OLATResource resource, MailPackage mailing);
 
 	
 	/**
@@ -422,7 +428,8 @@ public interface BusinessGroupService {
 	 * @param flags
 	 * @return
 	 */
-	public BusinessGroupAddResponse addToWaitingList(Identity ureqIdentity, List<Identity> addIdentities, BusinessGroup currBusinessGroup);
+	public BusinessGroupAddResponse addToWaitingList(Identity ureqIdentity, List<Identity> addIdentities,
+			BusinessGroup businessGroup, MailPackage mailing);
 
 	/**
 	 * Remove a list of users from a waiting-list as participant and does all the magic that needs
@@ -435,7 +442,7 @@ public interface BusinessGroupService {
 	 * @param currBusinessGroup
 	 * @param flags
 	 */
-	public void removeFromWaitingList(Identity ureqIdentity, List<Identity> identities, BusinessGroup currBusinessGroup);
+	public void removeFromWaitingList(Identity ureqIdentity, List<Identity> identities, BusinessGroup businessGroup, MailPackage mailing);
 
 	/**
 	 * Move users from a waiting-list to participant-list.
@@ -445,7 +452,8 @@ public interface BusinessGroupService {
 	 * @param flags
 	 * @return
 	 */
-	public BusinessGroupAddResponse moveIdentityFromWaitingListToParticipant(Identity ureqIdentity, List<Identity> identities, BusinessGroup currBusinessGroup);
+	public BusinessGroupAddResponse moveIdentityFromWaitingListToParticipant(Identity ureqIdentity, List<Identity> identities,
+			BusinessGroup currBusinessGroup, MailPackage mailing);
 
 	
 	public BusinessGroupAddResponse addToSecurityGroupAndFireEvent(Identity ureqIdentity, List<Identity> addIdentities, SecurityGroup secGroup);
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupMailing.java b/src/main/java/org/olat/group/manager/BusinessGroupMailing.java
new file mode 100644
index 0000000000000000000000000000000000000000..47145e8560ef05bf1e9717fabbf0b485af46d16a
--- /dev/null
+++ b/src/main/java/org/olat/group/manager/BusinessGroupMailing.java
@@ -0,0 +1,124 @@
+/**
+ * <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.group.manager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.core.id.Identity;
+import org.olat.core.util.mail.MailContext;
+import org.olat.core.util.mail.MailContextImpl;
+import org.olat.core.util.mail.MailPackage;
+import org.olat.core.util.mail.MailTemplate;
+import org.olat.core.util.mail.MailerResult;
+import org.olat.core.util.mail.MailerWithTemplate;
+import org.olat.group.BusinessGroupShort;
+import org.olat.group.model.BusinessGroupMembershipChange;
+import org.olat.group.model.MembershipModification;
+import org.olat.group.ui.BGMailHelper;
+import org.olat.group.ui.main.MemberPermissionChangeEvent;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http:/www.frentix.com
+ */
+public class BusinessGroupMailing {
+
+	public static MailType getDefaultTemplateType(MemberPermissionChangeEvent event) {
+		if(event.size() == 1) {
+			List<BusinessGroupMembershipChange> changes = event.getGroupChanges();
+			if(changes.size() == 1) {
+				BusinessGroupMembershipChange change = changes.get(0);
+				if(change.getParticipant() != null) {
+					return MailType.addParticipant;
+				} else if (change.getWaitingList() != null) {
+					return MailType.addToWaitingList;
+				}
+			}
+		}
+		return null;
+	}
+	
+	
+	public static MailType getDefaultTemplateType(MembershipModification mod) {
+		int total = mod.getAddOwners().size() + mod.getAddParticipants().size()
+				+ mod.getAddToWaitingList().size() + mod.getRemovedIdentities().size();
+
+		if(total == 1) {
+			if(mod.getAddOwners().size() == 1) {
+				return null;//no template for owner
+			} else if(mod.getAddParticipants().size() == 1) {
+				return MailType.addParticipant;
+			} else if(mod.getAddToWaitingList().size() == 1) {
+				return MailType.addToWaitingList;
+			}
+		}
+		return null;
+	}
+	
+	public static MailTemplate getDefaultTemplate(MailType type, BusinessGroupShort group, Identity ureqIdentity) {
+		if(type == null) return null;
+		
+		switch(type) {
+			case addParticipant:
+				return BGMailHelper.createAddParticipantMailTemplate(group, ureqIdentity);
+			case removeParticipant:
+				return BGMailHelper.createRemoveParticipantMailTemplate(group, ureqIdentity);
+			case addToWaitingList:
+				return BGMailHelper.createAddWaitinglistMailTemplate(group, ureqIdentity);
+			case removeToWaitingList:
+				return BGMailHelper.createRemoveWaitinglistMailTemplate(group, ureqIdentity);
+			case graduateFromWaitingListToParticpant:
+				return BGMailHelper.createWaitinglistTransferMailTemplate(group, ureqIdentity);
+		}
+		return null;
+	}
+		
+	protected static void sendEmail(Identity ureqIdentity, Identity identity, BusinessGroupShort group,
+			MailType type, MailPackage mailing, MailerWithTemplate mailer) {
+		
+		if(mailing != null && !mailing.isSendEmail()) {
+			return;
+		}
+
+		MailTemplate template = mailing == null ? null : mailing.getTemplate();
+		if(mailing == null || mailing.getTemplate() == null) {
+			template = getDefaultTemplate(type, group, ureqIdentity);
+		}
+		
+		MailContext context = mailing == null ? null : mailing.getContext();
+		if(context == null) {
+			context = new MailContextImpl(null, null, "[BusinessGroup:" + group.getKey() + "]");
+		}
+
+		MailerResult result = mailer.sendMailAsSeparateMails(context, Collections.singletonList(identity), null, template, null);
+		if(mailing != null) {
+			mailing.appendResult(result);
+		}
+	}
+
+	public enum MailType {
+		addParticipant,
+		removeParticipant,
+		addToWaitingList,
+		removeToWaitingList,
+		graduateFromWaitingListToParticpant,
+	}
+}
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index 217f89933bb5f2b437efd605fc5838f1f9eb2820..3279558b106713e8a9c1f9d7011c4a9a0b858eb3 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -57,6 +57,7 @@ import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.mail.MailContext;
 import org.olat.core.util.mail.MailContextImpl;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
 import org.olat.core.util.mail.MailerResult;
 import org.olat.core.util.mail.MailerWithTemplate;
@@ -78,6 +79,7 @@ import org.olat.group.DeletableReference;
 import org.olat.group.GroupLoggingAction;
 import org.olat.group.area.BGArea;
 import org.olat.group.area.BGAreaManager;
+import org.olat.group.manager.BusinessGroupMailing.MailType;
 import org.olat.group.model.BGMembership;
 import org.olat.group.model.BGRepositoryEntryRelation;
 import org.olat.group.model.BGResourceRelation;
@@ -144,6 +146,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	@Autowired
 	private NotificationsManager notificationsManager;
 	@Autowired
+	private MailerWithTemplate mailer;
+	@Autowired
 	private ACService acService;
 	@Autowired
 	private ACReservationDAO reservationDao;
@@ -277,7 +281,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		
 		if(currentMaxNumber > previousMaxNumber) {
 			//I can rank up some users
-			transferFirstIdentityFromWaitingToParticipant(identity, updatedGroup, syncIM);
+			transferFirstIdentityFromWaitingToParticipant(identity, updatedGroup, null, syncIM);
 		}
 	}
 
@@ -453,7 +457,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	}
 
 	@Override
-	public BusinessGroup mergeBusinessGroups(final Identity ureqIdentity, BusinessGroup targetGroup, final List<BusinessGroup> groupsToMerge) {
+	public BusinessGroup mergeBusinessGroups(final Identity ureqIdentity, BusinessGroup targetGroup,
+			final List<BusinessGroup> groupsToMerge, MailPackage mailing) {
 		groupsToMerge.remove(targetGroup);//to be sure
 		SyncUserListTask syncIM = new SyncUserListTask(targetGroup);
 		Roles ureqRoles = securityManager.getRoles(ureqIdentity);
@@ -503,10 +508,10 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			addOwner(newOwner, targetGroup, syncIM);
 		}
 		for(Identity newParticipant:newParticipants) {
-			addParticipant(ureqIdentity, ureqRoles, newParticipant, targetGroup, syncIM);
+			addParticipant(ureqIdentity, ureqRoles, newParticipant, targetGroup, mailing, syncIM);
 		}
 		for(Identity newWaiter:newWaiters) {
-			addToWaitingList(newWaiter, targetGroup);
+			addToWaitingList(ureqIdentity, newWaiter, targetGroup, mailing);
 		}
 			
 		syncIM(syncIM, targetGroup);
@@ -517,14 +522,16 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	}
 
 	@Override
-	public void updateMembership(Identity ureqIdentity, MembershipModification membersMod, List<BusinessGroup> groups) {
+	public void updateMembership(Identity ureqIdentity, MembershipModification membersMod,
+			List<BusinessGroup> groups, MailPackage mailing) {
 		Roles ureqRoles = securityManager.getRoles(ureqIdentity);
 		for(BusinessGroup group:groups) {
-			updateMembers(ureqIdentity, ureqRoles, membersMod, group);
+			updateMembers(ureqIdentity, ureqRoles, membersMod, group, mailing);
 		}
 	}
 	
-	private void updateMembers(Identity ureqIdentity, Roles ureqRoles, MembershipModification membersMod, BusinessGroup group) {
+	private void updateMembers(Identity ureqIdentity, Roles ureqRoles, MembershipModification membersMod,
+			BusinessGroup group, MailPackage mailing) {
 		final SyncUserListTask syncIM = new SyncUserListTask(group);
 		
 		group = businessGroupDAO.loadForUpdate(group.getKey());
@@ -540,12 +547,12 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		}
 		for(Identity participant:membersMod.getAddParticipants()) {
 			if(!currentParticipants.contains(participant)) {
-				addParticipant(ureqIdentity, ureqRoles, participant, group, syncIM);
+				addParticipant(ureqIdentity, ureqRoles, participant, group, mailing, syncIM);
 			}
 		}
 		for(Identity waitingIdentity:membersMod.getAddToWaitingList()) {
 			if(!currentWaitingList.contains(waitingIdentity)) {
-				addToWaitingList(waitingIdentity, group);
+				addToWaitingList(ureqIdentity, waitingIdentity, group, mailing);
 			}
 		}
 		
@@ -556,10 +563,10 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				ownerToRemove.add(removed);
 			}
 			if(currentParticipants.contains(removed)) {
-				removeParticipant(ureqIdentity, removed, group, syncIM);
+				removeParticipant(ureqIdentity, removed, group, mailing, syncIM);
 			}
 			if(currentWaitingList.contains(removed)) {
-				removeFromWaitingList(removed, group);
+				removeFromWaitingList(ureqIdentity, removed, group, mailing);
 			}
 		}
 		removeOwners(ureqIdentity, ownerToRemove, group);
@@ -572,7 +579,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 
 	@Override
 	@Transactional
-	public void updateMemberships(final Identity ureqIdentity, final List<BusinessGroupMembershipChange> changes) {
+	public void updateMemberships(final Identity ureqIdentity, final List<BusinessGroupMembershipChange> changes,
+			MailPackage mailing) {
 		Roles ureqRoles = securityManager.getRoles(ureqIdentity);
 		Map<Long,BusinessGroupMembershipsChanges> changesMap = new HashMap<Long,BusinessGroupMembershipsChanges>();
 		for(BusinessGroupMembershipChange change:changes) {
@@ -617,10 +625,10 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			group = businessGroupDAO.loadForUpdate(group.getKey());
 					
 			for(Identity id:changesWrapper.addToWaitingList) {
-				addToWaitingList(id, group);
+				addToWaitingList(ureqIdentity, id, group, mailing);
 			}
 			for(Identity id:changesWrapper.removeFromWaitingList) {
-				removeFromWaitingList(id, group);
+				removeFromWaitingList(ureqIdentity, id, group, mailing);
 			}
 			for(Identity id:changesWrapper.addTutors) {
 				addOwner(id, group, syncIM);
@@ -629,10 +637,10 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				removeOwner(ureqIdentity, id, group, syncIM);
 			}
 			for(Identity id:changesWrapper.addParticipants) {
-				addParticipant(ureqIdentity, ureqRoles, id, group, syncIM);
+				addParticipant(ureqIdentity, ureqRoles, id, group, mailing, syncIM);
 			}
 			for(Identity id:changesWrapper.removeParticipants) {
-				removeParticipant(ureqIdentity, id, group, syncIM);
+				removeParticipant(ureqIdentity, id, group, mailing, syncIM);
 			}
 			//release lock
 			dbInstance.commit();
@@ -885,7 +893,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	}
 	
 	private boolean addParticipant(Identity ureqIdentity, Roles ureqRoles, Identity identityToAdd, BusinessGroup group,
-			SyncUserListTask syncIM) {
+			MailPackage mailing, SyncUserListTask syncIM) {
 		
 		if(!securityManager.isIdentityInSecurityGroup(identityToAdd, group.getPartipiciantGroup())) {
 			boolean mustAccept = true;
@@ -906,12 +914,12 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 					ResourceReservation reservation =
 							reservationDao.createReservation(identityToAdd, "group_participant", expiration, group.getResource());
 					if(reservation != null) {
-						//TODO memail send mail
+						BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addParticipant, mailing, mailer);
 					}
 				}
 			} else {
-				//TODO memail send mail
 				internalAddParticipant(ureqIdentity, identityToAdd, group, syncIM);
+				BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addParticipant, mailing, mailer);
 			}
 			return true;
 		}
@@ -943,7 +951,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 
 	@Override
 	@Transactional
-	public BusinessGroupAddResponse addParticipants(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities, BusinessGroup group) {	
+	public BusinessGroupAddResponse addParticipants(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities,
+			BusinessGroup group, MailPackage mailing) {	
 		BusinessGroupAddResponse response = new BusinessGroupAddResponse();
 		SyncUserListTask syncIM = new SyncUserListTask(group);
 
@@ -951,7 +960,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		for (final Identity identity : addIdentities) {
 			if (securityManager.isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_GUESTONLY)) {
 				response.getIdentitiesWithoutPermission().add(identity);
-			} else if(addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, syncIM)) {
+			} else if(addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, mailing, syncIM)) {
 				response.getAddedIdentities().add(identity);
 				log.audit("added identity '" + identity.getName() + "' to securitygroup with key " + currBusinessGroup.getPartipiciantGroup().getKey());
 			} else {
@@ -978,41 +987,43 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		}
 	}
 
-	private void removeParticipant(Identity ureqIdentity, Identity identity, BusinessGroup group, SyncUserListTask syncIM) {
+	private void removeParticipant(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing, SyncUserListTask syncIM) {
 
-		securityManager.removeIdentityFromSecurityGroup(identity, group.getPartipiciantGroup());
-		// remove user from buddies rosters
-		syncIM.addUserToRemove(identity.getName());
-		//remove subscriptions if user gets removed
-		removeSubscriptions(identity, group);
-		
-		// notify currently active users of this business group
-		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
-		// do logging
-		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_REMOVED, getClass(), LoggingResourceable.wrap(identity), LoggingResourceable.wrap(group));
-		// 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
-			transferFirstIdentityFromWaitingToParticipant(ureqIdentity, group, syncIM);
-		}	
-		// send notification mail in your controller!
-		
+		boolean removed = securityManager.removeIdentityFromSecurityGroup(identity, group.getPartipiciantGroup());
+		if(removed) {
+			// remove user from buddies rosters
+			syncIM.addUserToRemove(identity.getName());
+			//remove subscriptions if user gets removed
+			removeSubscriptions(identity, group);
+			
+			// notify currently active users of this business group
+			BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
+			// do logging
+			ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_REMOVED, getClass(), LoggingResourceable.wrap(identity), LoggingResourceable.wrap(group));
+			// 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
+				transferFirstIdentityFromWaitingToParticipant(ureqIdentity, group, null, syncIM);
+			}	
+			// send mail
+			BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.removeParticipant, mailing, mailer);
+		}
 	}
 	
 	@Override
 	@Transactional
-	public void removeParticipants(Identity ureqIdentity, List<Identity> identities, BusinessGroup group) {
+	public void removeParticipants(Identity ureqIdentity, List<Identity> identities, BusinessGroup group, MailPackage mailing) {
 		final SyncUserListTask syncIM = new SyncUserListTask(group);
 		group = businessGroupDAO.loadForUpdate(group.getKey());
 		for (Identity identity : identities) {
-		  removeParticipant(ureqIdentity, identity, group, syncIM);
+		  removeParticipant(ureqIdentity, identity, group, mailing, syncIM);
 		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + group.getPartipiciantGroup().getKey());
 		}
 		syncIM(syncIM, group);
 	}
 
 	@Override
-	public void removeMembers(Identity ureqIdentity, List<Identity> identities, OLATResource resource) {
+	public void removeMembers(Identity ureqIdentity, List<Identity> identities, OLATResource resource, MailPackage mailing) {
 		if(identities == null || identities.isEmpty() || resource == null) return;//nothing to do
 		
 		List<BusinessGroup> groups = null;
@@ -1056,7 +1067,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			Long groupKey = currentMembership.getGroupKey();
 			BusinessGroup nextGroup = businessGroupDAO.loadForUpdate(groupKey);
 			SyncUserListTask syncIM = new SyncUserListTask(nextGroup);
-			nextGroupMembership = removeGroupMembers(ureqIdentity, currentMembership, nextGroup, keyToIdentityMap, itMembership, syncIM);
+			nextGroupMembership = removeGroupMembers(ureqIdentity, currentMembership, nextGroup, keyToIdentityMap, itMembership, mailing, syncIM);
 			//release the lock
 			dbInstance.commit();
 			syncIM(syncIM, nextGroup);
@@ -1072,7 +1083,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	
 	private final BusinessGroupMembershipViewImpl removeGroupMembers(Identity ureqIdentity, BusinessGroupMembershipViewImpl currentMembership,
 			BusinessGroup currentGroup, Map<Long,Identity> keyToIdentityMap, Iterator<BusinessGroupMembershipViewImpl> itMembership,
-			SyncUserListTask syncIM) {
+			MailPackage mailing, SyncUserListTask syncIM) {
 
 		BusinessGroupMembershipViewImpl previsousComputedMembership = currentMembership;
 		BusinessGroupMembershipViewImpl membership;
@@ -1094,10 +1105,10 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 					removeOwner(ureqIdentity, id, currentGroup, syncIM);
 				}
 				if(membership.getParticipantGroupKey() != null) {
-					removeParticipant(ureqIdentity, id, currentGroup, syncIM);
+					removeParticipant(ureqIdentity, id, currentGroup, mailing, syncIM);
 				}
 				if(membership.getWaitingGroupKey() != null) {
-					removeFromWaitingList(id, currentGroup);
+					removeFromWaitingList(ureqIdentity, id, currentGroup, mailing);
 				}
 			} else {
 				return membership;
@@ -1107,18 +1118,19 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		return null;
 	}
 
-	private void addToWaitingList(Identity identity, BusinessGroup group) {
+	private void addToWaitingList(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing) {
 		securityManager.addIdentityToSecurityGroup(identity, group.getWaitingGroup());
 
 		// notify currently active users of this business group
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identity);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_TO_WAITING_LIST_ADDED, getClass(), LoggingResourceable.wrap(identity));
-		// send notification mail in your controller!
+		// send mail
+		BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.addToWaitingList, mailing, mailer);
 	}
 	
 	@Override
-	public BusinessGroupAddResponse addToWaitingList(Identity ureqIdentity, List<Identity> addIdentities, BusinessGroup group) {
+	public BusinessGroupAddResponse addToWaitingList(Identity ureqIdentity, List<Identity> addIdentities, BusinessGroup group, MailPackage mailing) {
 		BusinessGroupAddResponse response = new BusinessGroupAddResponse();
 		BusinessGroup currBusinessGroup = businessGroupDAO.loadForUpdate(group.getKey()); // reload business group
 
@@ -1134,7 +1146,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				response.getIdentitiesAlreadyInGroup().add(identity);
 			} else {
 				// identity has permission and is not already in group => add it
-				addToWaitingList(identity, currBusinessGroup);
+				addToWaitingList(ureqIdentity, identity, currBusinessGroup, mailing);
 				response.getAddedIdentities().add(identity);
 				log.audit("added identity '" + identity.getName() + "' to securitygroup with key " + currBusinessGroup.getPartipiciantGroup().getKey());
 			}
@@ -1142,22 +1154,23 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		return response;
 	}
 
-	private final void removeFromWaitingList(Identity identity, BusinessGroup group) {
+	private final void removeFromWaitingList(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing) {
 		securityManager.removeIdentityFromSecurityGroup(identity, group.getWaitingGroup());
 		// notify currently active users of this business group
 		BusinessGroupModifiedEvent.fireModifiedGroupEvents(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity);
 		// do logging
 		ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_FROM_WAITING_LIST_REMOVED, getClass(), LoggingResourceable.wrap(identity));
-		// send notification mail in your controller!
+		// send mail
+		BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.removeToWaitingList, mailing, mailer);
 	}
 	
 	@Override
-	public void removeFromWaitingList(Identity ureqIdentity, List<Identity> identities, BusinessGroup currBusinessGroup) {
-		currBusinessGroup = businessGroupDAO.loadForUpdate(currBusinessGroup.getKey());
+	public void removeFromWaitingList(Identity ureqIdentity, List<Identity> identities, BusinessGroup businessGroup, MailPackage mailing) {
+		businessGroup = businessGroupDAO.loadForUpdate(businessGroup.getKey());
 		
 		for (Identity identity : identities) {
-		  removeFromWaitingList(identity, currBusinessGroup);
-		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + currBusinessGroup.getOwnerGroup().getKey());
+		  removeFromWaitingList(ureqIdentity, identity, businessGroup, mailing);
+		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + businessGroup.getOwnerGroup().getKey());
 		}
 	}
 	
@@ -1177,7 +1190,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 
 	@Override
 	public BusinessGroupAddResponse moveIdentityFromWaitingListToParticipant(Identity ureqIdentity, List<Identity> identities, 
-			BusinessGroup currBusinessGroup) {
+			BusinessGroup currBusinessGroup, MailPackage mailing) {
 		
 		Roles ureqRoles = securityManager.getRoles(ureqIdentity);
 		
@@ -1186,11 +1199,11 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		currBusinessGroup = businessGroupDAO.loadForUpdate(currBusinessGroup.getKey());
 		
 		for (Identity identity : identities) {
-			// check if idenity is allready in participant
+			// check if identity is already in participant
 			if (!securityManager.isIdentityInSecurityGroup(identity,currBusinessGroup.getPartipiciantGroup()) ) {
-				// Idenity is not in participant-list => move idenity from waiting-list to participant-list
-				addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, syncIM);
-				removeFromWaitingList(identity, currBusinessGroup);
+				// Identity is not in participant-list => move idenity from waiting-list to participant-list
+				addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, mailing, syncIM);
+				removeFromWaitingList(ureqIdentity, identity, currBusinessGroup, mailing);
 				response.getAddedIdentities().add(identity);
 				// notification mail is handled in controller
 			} else {
@@ -1235,7 +1248,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 	}
 	
 	@Override
-	public EnrollState enroll(Identity ureqIdentity, Roles ureqRoles, Identity identity, BusinessGroup group) {
+	public EnrollState enroll(Identity ureqIdentity, Roles ureqRoles, Identity identity, BusinessGroup group,
+			MailPackage mailing) {
 		final BusinessGroup reloadedGroup = businessGroupDAO.loadForUpdate(group.getKey());
 		
 		log.info("doEnroll start: group=" + OresHelper.createStringRepresenting(group), identity.getName());
@@ -1246,7 +1260,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		
 		//reservation has the highest priority over max participant or other settings
 		if(reservation != null) {
-			addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, syncIM);
+			addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, mailing, syncIM);
 			enrollStatus.setEnrolled(BGMembership.participant);
 			log.info("doEnroll (reservation) - setIsEnrolled ", identity.getName());
 			if(reservation != null) {
@@ -1260,7 +1274,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			if (reservation == null && (participantsCounter + reservations) >= reloadedGroup.getMaxParticipants().intValue()) {
 				// already full, show error and updated choose page again
 				if (reloadedGroup.getWaitingListEnabled().booleanValue()) {
-					addToWaitingList(identity, reloadedGroup);
+					addToWaitingList(ureqIdentity, identity, reloadedGroup, mailing);
 					enrollStatus.setEnrolled(BGMembership.waiting);
 				} else {
 					// No Waiting List => List is full
@@ -1269,13 +1283,13 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				}
 			} else {
 				//enough place
-				addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, syncIM);
+				addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, mailing, syncIM);
 				enrollStatus.setEnrolled(BGMembership.participant);
 				log.info("doEnroll - setIsEnrolled ", identity.getName());
 			}
 		} else {
 			if (log.isDebug()) log.debug("doEnroll as participant beginTransaction");
-			addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, syncIM);
+			addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, mailing, syncIM);
 			enrollStatus.setEnrolled(BGMembership.participant);						
 			if (log.isDebug()) log.debug("doEnroll as participant committed");
 		}
@@ -1285,7 +1299,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		return enrollStatus;
 	}
 
-	private void transferFirstIdentityFromWaitingToParticipant(Identity ureqIdentity, BusinessGroup group, SyncUserListTask syncIM) {
+	private void transferFirstIdentityFromWaitingToParticipant(Identity ureqIdentity, BusinessGroup group, 
+			MailPackage mailing, SyncUserListTask syncIM) {
 
 		// Check if waiting-list is enabled and auto-rank-up
 		if (group.getWaitingListEnabled() != null && group.getWaitingListEnabled().booleanValue()
@@ -1317,21 +1332,14 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 							//            that get triggered in the next two methods to be of ActionType admin
 							//            This is needed to make sure the targetIdentity ends up in the o_loggingtable
 							ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
-							addParticipant(ureqIdentity, null, firstWaitingListIdentity, group, syncIM);
-							removeFromWaitingList(firstWaitingListIdentity, group);
+							MailPackage subMailing = new MailPackage(false);//doesn0t send these emails but a specific one
+							addParticipant(ureqIdentity, null, firstWaitingListIdentity, group, subMailing, syncIM);
+							removeFromWaitingList(ureqIdentity, firstWaitingListIdentity, group, subMailing);
 						} finally {
 							ThreadLocalUserActivityLogger.setStickyActionType(formerStickyActionType);
 						}
-						// send a notification mail if available
-						MailTemplate mailTemplate = BGMailHelper.createWaitinglistTransferMailTemplate(group, ureqIdentity);
-						if (mailTemplate != null) {
-							MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-							//fxdiff VCRP-16: intern mail system
-							MailContext context = new MailContextImpl("[BusinessGroup:" + group.getKey() + "]");
-							mailer.sendMailAsSeparateMails(context, Collections.singletonList(firstWaitingListIdentity), null, mailTemplate, null);
-							// Does not report errors to current screen because this is the identity who triggered the transfer
-							log.warn("Could not send WaitinglistTransferMail for identity=" + firstWaitingListIdentity.getName());
-						}						
+
+						BusinessGroupMailing.sendEmail(ureqIdentity, firstWaitingListIdentity, group, MailType.graduateFromWaitingListToParticpant, mailing, mailer);				
 						counter++;
 				  }
 				}
diff --git a/src/main/java/org/olat/group/model/MembershipModification.java b/src/main/java/org/olat/group/model/MembershipModification.java
index b313d5089972c0d13c0eb6aafafd407a31a93f6a..c9f3f50cac78b6cf533bc244587303aa16fc5731 100644
--- a/src/main/java/org/olat/group/model/MembershipModification.java
+++ b/src/main/java/org/olat/group/model/MembershipModification.java
@@ -72,6 +72,10 @@ public class MembershipModification {
 				&& addToWaitingList.isEmpty() && removedIdentities.isEmpty();
 	}
 	
+	public int size() {
+		return addOwners.size() + addParticipants.size() + addToWaitingList.size() + removedIdentities.size();
+	}
+	
 	public List<Identity> getAllIdentities() {
 		List<Identity> allIdentities = new ArrayList<Identity>();
 		allIdentities.addAll(addOwners);
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupMembersController.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupMembersController.java
index 63fbab3a2f5ba1c43c8db80d8a36c804d08c12ce..4e0b6de85f40b38d3e3069ffe0502fc6247b6159 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupMembersController.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupMembersController.java
@@ -37,12 +37,9 @@ import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
-import org.olat.core.util.mail.MailContext;
-import org.olat.core.util.mail.MailContextImpl;
 import org.olat.core.util.mail.MailHelper;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
-import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.course.member.wizard.ImportMember_1a_LoginListStep;
 import org.olat.course.member.wizard.ImportMember_1b_ChooseMemberStep;
 import org.olat.group.BusinessGroup;
@@ -197,14 +194,10 @@ public class BusinessGroupMembersController extends BasicController {
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
-		businessGroupService.updateMemberships(getIdentity(), allModifications);
 		
 		MailTemplate template = (MailTemplate)runContext.get("mailTemplate");
-		if (template != null && !members.isEmpty()) {
-			MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-			MailContext context = new MailContextImpl(null, null, getWindowControl().getBusinessControl().getAsString());
-			MailerResult mailerResult = mailer.sendMailAsSeparateMails(context, members, null, template, getIdentity());
-			MailHelper.printErrorsAndWarnings(mailerResult, getWindowControl(), getLocale());
-		}
+		MailPackage mailing = new MailPackage(template, getWindowControl().getBusinessControl().getAsString(), true);
+		businessGroupService.updateMemberships(getIdentity(), allModifications, mailing);
+		MailHelper.printErrorsAndWarnings(mailing.getResult(), getWindowControl(), getLocale());
 	}
 }
diff --git a/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java b/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
index 864e925d24764b0d592fb0cbae86078535a858a7..3f16fc940cf6d4052539668f2ca6b054a517ee46 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
@@ -63,13 +63,9 @@ import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
 import org.olat.core.util.Util;
-import org.olat.core.util.mail.MailContext;
-import org.olat.core.util.mail.MailContextImpl;
 import org.olat.core.util.mail.MailHelper;
-import org.olat.core.util.mail.MailNotificationEditController;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
-import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupMembership;
@@ -79,6 +75,8 @@ import org.olat.group.BusinessGroupShort;
 import org.olat.group.BusinessGroupView;
 import org.olat.group.GroupLoggingAction;
 import org.olat.group.area.BGAreaManager;
+import org.olat.group.manager.BusinessGroupMailing;
+import org.olat.group.manager.BusinessGroupMailing.MailType;
 import org.olat.group.model.BGRepositoryEntryRelation;
 import org.olat.group.model.BusinessGroupSelectionEvent;
 import org.olat.group.model.MembershipModification;
@@ -90,6 +88,7 @@ import org.olat.group.ui.wizard.BGConfigToolsStep;
 import org.olat.group.ui.wizard.BGCopyBusinessGroup;
 import org.olat.group.ui.wizard.BGCopyPreparationStep;
 import org.olat.group.ui.wizard.BGEmailSelectReceiversStep;
+import org.olat.group.ui.wizard.BGMailNotificationEditController;
 import org.olat.group.ui.wizard.BGMergeStep;
 import org.olat.group.ui.wizard.BGUserMailTemplate;
 import org.olat.group.ui.wizard.BGUserManagementController;
@@ -129,7 +128,7 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 	
 	private NewBGController groupCreateController;
 	private BGUserManagementController userManagementController;
-	private MailNotificationEditController userManagementSendMailController;
+	private BGMailNotificationEditController userManagementSendMailController;
 	private BusinessGroupDeleteDialogBoxController deleteDialogBox;
 	private StepsMainRunController businessGroupWizard;
 	protected CloseableModalController cmc;
@@ -417,7 +416,7 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 		// now remove the identity also as participant.
 		// 2) remove as participant
 		List<Identity> identities = Collections.singletonList(getIdentity());
-		businessGroupService.removeParticipants(ureq.getIdentity(), identities, group);
+		businessGroupService.removeParticipants(ureq.getIdentity(), identities, group, null);//TODO memail
 	}
 	
 	/**
@@ -603,10 +602,20 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 		removeAsListenerAndDispose(cmc);
 		removeAsListenerAndDispose(userManagementSendMailController);
 		
-		MailTemplate template = new BGUserMailTemplate(groups, mod, "", "");
+		MailTemplate defaultTemplate = null;
+		int totalModification = (mod.size() * groups.size());
+		if(totalModification == 1) {
+			MailType type = BusinessGroupMailing.getDefaultTemplateType(mod);
+			if(type != null) {
+				defaultTemplate = BusinessGroupMailing.getDefaultTemplate(type, groups.get(0), ureq.getIdentity());
+			}
+		}
+		
+		MailTemplate template = new BGUserMailTemplate(groups, mod, defaultTemplate);
 		boolean mandatoryEmail = !mod.getAddParticipants().isEmpty() &&
 				groupModule.isMandatoryEnrolmentEmail(ureq.getUserSession().getRoles());
-		userManagementSendMailController = new MailNotificationEditController(getWindowControl(), ureq, template, false, mandatoryEmail);
+		userManagementSendMailController = new BGMailNotificationEditController(getWindowControl(), ureq, template,
+				totalModification == 1, totalModification == 1, false, mandatoryEmail);
 		Component cmp = userManagementSendMailController.getInitialComponent();
 		listenTo(userManagementSendMailController);
 		cmc = new CloseableModalController(getWindowControl(), translate("close"), cmp, true, translate("users.group"));
@@ -615,15 +624,9 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 	}
 	
 	private void finishUserManagement(MembershipModification mod, List<BusinessGroup> groups, MailTemplate template, boolean sendMail) {
-		businessGroupService.updateMembership(getIdentity(), mod, groups);
-		
-		if (template != null && !mod.isEmpty() && sendMail) {
-			List<Identity> movedIdentities = mod.getAllIdentities();
-			MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-			MailContext context = new MailContextImpl(null, null, getWindowControl().getBusinessControl().getAsString());
-			MailerResult mailerResult = mailer.sendMailAsSeparateMails(context, movedIdentities, null, template, getIdentity());
-			MailHelper.printErrorsAndWarnings(mailerResult, getWindowControl(), getLocale());
-		}
+		MailPackage mailing = new MailPackage(template, getWindowControl().getBusinessControl().getAsString(), sendMail);
+		businessGroupService.updateMembership(getIdentity(), mod, groups, mailing);
+		MailHelper.printErrorsAndWarnings(mailing.getResult(), getWindowControl(), getLocale());
 	}
 	
 	private void doSelect(UserRequest ureq, List<BGTableItem> items) {
@@ -657,7 +660,7 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 			public Step execute(UserRequest ureq, WindowControl wControl, StepsRunContext runContext) {
 				BusinessGroup targetGroup = (BusinessGroup)runContext.get("targetGroup");
 				groups.remove(targetGroup);
-				businessGroupService.mergeBusinessGroups(getIdentity(), targetGroup, groups);
+				businessGroupService.mergeBusinessGroups(getIdentity(), targetGroup, groups, null);//TODO memail
 				return StepsMainRunController.DONE_MODIFIED;
 			}
 		};
diff --git a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
index a344e4b304d75ff1b15740895722e9a2aa63eb4d..3f56a870db619e839352ae4f4f15918b51a0b3f7 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
@@ -61,6 +61,7 @@ import org.olat.core.util.Util;
 import org.olat.core.util.mail.ContactList;
 import org.olat.core.util.mail.ContactMessage;
 import org.olat.core.util.mail.MailHelper;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
 import org.olat.core.util.mail.MailerResult;
 import org.olat.core.util.mail.MailerWithTemplate;
@@ -318,10 +319,10 @@ public abstract class AbstractMemberListController extends BasicController imple
 	protected void doChangePermission(UserRequest ureq, MemberPermissionChangeEvent e) {
 		if(repoEntry != null) {
 			List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e);
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, changes);
+			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, changes, null);
 		}
 
-		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges());
+		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), null);//TODO memail
 		//make sure all is committed before loading the model again (I see issues without)
 		DBFactory.getInstance().commitAndCloseSession();
 		
@@ -337,12 +338,12 @@ public abstract class AbstractMemberListController extends BasicController imple
 	protected void doChangePermission(UserRequest ureq, MemberPermissionChangeEvent changes, List<Identity> members) {
 		if(repoEntry != null) {
 			List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges);
+			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, null);
 		}
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
-		businessGroupService.updateMemberships(getIdentity(), allModifications);
+		businessGroupService.updateMemberships(getIdentity(), allModifications, null);//TODO memail
 		DBFactory.getInstance().commitAndCloseSession();
 		
 		if(allModifications != null && !allModifications.isEmpty()) {
@@ -356,6 +357,7 @@ public abstract class AbstractMemberListController extends BasicController imple
 		reloadModel();
 	}
 	
+	//TODO memail
 	protected void sendMailAfterChangePermission(BusinessGroupMembershipChange mod) {
 		MailTemplate template = null;
 		if(mod.getParticipant() != null) {
@@ -382,11 +384,12 @@ public abstract class AbstractMemberListController extends BasicController imple
 	}
 	
 	protected void doLeave(List<Identity> members) {
+		MailPackage mailing = new MailPackage();
 		if(repoEntry != null) {
 			repositoryManager.removeMembers(members, repoEntry);
-			businessGroupService.removeMembers(getIdentity(), members, repoEntry.getOlatResource());
+			businessGroupService.removeMembers(getIdentity(), members, repoEntry.getOlatResource(), mailing);
 		} else {
-			businessGroupService.removeMembers(getIdentity(), members, businessGroup.getResource());
+			businessGroupService.removeMembers(getIdentity(), members, businessGroup.getResource(), mailing);
 		}
 		reloadModel();
 	}
@@ -415,10 +418,13 @@ public abstract class AbstractMemberListController extends BasicController imple
 		if(businessGroup != null) {
 			List<Long> identityKeys = getMemberKeys(members);
 			List<Identity> identitiesToGraduate = securityManager.loadIdentityByKeys(identityKeys);
-			businessGroupService.moveIdentityFromWaitingListToParticipant(getIdentity(), identitiesToGraduate, businessGroup);
+			businessGroupService.moveIdentityFromWaitingListToParticipant(getIdentity(), identitiesToGraduate,
+					businessGroup, null);//TODO memail
 		} else {
 			//TODO memail do something
 		}
+		
+		reloadModel();
 	}
 	
 	protected List<Long> getMemberKeys(List<MemberView> members) {
diff --git a/src/main/java/org/olat/group/ui/main/MemberPermissionChangeEvent.java b/src/main/java/org/olat/group/ui/main/MemberPermissionChangeEvent.java
index d4891228d52388096595733a9c80ee85ce9154ca..d7d7107a5f07856538194ce7778730e9e8c25245 100644
--- a/src/main/java/org/olat/group/ui/main/MemberPermissionChangeEvent.java
+++ b/src/main/java/org/olat/group/ui/main/MemberPermissionChangeEvent.java
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.List;
 
 import org.olat.core.id.Identity;
+import org.olat.group.BusinessGroupShort;
 import org.olat.group.model.BusinessGroupMembershipChange;
 import org.olat.repository.model.RepositoryEntryPermissionChangeEvent;
 
@@ -40,6 +41,19 @@ public class MemberPermissionChangeEvent extends RepositoryEntryPermissionChange
 		super(member);
 	}
 	
+	public List<BusinessGroupShort> getGroups() {
+		List<BusinessGroupShort> groups = new ArrayList<BusinessGroupShort>();
+		if(groupChanges != null && !groupChanges.isEmpty()) {
+			for(BusinessGroupMembershipChange change:groupChanges) {
+				BusinessGroupShort group = change.getGroup();
+				if(!groups.contains(group)) {
+					groups.add(group);
+				}
+			}
+		}
+		return groups;
+	}
+	
 	public List<BusinessGroupMembershipChange> getGroupChanges() {
 		return groupChanges;
 	}
@@ -48,6 +62,11 @@ public class MemberPermissionChangeEvent extends RepositoryEntryPermissionChange
 		this.groupChanges = changes;
 	}
 	
+	@Override
+	public int size() {
+		return (groupChanges == null ? 0 : groupChanges.size()) + super.size();
+	}
+	
 	public List<RepositoryEntryPermissionChangeEvent> generateRepositoryChanges(List<Identity> members) {
 		if(members == null || members.isEmpty()) {
 			return Collections.emptyList();
diff --git a/src/main/java/org/olat/group/ui/wizard/BGMailNotificationEditController.java b/src/main/java/org/olat/group/ui/wizard/BGMailNotificationEditController.java
new file mode 100644
index 0000000000000000000000000000000000000000..31794d144eb53db1c4b81ba575744260690daab0
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/wizard/BGMailNotificationEditController.java
@@ -0,0 +1,143 @@
+/**
+ * <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.group.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+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.controller.BasicController;
+import org.olat.core.util.mail.MailTemplate;
+
+/**
+ * 
+ * Description:<br>
+ * The MailNotificationEditController allows the user to enter a mailtext based
+ * on the MailTemplate. It will be surrounded by some comments which variables
+ * that can be used in this context.
+ * <p>
+ * Events:
+ * <ul>
+ * <li>Event.DONE_EVENT</li>
+ * <li>Event.CANCEL_EVENT</li>
+ * </ul>
+ * <p>
+ * Initial Date: 23.11.2006 <br>
+ * 
+ * @author Florian Gnaegi, frentix GmbH<br>
+ *         http://www.frentix.com
+ */
+public class BGMailNotificationEditController extends BasicController {
+	
+	// components
+	private VelocityContainer mainVC;
+	private BGMailTemplateController mailForm;
+	
+	// data model
+	private MailTemplate mailTemplate;
+	private String orgMailSubject;
+	private String orgMailBody;
+	private Boolean cpFrom;
+
+	/**
+	 * Constructor for the email 
+	 * @param wControl
+	 * @param ureq
+	 * @param mailTemplate
+	 * @param useCancel
+	 */
+	public BGMailNotificationEditController(WindowControl wControl, UserRequest ureq, MailTemplate mailTemplate,
+			boolean ccSenderAllowed, boolean customizingAvailable, boolean useCancel, boolean mandatory) {
+		super(ureq, wControl);
+		this.mailTemplate = mailTemplate;
+		orgMailSubject = mailTemplate.getSubjectTemplate();
+		orgMailBody = mailTemplate.getBodyTemplate();
+		cpFrom = mailTemplate.getCpfrom();
+		
+		mainVC = createVelocityContainer("mailnotification");
+		mailForm = new BGMailTemplateController(ureq, wControl, mailTemplate,
+				ccSenderAllowed, customizingAvailable, useCancel, mandatory);
+		listenTo(mailForm);
+		
+		mainVC.put("mailForm", mailForm.getInitialComponent());
+		
+		putInitialPanel (mainVC);
+	}
+	
+	@Override
+	public void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+	
+	@Override
+	public void event(UserRequest ureq, Controller source, Event event) {
+		if (source == mailForm) {
+			if (event == Event.DONE_EVENT) {
+				mailForm.updateTemplateFromForm(mailTemplate);
+				fireEvent(ureq, event);
+			} else if (event == Event.CANCELLED_EVENT) {
+				mailTemplate = null;
+				fireEvent(ureq, event);
+			}
+		}
+	}
+	
+	public boolean isSendMail() {
+		return mailForm.sendMailSwitchEnabled();
+	}
+
+	/**
+	 * @return The mail template containing the configured mail or null if user
+	 *         decided to not send a mail
+	 */
+	public MailTemplate getMailTemplate() {
+		if (mailForm.sendMailSwitchEnabled()) {
+			return mailTemplate;
+		} else {
+			return null;
+		}
+	}
+	
+	/**
+	 * Return the current template, always
+	 * @return
+	 */
+	public MailTemplate getTemplate() {
+		return mailTemplate;
+	}
+	
+	/**
+	 * 
+	 * @return Boolean
+	 */
+	public boolean isTemplateChanged() {
+		return !orgMailSubject.equals(mailTemplate.getSubjectTemplate()) 
+					|| !orgMailBody.equals(mailTemplate.getBodyTemplate())
+					|| !cpFrom.equals(mailTemplate.getCpfrom());
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+}
diff --git a/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java b/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java
new file mode 100644
index 0000000000000000000000000000000000000000..28e8b2210fef8cee9ef2b881a07c62d4e78620b5
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java
@@ -0,0 +1,228 @@
+/**
+* OLAT - Online Learning and Training<br>
+* http://www.olat.org
+* <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
+* <p>
+* http://www.apache.org/licenses/LICENSE-2.0
+* <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>
+* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
+* University of Zurich, Switzerland.
+* <hr>
+* <a href="http://www.openolat.org">
+* OpenOLAT - Online Learning and Training</a><br>
+* This file has been modified by the OpenOLAT community. Changes are licensed
+* under the Apache 2.0 license as the original file.  
+* <p>
+*/ 
+package org.olat.group.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.SelectionElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+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.util.StringHelper;
+import org.olat.core.util.mail.MailTemplate;
+
+/**
+ * This is a specialized form to send mail to...
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BGMailTemplateController extends FormBasicController {
+
+	private TextElement subjectElem;
+	private TextElement bodyElem;
+	private SelectionElement sendMail;
+	private SelectionElement ccSender;
+	private SelectionElement defaultTemplate;
+	private final static String NLS_CONTACT_SEND_CP_FROM = "contact.cp.from";
+	
+	private final boolean useCancel;
+	private final boolean useSubmit;
+	private MailTemplate template;
+	private final boolean mandatoryEmail;
+	private final boolean ccSenderAllowed;
+	private final boolean customizingAvailable;
+	
+	/**
+	 * Constructor for the mail notification form
+	 * @param locale
+	 * @param template Default values taken from this template
+	 * @param useCancel 
+	 * @param listeningController Controller that listens to form events
+	 */
+	public BGMailTemplateController(UserRequest ureq, WindowControl wControl, MailTemplate template,
+			boolean ccSenderAllowed, boolean customizingAvailable, boolean useCancel, boolean mandatoryEmail) {
+		super(ureq, wControl);
+		this.template = template;
+		this.useCancel = useCancel;
+		this.useSubmit = true;
+		this.mandatoryEmail = mandatoryEmail;
+		this.ccSenderAllowed = ccSenderAllowed;
+		this.customizingAvailable = customizingAvailable;
+		initForm (ureq);
+	}
+	
+	public BGMailTemplateController(UserRequest ureq, WindowControl wControl, MailTemplate template,
+			boolean ccSenderAllowed, boolean customizingAvailable, boolean useCancel, boolean mandatoryEmail,
+			Form rootForm) {
+		super(ureq, wControl, LAYOUT_DEFAULT, null, rootForm);
+		this.template = template;
+		this.useCancel = useCancel;
+		this.useSubmit = false;
+		this.mandatoryEmail = mandatoryEmail;
+		this.ccSenderAllowed = ccSenderAllowed;
+		this.customizingAvailable = customizingAvailable;
+		initForm (ureq);
+	}
+
+	/**
+	 * Update the given templates with the values entered in the form
+	 * @param template 
+	 */
+	public void updateTemplateFromForm(MailTemplate template) {
+		if(subjectElem != null) {
+			template.setSubjectTemplate(subjectElem.getValue());
+		}
+		if(bodyElem != null) {
+			template.setBodyTemplate(bodyElem.getValue());
+		}
+		template.setCpfrom(ccSender.isSelected(0));
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		fireEvent (ureq, Event.DONE_EVENT);
+	}
+	
+	@Override
+	protected void formCancelled(UserRequest ureq) {
+		fireEvent (ureq, Event.CANCELLED_EVENT);
+	}
+	
+	@Override
+	public boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+		// validate only when sendMail is enabled
+		if(defaultTemplate.isSelected(0)) {
+			allOk &= true;
+		} else if (mandatoryEmail || sendMail.isSelected(0)) {
+			subjectElem.clearError();
+			if (subjectElem.getValue().trim().length() == 0) {
+				subjectElem.setErrorKey("mailtemplateform.error.emptyfield", null);
+				allOk &= false;
+			}
+			if (subjectElem.getValue().indexOf("#") != -1) {
+				subjectElem.setErrorKey("mailtemplateform.error.velocity", null);
+				allOk &= false;
+			}
+			
+			bodyElem.clearError();
+			if (bodyElem.getValue().trim().length() == 0) {
+				bodyElem.setErrorKey("mailtemplateform.error.emptyfield", null);
+				allOk &= false;
+			}
+			if (bodyElem.getValue().indexOf("#") != -1) {
+				bodyElem.setErrorKey("mailtemplateform.error.velocity", null);
+				allOk &= false;
+			}
+		}
+		return allOk && super.validateFormLogic(ureq);
+	}
+	
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		if(!mandatoryEmail) {
+			sendMail = uifactory.addCheckboxesVertical("sendmail", "", formLayout, new String[]{"xx"}, new String[]{translate("mailtemplateform.sendMailSwitchElem")}, null, 1);
+			sendMail.select("xx", true);
+			sendMail.addActionListener(listener, FormEvent.ONCLICK);
+		}
+		
+		defaultTemplate = uifactory.addCheckboxesVertical("deftemplate", "", formLayout, new String[]{"xx"}, new String[]{translate("mailtemplateform.defaultTemplate")}, null, 1);
+		if(!customizingAvailable || !StringHelper.containsNonWhitespace(template.getSubjectTemplate())) {
+			defaultTemplate.select("xx", true);
+		}
+		
+		defaultTemplate.addActionListener(listener, FormEvent.ONCLICK);
+		defaultTemplate.setEnabled(customizingAvailable);
+
+		if(customizingAvailable) {
+			subjectElem = uifactory.addTextElement("subjectElem", "mailtemplateform.subject", 128, template.getSubjectTemplate(), formLayout);
+			subjectElem.setDisplaySize(60);
+			subjectElem.setMandatory(true);
+		
+			bodyElem = uifactory.addTextAreaElement("bodyElem", "mailtemplateform.body", -1, 15, 60, true, template.getBodyTemplate(), formLayout);
+			bodyElem.setMandatory(true);
+		}
+		
+		if(ccSenderAllowed) {
+			ccSender = uifactory.addCheckboxesVertical("tcpfrom", "", formLayout, new String[]{"xx"}, new String[]{translate(NLS_CONTACT_SEND_CP_FROM)}, null, 1);
+		}
+		
+		if(useSubmit || useCancel) {
+			FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
+			formLayout.add(buttonGroupLayout);
+			if(useSubmit) {
+				uifactory.addFormSubmitButton("continue", "mailtemplateform.continue", buttonGroupLayout);
+			}
+			if (useCancel) {
+				uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl());
+			}
+		}
+
+		update();
+	}
+	
+	private void update () {
+		boolean sm = customizingAvailable && !defaultTemplate.isSelected(0);
+		if(subjectElem != null) {
+			subjectElem.setVisible(sm);
+		}
+		if(bodyElem != null) {
+			bodyElem.setVisible(sm);
+		}
+		if(ccSender != null) {
+			ccSender.setVisible(sm);
+		}
+	}
+	
+	@Override
+	protected void formInnerEvent (UserRequest ureq, FormItem source, FormEvent event) {
+		update();
+	}
+	
+	/**
+	 * @return true: mail switch is enabled; false: otherwise
+	 */
+	public boolean sendMailSwitchEnabled() {
+		return mandatoryEmail || sendMail.isSelected(0);
+	}
+	
+	public boolean isDefaultTemplate() {
+		return defaultTemplate.isSelected(0);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/wizard/BGUserMailTemplate.java b/src/main/java/org/olat/group/ui/wizard/BGUserMailTemplate.java
index ea4cb05ca4e35acda20039b86b8265b9052087dc..ca15844f824d71a0b5d5e2091d293808125f3a82 100644
--- a/src/main/java/org/olat/group/ui/wizard/BGUserMailTemplate.java
+++ b/src/main/java/org/olat/group/ui/wizard/BGUserMailTemplate.java
@@ -36,8 +36,8 @@ public class BGUserMailTemplate extends MailTemplate {
 	private final MembershipModification modifications;
 	
 	public BGUserMailTemplate(List<BusinessGroup> groups, MembershipModification modifications, 
-			String subject, String body) {
-		super(subject, body, null);
+			MailTemplate template) {
+		super(template == null ? "" : template.getSubjectTemplate(), template == null ? "" : template.getBodyTemplate(), null);
 		this.groups = groups;
 		this.modifications = modifications;
 	}
diff --git a/src/main/java/org/olat/group/ui/wizard/_content/mailnotification.html b/src/main/java/org/olat/group/ui/wizard/_content/mailnotification.html
new file mode 100644
index 0000000000000000000000000000000000000000..0357ed4f616004ca1f0f740eb6c5474ca3b2946b
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/wizard/_content/mailnotification.html
@@ -0,0 +1,5 @@
+<fieldset>
+	<legend>$r.translate("mailnotification.title")</legend>
+  	$r.contextHelpWithWrapper("org.olat.core.util.mail","mail-templ.html","help.hover.mail-templ")
+	$r.render("mailForm")
+</fieldset>
diff --git a/src/main/java/org/olat/group/ui/wizard/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/wizard/_i18n/LocalStrings_de.properties
index 799c05f64c8452075c3ea2af8bdf1cf7d1bcb958..16f9e1e35f335342f0c373d8a8b140218c4aa5d9 100644
--- a/src/main/java/org/olat/group/ui/wizard/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/wizard/_i18n/LocalStrings_de.properties
@@ -79,4 +79,13 @@ remove=Entfernen
 table.user.login=Login
 table.subject.action=Aktion
 table.status=Status
-table.role=Rolle
\ No newline at end of file
+table.role=Rolle
+
+mailtemplateform.defaultTemplate=Default template
+mailnotification.title=$org.olat.core.util.mail\:mailnotification.title
+mailtemplateform.sendMailSwitchElem=$org.olat.core.util.mail\:mailtemplateform.sendMailSwitchElem
+mailtemplateform.subject=$org.olat.core.util.mail\:mailtemplateform.subject
+mailtemplateform.body=$org.olat.core.util.mail\:mailtemplateform.body
+mailtemplateform.continue=$org.olat.core.util.mail\:mailtemplateform.continue
+mailtemplateform.error.emptyfield=$org.olat.core.util.mail\:mailtemplateform.error.emptyfield
+contact.cp.from=$org.olat.core.util.mail\:contact.cp.from
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/RepositoryMailing.java b/src/main/java/org/olat/repository/RepositoryMailing.java
new file mode 100644
index 0000000000000000000000000000000000000000..39a0bc48eb37bea48c6382143b45d149590b4f45
--- /dev/null
+++ b/src/main/java/org/olat/repository/RepositoryMailing.java
@@ -0,0 +1,155 @@
+/**
+ * <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.repository;
+
+import java.util.Collections;
+import java.util.Locale;
+
+import org.apache.velocity.VelocityContext;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.id.Identity;
+import org.olat.core.id.User;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.Util;
+import org.olat.core.util.i18n.I18nManager;
+import org.olat.core.util.mail.MailContext;
+import org.olat.core.util.mail.MailContextImpl;
+import org.olat.core.util.mail.MailPackage;
+import org.olat.core.util.mail.MailTemplate;
+import org.olat.core.util.mail.MailerResult;
+import org.olat.core.util.mail.MailerWithTemplate;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public class RepositoryMailing {
+	
+	/**
+	 * The mail template when adding users to a group. The method chooses
+	 * automatically the right translator for the given group type to customize
+	 * the template text
+	 * 
+	 * @param re
+	 * @param actor
+	 * @return the generated MailTemplate
+	 */
+	public static MailTemplate createAddParticipantMailTemplate(RepositoryEntry re, Identity actor) {
+		String subjectKey = "notification.mail.added.subject";
+		String bodyKey = "notification.mail.added.body";
+		return createMailTemplate(re, actor, subjectKey, bodyKey);
+	}
+	
+	/**
+	 * The mail template when removing users from a repository entry. The method chooses
+	 * automatically the right translator for the given group type to customize
+	 * the template text
+	 * 
+	 * @param re
+	 * @param actor
+	 * @return the generated MailTemplate
+	 */
+	public static MailTemplate createRemoveParticipantMailTemplate(RepositoryEntry re, Identity actor) {
+		String subjectKey = "notification.mail.removed.subject";
+		String bodyKey = "notification.mail.removed.body";
+		return createMailTemplate(re, actor, subjectKey, bodyKey);
+	}
+	
+	public static MailTemplate getDefaultTemplate(Type type, RepositoryEntry re, Identity ureqIdentity) {
+		if(type == null) return null;
+		
+		switch(type) {
+			case addParticipant:
+				return createAddParticipantMailTemplate(re, ureqIdentity);
+			case removeParticipant:
+				return createRemoveParticipantMailTemplate(re, ureqIdentity);
+		}
+		return null;
+	}
+
+	protected static void sendEmail(Identity ureqIdentity, Identity identity, RepositoryEntry re,
+			Type type, MailPackage mailing, MailerWithTemplate mailer) {
+		
+		if(mailing != null && !mailing.isSendEmail()) {
+			return;
+		}
+
+		MailTemplate template = mailing == null ? null : mailing.getTemplate();
+		if(mailing == null || mailing.getTemplate() == null) {
+			template = getDefaultTemplate(type, re, ureqIdentity);
+		}
+		
+		MailContext context = mailing == null ? null : mailing.getContext();
+		if(context == null) {
+			context = new MailContextImpl(null, null, "[RepositoryEntry:" + re.getKey() + "]");
+		}
+		
+		System.out.println("***************************** Send mail");
+
+		MailerResult result = mailer.sendMailAsSeparateMails(context, Collections.singletonList(identity), null, template, null);
+		if(mailing != null) {
+			mailing.appendResult(result);
+		}
+	}
+
+	public enum Type {
+		addParticipant,
+		removeParticipant,
+	}
+	
+	private static MailTemplate createMailTemplate(RepositoryEntry re, Identity actor, String subjectKey, String bodyKey) {
+		// build learning resources as list of url as string
+		final String reName = re.getDisplayname();
+		final String redescription = re.getDescription(); 
+
+		// get some data about the actor and fetch the translated subject / body via i18n module
+		String[] bodyArgs = new String[] {
+				actor.getUser().getProperty(UserConstants.FIRSTNAME, null),
+				actor.getUser().getProperty(UserConstants.LASTNAME, null),
+				actor.getUser().getProperty(UserConstants.EMAIL, null),
+				actor.getName()
+			};
+		
+		Locale locale = I18nManager.getInstance().getLocaleOrDefault(actor.getUser().getPreferences().getLanguage());
+		Translator trans = Util.createPackageTranslator(RepositoryManager.class, locale);
+		String subject = trans.translate(subjectKey);
+		String body = trans.translate(bodyKey, bodyArgs);
+		
+		subject = subject.replaceAll("\\$coursename", reName == null ? "" : reName);
+		body = body.replaceAll("\\$coursedescription", redescription == null ? "" : redescription);
+		
+		// create a mail template which all these data
+		MailTemplate mailTempl = new MailTemplate(subject, body, null) {
+			@Override
+			public void putVariablesInMailContext(VelocityContext context, Identity identity) {
+				// Put user variables into velocity context
+				User user = identity.getUser();
+				context.put("firstname", user.getProperty(UserConstants.FIRSTNAME, null));
+				context.put("lastname", user.getProperty(UserConstants.LASTNAME, null));
+				context.put("login", identity.getName());
+				// Put variables from greater context
+				context.put("coursename", reName);
+				context.put("coursedescription", redescription);
+			}
+		};
+		return mailTempl;
+	}
+
+}
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index c4a123fcc7573e01197a36dd744b0df00f56a207..1b1662c41dcbe808f13512a83833575c7675f3d9 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -69,6 +69,8 @@ import org.olat.core.util.FileUtils;
 import org.olat.core.util.ImageHelper;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.image.Size;
+import org.olat.core.util.mail.MailPackage;
+import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.core.util.vfs.LocalFolderImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSItem;
@@ -122,6 +124,8 @@ public class RepositoryManager extends BasicManager {
 	private RepositoryModule repositoryModule;
 	@Autowired
 	private ACReservationDAO reservationDao;
+	@Autowired
+	private MailerWithTemplate mailer;
 
 	
 	/**
@@ -1666,7 +1670,7 @@ public class RepositoryManager extends BasicManager {
 	 * @param re
 	 * @param userActivityLogger
 	 */
-	public void addParticipants(Identity ureqIdentity, Roles ureqRoles, IdentitiesAddEvent iae, RepositoryEntry re) {
+	public void addParticipants(Identity ureqIdentity, Roles ureqRoles, IdentitiesAddEvent iae, RepositoryEntry re, MailPackage mailing) {
 		List<Identity> addIdentities = iae.getAddIdentities();
 		List<Identity> reallyAddedId = new ArrayList<Identity>();
 		for (Identity identityToAdd : addIdentities) {
@@ -1690,12 +1694,13 @@ public class RepositoryManager extends BasicManager {
 						ResourceReservation reservation =
 								reservationDao.createReservation(identityToAdd, "repo_participant", expiration, re.getOlatResource());
 						if(reservation != null) {
-							//TODO memail send mail
+							RepositoryMailing.sendEmail(ureqIdentity, identityToAdd, re, RepositoryMailing.Type.addParticipant, mailing, mailer);
 						}
 					}
 				} else {
 					addInternalParticipant(ureqIdentity, identityToAdd, re);
 					reallyAddedId.add(identityToAdd);
+					RepositoryMailing.sendEmail(ureqIdentity, identityToAdd, re, RepositoryMailing.Type.addParticipant, mailing, mailer);
 				}
 			}
 		}
@@ -1730,9 +1735,11 @@ public class RepositoryManager extends BasicManager {
 	 * @param re
 	 * @param logger
 	 */
-	public void removeParticipants(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re){
+	public void removeParticipants(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re, MailPackage mailing){
 		for (Identity identity : removeIdentities) {
     	securityManager.removeIdentityFromSecurityGroup(identity, re.getParticipantGroup());
+    	
+    	RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing, mailer);
 
 			ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 			ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
@@ -1744,6 +1751,8 @@ public class RepositoryManager extends BasicManager {
 			}
 			logAudit("Idenitity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
 					+ "' from securitygroup with key " + re.getParticipantGroup().getKey());
+			
+			
     }
 	}
 	
@@ -1932,7 +1941,8 @@ public class RepositoryManager extends BasicManager {
 		return entries;
 	}
 	
-	public void updateRepositoryEntryMembership(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re, List<RepositoryEntryPermissionChangeEvent> changes) {
+	public void updateRepositoryEntryMembership(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
+			List<RepositoryEntryPermissionChangeEvent> changes, MailPackage mailing) {
 		for(RepositoryEntryPermissionChangeEvent e:changes) {
 			if(e.getRepoOwner() != null) {
 				if(e.getRepoOwner().booleanValue()) {
@@ -1952,9 +1962,9 @@ public class RepositoryManager extends BasicManager {
 			
 			if(e.getRepoParticipant() != null) {
 				if(e.getRepoParticipant().booleanValue()) {
-					addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(e.getMember()), re);
+					addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(e.getMember()), re, mailing);
 				} else {
-					removeParticipants(ureqIdentity, Collections.singletonList(e.getMember()), re);
+					removeParticipants(ureqIdentity, Collections.singletonList(e.getMember()), re, mailing);
 				}
 			}
 		}
diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
index 9e1c9034c1ff9a1a58805904d8828000dfa9abbb..7400c07ef8f76b9e2892c90d3f4983764bc0157e 100644
--- a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties
@@ -487,4 +487,14 @@ chelp.rep-meta-info-reference.title=Lernressource: Informationen zur Verwendung
 
 help.hover.meta.info.owner=Hilfe zu den Besitzern
 chelp.rep-meta-info-owner.title=Lernressource: Besitzerinformationen
-repositoryentry.not.existing=Diese Ressource ist nicht mehr verfügbar.
\ No newline at end of file
+repositoryentry.not.existing=Diese Ressource ist nicht mehr verfügbar.
+
+notification.mail.added.subject=Kurs $coursename 
+notification.mail.added.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\nSie wurden von {0} {1} ({3}) in einen Kurs eingeladen\: \n\nKursname\: $coursename\nBeschreibung\: $coursedescription\n\nBei Fragen kontaktieren Sie bitte {0} {1} ({2}).
+notification.mail.removed.subject=Kurs $coursename\: Sie wurden ausgetragen.
+notification.mail.removed.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\nSie wurden von {0} {1} ({3}) aus dem Kurs ausgetragen\: \n\nKursname\: $coursename\nBeschreibung\: $coursedescription\n\nBei Fragen kontaktieren Sie bitte {0} {1} ({2}).
+
+
+
+
+
diff --git a/src/main/java/org/olat/repository/controllers/RepositoryMembersController.java b/src/main/java/org/olat/repository/controllers/RepositoryMembersController.java
index 3a0e369cdedd694adbed3f4e128e8f7139c3a266..153a460f34fee05fef9902e41760ef66d98328cd 100644
--- a/src/main/java/org/olat/repository/controllers/RepositoryMembersController.java
+++ b/src/main/java/org/olat/repository/controllers/RepositoryMembersController.java
@@ -15,12 +15,10 @@ import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
 import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
 import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
-import org.olat.core.util.mail.MailContext;
-import org.olat.core.util.mail.MailContextImpl;
 import org.olat.core.util.mail.MailHelper;
+import org.olat.core.util.mail.MailPackage;
 import org.olat.core.util.mail.MailTemplate;
 import org.olat.core.util.mail.MailerResult;
-import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.course.member.wizard.ImportMember_1a_LoginListStep;
 import org.olat.course.member.wizard.ImportMember_1b_ChooseMemberStep;
 import org.olat.group.BusinessGroupService;
@@ -138,19 +136,17 @@ public class RepositoryMembersController extends AbstractMemberListController {
 		MemberPermissionChangeEvent changes = (MemberPermissionChangeEvent)runContext.get("permissions");
 		
 		//commit changes to the repository entry
+		MailerResult result = new MailerResult();
+		MailPackage reMailing = new MailPackage(result, getWindowControl().getBusinessControl().getAsString(), true);
 		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
-		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges);
+		repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
-		businessGroupService.updateMemberships(getIdentity(), allModifications);
-		
+
 		MailTemplate template = (MailTemplate)runContext.get("mailTemplate");
-		if (template != null && !members.isEmpty()) {
-			MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-			MailContext context = new MailContextImpl(null, null, getWindowControl().getBusinessControl().getAsString());
-			MailerResult mailerResult = mailer.sendMailAsSeparateMails(context, members, null, template, getIdentity());
-			MailHelper.printErrorsAndWarnings(mailerResult, getWindowControl(), getLocale());
-		}
+		MailPackage bgMailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), true);
+		businessGroupService.updateMemberships(getIdentity(), allModifications, bgMailing);
+		MailHelper.printErrorsAndWarnings(result, getWindowControl(), getLocale());
 	}
 }
diff --git a/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java b/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
index f700be8ebd9fa8933c6ea740c9b2b240b87b014c..007716f3be32bfdf3924a5ed43af9d1bca1c8c04 100644
--- a/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
+++ b/src/main/java/org/olat/repository/controllers/WizardCloseCourseController.java
@@ -263,10 +263,10 @@ public class WizardCloseCourseController extends WizardController implements Wiz
 					businessGroupService.removeOwners(identity, securityManager.getIdentitiesOfSecurityGroup(secGroupOwner), bGroup);
 				}
 				if(secGroupPartipiciant != null) {
-					businessGroupService.removeParticipants(identity, securityManager.getIdentitiesOfSecurityGroup(secGroupPartipiciant), bGroup);
+					businessGroupService.removeParticipants(identity, securityManager.getIdentitiesOfSecurityGroup(secGroupPartipiciant), bGroup, null);
 				}
 				if(secGroupWaiting != null) {
-					businessGroupService.removeFromWaitingList(identity, securityManager.getIdentitiesOfSecurityGroup(secGroupWaiting), bGroup);
+					businessGroupService.removeFromWaitingList(identity, securityManager.getIdentitiesOfSecurityGroup(secGroupWaiting), bGroup, null);
 				}
 			}
 			
diff --git a/src/main/java/org/olat/repository/model/RepositoryEntryPermissionChangeEvent.java b/src/main/java/org/olat/repository/model/RepositoryEntryPermissionChangeEvent.java
index d55409b0591f18499b517c4fd3ec39abfd73a9d1..5fdf5ac444780d0b888aa5a37dec2f704105389b 100644
--- a/src/main/java/org/olat/repository/model/RepositoryEntryPermissionChangeEvent.java
+++ b/src/main/java/org/olat/repository/model/RepositoryEntryPermissionChangeEvent.java
@@ -74,4 +74,8 @@ public class RepositoryEntryPermissionChangeEvent extends Event {
 	public void setRepoParticipant(Boolean repoParticipant) {
 		this.repoParticipant = repoParticipant;
 	}
+	
+	public int size() {
+		return (repoOwner == null ? 0 : 1) + (repoTutor == null ? 0 : 1) + (repoParticipant == null ? 0 : 1);
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
index e2f76a521173d81bc28158b01cf0a3a955c1f3e1..69421d8f7714ace3ce8cdf16b7bc5e74687d38dd 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
@@ -315,7 +315,7 @@ public class ACFrontendManager extends BasicManager implements ACService {
 			if(re != null) {
 				IdentitiesAddEvent iae = new IdentitiesAddEvent(identity);
 				//roles is not needed as I add myself as participant
-				repositoryManager.addParticipants(identity, null, iae, re);
+				repositoryManager.addParticipants(identity, null, iae, re, null);
 				removeReservation(reservation);
 			}
 		}
@@ -412,7 +412,7 @@ public class ACFrontendManager extends BasicManager implements ACService {
 		} else if("BusinessGroup".equals(resourceType)) {
 			BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
 			if(group != null) {
-				EnrollState result = businessGroupService.enroll(identity, null, identity, group);
+				EnrollState result = businessGroupService.enroll(identity, null, identity, group, null);//TODO memail
 				return result.isFailed() ? Boolean.FALSE : Boolean.TRUE;
 			}
 		}
diff --git a/src/main/java/org/olat/restapi/group/LearningGroupWebService.java b/src/main/java/org/olat/restapi/group/LearningGroupWebService.java
index 418b95e080ec73070b109074f7f8de696a6cc840..a94fda02c05bdbab3e3213d5d9bd06b29a665239 100644
--- a/src/main/java/org/olat/restapi/group/LearningGroupWebService.java
+++ b/src/main/java/org/olat/restapi/group/LearningGroupWebService.java
@@ -642,7 +642,7 @@ public class LearningGroupWebService {
 				return Response.serverError().status(Status.NOT_FOUND).build();
 			}
 
-			BusinessGroupAddResponse state = bgs.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), Collections.singletonList(identity), group);
+			BusinessGroupAddResponse state = bgs.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), Collections.singletonList(identity), group, null);//TODO memail
 			if(state.getAddedIdentities().contains(identity)) {
 				return Response.ok().build();
 			} else if(state.getIdentitiesAlreadyInGroup().contains(identity)) {
@@ -680,7 +680,7 @@ public class LearningGroupWebService {
 			if(identity == null || group == null) {
 				return Response.serverError().status(Status.NOT_FOUND).build();
 			}
-			bgs.removeParticipants(ureq.getIdentity(), Collections.singletonList(identity), group);
+			bgs.removeParticipants(ureq.getIdentity(), Collections.singletonList(identity), group, null);
 
 			return Response.ok().build();
 		} catch (Exception e) {
diff --git a/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java b/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
index 4d816a2f5c922b52c5d6d569cf0a070765bc4f40..8fa7bcfbb82f77f835a6842d5fc69c66c4b40e4b 100644
--- a/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
+++ b/src/main/java/org/olat/restapi/repository/RepositoryEntryResource.java
@@ -397,7 +397,7 @@ public class RepositoryEntryResource {
 
 			UserRequest ureq = RestSecurityHelper.getUserRequest(request);
 			IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd);
-			repositoryManager.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry);
+			repositoryManager.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry, null);
 			return Response.ok().build();
 		} catch (Exception e) {
 			log.error("Trying to add a participant to a repository entry", e);
@@ -433,7 +433,7 @@ public class RepositoryEntryResource {
 			}
 
 			final UserRequest ureq = RestSecurityHelper.getUserRequest(request);
-			repositoryManager.removeParticipants(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry);
+			repositoryManager.removeParticipants(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry, null);
 			return Response.ok().build();
 		} catch (Exception e) {
 			log.error("Trying to remove a participant from a repository entry", e);
diff --git a/src/test/java/org/olat/group/test/BusinessGroupServiceTest.java b/src/test/java/org/olat/group/test/BusinessGroupServiceTest.java
index b652ed553f5a31f30738876baeea7ba229de40c7..4cc5a71c9af9a8608b11e0e8d790c4dd42ce9d8f 100644
--- a/src/test/java/org/olat/group/test/BusinessGroupServiceTest.java
+++ b/src/test/java/org/olat/group/test/BusinessGroupServiceTest.java
@@ -380,7 +380,7 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		groupsToMerge.add(g1);
 		groupsToMerge.add(g2);
 		groupsToMerge.add(g3);
-		BusinessGroup mergedGroup = businessGroupService.mergeBusinessGroups(wg1, g3, groupsToMerge);
+		BusinessGroup mergedGroup = businessGroupService.mergeBusinessGroups(wg1, g3, groupsToMerge, null);
 		Assert.assertNotNull(mergedGroup);
 		Assert.assertEquals(g3, mergedGroup);
 		dbInstance.commitAndCloseSession();
@@ -461,15 +461,15 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("move-w1-2-" + UUID.randomUUID().toString());
 		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("move-w1-3-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "move-bg-1", "move-desc", 0, 10, true, false, null);
-		businessGroupService.addToWaitingList(admin, Collections.singletonList(id1), group);
-		businessGroupService.addToWaitingList(admin, Collections.singletonList(id2), group);
-		businessGroupService.addParticipants(admin, JunitTestHelper.getAdminRoles(), Collections.singletonList(id3), group);
+		businessGroupService.addToWaitingList(admin, Collections.singletonList(id1), group, null);
+		businessGroupService.addToWaitingList(admin, Collections.singletonList(id2), group, null);
+		businessGroupService.addParticipants(admin, JunitTestHelper.getAdminRoles(), Collections.singletonList(id3), group, null);
 		
 		dbInstance.commitAndCloseSession();
 		
 		//move id1 from waiting-list to participant
 		List<Identity> identities = Collections.singletonList(id1);
-		businessGroupService.moveIdentityFromWaitingListToParticipant(admin, identities, group);
+		businessGroupService.moveIdentityFromWaitingListToParticipant(admin, identities, group, null);
 		//check position of 'id2'
 		int pos = businessGroupService.getPositionInWaitingListFor(id2, group);
 		Assert.assertEquals("pos must be 1, bit is=" + pos, 1, pos);
@@ -489,13 +489,13 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("move-w2-3-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "move-bg-1", "move-desc", 0, 10, true, false, null);
 		//add id1
-		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group);
+		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group, null);
 		dbInstance.commitAndCloseSession();
 		//add id2
-		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group);
+		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group, null);
 		dbInstance.commitAndCloseSession();
 		//add id3
-		businessGroupService.addToWaitingList(id3, Collections.singletonList(id3), group);
+		businessGroupService.addToWaitingList(id3, Collections.singletonList(id3), group, null);
 		dbInstance.commitAndCloseSession();
 		
 
@@ -521,13 +521,13 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("move-w3-2-" + UUID.randomUUID().toString());
 		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("move-w3-3-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "move-bg-3", "move-desc", 0, 10, true, false, null);
-		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group);
-		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group);
-		businessGroupService.addToWaitingList(id3, Collections.singletonList(id3), group);
+		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group, null);
+		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group, null);
+		businessGroupService.addToWaitingList(id3, Collections.singletonList(id3), group, null);
 		dbInstance.commitAndCloseSession();
 		
 		//remove id2
-		businessGroupService.removeFromWaitingList(wg1, Collections.singletonList(id2), group);
+		businessGroupService.removeFromWaitingList(wg1, Collections.singletonList(id2), group, null);
 		dbInstance.commitAndCloseSession();
 		
 		//check position of 'id1'
@@ -571,7 +571,7 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		List<Identity> identitiesToRemove = new ArrayList<Identity>();
 		identitiesToRemove.add(id1);
 		identitiesToRemove.add(id3);
-		businessGroupService.removeMembers(admin, identitiesToRemove, resource.getOlatResource());
+		businessGroupService.removeMembers(admin, identitiesToRemove, resource.getOlatResource(), null);
 		dbInstance.commitAndCloseSession();
 
 		//check in group1 stay only id2 in waiting list
@@ -619,14 +619,14 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("move-w4-1-" + UUID.randomUUID().toString());
 		Roles rolesId1 = securityManager.getRoles(id1);
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "move-bg-4", "move-desc", 0, 10, true, false, null);
-		businessGroupService.addParticipants(id1, rolesId1, Collections.singletonList(id1), group);
+		businessGroupService.addParticipants(id1, rolesId1, Collections.singletonList(id1), group, null);
 		dbInstance.commitAndCloseSession();
 
 		//add a user to waiting-list which is already in participant-list 
-		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group);
+		businessGroupService.addToWaitingList(id1, Collections.singletonList(id1), group, null);
 		dbInstance.commitAndCloseSession();
 		//try to move this user => user will be removed from waiting-list
-		businessGroupService.moveIdentityFromWaitingListToParticipant(id1, Collections.singletonList(id1), group);
+		businessGroupService.moveIdentityFromWaitingListToParticipant(id1, Collections.singletonList(id1), group, null);
 		dbInstance.commitAndCloseSession();
 
 		//check position of 'id1'
@@ -644,12 +644,12 @@ public class BusinessGroupServiceTest extends OlatTestCase {
 		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("move-w5-2-" + UUID.randomUUID().toString());;
 		Roles rolesId1 = securityManager.getRoles(id1);
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "move-bg-5", "move-desc", 0, 1, true, true, null);
-		businessGroupService.addParticipants(id1, rolesId1, Collections.singletonList(id1), group);
-		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group);
+		businessGroupService.addParticipants(id1, rolesId1, Collections.singletonList(id1), group, null);
+		businessGroupService.addToWaitingList(id2, Collections.singletonList(id2), group, null);
 		dbInstance.commitAndCloseSession();
 
 		//add a user to waiting-list which is already in participant-list 
-		businessGroupService.removeParticipants(id1, Collections.singletonList(id1), group);
+		businessGroupService.removeParticipants(id1, Collections.singletonList(id1), group, null);
 		dbInstance.commitAndCloseSession();
 
 		//check position of 'id2'
diff --git a/src/test/java/org/olat/restapi/RepositoryEntriesTest.java b/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
index d57bda242b9ffeab4abcb7a37cb70ae9d835eaaf..3dbc768cf3fc3d94e9fce8cf2692926629237c4e 100644
--- a/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
+++ b/src/test/java/org/olat/restapi/RepositoryEntriesTest.java
@@ -498,8 +498,8 @@ public class RepositoryEntriesTest extends OlatJerseyTestCase {
 		Identity participant2 = JunitTestHelper.createAndPersistIdentityAsAuthor("participant-2-" + UUID.randomUUID().toString());
 		Roles part1Roles = securityManager.getRoles(participant1);
 		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
-		repositoryManager.addParticipants(participant1, part1Roles, new IdentitiesAddEvent(participant1), re);
-		repositoryManager.addParticipants(participant1, part1Roles, new IdentitiesAddEvent(participant2), re);
+		repositoryManager.addParticipants(participant1, part1Roles, new IdentitiesAddEvent(participant1), re, null);
+		repositoryManager.addParticipants(participant1, part1Roles, new IdentitiesAddEvent(participant2), re, null);
 		dbInstance.commitAndCloseSession();
 
 		//get the coaches
@@ -559,7 +559,7 @@ public class RepositoryEntriesTest extends OlatJerseyTestCase {
 		Identity participant = JunitTestHelper.createAndPersistIdentityAsAuthor("participant-4-" + UUID.randomUUID().toString());
 		Roles partRoles = securityManager.getRoles(participant);
 		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
-		repositoryManager.addParticipants(participant, partRoles, new IdentitiesAddEvent(participant), re);
+		repositoryManager.addParticipants(participant, partRoles, new IdentitiesAddEvent(participant), re, null);
 		dbInstance.commitAndCloseSession();
 
 		//remove the owner