From 458edfcfc5bfd82e6bb699a996498c6e24f66857 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 14 Dec 2012 17:14:47 +0100
Subject: [PATCH] OO-377: map the reservations to the right roles, upgrade
 waiting list after a cancellation, better choice of templates, fix remove of
 reservations as coach/admin

---
 .../den/DENManageParticipantsController.java  |  2 +-
 .../bulkChange/UserBulkChangeManager.java     | 19 +-----
 .../user/groups/GroupOverviewController.java  | 15 +----
 .../admin/user/imp/UserImportController.java  |  2 +-
 .../wizard/ImportMemberMailController.java    | 13 ++++-
 .../course/nodes/en/EnrollmentManager.java    |  4 +-
 .../ProjectBrokerCourseEditorController.java  |  3 +-
 .../projectbroker/ProjectGroupController.java |  5 +-
 .../service/ProjectGroupManagerImpl.java      |  3 +-
 .../org/olat/group/BusinessGroupService.java  |  8 ++-
 .../manager/BusinessGroupServiceImpl.java     | 46 ++++++++++++---
 .../java/org/olat/group/ui/BGMailHelper.java  | 58 +++++++++++--------
 .../AbstractBusinessGroupListController.java  |  4 +-
 .../ui/main/AbstractMemberListController.java | 10 +---
 .../ui/main/PendingEnrollmentController.java  |  4 +-
 .../group/ui/run/DisposedBusinessGroup.java   |  9 +--
 .../ui/wizard/BGMailTemplateController.java   |  2 +-
 .../olat/repository/RepositoryManager.java    | 23 ++++++++
 .../resource/accesscontrol/ACService.java     |  9 ++-
 .../manager/ACFrontendManager.java            | 23 ++++----
 .../provider/paypal/PaypalIPNFilter.java      |  8 +--
 .../paypal/manager/PaypalManagerImpl.java     |  4 +-
 .../group/LearningGroupWebService.java        |  2 +-
 23 files changed, 152 insertions(+), 124 deletions(-)

diff --git a/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java b/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
index f159535bb1d..a1d82299272 100644
--- a/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
+++ b/src/main/java/de/bps/course/nodes/den/DENManageParticipantsController.java
@@ -305,7 +305,7 @@ public class DENManageParticipantsController extends BasicController {
 	private void createAddedNotificationMail(UserRequest ureq, String subjectStr) {
 		MailTemplate mailTempl = denManager.getAddedMailTemplate(ureq, subjectStr, getTranslator());
 		removeAsListenerAndDispose(addedNotificationCtr);
-		//TODO memail
+
 		addedNotificationCtr = new MailNotificationEditController(getWindowControl(), ureq, mailTempl, false, false);
 		listenTo(addedNotificationCtr);
 		
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 2fff30465fd..327ca480f81 100644
--- a/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
+++ b/src/main/java/org/olat/admin/user/bulkChange/UserBulkChangeManager.java
@@ -231,25 +231,8 @@ public class UserBulkChangeManager extends BasicManager {
 
 			BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
 			MailPackage mailing = new MailPackage();
-			bgs.updateMemberships(addingIdentity, changes, mailing);//TODO memail
+			bgs.updateMemberships(addingIdentity, changes, mailing);
 			DBFactory.getInstance().commit();
-			
-			//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);
-						if (mailerResult.getReturnCode() != MailerResult.OK && isLogDebugEnabled()) {
-							logDebug("Problems sending Group invitation mail for identity: " + selIdentity.getName() + " and group: " 
-									+ group.getName() + " key: " + group.getKey() + " mailerresult: " + mailerResult.getReturnCode(), null);
-						}
-					}
-				}
-			}*/
 		}
 	}
 
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 0837b6c77be..cbef349ce72 100644
--- a/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
+++ b/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
@@ -267,22 +267,9 @@ public class GroupOverviewController extends BasicController {
 			}
 		}
 		
-		MailPackage mailing = new MailPackage();//TODO memail
+		MailPackage mailing = new MailPackage();
 		businessGroupService.updateMemberships(getIdentity(), changes, mailing);
 		DBFactory.getInstance().commit();
-		
-		/*if(e.getMailForGroupsList() != null && !e.getMailForGroupsList().isEmpty()) {
-			List<BusinessGroup> notifGroups = businessGroupService.loadBusinessGroups(e.getMailForGroupsList());
-			for (BusinessGroup group : notifGroups) {
-				MailTemplate mailTemplate = BGMailHelper.createAddParticipantMailTemplate(group, getIdentity());
-				MailerWithTemplate mailer = MailerWithTemplate.getInstance();
-				MailerResult mailerResult = mailer.sendMailAsSeparateMails(null, Collections.singletonList(identity), null, mailTemplate, null);
-				if (mailerResult.getReturnCode() != MailerResult.OK && isLogDebugEnabled()) {
-					logDebug("Problems sending Group invitation mail for identity: " + identity.getName() + " and group: " 
-							+ group.getName() + " key: " + group.getKey() + " mailerresult: " + mailerResult.getReturnCode(), null);
-				}
-			}
-		}*/
 	}
 	
 	private void doLeave(UserRequest ureq, List<BusinessGroup> groupsToLeave) {
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 c7922d4fc28..ff1b71eb56d 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, null);//TODO memail
+		businessGroupService.updateMemberships(getIdentity(), changes, null);
 		DBFactory.getInstance().commit();
 		
 		if(mailGroups != null && !mailGroups.isEmpty()) {
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 8018db9f727..8b4e6d73d47 100644
--- a/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java
+++ b/src/main/java/org/olat/course/member/wizard/ImportMemberMailController.java
@@ -65,7 +65,6 @@ public class ImportMemberMailController extends StepFormBasicController {
 			}
 		}
 		
-		boolean customizing = e.size() == 1;
 		MailType defaultGroupType = BusinessGroupMailing.getDefaultTemplateType(e);
 		RepositoryMailing.Type defaultRepoType = RepositoryMailing.getDefaultTemplateType(e);
 
@@ -74,14 +73,22 @@ public class ImportMemberMailController extends StepFormBasicController {
 			mailTemplate = BusinessGroupMailing.getDefaultTemplate(defaultGroupType, group, getIdentity());
 		} else if(defaultRepoType != null) {
 			mailTemplate = RepositoryMailing.getDefaultTemplate(defaultRepoType, repoEntry, getIdentity());
+		} else if(hasCouresRights(e)) {
+			mailTemplate = RepositoryMailing.createAddParticipantMailTemplate(repoEntry, getIdentity());
 		} else {
-			mailTemplate = new TestMailTemplate();
+			mailTemplate = BusinessGroupMailing.getDefaultTemplate(MailType.addParticipant, null, getIdentity());
 		}
-		mailTemplateForm = new BGMailTemplateController(ureq, wControl, mailTemplate, false, customizing, false, mandatoryEmail, rootForm);
+		mailTemplateForm = new BGMailTemplateController(ureq, wControl, mailTemplate, false, true, false, mandatoryEmail, rootForm);
 		
 		initForm (ureq);
 	}
 	
+	private boolean hasCouresRights(MemberPermissionChangeEvent e) {
+		return ((e.getRepoOwner() != null && e.getRepoOwner().booleanValue())
+				|| (e.getRepoParticipant() != null && e.getRepoParticipant().booleanValue())
+				|| (e.getRepoTutor() != null && e.getRepoTutor().booleanValue()));
+	}
+	
 	private boolean hasParticipantOrTutorsRightsChanges(MemberPermissionChangeEvent e) {
 		if((e.getRepoParticipant() != null && e.getRepoParticipant().booleanValue())
 				|| (e.getRepoTutor() != null && e.getRepoTutor().booleanValue())){
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 2010d66ae20..02a0c41f8f5 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, null);//TODO memail
+			EnrollState state =businessGroupService.enroll(identity, roles, identity, group, null);
 			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, null);//TODO memail
+		businessGroupService.removeParticipants(identity, Collections.singletonList(identity), enrolledGroup, null);
 		logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName());
 
 		logInfo("doCancelEnrollment in group " + enrolledGroup, identity.getName());
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 63f31ab6324..7aca8cc362d 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerCourseEditorController.java
@@ -189,7 +189,6 @@ public class ProjectBrokerCourseEditorController extends ActivateableTabbableDef
     String groupDescription = translate("account.manager.groupdescription", node.getShortTitle());
     accountManagerGroup = ProjectBrokerManagerFactory.getProjectGroupManager().getAccountManagerGroupFor(cpm, node, course, groupName, groupDescription, ureq.getIdentity());
     if (accountManagerGroup != null) {
-		//TODO memail
     	accountManagerGroupController = new GroupController(ureq, getWindowControl(), true, false, true, false, true, false, accountManagerGroup.getPartipiciantGroup());
 			listenTo(accountManagerGroupController);
 			// add mail templates used when adding and removing users
@@ -302,7 +301,7 @@ public class ProjectBrokerCourseEditorController extends ActivateableTabbableDef
 			if (event instanceof IdentitiesAddEvent) {
 				IdentitiesAddEvent identitiesAddedEvent = (IdentitiesAddEvent)event;
 				BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(),
-						identitiesAddedEvent.getAddIdentities(), accountManagerGroup, null);//TODO memail
+						identitiesAddedEvent.getAddIdentities(), accountManagerGroup, null);
 				identitiesAddedEvent.setIdentitiesAddedEvent(response.getAddedIdentities());
 				identitiesAddedEvent.setIdentitiesWithoutPermission(response.getIdentitiesWithoutPermission());
 				identitiesAddedEvent.setIdentitiesAlreadyInGroup(response.getIdentitiesAlreadyInGroup());
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 ae8d5759be9..60580fdeccb 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectGroupController.java
@@ -87,13 +87,11 @@ public class ProjectGroupController extends BasicController {
 		VelocityContainer myContent = createVelocityContainer("projectgroup_management");
 
 		// Project Leader Management
-		//TODO memail
 		projectLeaderController = new GroupController(ureq, getWindowControl(), true, true, true, false, true, false, project.getProjectLeaderGroup());
 		listenTo(projectLeaderController);
 		myContent.put("projectLeaderController", projectLeaderController.getInitialComponent());
 
 		// Project Member Management
-		//TODO memail
 		projectMemberController = new GroupController(ureq, getWindowControl(), true, false, true, false, true, false, project.getProjectParticipantGroup());
 		listenTo(projectMemberController);
 		myContent.put("projectMemberController", projectMemberController.getInitialComponent());
@@ -105,7 +103,6 @@ public class ProjectGroupController extends BasicController {
 
 		// Project Candidates Management
 		if (projectBrokerModuleConfiguration.isAcceptSelectionManually()) {
-			//TODO memail
 			projectCandidatesController = new WaitingGroupController(ureq, getWindowControl(), true, false, true, true, false, project.getCandidateGroup());
 			listenTo(projectCandidatesController);
 			myContent.contextPut("isProjectCandidatesListEmpty", ProjectBrokerManagerFactory.getProjectGroupManager().isCandidateListEmpty(project.getCandidateGroup()) );
@@ -186,7 +183,7 @@ public class ProjectGroupController extends BasicController {
 		if (event instanceof IdentitiesAddEvent) {
 			IdentitiesAddEvent identitiesAddedEvent = (IdentitiesAddEvent)event;
 			BusinessGroupAddResponse response = businessGroupService.addParticipants(urequest.getIdentity(), urequest.getUserSession().getRoles(),
-					identitiesAddedEvent.getAddIdentities(), project.getProjectGroup(), null);//TODO memail
+					identitiesAddedEvent.getAddIdentities(), project.getProjectGroup(), null);
 			identitiesAddedEvent.setIdentitiesAddedEvent(response.getAddedIdentities());
 			identitiesAddedEvent.setIdentitiesWithoutPermission(response.getIdentitiesWithoutPermission());
 			identitiesAddedEvent.setIdentitiesAlreadyInGroup(response.getIdentitiesAlreadyInGroup());
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 137ed01e6a5..7158c555b6c 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
@@ -247,8 +247,7 @@ public class ProjectGroupManagerImpl extends BasicManager implements ProjectGrou
 		final Project reloadedProject = (Project) DBFactory.getInstance().loadObject(project, true);
 		final BusinessGroupAddResponse response = new BusinessGroupAddResponse();
 		final BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
-		//TODO memail
-		BusinessGroupAddResponse state = bgs.addParticipants(actionIdentity, null, identities, reloadedProject.getProjectGroup(), null);//TODO memail
+		BusinessGroupAddResponse state = bgs.addParticipants(actionIdentity, null, identities, reloadedProject.getProjectGroup(), null);
 		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 91f2cc4c6e5..d99a3145135 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -41,6 +41,7 @@ import org.olat.group.model.SearchBusinessGroupParams;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryShort;
 import org.olat.resource.OLATResource;
+import org.olat.resource.accesscontrol.model.ResourceReservation;
 
 /**
  * 
@@ -380,10 +381,13 @@ public interface BusinessGroupService {
 	
 	/**
 	 * 
-	 * @param identity
+	 * @param ureqIdentity
+	 * @param reservationOwner
 	 * @param resource
 	 */
-	public void acceptPendingParticipation(Identity ureqIdentity, Identity identity, OLATResource resource);
+	public void acceptPendingParticipation(Identity ureqIdentity, Identity reservationOwner, OLATResource resource);
+	
+	public void cancelPendingParticipation(Identity ureqIdentity, ResourceReservation reservation);
 	
 	/**
 	 * Remove a list of users from a group as participant and does all the magic that needs
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index 054976659bf..83268c53d75 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -1005,18 +1005,41 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		syncIM(syncIM, group);
 		return response;
 	}
+	
+	@Override
+	@Transactional
+	public void cancelPendingParticipation(Identity ureqIdentity, ResourceReservation reservation) {
+		if(reservation != null && "BusinessGroup".equals(reservation.getResource().getResourceableTypeName())) {
+			BusinessGroup group = businessGroupDAO.loadForUpdate(reservation.getResource().getResourceableId());
+
+			SyncUserListTask syncIM = new SyncUserListTask(group);
+			transferFirstIdentityFromWaitingToParticipant(ureqIdentity, group, null, null);
+			syncIM(syncIM, group);
+		}
+	}
 
 	@Override
 	@Transactional
-	public void acceptPendingParticipation(Identity ureqIdentity, Identity identityToAdd, OLATResource resource) {
-		ResourceReservation reservation = acService.getReservation(identityToAdd, resource);
+	public void acceptPendingParticipation(Identity ureqIdentity, Identity reservationOwner, OLATResource resource) {
+		ResourceReservation reservation = acService.getReservation(reservationOwner, resource);
 		if(reservation != null && "BusinessGroup".equals(resource.getResourceableTypeName())) {
 			BusinessGroup group = businessGroupDAO.loadForUpdate(resource.getResourceableId());
-			if(!securityManager.isIdentityInSecurityGroup(identityToAdd, group.getPartipiciantGroup())) {
-				SyncUserListTask syncIM = new SyncUserListTask(group);
-				internalAddParticipant(identityToAdd, group, syncIM);
-				syncIM(syncIM, group);
+
+			String type = reservation.getType();
+			if("group_coach".equals(type)) {
+				if(!securityManager.isIdentityInSecurityGroup(reservationOwner, group.getOwnerGroup())) {
+					SyncUserListTask syncIM = new SyncUserListTask(group);
+					internalAddCoach(reservationOwner, group, syncIM);
+					syncIM(syncIM, group);
+				}
+			} else if("group_participant".equals(type)) {
+				if(!securityManager.isIdentityInSecurityGroup(reservationOwner, group.getPartipiciantGroup())) {
+					SyncUserListTask syncIM = new SyncUserListTask(group);
+					internalAddParticipant(reservationOwner, group, syncIM);
+					syncIM(syncIM, group);
+				}
 			}
+			
 			reservationDao.deleteReservation(reservation);
 		}
 	}
@@ -1298,7 +1321,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 			enrollStatus.setEnrolled(BGMembership.participant);
 			log.info("doEnroll (reservation) - setIsEnrolled ", identity.getName());
 			if(reservation != null) {
-				acService.removeReservation(reservation);
+				reservationDao.deleteReservation(reservation);
 			}
 		} else if (reloadedGroup.getMaxParticipants() != null) {
 			int participantsCounter = securityManager.countIdentitiesOfSecurityGroup(reloadedGroup.getPartipiciantGroup());
@@ -1333,6 +1356,13 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		return enrollStatus;
 	}
 
+	/**
+	 * Don't forget to lock the business group before calling this method.
+	 * @param ureqIdentity
+	 * @param group
+	 * @param mailing
+	 * @param syncIM
+	 */
 	private void transferFirstIdentityFromWaitingToParticipant(Identity ureqIdentity, BusinessGroup group, 
 			MailPackage mailing, SyncUserListTask syncIM) {
 
@@ -1378,8 +1408,6 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 				  }
 				}
 			}
-		} else {
-			log.warn("Called method transferFirstIdentityFromWaitingToParticipant but waiting-list or autoCloseRanks is disabled.");
 		}
 	}
 	
diff --git a/src/main/java/org/olat/group/ui/BGMailHelper.java b/src/main/java/org/olat/group/ui/BGMailHelper.java
index f4532af14c0..edc64b0d7d3 100644
--- a/src/main/java/org/olat/group/ui/BGMailHelper.java
+++ b/src/main/java/org/olat/group/ui/BGMailHelper.java
@@ -188,37 +188,47 @@ public class BGMailHelper {
 	 * @return
 	 */
 	private static MailTemplate createMailTemplate(BusinessGroupShort group, Identity actor, String subjectKey, String bodyKey) {
-		// build learning resources as list of url as string
-		StringBuilder learningResources = new StringBuilder();
-		BusinessGroupService businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
-		List<RepositoryEntryShort> repoEntries = businessGroupService.findShortRepositoryEntries(Collections.singletonList(group), 0, -1);
-		for (RepositoryEntryShort entry: repoEntries) {
-			String title = entry.getDisplayname();
-			String url = BusinessControlFactory.getInstance().getURLFromBusinessPathString("[RepositoryEntry:" + entry.getKey() + "]");
-			learningResources.append(title);
-			learningResources.append(" (");
-			learningResources.append(url);
-			learningResources.append(")\n");
-		}
-
-		final String courselist = learningResources.toString();
-		// get group name and description
-		final String groupname = group.getName();
-		final String groupdescription = (group instanceof BusinessGroup ?
-				FilterFactory.getHtmlTagAndDescapingFilter().filter(((BusinessGroup)group).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() };
+					actor.getName() };
 		Locale locale = I18nManager.getInstance().getLocaleOrDefault(actor.getUser().getPreferences().getLanguage());
 		Translator trans = Util.createPackageTranslator(BGMailHelper.class, locale);
 		String subject = trans.translate(subjectKey);
 		String body = trans.translate(bodyKey, bodyArgs);
 		
-		subject = subject.replaceAll("\\$groupname", groupname == null ? "" : groupname);
-		body = body.replaceAll("\\$groupname", groupname == null ? "" : groupname);
-		body = body.replaceAll("\\$groupdescription", groupdescription == null ? "" : groupdescription);
-		body = body.replaceAll("\\$courselist", courselist == null ? "" : courselist);
+		// build learning resources as list of url as string
+		
+		final String courselist;
+		final String groupname;
+		final String groupdescription; 
+		StringBuilder learningResources = new StringBuilder();
+		if(group != null) {
+			BusinessGroupService businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
+			List<RepositoryEntryShort> repoEntries = businessGroupService.findShortRepositoryEntries(Collections.singletonList(group), 0, -1);
+			for (RepositoryEntryShort entry: repoEntries) {
+				String title = entry.getDisplayname();
+				String url = BusinessControlFactory.getInstance().getURLFromBusinessPathString("[RepositoryEntry:" + entry.getKey() + "]");
+				learningResources.append(title);
+				learningResources.append(" (");
+				learningResources.append(url);
+				learningResources.append(")\n");
+			}
+	
+			courselist = learningResources.toString();
+			// get group name and description
+			groupname = group.getName();
+			groupdescription = (group instanceof BusinessGroup ?
+					FilterFactory.getHtmlTagAndDescapingFilter().filter(((BusinessGroup)group).getDescription()) : ""); 
+
+			subject = subject.replaceAll("\\$groupname", groupname == null ? "" : groupname);
+			body = body.replaceAll("\\$groupname", groupname == null ? "" : groupname);
+			body = body.replaceAll("\\$groupdescription", groupdescription == null ? "" : groupdescription);
+			body = body.replaceAll("\\$courselist", courselist == null ? "" : courselist);
+		} else {
+			courselist = "";
+			groupname = "";
+			groupdescription = "";
+		}
 		
 		// create a mail template which all these data
 		MailTemplate mailTempl = new MailTemplate(subject, body, null) {
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 b7b36792b06..b87e299f474 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
@@ -419,7 +419,7 @@ public abstract class AbstractBusinessGroupListController extends BasicControlle
 		// if identity was also owner it must have successfully removed to end here.
 		// now remove the identity also as participant.
 		// 2) remove as participant
-		businessGroupService.removeParticipants(getIdentity(), identityToRemove, group, null);//TODO memail
+		businessGroupService.removeParticipants(getIdentity(), identityToRemove, group, null);
 		// 3) remove from waiting list
 		businessGroupService.removeFromWaitingList(getIdentity(), identityToRemove, group, null);
 	}
@@ -665,7 +665,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, null);//TODO memail
+				businessGroupService.mergeBusinessGroups(getIdentity(), targetGroup, groups, null);
 				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 3f56a870db6..505a53b2f39 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java
@@ -322,7 +322,7 @@ public abstract class AbstractMemberListController extends BasicController imple
 			repositoryManager.updateRepositoryEntryMembership(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, changes, null);
 		}
 
-		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), null);//TODO memail
+		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), null);
 		//make sure all is committed before loading the model again (I see issues without)
 		DBFactory.getInstance().commitAndCloseSession();
 		
@@ -343,7 +343,7 @@ public abstract class AbstractMemberListController extends BasicController imple
 
 		//commit all changes to the group memberships
 		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
-		businessGroupService.updateMemberships(getIdentity(), allModifications, null);//TODO memail
+		businessGroupService.updateMemberships(getIdentity(), allModifications, null);
 		DBFactory.getInstance().commitAndCloseSession();
 		
 		if(allModifications != null && !allModifications.isEmpty()) {
@@ -357,7 +357,6 @@ public abstract class AbstractMemberListController extends BasicController imple
 		reloadModel();
 	}
 	
-	//TODO memail
 	protected void sendMailAfterChangePermission(BusinessGroupMembershipChange mod) {
 		MailTemplate template = null;
 		if(mod.getParticipant() != null) {
@@ -419,11 +418,8 @@ public abstract class AbstractMemberListController extends BasicController imple
 			List<Long> identityKeys = getMemberKeys(members);
 			List<Identity> identitiesToGraduate = securityManager.loadIdentityByKeys(identityKeys);
 			businessGroupService.moveIdentityFromWaitingListToParticipant(getIdentity(), identitiesToGraduate,
-					businessGroup, null);//TODO memail
-		} else {
-			//TODO memail do something
+					businessGroup, null);
 		}
-		
 		reloadModel();
 	}
 	
diff --git a/src/main/java/org/olat/group/ui/main/PendingEnrollmentController.java b/src/main/java/org/olat/group/ui/main/PendingEnrollmentController.java
index 67bc50d98e4..056402de737 100644
--- a/src/main/java/org/olat/group/ui/main/PendingEnrollmentController.java
+++ b/src/main/java/org/olat/group/ui/main/PendingEnrollmentController.java
@@ -169,7 +169,7 @@ public class PendingEnrollmentController extends FormBasicController implements
 				if(reservation.getAccept().booleanValue()) {
 					acService.acceptReservationToResource(getIdentity(), reservation.getReservation());
 				} else {
-					acService.removeReservation(reservation.getReservation());
+					acService.removeReservation(getIdentity(), getIdentity(), reservation.getReservation());
 				}
 			}
 		}
@@ -198,7 +198,7 @@ public class PendingEnrollmentController extends FormBasicController implements
 		}
 		
 		public boolean isCoach() {
-			return "group_coach".equals(reservation.getType());
+			return "group_coach".equals(reservation.getType()) || "repo_tutors".equals(reservation.getType());
 		}
 
 		public String getName() {
diff --git a/src/main/java/org/olat/group/ui/run/DisposedBusinessGroup.java b/src/main/java/org/olat/group/ui/run/DisposedBusinessGroup.java
index f40e35e825f..d7cd743784d 100644
--- a/src/main/java/org/olat/group/ui/run/DisposedBusinessGroup.java
+++ b/src/main/java/org/olat/group/ui/run/DisposedBusinessGroup.java
@@ -33,7 +33,7 @@ import org.olat.core.gui.control.controller.BasicController;
 
 /**
  * Description:<br>
- * TODO: patrickb Class Description for DisposedBusinessGroup
+ * Empty controller for deleted groups
  * 
  * <P>
  * Initial Date:  28.04.2008 <br>
@@ -52,8 +52,7 @@ class DisposedBusinessGroup extends BasicController {
 	 */
 	@Override
 	protected void doDispose() {
-	// TODO Auto-generated method stub
-
+		//
 	}
 
 	/**
@@ -61,8 +60,6 @@ class DisposedBusinessGroup extends BasicController {
 	 */
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
-	// TODO Auto-generated method stub
-
+		//
 	}
-
 }
diff --git a/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java b/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java
index f72867f1e31..4fa0e33bf85 100644
--- a/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java
+++ b/src/main/java/org/olat/group/ui/wizard/BGMailTemplateController.java
@@ -159,7 +159,7 @@ public class BGMailTemplateController extends FormBasicController {
 		}
 		
 		defaultTemplate = uifactory.addCheckboxesVertical("deftemplate", "", formLayout, new String[]{"xx"}, new String[]{translate("mailtemplateform.defaultTemplate")}, null, 1);
-		if(!customizingAvailable || !StringHelper.containsNonWhitespace(template.getSubjectTemplate())) {
+		if(!customizingAvailable && StringHelper.containsNonWhitespace(template.getSubjectTemplate())) {
 			defaultTemplate.select("xx", true);
 		}
 		
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 803b2394b30..22300a30cad 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -1633,6 +1633,22 @@ public class RepositoryManager extends BasicManager {
     }
 	}
 	
+	public void acceptPendingParticipation(Identity ureqIdentity, Identity identityToAdd, OLATResource resource, ResourceReservation reservation) {
+		RepositoryEntry re = lookupRepositoryEntry(resource, false);
+		if(re != null) {
+			if("repo_participant".equals(reservation.getType())) {
+				IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd);
+				//roles is not needed as I add myself as participant
+				addParticipants(ureqIdentity, null, iae, re, null);
+			} else if("repo_tutors".equals(reservation.getType())) {
+				IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd);
+				//roles is not needed as I add myself as tutor
+				addTutors(ureqIdentity, null, iae, re, null);
+			}
+			reservationDao.deleteReservation(reservation);
+		}
+	}
+	
 	/**
 	 * add provided list of identities as tutor to the repo entry. silently ignore
 	 * if some identities were already tutor before.
@@ -1848,6 +1864,13 @@ public class RepositoryManager extends BasicManager {
 			}
 		}
 		
+		List<ResourceReservation> reservations = reservationDao.loadReservations(Collections.singletonList(re.getOlatResource()));
+		for(ResourceReservation reservation:reservations) {
+			if(members.contains(reservation.getIdentity())) {
+				reservationDao.deleteReservation(reservation);
+			}
+		}
+		
 		return securityManager.removeIdentityFromSecurityGroups(members, secGroups);
 	}
 
diff --git a/src/main/java/org/olat/resource/accesscontrol/ACService.java b/src/main/java/org/olat/resource/accesscontrol/ACService.java
index ebc0099f698..444cb59d30a 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ACService.java
+++ b/src/main/java/org/olat/resource/accesscontrol/ACService.java
@@ -111,8 +111,6 @@ public interface ACService {
 	public boolean denyAccesToResource(Identity identity, Offer offer);
 	
 	
-	public void removeReservation(ResourceReservation reservation);
-	
 	/**
 	 * Get the reservation form an identity on a resource
 	 * @param identity
@@ -150,6 +148,13 @@ public interface ACService {
 	 */
 	public void acceptReservationToResource(Identity identity, ResourceReservation reservation);
 	
+	/**
+	 * Cancel a reservation
+	 * @param identity
+	 * @param reservation
+	 */
+	public void removeReservation(Identity ureqIdentity, Identity identity, ResourceReservation reservation);
+	
 	public int countReservations(OLATResource resource);
 	
 	public void cleanupReservations();
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 4a5037777b1..6beccb866d3 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
@@ -30,8 +30,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.olat.admin.securitygroup.gui.IdentitiesAddEvent;
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.manager.BasicManager;
@@ -76,6 +76,8 @@ import org.springframework.stereotype.Service;
 @Service("acService")
 public class ACFrontendManager extends BasicManager implements ACService {
 	
+	@Autowired
+	private DB dbInstance;
 	@Autowired
 	private BaseSecurity securityManager;
 	@Autowired
@@ -304,8 +306,6 @@ public class ACFrontendManager extends BasicManager implements ACService {
 		return new AccessResult(false);
 	}
 	
-	
-	
 	@Override
 	public void acceptReservationToResource(Identity identity, ResourceReservation reservation) {
 		OLATResource resource = reservation.getResource();
@@ -313,19 +313,18 @@ public class ACFrontendManager extends BasicManager implements ACService {
 			//it's a reservation for a group
 			businessGroupService.acceptPendingParticipation(identity, identity, resource);
 		} else {
-			RepositoryEntry re = repositoryManager.lookupRepositoryEntry(resource, false);
-			if(re != null) {
-				IdentitiesAddEvent iae = new IdentitiesAddEvent(identity);
-				//roles is not needed as I add myself as participant
-				repositoryManager.addParticipants(identity, null, iae, re, null);
-				removeReservation(reservation);
-			}
+			repositoryManager.acceptPendingParticipation(identity, identity, resource, reservation);
 		}
 	}
 
 	@Override
-	public void removeReservation(ResourceReservation reservation) {
+	public void removeReservation(Identity ureqIdentity, Identity identity, ResourceReservation reservation) {
+		OLATResource resource = reservation.getResource();
 		reservationDao.deleteReservation(reservation);
+		if("BusinessGroup".equals(resource.getResourceableTypeName())) {
+			dbInstance.commit();//needed to have the right number of participants to calculate upgrade from waiting list
+			businessGroupService.cancelPendingParticipation(ureqIdentity, reservation);
+		}
 	}
 
 	@Override
@@ -414,7 +413,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, null);//TODO memail
+				EnrollState result = businessGroupService.enroll(identity, null, identity, group, null);
 				return result.isFailed() ? Boolean.FALSE : Boolean.TRUE;
 			}
 		}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalIPNFilter.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalIPNFilter.java
index d6ab240b0ad..29996af16e0 100644
--- a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalIPNFilter.java
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/PaypalIPNFilter.java
@@ -82,23 +82,17 @@ public class PaypalIPNFilter implements Filter {
 	@SuppressWarnings("deprecation")
 	private void verify(ServletRequest request, PaypalManager paypalManager) {
 		try {
-			System.out.println("Verifify **************************************************");
-			
 			//code from the Paypal example
 			// read post from PayPal system and add 'cmd'	
 			StringBuilder sb = new StringBuilder();
 			sb.append("cmd=_notify-validate");
 			Map<String,String> values = new HashMap<String,String>();
-			for(@SuppressWarnings("unchecked")
-			Enumeration<String> en = request.getParameterNames(); en.hasMoreElements(); ){
+			for(Enumeration<String> en = request.getParameterNames(); en.hasMoreElements(); ){
 				String paramName = en.nextElement();
 				String paramValue = request.getParameter(paramName);
 				sb.append("&").append(paramName).append("=").append(URLEncoder.encode(paramValue));
 				values.put(paramName, paramValue);
 			}
-			
-
-			System.out.println("Verifify " + values);
 
 			// post back to PayPal system to validate
 			// NOTE: change http: to https: in the following URL to verify using SSL (for increased security).
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java
index 5ec0964174d..061165f8c9a 100644
--- a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/manager/PaypalManagerImpl.java
@@ -387,7 +387,7 @@ public class PaypalManagerImpl extends BasicManager implements PaypalManager {
 					OLATResource resource = line.getOffer().getResource();
 					ResourceReservation reservation = acService.getReservation(identity, resource);
 					if(reservation != null) {
-						acService.removeReservation(reservation);
+						acService.removeReservation(identity, identity, reservation);
 						logAudit("Remove reservation after cancellation for: " + reservation + " to " + identity, null);
 					}
 				}
@@ -455,7 +455,7 @@ public class PaypalManagerImpl extends BasicManager implements PaypalManager {
 
 						ResourceReservation reservation = reservationDao.loadReservation(identity, line.getOffer().getResource());
 						if(reservation != null) {
-							acService.removeReservation(reservation);
+							acService.removeReservation(identity, identity, reservation);
 							logAudit("Remove reservation after cancellation for: " + reservation + " to " + identity, null);
 						}
 					}
diff --git a/src/main/java/org/olat/restapi/group/LearningGroupWebService.java b/src/main/java/org/olat/restapi/group/LearningGroupWebService.java
index fcb9a39e374..77a06c5aba3 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, null);//TODO memail
+			BusinessGroupAddResponse state = bgs.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), Collections.singletonList(identity), group, null);
 			if(state.getAddedIdentities().contains(identity)) {
 				return Response.ok().build();
 			} else if(state.getIdentitiesAlreadyInGroup().contains(identity)) {
-- 
GitLab