/** * <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.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import org.hibernate.ObjectNotFoundException; import org.hibernate.StaleObjectStateException; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.Constants; import org.olat.basesecurity.Group; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.IdentityRef; import org.olat.basesecurity.SecurityGroup; import org.olat.collaboration.CollaborationTools; import org.olat.collaboration.CollaborationToolsFactory; import org.olat.commons.info.manager.InfoMessageFrontendManager; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.id.Identity; import org.olat.core.id.Roles; import org.olat.core.logging.DBRuntimeException; import org.olat.core.logging.KnownIssueException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.ActionType; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.async.ProgressDelegate; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.mail.ContactList; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailContextImpl; import org.olat.core.util.mail.MailManager; 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.resource.OLATResourceableJustBeforeDeletedEvent; import org.olat.core.util.resource.OresHelper; import org.olat.course.assessment.manager.AssessmentModeDAO; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupAddResponse; import org.olat.group.BusinessGroupManagedFlag; import org.olat.group.BusinessGroupMembership; import org.olat.group.BusinessGroupModule; import org.olat.group.BusinessGroupOrder; import org.olat.group.BusinessGroupRef; import org.olat.group.BusinessGroupService; import org.olat.group.BusinessGroupShort; import org.olat.group.DeletableGroupData; 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.BusinessGroupEnvironment; import org.olat.group.model.BusinessGroupMembershipChange; import org.olat.group.model.BusinessGroupMembershipImpl; import org.olat.group.model.BusinessGroupMembershipViewImpl; import org.olat.group.model.BusinessGroupMembershipsChanges; import org.olat.group.model.BusinessGroupQueryParams; import org.olat.group.model.BusinessGroupRelationModified; import org.olat.group.model.BusinessGroupRow; import org.olat.group.model.EnrollState; import org.olat.group.model.IdentityGroupKey; import org.olat.group.model.LeaveOption; import org.olat.group.model.MembershipModification; import org.olat.group.model.OpenBusinessGroupRow; import org.olat.group.model.SearchBusinessGroupParams; import org.olat.group.model.StatisticsBusinessGroupRow; import org.olat.group.right.BGRightManager; import org.olat.group.right.BGRightsRole; import org.olat.group.ui.BGMailHelper; import org.olat.group.ui.edit.BusinessGroupModifiedEvent; import org.olat.properties.PropertyManager; import org.olat.repository.LeavingStatusList; import org.olat.repository.RepositoryDeletionModule; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.olat.repository.RepositoryEntryRelationType; import org.olat.repository.RepositoryEntryShort; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryService; import org.olat.repository.manager.RepositoryEntryRelationDAO; import org.olat.repository.model.RepositoryEntryToGroupRelation; import org.olat.repository.model.SearchRepositoryEntryParameters; import org.olat.resource.OLATResource; import org.olat.resource.accesscontrol.ResourceReservation; import org.olat.resource.accesscontrol.manager.ACReservationDAO; import org.olat.user.UserDataDeletable; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ @Service("businessGroupService") public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataDeletable { private final OLog log = Tracing.createLoggerFor(BusinessGroupServiceImpl.class); @Autowired private BGAreaManager areaManager; @Autowired private BGRightManager rightManager; @Autowired private BusinessGroupModule groupModule; @Autowired private BusinessGroupDAO businessGroupDAO; @Autowired private RepositoryService repositoryService; @Autowired private RepositoryManager repositoryManager; @Autowired private PropertyManager propertyManager; @Autowired private BaseSecurity securityManager; @Autowired private ContactDAO contactDao; @Autowired private AssessmentModeDAO assessmentModeDao; @Autowired private BusinessGroupRelationDAO businessGroupRelationDAO; @Autowired private RepositoryEntryRelationDAO repositoryEntryRelationDao; @Autowired private RepositoryDeletionModule deletionManager; @Autowired private NotificationsManager notificationsManager; @Autowired private InfoMessageFrontendManager infoMessageManager; @Autowired private MailManager mailManager; @Autowired private ACReservationDAO reservationDao; @Autowired private DB dbInstance; @Override public void deleteUserData(Identity identity, String newDeletedUserName, File archivePath) { // remove as Participant List<BusinessGroup> attendedGroups = findBusinessGroupsAttendedBy(identity); for (Iterator<BusinessGroup> iter = attendedGroups.iterator(); iter.hasNext();) { businessGroupRelationDAO.removeRole(identity, iter.next(), GroupRoles.participant.name()); } log.debug("Remove partipiciant identity=" + identity + " from " + attendedGroups.size() + " groups"); // remove from waitinglist List<BusinessGroup> waitingGroups = findBusinessGroupsWithWaitingListAttendedBy(identity, null); for (Iterator<BusinessGroup> iter = waitingGroups.iterator(); iter.hasNext();) { businessGroupRelationDAO.removeRole(identity, iter.next(), GroupRoles.waiting.name()); } log.debug("Remove from waiting-list identity=" + identity + " in " + waitingGroups.size() + " groups"); // remove as owner List<BusinessGroup> ownerGroups = findBusinessGroupsOwnedBy(identity); for (Iterator<BusinessGroup> iter = ownerGroups.iterator(); iter.hasNext();) { BusinessGroup businessGroup = iter.next(); businessGroupRelationDAO.removeRole(identity, businessGroup, GroupRoles.coach.name()); if (businessGroupRelationDAO.countRoles(businessGroup, GroupRoles.coach.name()) == 0) { Identity admin = deletionManager.getAdminUserIdentity(); businessGroupRelationDAO.addRole(admin, businessGroup, GroupRoles.coach.name()); log.info("Delete user-data, add Administrator-identity as owner of businessGroup=" + businessGroup.getName()); } } log.debug("Remove owner identity=" + identity + " from " + ownerGroups.size() + " groups"); log.debug("All entries in groups deleted for identity=" + identity); } @Override public BusinessGroup createBusinessGroup(Identity creator, String name, String description, Integer minParticipants, Integer maxParticipants, boolean waitingListEnabled, boolean autoCloseRanksEnabled, RepositoryEntry re) { return createBusinessGroup(creator, name, description, null, null, minParticipants, maxParticipants, waitingListEnabled, autoCloseRanksEnabled, re); } @Override public BusinessGroup createBusinessGroup(Identity creator, String name, String description, String externalId, String managedFlags, Integer minParticipants, Integer maxParticipants, boolean waitingListEnabled, boolean autoCloseRanksEnabled, RepositoryEntry re) { if("".equals(managedFlags) || "none".equals(managedFlags)) { managedFlags = null; } BusinessGroup group = businessGroupDAO.createAndPersist(creator, name, description, externalId, managedFlags, minParticipants, maxParticipants, waitingListEnabled, autoCloseRanksEnabled, false, false, false); if(re != null) { addResourceTo(group, re); } return group; } @Override public BusinessGroup updateBusinessGroup(Identity ureqIdentity, BusinessGroup group, String name, String description, String externalId, String managedFlags, Integer minParticipants, Integer maxParticipants) { BusinessGroup bg = businessGroupDAO.loadForUpdate(group); Integer previousMaxParticipants = bg.getMaxParticipants(); bg.setName(name); bg.setDescription(description); bg.setMaxParticipants(maxParticipants); bg.setMinParticipants(minParticipants); bg.setLastUsage(new Date(System.currentTimeMillis())); //strip if("none".equals(managedFlags) || "".equals(managedFlags)) { managedFlags = null; } bg.setManagedFlagsString(managedFlags); bg.setExternalId(externalId); //auto rank if possible List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); autoRankCheck(ureqIdentity, bg, previousMaxParticipants, events); BusinessGroup updatedGroup = businessGroupDAO.merge(bg); dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return updatedGroup; } @Override public BusinessGroup updateBusinessGroup(Identity ureqIdentity, BusinessGroup group, String name, String description, Integer minParticipants, Integer maxParticipants, Boolean waitingList, Boolean autoCloseRanks) { BusinessGroup bg = businessGroupDAO.loadForUpdate(group); Integer previousMaxParticipants = bg.getMaxParticipants(); bg.setName(name); bg.setDescription(description); bg.setMaxParticipants(maxParticipants); bg.setMinParticipants(minParticipants); bg.setWaitingListEnabled(waitingList); bg.setAutoCloseRanksEnabled(autoCloseRanks); bg.setLastUsage(new Date(System.currentTimeMillis())); //auto rank if possible List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); autoRankCheck(ureqIdentity, bg, previousMaxParticipants, events); BusinessGroup mergedGroup = businessGroupDAO.merge(bg); //prevents lazy loading issues mergedGroup.getBaseGroup().getKey(); dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return mergedGroup; } private void autoRankCheck(Identity identity, BusinessGroup updatedGroup, Integer previousMaxParticipants, List<BusinessGroupModifiedEvent.Deferred> events) { if(updatedGroup.getWaitingListEnabled() == null || !updatedGroup.getWaitingListEnabled().booleanValue() || updatedGroup.getAutoCloseRanksEnabled() == null || !updatedGroup.getAutoCloseRanksEnabled().booleanValue()) { //do not check further, no waiting list, no automatic ranks return; } int currentMaxNumber = updatedGroup.getMaxParticipants() == null || updatedGroup.getMaxParticipants().intValue() <= 0 ? -1 : updatedGroup.getMaxParticipants().intValue(); int previousMaxNumber = previousMaxParticipants == null || previousMaxParticipants.intValue() <= 0 ? -1 : previousMaxParticipants.intValue(); if(currentMaxNumber > previousMaxNumber) { //I can rank up some users transferFirstIdentityFromWaitingToParticipant(identity, updatedGroup, null, events); } } @Override public BusinessGroup updateDisplayMembers(BusinessGroup group, boolean ownersIntern, boolean participantsIntern, boolean waitingListIntern, boolean ownersPublic, boolean participantsPublic, boolean waitingListPublic, boolean download) { BusinessGroup reloadedBusinessGroup = businessGroupDAO.loadForUpdate(group); BusinessGroup mergedGroup = null; if(reloadedBusinessGroup != null) { reloadedBusinessGroup.setOwnersVisibleIntern(ownersIntern); reloadedBusinessGroup.setOwnersVisiblePublic(ownersPublic); reloadedBusinessGroup.setParticipantsVisibleIntern(participantsIntern); reloadedBusinessGroup.setParticipantsVisiblePublic(participantsPublic); reloadedBusinessGroup.setWaitingListVisibleIntern(waitingListIntern); reloadedBusinessGroup.setWaitingListVisiblePublic(waitingListPublic); reloadedBusinessGroup.setDownloadMembersLists(download); mergedGroup = businessGroupDAO.merge(reloadedBusinessGroup); //prevent lazy loading issues mergedGroup.getBaseGroup().getKey(); } dbInstance.commit(); return mergedGroup; } @Override public BusinessGroup updateAllowToLeaveBusinessGroup(BusinessGroup group, boolean allow) { BusinessGroup reloadedBusinessGroup = businessGroupDAO.loadForUpdate(group); BusinessGroup mergedGroup = null; if(reloadedBusinessGroup != null) { reloadedBusinessGroup.setAllowToLeave(allow); mergedGroup = businessGroupDAO.merge(reloadedBusinessGroup); //prevent lazy loading issues mergedGroup.getBaseGroup().getKey(); } dbInstance.commit(); return mergedGroup; } @Override public BusinessGroup setLastUsageFor(final Identity identity, final BusinessGroup group) { BusinessGroup reloadedBusinessGroup = businessGroupDAO.loadForUpdate(group); BusinessGroup mergedGroup = null; if(reloadedBusinessGroup != null) { reloadedBusinessGroup.setLastUsage(new Date()); if(identity != null) { businessGroupRelationDAO.touchMembership(identity, group); } mergedGroup = businessGroupDAO.merge(reloadedBusinessGroup); } dbInstance.commit(); return mergedGroup; } @Override public BusinessGroup loadBusinessGroup(BusinessGroup group) { return businessGroupDAO.load(group.getKey()); } @Override public BusinessGroup loadBusinessGroup(Long key) { return businessGroupDAO.load(key); } @Override public BusinessGroup loadBusinessGroup(OLATResource resource) { return businessGroupDAO.load(resource.getResourceableId()); } @Override public List<BusinessGroup> loadBusinessGroups(Collection<Long> keys) { return businessGroupDAO.load(keys); } @Override public List<BusinessGroupShort> loadShortBusinessGroups(Collection<Long> keys) { return businessGroupDAO.loadShort(keys); } @Override public List<BusinessGroup> loadAllBusinessGroups() { return businessGroupDAO.loadAll(); } @Override public BusinessGroup copyBusinessGroup(Identity identity, BusinessGroup sourceBusinessGroup, String targetName, String targetDescription, Integer targetMin, Integer targetMax, boolean copyAreas, boolean copyCollabToolConfig, boolean copyRights, boolean copyOwners, boolean copyParticipants, boolean copyMemberVisibility, boolean copyWaitingList, boolean copyRelations) { // 1. create group, set waitingListEnabled, enableAutoCloseRanks like source business-group BusinessGroup newGroup = createBusinessGroup(null, targetName, targetDescription, targetMin, targetMax, sourceBusinessGroup.getWaitingListEnabled(), sourceBusinessGroup.getAutoCloseRanksEnabled(), null); // return immediately with null value to indicate an already take groupname if (newGroup == null) { return null; } // 2. copy tools if (copyCollabToolConfig) { CollaborationToolsFactory toolsF = CollaborationToolsFactory.getInstance(); // get collab tools from original group and the new group CollaborationTools oldTools = toolsF.getOrCreateCollaborationTools(sourceBusinessGroup); CollaborationTools newTools = toolsF.getOrCreateCollaborationTools(newGroup); // copy the collab tools settings String[] availableTools = CollaborationToolsFactory.getInstance().getAvailableTools().clone(); for (int i = 0; i < availableTools.length; i++) { String tool = availableTools[i]; newTools.setToolEnabled(tool, oldTools.isToolEnabled(tool)); } String oldNews = oldTools.lookupNews(); newTools.saveNews(oldNews); } // 3. copy member visibility if (copyMemberVisibility) { newGroup.setOwnersVisibleIntern(sourceBusinessGroup.isOwnersVisibleIntern()); newGroup.setOwnersVisiblePublic(sourceBusinessGroup.isOwnersVisiblePublic()); newGroup.setParticipantsVisibleIntern(sourceBusinessGroup.isParticipantsVisibleIntern()); newGroup.setParticipantsVisiblePublic(sourceBusinessGroup.isParticipantsVisiblePublic()); newGroup.setWaitingListVisibleIntern(sourceBusinessGroup.isWaitingListVisibleIntern()); newGroup.setWaitingListVisiblePublic(sourceBusinessGroup.isWaitingListVisiblePublic()); newGroup.setDownloadMembersLists(sourceBusinessGroup.isDownloadMembersLists()); } // 4. copy areas if (copyAreas) { List<BGArea> areas = areaManager.findBGAreasOfBusinessGroup(sourceBusinessGroup); for(BGArea area : areas) { // reference target group to source groups areas areaManager.addBGToBGArea(newGroup, area); } } // 5. copy owners if (copyOwners) { List<Identity> owners = businessGroupRelationDAO.getMembers(sourceBusinessGroup, GroupRoles.coach.name()); if(owners.isEmpty()) { businessGroupRelationDAO.addRole(identity, newGroup, GroupRoles.coach.name()); } else { for (Identity owner:owners) { businessGroupRelationDAO.addRole(owner, newGroup, GroupRoles.coach.name()); } } } else { businessGroupRelationDAO.addRole(identity, newGroup, GroupRoles.coach.name()); } // 6. copy participants if (copyParticipants) { List<Identity> participants = businessGroupRelationDAO.getMembers(sourceBusinessGroup, GroupRoles.participant.name()); for(Identity participant:participants) { businessGroupRelationDAO.addRole(participant, newGroup, GroupRoles.participant.name()); } } // 7. copy rights if (copyRights) { List<String> participantRights = rightManager.findBGRights(sourceBusinessGroup, BGRightsRole.participant); for (String sourceRight:participantRights) { rightManager.addBGRight(sourceRight, newGroup, BGRightsRole.participant); } List<String> tutorRights = rightManager.findBGRights(sourceBusinessGroup, BGRightsRole.tutor); for (String sourceRight:tutorRights) { rightManager.addBGRight(sourceRight, newGroup, BGRightsRole.tutor); } } // 8. copy waiting-lisz if (copyWaitingList) { List<Identity> waitingList = getMembers(sourceBusinessGroup, GroupRoles.waiting.name()); for (Identity waiting:waitingList) { businessGroupRelationDAO.addRole(waiting, newGroup, GroupRoles.waiting.name()); } } //9. copy relations if(copyRelations) { List<RepositoryEntry> resources = businessGroupRelationDAO.findRepositoryEntries(Collections.singletonList(sourceBusinessGroup), 0, -1); addResourcesTo(Collections.singletonList(newGroup), resources); } return newGroup; } @Override public BusinessGroup mergeBusinessGroups(final Identity ureqIdentity, BusinessGroup targetGroup, final List<BusinessGroup> groupsToMerge, MailPackage mailing) { groupsToMerge.remove(targetGroup);//to be sure Roles ureqRoles = securityManager.getRoles(ureqIdentity); targetGroup = businessGroupDAO.loadForUpdate(targetGroup); Set<Identity> currentOwners = new HashSet<Identity>(businessGroupRelationDAO.getMembers(targetGroup, GroupRoles.coach.name())); Set<Identity> currentParticipants = new HashSet<Identity>(businessGroupRelationDAO.getMembers(targetGroup, GroupRoles.participant.name())); Set<Identity> currentWaiters = new HashSet<Identity>(businessGroupRelationDAO.getMembers(targetGroup, GroupRoles.waiting.name())); Set<Identity> newOwners = new HashSet<Identity>(); Set<Identity> newParticipants = new HashSet<Identity>(); Set<Identity> newWaiters = new HashSet<Identity>(); //collect the owners for(BusinessGroup group:groupsToMerge) { List<Identity> owners = businessGroupRelationDAO.getMembers(group, GroupRoles.coach.name()); owners.removeAll(currentOwners); newOwners.addAll(owners); } //collect the participants but test if they are not already owners for(BusinessGroup group:groupsToMerge) { List<Identity> participants = businessGroupRelationDAO.getMembers(group, GroupRoles.participant.name()); participants.removeAll(currentParticipants); for(Identity participant:participants) { if(!newOwners.contains(participant)) { newParticipants.add(participant); } } } //collect the waiting list but test if they are not already owners or participants for(BusinessGroup group:groupsToMerge) { List<Identity> waitingList = businessGroupRelationDAO.getMembers(group, GroupRoles.waiting.name()); waitingList.removeAll(currentWaiters); for(Identity waiter:waitingList) { if(!newOwners.contains(waiter) && !newParticipants.contains(waiter)) { newWaiters.add(waiter); } } } List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for(Identity newOwner:newOwners) { addOwner(ureqIdentity, ureqRoles, newOwner, targetGroup, mailing, events); } for(Identity newParticipant:newParticipants) { addParticipant(ureqIdentity, ureqRoles, newParticipant, targetGroup, mailing, events); } for(Identity newWaiter:newWaiters) { addToWaitingList(ureqIdentity, newWaiter, targetGroup, mailing, events); } for(BusinessGroup group:groupsToMerge) { deleteBusinessGroup(group); } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return targetGroup; } @Override 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, mailing); } } private void updateMembers(Identity ureqIdentity, Roles ureqRoles, MembershipModification membersMod, BusinessGroup group, MailPackage mailing) { group = businessGroupDAO.loadForUpdate(group); List<Identity> currentOwners = businessGroupRelationDAO.getMembers(group, GroupRoles.coach.name()); List<Identity> currentParticipants = businessGroupRelationDAO.getMembers(group, GroupRoles.participant.name()); List<Identity> currentWaitingList = businessGroupRelationDAO.getMembers(group, GroupRoles.waiting.name()); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for(Identity owner:membersMod.getAddOwners()) { if(!currentOwners.contains(owner)) { addOwner(ureqIdentity, ureqRoles, owner, group, mailing, events); } } for(Identity participant:membersMod.getAddParticipants()) { if(!currentParticipants.contains(participant)) { addParticipant(ureqIdentity, ureqRoles, participant, group, mailing, events); } } for(Identity waitingIdentity:membersMod.getAddToWaitingList()) { if(!currentWaitingList.contains(waitingIdentity)) { addToWaitingList(ureqIdentity, waitingIdentity, group, mailing, events); } } for(Identity removed:membersMod.getRemovedIdentities()) { if(currentOwners.contains(removed)) { removeOwner(ureqIdentity, removed, group, events); } if(currentParticipants.contains(removed)) { removeParticipant(ureqIdentity, removed, group, mailing, events); } if(currentWaitingList.contains(removed)) { removeFromWaitingList(ureqIdentity, removed, group, mailing, events); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override 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) { BusinessGroupMembershipsChanges changesWrapper; if(changesMap.containsKey(change.getGroupKey())) { changesWrapper = changesMap.get(change.getGroupKey()); } else { changesWrapper = new BusinessGroupMembershipsChanges(); changesMap.put(change.getGroupKey(), changesWrapper); } Identity id = change.getMember(); if(change.getTutor() != null) { if(change.getTutor().booleanValue()) { changesWrapper.addTutors.add(id); } else { changesWrapper.removeTutors.add(id); } } if(change.getParticipant() != null) { if(change.getParticipant().booleanValue()) { changesWrapper.addParticipants.add(id); } else { changesWrapper.removeParticipants.add(id); } } if(change.getWaitingList() != null) { if(change.getWaitingList().booleanValue()) { changesWrapper.addToWaitingList.add(id); } else { changesWrapper.removeFromWaitingList.add(id); } } } List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); List<BusinessGroup> groups = loadBusinessGroups(changesMap.keySet()); for(BusinessGroup group:groups) { BusinessGroupMembershipsChanges changesWrapper = changesMap.get(group.getKey()); group = businessGroupDAO.loadForUpdate(group); for(Identity id:changesWrapper.addToWaitingList) { addToWaitingList(ureqIdentity, id, group, mailing, events); } for(Identity id:changesWrapper.removeFromWaitingList) { removeFromWaitingList(ureqIdentity, id, group, mailing, events); } for(Identity id:changesWrapper.addTutors) { addOwner(ureqIdentity, ureqRoles, id, group, mailing, events); } for(Identity id:changesWrapper.removeTutors) { removeOwner(ureqIdentity, id, group, events); } for(Identity id:changesWrapper.addParticipants) { addParticipant(ureqIdentity, ureqRoles, id, group, mailing, events); } for(Identity id:changesWrapper.removeParticipants) { removeParticipant(ureqIdentity, id, group, mailing, events); } //release lock dbInstance.commit(); } BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override public List<BusinessGroup> findBusinessGroups(Identity identity, int maxResults, BusinessGroupOrder... orderBy) { return businessGroupDAO.findBusinessGroup(identity, maxResults, orderBy); } @Override public List<BusinessGroup> findBusinessGroupsOwnedBy(Identity identity) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(identity, true, false); return businessGroupDAO.findBusinessGroups(params, null, 0, -1); } @Override public List<BusinessGroup> findBusinessGroupsAttendedBy(Identity identity) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(identity, false, true); return businessGroupDAO.findBusinessGroups(params, null, 0, -1); } @Override public List<BusinessGroup> findBusinessGroupsWithWaitingListAttendedBy(Identity identity, RepositoryEntryRef resource) { return businessGroupDAO.findBusinessGroupsWithWaitingListAttendedBy(identity, resource); } @Override public int countBusinessGroups(SearchBusinessGroupParams params, RepositoryEntryRef resource) { if(params == null) { params = new SearchBusinessGroupParams(); } return businessGroupDAO.countBusinessGroups(params, resource); } @Override public List<BusinessGroup> findBusinessGroups(SearchBusinessGroupParams params, RepositoryEntryRef resource, int firstResult, int maxResults, BusinessGroupOrder... ordering) { if(params == null) { params = new SearchBusinessGroupParams(); } return businessGroupDAO.findBusinessGroups(params, resource, firstResult, maxResults); } @Override public List<BusinessGroupRow> findBusinessGroupsWithMemberships(BusinessGroupQueryParams params, IdentityRef identity) { return businessGroupDAO.searchBusinessGroupsWithMemberships(params, identity); } @Override public List<StatisticsBusinessGroupRow> findBusinessGroupsFromRepositoryEntry(BusinessGroupQueryParams params, IdentityRef identity, RepositoryEntryRef entry) { return businessGroupDAO.searchBusinessGroupsForRepositoryEntry(params, identity, entry); } @Override public List<StatisticsBusinessGroupRow> findBusinessGroupsForSelection(BusinessGroupQueryParams params, IdentityRef identity) { return businessGroupDAO.searchBusinessGroupsForSelection(params, identity); } @Override public List<StatisticsBusinessGroupRow> findBusinessGroupsStatistics(BusinessGroupQueryParams params) { return businessGroupDAO.searchBusinessGroupsStatistics(params); } @Override public List<OpenBusinessGroupRow> findPublishedBusinessGroups(BusinessGroupQueryParams params, IdentityRef identity) { return businessGroupDAO.searchPublishedBusinessGroups(params, identity); } @Override public List<Long> toGroupKeys(String groupNames, RepositoryEntryRef resource) { return businessGroupRelationDAO.toGroupKeys(groupNames, resource); } @Override public int countContacts(Identity identity) { return contactDao.countContacts(identity); } @Override public List<Identity> findContacts(Identity identity, int firstResult, int maxResults) { return contactDao.findContacts(identity, firstResult, maxResults); } @Override public void deleteBusinessGroup(BusinessGroup group) { try{ OLATResourceableJustBeforeDeletedEvent delEv = new OLATResourceableJustBeforeDeletedEvent(group); // notify all (currently running) BusinessGroupXXXcontrollers // about the deletion which will occur. CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(delEv, group); // refresh object to avoid stale object exceptions group = loadBusinessGroup(group); // 0) Loop over all deletableGroupData Map<String,DeletableGroupData> deleteListeners = CoreSpringFactory.getBeansOfType(DeletableGroupData.class); for (DeletableGroupData deleteListener : deleteListeners.values()) { if(log.isDebug()) { log.debug("deleteBusinessGroup: call deleteListener=" + deleteListener); } deleteListener.deleteGroupDataFor(group); } // 1) Delete all group properties CollaborationTools ct = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(group); ct.deleteTools(group);// deletes everything concerning properties&collabTools // 1.c)delete user in security groups //removeFromRepositoryEntrySecurityGroup(group); // 2) Delete the group areas areaManager.deleteBGtoAreaRelations(group); // 3) Delete the relations businessGroupRelationDAO.deleteRelationsToRepositoryEntry(group); assessmentModeDao.deleteAssessmentModesToGroup(group); // 4) delete properties propertyManager.deleteProperties(null, group, null, null, null); propertyManager.deleteProperties(null, null, group, null, null); // 5) delete the publisher attached to this group (e.g. the forum and folder // publisher) notificationsManager.deletePublishersOf(group); // 6) delete info messages and subscription context associated with this group infoMessageManager.removeInfoMessagesAndSubscriptionContext(group); // 7) the group businessGroupDAO.delete(group); // 8) delete the associated security groups //TODO group dbInstance.commit(); log.audit("Deleted Business Group", group.toString()); } catch(DBRuntimeException dbre) { Throwable th = dbre.getCause(); if ((th instanceof ObjectNotFoundException) && th.getMessage().contains("org.olat.group.BusinessGroupImpl")) { //group already deleted return; } if ((th instanceof StaleObjectStateException) && (th.getMessage().startsWith("Row was updated or deleted by another transaction"))) { // known issue OLAT-3654 log.info("Group was deleted by another user in the meantime. Known issue OLAT-3654"); throw new KnownIssueException("Group was deleted by another user in the meantime", 3654); } else { throw dbre; } } } @Override public MailerResult deleteBusinessGroupWithMail(BusinessGroup businessGroupTodelete, String businessPath, Identity deletedBy, Locale locale) { List<Identity> users = businessGroupRelationDAO.getMembers(businessGroupTodelete, GroupRoles.coach.name(), GroupRoles.participant.name(), GroupRoles.waiting.name()); // now delete the group first deleteBusinessGroup(businessGroupTodelete); dbInstance.commit(); // finally send email MailTemplate mailTemplate = BGMailHelper.createDeleteGroupMailTemplate(businessGroupTodelete, deletedBy); if (mailTemplate != null) { String metaId = UUID.randomUUID().toString(); MailContext context = new MailContextImpl(businessPath); MailerResult result = new MailerResult(); MailBundle[] bundles = mailManager.makeMailBundles(context, users, mailTemplate, null, metaId, result); result.append(mailManager.sendMessage(bundles)); return result; } return null; } @Override public List<Identity> getMembersOf(RepositoryEntryRef resource, boolean owner, boolean attendee) { return businessGroupRelationDAO.getMembersOf(resource, owner, attendee); } @Override public void leave(Identity identity, RepositoryEntry entry, LeavingStatusList status, MailPackage mailing) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(); params.setIdentity(identity); params.setAttendee(true); List<BusinessGroup> groups = businessGroupDAO.findBusinessGroups(params, entry, 0, -1); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for(BusinessGroup group:groups) { if(BusinessGroupManagedFlag.isManaged(group, BusinessGroupManagedFlag.membersmanagement)) { status.setWarningManagedGroup(true); } else if(businessGroupRelationDAO.countResources(group) > 1) { status.setWarningGroupWithMultipleResources(true); } else { removeParticipant(identity, identity, group, mailing, null); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override public BusinessGroupAddResponse addOwners(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities, BusinessGroup group, MailPackage mailing) { BusinessGroupAddResponse response = new BusinessGroupAddResponse(); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for (Identity identity : addIdentities) { group = loadBusinessGroup(group); // reload business group if (securityManager.isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_GUESTONLY)) { response.getIdentitiesWithoutPermission().add(identity); } else if(addOwner(ureqIdentity, ureqRoles, identity, group, mailing, events)) { response.getAddedIdentities().add(identity); } else { response.getIdentitiesAlreadyInGroup().add(identity); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return response; } private boolean addOwner(Identity ureqIdentity, Roles ureqRoles, Identity identityToAdd, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { if (!businessGroupRelationDAO.hasRole(identityToAdd, group, GroupRoles.coach.name())) { boolean mustAccept = true; if(ureqIdentity != null && ureqIdentity.equals(identityToAdd)) { mustAccept = false;//adding itself, we hope that he knows what he makes } else if(ureqRoles == null || ureqIdentity == null) { mustAccept = false;//administrative task } else { mustAccept = groupModule.isAcceptMembership(ureqRoles); } if(mustAccept) { ResourceReservation olderReservation = reservationDao.loadReservation(identityToAdd, group.getResource()); if(olderReservation == null) { Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, 6); Date expiration = cal.getTime(); ResourceReservation reservation = reservationDao.createReservation(identityToAdd, "group_coach", expiration, group.getResource()); if(reservation != null) { BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addCoach, mailing); // logging log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to group with key " + group.getKey()); } } } else { internalAddCoach(ureqIdentity, identityToAdd, group, events); BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addCoach, mailing); } return true; } return false; } private void internalAddCoach(Identity ureqIdentity, Identity identityToAdd, BusinessGroup group, List<BusinessGroupModifiedEvent.Deferred> events) { businessGroupRelationDAO.addRole(identityToAdd, group, GroupRoles.coach.name()); // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identityToAdd); if(events != null) { events.add(event); } // do logging ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_ADDED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToAdd)); log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to group with key " + group.getKey()); } private boolean addParticipant(Identity ureqIdentity, Roles ureqRoles, Identity identityToAdd, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { if(!businessGroupRelationDAO.hasRole(identityToAdd, group, GroupRoles.participant.name())) { boolean mustAccept = true; if(ureqIdentity != null && ureqIdentity.equals(identityToAdd)) { mustAccept = false;//adding itself, we hope that he knows what he makes } else if(ureqRoles == null || ureqIdentity == null) { mustAccept = false;//administrative task } else { mustAccept = groupModule.isAcceptMembership(ureqRoles); } if(mustAccept) { ResourceReservation olderReservation = reservationDao.loadReservation(identityToAdd, group.getResource()); if(olderReservation == null) { Calendar cal = Calendar.getInstance(); cal.add(Calendar.MONTH, 6); Date expiration = cal.getTime(); ResourceReservation reservation = reservationDao.createReservation(identityToAdd, "group_participant", expiration, group.getResource()); if(reservation != null) { BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addParticipant, mailing); } } } else { internalAddParticipant(ureqIdentity, identityToAdd, group, events); BusinessGroupMailing.sendEmail(ureqIdentity, identityToAdd, group, MailType.addParticipant, mailing); } return true; } return false; } /** * this method is for internal usage only. It add the identity to to group without synchronization or checks! * @param ureqIdentity * @param ureqRoles * @param identityToAdd * @param group * @param syncIM */ private void internalAddParticipant(Identity ureqIdentity, Identity identityToAdd, BusinessGroup group, List<BusinessGroupModifiedEvent.Deferred> events) { businessGroupRelationDAO.addRole(identityToAdd, group, GroupRoles.participant.name()); // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identityToAdd); if(events != null) { events.add(event); } // do logging ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_ADDED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToAdd)); log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identityToAdd.getName() + "' to group with key " + group.getKey()); // send notification mail in your controller! } @Override public BusinessGroupAddResponse addParticipants(Identity ureqIdentity, Roles ureqRoles, List<Identity> addIdentities, BusinessGroup group, MailPackage mailing) { BusinessGroupAddResponse response = new BusinessGroupAddResponse(); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); BusinessGroup currBusinessGroup = businessGroupDAO.loadForUpdate(group); 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, mailing, events)) { response.getAddedIdentities().add(identity); } else { response.getIdentitiesAlreadyInGroup().add(identity); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return response; } @Override public void cancelPendingParticipation(Identity ureqIdentity, ResourceReservation reservation) { if(reservation != null && "BusinessGroup".equals(reservation.getResource().getResourceableTypeName())) { BusinessGroup group = businessGroupDAO.loadForUpdate(reservation.getResource().getResourceableId()); if(group != null) { List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); transferFirstIdentityFromWaitingToParticipant(ureqIdentity, group, null, events); dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } } } @Override public void acceptPendingParticipation(Identity ureqIdentity, Identity reservationOwner, OLATResource resource) { ResourceReservation reservation = reservationDao.loadReservation(reservationOwner, resource); if(reservation != null && "BusinessGroup".equals(resource.getResourceableTypeName())) { BusinessGroup group = businessGroupDAO.loadForUpdate(resource.getResourceableId()); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); if(group != null) { String type = reservation.getType(); if("group_coach".equals(type)) { if(!businessGroupRelationDAO.hasRole(reservationOwner, group, GroupRoles.coach.name())) { internalAddCoach(ureqIdentity, reservationOwner, group, events); } } else if("group_participant".equals(type)) { if(!businessGroupRelationDAO.hasRole(reservationOwner, group, GroupRoles.participant.name())) { internalAddParticipant(ureqIdentity, reservationOwner, group, events); } } } reservationDao.deleteReservation(reservation); dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } } private void removeParticipant(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { boolean removed = businessGroupRelationDAO.removeRole(identity, group, GroupRoles.participant.name()); if(removed) { // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity); if(events != null) { events.add(event); } // do logging ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_PARTICIPANT_REMOVED, getClass(), LoggingResourceable.wrap(identity), LoggingResourceable.wrap(group)); log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName() + "' from group with key " + group.getKey()); // Check if a waiting-list with auto-close-ranks is configurated if ( group.getWaitingListEnabled().booleanValue() && group.getAutoCloseRanksEnabled().booleanValue() ) { // even when doOnlyPostRemovingStuff is set to true we really transfer the first Identity here transferFirstIdentityFromWaitingToParticipant(ureqIdentity, group, null, events); } // send mail BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.removeParticipant, mailing); } } @Override public LeaveOption isAllowToLeaveBusinessGroup(Identity identity, BusinessGroup group) { LeaveOption opt; if(groupModule.isAllowLeavingGroupOverride()) { if(group.isAllowToLeave()) { opt = new LeaveOption(); } else { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } } else if(groupModule.isAllowLeavingGroupCreatedByAuthors() && groupModule.isAllowLeavingGroupCreatedByLearners()) { opt = new LeaveOption(); } else if(!groupModule.isAllowLeavingGroupCreatedByAuthors() && !groupModule.isAllowLeavingGroupCreatedByLearners()) { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } else { int numOfCoaches = countMembers(group, GroupRoles.coach.name()); if(numOfCoaches == 0) { int numOfResources = businessGroupRelationDAO.countResources(group); if(numOfResources > 0) { //author group if(groupModule.isAllowLeavingGroupCreatedByAuthors()) { opt = new LeaveOption(); } else { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } //learner group } else if(groupModule.isAllowLeavingGroupCreatedByLearners()) { opt = new LeaveOption(); } else { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } } else { int numOfAuthors = businessGroupRelationDAO.countAuthors(group); if(numOfAuthors > 0) { if(groupModule.isAllowLeavingGroupCreatedByAuthors()) { opt = new LeaveOption(); } else { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } } else if(groupModule.isAllowLeavingGroupCreatedByLearners()) { opt = new LeaveOption(); } else { ContactList list = getAdminContactList(group); opt = new LeaveOption(false, list); } } } return opt; } public ContactList getAdminContactList(BusinessGroup group) { ContactList list = new ContactList("Contact"); List<Identity> coaches = getMembers(group, GroupRoles.coach.name()); if(coaches.isEmpty()) { Collection<BusinessGroup> groups = Collections.singletonList(group); List<RepositoryEntry> entries = businessGroupRelationDAO.findRepositoryEntries(groups, 0, -1); for(RepositoryEntry re:entries) { coaches.addAll(repositoryService.getMembers(re, GroupRoles.coach.name())); } if(coaches.isEmpty()) { //get system administrators SecurityGroup adminSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ADMIN); List<Identity> admins = securityManager.getIdentitiesByPowerSearch(null, null, false, new SecurityGroup[]{ adminSecGroup }, null, null, null, null, null, null, Identity.STATUS_VISIBLE_LIMIT); coaches.addAll(admins); } } list.addAllIdentites(coaches); return list; } @Override public void removeParticipants(Identity ureqIdentity, List<Identity> identities, BusinessGroup group, MailPackage mailing) { group = businessGroupDAO.loadForUpdate(group); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for (Identity identity : identities) { removeParticipant(ureqIdentity, identity, group, mailing, events); } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override 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; if("BusinessGroup".equals(resource.getResourceableTypeName())) { //it's a group resource BusinessGroup group = loadBusinessGroup(resource); if(group != null) { groups = Collections.singletonList(group); } } else { RepositoryEntryRef re = repositoryManager.lookupRepositoryEntry(resource, false); groups = findBusinessGroups(null, re, 0, -1); } if(groups == null || groups.isEmpty()) { return;//nothing to do } //remove managed groups for(Iterator<BusinessGroup> groupIt=groups.iterator(); groupIt.hasNext(); ) { boolean managed = BusinessGroupManagedFlag.isManaged(groupIt.next(), BusinessGroupManagedFlag.membersmanagement); if(managed) { groupIt.remove(); } } if(groups.isEmpty()) { return;//nothing to do } List<OLATResource> groupResources = new ArrayList<OLATResource>(); Map<Long,BusinessGroup> idToGroup = new HashMap<>(); for(BusinessGroup group:groups) { groupResources.add(group.getResource()); idToGroup.put(group.getKey(), group); } final Map<Long,Identity> keyToIdentityMap = new HashMap<Long,Identity>(); for(Identity identity:identities) { keyToIdentityMap.put(identity.getKey(), identity); } List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); List<BusinessGroupMembershipViewImpl> memberships = businessGroupDAO.getMembershipInfoInBusinessGroups(groups, identities); Collections.sort(memberships, new BusinessGroupMembershipViewComparator()); BusinessGroupMembershipViewImpl nextGroupMembership = null; for(final Iterator<BusinessGroupMembershipViewImpl> itMembership=memberships.iterator(); nextGroupMembership != null || itMembership.hasNext(); ) { final BusinessGroupMembershipViewImpl currentMembership; if(nextGroupMembership == null) { currentMembership = itMembership.next(); } else { currentMembership = nextGroupMembership; nextGroupMembership = null; } Long groupKey = currentMembership.getGroupKey(); BusinessGroup nextGroup = businessGroupDAO.loadForUpdate(idToGroup.get(groupKey)); nextGroupMembership = removeGroupMembers(ureqIdentity, currentMembership, nextGroup, keyToIdentityMap, itMembership, mailing, events); //release the lock dbInstance.commit(); } List<ResourceReservation> reservations = reservationDao.loadReservations(groupResources); for(ResourceReservation reservation:reservations) { if(identities.contains(reservation.getIdentity())) { reservationDao.deleteReservation(reservation); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } private final BusinessGroupMembershipViewImpl removeGroupMembers(Identity ureqIdentity, BusinessGroupMembershipViewImpl currentMembership, BusinessGroup currentGroup, Map<Long,Identity> keyToIdentityMap, Iterator<BusinessGroupMembershipViewImpl> itMembership, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { BusinessGroupMembershipViewImpl previsousComputedMembership = currentMembership; BusinessGroupMembershipViewImpl membership; do { if(previsousComputedMembership != null) { membership = previsousComputedMembership; previsousComputedMembership = null; } else if(itMembership.hasNext()) { membership = itMembership.next(); } else { //security, nothing to do return null; } if(currentGroup.getKey().equals(membership.getGroupKey())) { Identity id = keyToIdentityMap.get(membership.getIdentityKey()); if(GroupRoles.coach.name().equals(membership.getRole())) { removeOwner(ureqIdentity, id, currentGroup, events); } if(GroupRoles.participant.name().equals(membership.getRole())) { removeParticipant(ureqIdentity, id, currentGroup, mailing, events); } if(GroupRoles.waiting.name().equals(membership.getRole())) { removeFromWaitingList(ureqIdentity, id, currentGroup, mailing, events); } } else { return membership; } } while (itMembership.hasNext()); return null; } private void addToWaitingList(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { businessGroupRelationDAO.addRole(identity, group, GroupRoles.waiting.name()); // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, group, identity); if(events != null) { events.add(event); } // do logging ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_TO_WAITING_LIST_ADDED, getClass(), LoggingResourceable.wrap(identity)); log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identity.getName() + "' to group with key " + group.getKey()); // send mail BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.addToWaitingList, mailing); } @Override public BusinessGroupAddResponse addToWaitingList(Identity ureqIdentity, List<Identity> addIdentities, BusinessGroup group, MailPackage mailing) { BusinessGroupAddResponse response = new BusinessGroupAddResponse(); BusinessGroup currBusinessGroup = businessGroupDAO.loadForUpdate(group); // reload business group List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for (final Identity identity : addIdentities) { if (securityManager.isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_GUESTONLY)) { response.getIdentitiesWithoutPermission().add(identity); } // Check if identity is already in group. make a db query in case // someone in another workflow already added this user to this group. if // found, add user to model else { List<String> roles = businessGroupRelationDAO.getRoles(identity, currBusinessGroup); if (roles.contains(GroupRoles.waiting.name()) || roles.contains(GroupRoles.participant.name())) { response.getIdentitiesAlreadyInGroup().add(identity); } else { // identity has permission and is not already in group => add it addToWaitingList(ureqIdentity, identity, currBusinessGroup, mailing, events); response.getAddedIdentities().add(identity); } } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return response; } private final void removeFromWaitingList(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { businessGroupRelationDAO.removeRole(identity, group, GroupRoles.waiting.name()); // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identity); if(events != null) { events.add(event); } // do logging ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_FROM_WAITING_LIST_REMOVED, getClass(), LoggingResourceable.wrap(identity)); log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName() + "' from group with key " + group.getKey()); // send mail BusinessGroupMailing.sendEmail(ureqIdentity, identity, group, MailType.removeToWaitingList, mailing); } @Override public void removeFromWaitingList(Identity ureqIdentity, List<Identity> identities, BusinessGroup businessGroup, MailPackage mailing) { List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); businessGroup = businessGroupDAO.loadForUpdate(businessGroup); for (Identity identity : identities) { removeFromWaitingList(ureqIdentity, identity, businessGroup, mailing, events); } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override public int getPositionInWaitingListFor(IdentityRef identity, BusinessGroupRef businessGroup) { // get position in waiting-list List<Long> identities = businessGroupRelationDAO.getMemberKeysOrderByDate(businessGroup, GroupRoles.waiting.name()); for (int i = 0; i<identities.size(); i++) { Long waitingListIdentity = identities.get(i); if (waitingListIdentity.equals(identity.getKey()) ) { return i+1;// '+1' because list begins with 0 } } return -1; } @Override public BusinessGroupAddResponse moveIdentityFromWaitingListToParticipant(Identity ureqIdentity, List<Identity> identities, BusinessGroup currBusinessGroup, MailPackage mailing) { Roles ureqRoles = securityManager.getRoles(ureqIdentity); BusinessGroupAddResponse response = new BusinessGroupAddResponse(); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); currBusinessGroup = businessGroupDAO.loadForUpdate(currBusinessGroup); for (Identity identity : identities) { // check if identity is already in participant if (!businessGroupRelationDAO.hasRole(identity, currBusinessGroup, GroupRoles.participant.name()) ) { // Identity is not in participant-list => move identity from waiting-list to participant-list addParticipant(ureqIdentity, ureqRoles, identity, currBusinessGroup, mailing, events); removeFromWaitingList(ureqIdentity, identity, currBusinessGroup, mailing, events); response.getAddedIdentities().add(identity); // notification mail is handled in controller } else { if (businessGroupRelationDAO.hasRole(identity, currBusinessGroup, GroupRoles.waiting.name()) ) { removeFromWaitingList(ureqIdentity, identity, currBusinessGroup, mailing, events); } response.getIdentitiesAlreadyInGroup().add(identity); } } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); return response; } @Override public EnrollState enroll(Identity ureqIdentity, Roles ureqRoles, Identity identity, BusinessGroup group, MailPackage mailing) { final BusinessGroup reloadedGroup = businessGroupDAO.loadForUpdate(group); log.info("doEnroll start: group=" + OresHelper.createStringRepresenting(group), identity.getName()); EnrollState enrollStatus = new EnrollState(); List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); ResourceReservation reservation = reservationDao.loadReservation(identity, reloadedGroup.getResource()); //reservation has the highest priority over max participant or other settings if(reservation != null) { addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, mailing, events); enrollStatus.setEnrolled(BGMembership.participant); log.info("doEnroll (reservation) - setIsEnrolled ", identity.getName()); if(reservation != null) { reservationDao.deleteReservation(reservation); } } else if (reloadedGroup.getMaxParticipants() != null) { int participantsCounter = businessGroupRelationDAO.countEnrollment(reloadedGroup); int reservations = reservationDao.countReservations(reloadedGroup.getResource()); log.info("doEnroll - participantsCounter: " + participantsCounter + ", reservations: " + reservations + " maxParticipants: " + reloadedGroup.getMaxParticipants().intValue(), identity.getName()); if ((participantsCounter + reservations) >= reloadedGroup.getMaxParticipants().intValue()) { // already full, show error and updated choose page again if (reloadedGroup.getWaitingListEnabled().booleanValue()) { addToWaitingList(ureqIdentity, identity, reloadedGroup, mailing, events); enrollStatus.setEnrolled(BGMembership.waiting); } else { // No Waiting List => List is full enrollStatus.setI18nErrorMessage("error.group.full"); enrollStatus.setFailed(true); } } else { //enough place addParticipant(ureqIdentity, ureqRoles, identity, reloadedGroup, mailing, events); 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, mailing, events); enrollStatus.setEnrolled(BGMembership.participant); if (log.isDebug()) log.debug("doEnroll as participant committed"); } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); log.info("doEnroll end", identity.getName()); 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, List<BusinessGroupModifiedEvent.Deferred> events) { // Check if waiting-list is enabled and auto-rank-up if (group.getWaitingListEnabled() != null && group.getWaitingListEnabled().booleanValue() && group.getAutoCloseRanksEnabled() != null && group.getAutoCloseRanksEnabled().booleanValue()) { // Check if participant is not full Integer maxSize = group.getMaxParticipants(); int reservations = reservationDao.countReservations(group.getResource()); int partipiciantSize = businessGroupRelationDAO.countRoles(group, GroupRoles.participant.name()); if (maxSize != null && (partipiciantSize + reservations) < maxSize.intValue()) { // ok it has free places => get first identities from waiting list List<Identity> identities = businessGroupRelationDAO.getMembersOrderByDate(group, GroupRoles.waiting.name()); int counter = 0; int freeSlot = maxSize - (partipiciantSize + reservations); for(Identity firstWaitingListIdentity: identities) { if(counter >= freeSlot) { break; } // It has an identity and transfer from waiting-list to participant-group is not done // Check if firstWaitingListIdentity is not allready in participant-group if (!businessGroupRelationDAO.hasRole(firstWaitingListIdentity, group, GroupRoles.participant.name())) { // move the identity from the waitinglist to the participant group ActionType formerStickyActionType = ThreadLocalUserActivityLogger.getStickyActionType(); try{ // OLAT-4955: force add-participant and remove-from-waitinglist logging actions // 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); // Don't send mails for the sub-actions "adding to group" and "remove from waiting list", instead // send a specific "graduate from waiting list" mailing a few lines below MailPackage subMailing = new MailPackage(false); addParticipant(ureqIdentity, null, firstWaitingListIdentity, group, subMailing, events); removeFromWaitingList(ureqIdentity, firstWaitingListIdentity, group, subMailing, events); } finally { ThreadLocalUserActivityLogger.setStickyActionType(formerStickyActionType); } // Send mail to let user know he is now in group if (mailing == null) { mailing = new MailPackage(true); } BusinessGroupMailing.sendEmail(null, firstWaitingListIdentity, group, MailType.graduateFromWaitingListToParticpant, mailing); counter++; } } } } } private void removeOwner(Identity ureqIdentity, Identity identityToRemove, BusinessGroup group, List<BusinessGroupModifiedEvent.Deferred> events) { businessGroupRelationDAO.removeRole(identityToRemove, group, GroupRoles.coach.name()); // notify currently active users of this business group BusinessGroupModifiedEvent.Deferred event; if (identityToRemove.getKey().equals(ureqIdentity.getKey()) ) { event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.MYSELF_ASOWNER_REMOVED_EVENT, group, identityToRemove); } else { event = BusinessGroupModifiedEvent.createDeferredEvent(BusinessGroupModifiedEvent.IDENTITY_REMOVED_EVENT, group, identityToRemove); } if(events != null) { events.add(event); } // do logging log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identiy '" + identityToRemove.getName() + "' from group with key " + group.getKey()); ThreadLocalUserActivityLogger.log(GroupLoggingAction.GROUP_OWNER_REMOVED, getClass(), LoggingResourceable.wrap(group), LoggingResourceable.wrap(identityToRemove)); } @Override public void removeOwners(Identity ureqIdentity, Collection<Identity> identitiesToRemove, BusinessGroup group) { List<BusinessGroupModifiedEvent.Deferred> events = new ArrayList<BusinessGroupModifiedEvent.Deferred>(); for(Identity identityToRemove:identitiesToRemove) { removeOwner(ureqIdentity, identityToRemove, group, events); } dbInstance.commit(); BusinessGroupModifiedEvent.fireDeferredEvents(events); } @Override public boolean hasResources(BusinessGroup group) { return businessGroupRelationDAO.countResources(group) > 0; } @Override public boolean hasResources(List<BusinessGroup> groups) { return businessGroupRelationDAO.hasResources(groups); } @Override public void addResourceTo(BusinessGroup group, RepositoryEntry re) { businessGroupRelationDAO.addRelationToResource(group, re); } @Override public void addResourcesTo(List<BusinessGroup> groups, List<RepositoryEntry> resources) { if(groups == null || groups.isEmpty()) return; if(resources == null || resources.isEmpty()) return; List<Group> baseGroupKeys = new ArrayList<Group>(); for(BusinessGroup group:groups) { baseGroupKeys.add(group.getBaseGroup()); } //check for duplicate entries List<RepositoryEntryToGroupRelation> relations = repositoryEntryRelationDao.getRelations(baseGroupKeys); for(BusinessGroup group:groups) { //reload the base group to prevent lazy loading exception Group baseGroup = businessGroupRelationDAO.getGroup(group); if(baseGroup == null) { continue; } for(RepositoryEntry re:resources) { boolean found = false; for(RepositoryEntryToGroupRelation relation:relations) { if(relation.getGroup().equals(baseGroup) && relation.getEntry().equals(re)) { found = true; } } if(!found) { repositoryEntryRelationDao.createRelation(baseGroup, re); } } } } @Override public void dedupMembers(Identity ureqIdentity, boolean coaches, boolean participants, ProgressDelegate delegate) { SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters(); params.setRoles(new Roles(true, false, false, false, false, false, false)); params.setResourceTypes(Collections.singletonList("CourseModule")); float ratio = -1.0f; if(delegate != null) { int numOfEntries = repositoryManager.countGenericANDQueryWithRolesRestriction(params); ratio = 100.0f / numOfEntries; } int counter = 0; int countForCommit = 0; float actual = 100.0f; int batch = 25; List<RepositoryEntry> entries; do { entries = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, batch, true); for(RepositoryEntry re:entries) { countForCommit += 2 + dedupSingleRepositoryentry(ureqIdentity, re, coaches, participants, false); if(countForCommit > 25) { dbInstance.intermediateCommit(); countForCommit = 0; } } counter += entries.size(); if(delegate != null) { actual -= (entries.size() * ratio); delegate.setActual(actual); } } while(entries.size() == batch); if(delegate != null) { delegate.finished(); } } @Override public void dedupMembers(Identity ureqIdentity, RepositoryEntry entry, boolean coaches, boolean participants) { dedupSingleRepositoryentry(ureqIdentity, entry, coaches, participants, false); dbInstance.commit(); } @Override public int countDuplicateMembers(RepositoryEntry entry, boolean coaches, boolean participants) { return dedupSingleRepositoryentry(null, entry, coaches, participants, true); } private int dedupSingleRepositoryentry(Identity ureqIdentity, RepositoryEntry entry, boolean coaches, boolean participants, boolean dryRun) { int count = 0; List<BusinessGroup> groups = null;//load only if needed if(coaches) { List<Identity> repoTutorList = repositoryEntryRelationDao.getMembers(entry, RepositoryEntryRelationType.defaultGroup, GroupRoles.coach.name()); if(!repoTutorList.isEmpty()) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(); groups = businessGroupDAO.findBusinessGroups(params, entry, 0, -1); List<Identity> ownerList = getMembers(groups, GroupRoles.participant.name()); repoTutorList.retainAll(ownerList); if(!dryRun) { repositoryManager.removeTutors(ureqIdentity, repoTutorList, entry); } count += repoTutorList.size(); } } if(participants) { List<Identity> repoParticipantList = repositoryEntryRelationDao.getMembers(entry, RepositoryEntryRelationType.defaultGroup, GroupRoles.participant.name()); if(!repoParticipantList.isEmpty()) { if(groups == null) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(); groups = businessGroupDAO.findBusinessGroups(params, entry, 0, -1); } List<Identity> participantList = getMembers(groups, GroupRoles.participant.name()); repoParticipantList.retainAll(participantList); if(!dryRun) { repositoryManager.removeParticipants(ureqIdentity, repoParticipantList, entry, null, false); } count += repoParticipantList.size(); } } return count; } @Override public void removeResourceFrom(List<BusinessGroup> groups, RepositoryEntryRef re) { if(groups == null || groups.isEmpty()) { return; // nothing to do } List<BusinessGroupRelationModified> events = new ArrayList<BusinessGroupRelationModified>(); int count = 0; for(BusinessGroup group:groups) { businessGroupRelationDAO.deleteRelation(group, re); events.add(new BusinessGroupRelationModified(BusinessGroupRelationModified.RESOURCE_REMOVED_EVENT, group.getKey(), re.getKey())); if(count++ % 20 == 0) { dbInstance.commit(); } } dbInstance.commit(); for(BusinessGroupRelationModified event:events) { CoordinatorManager.getInstance().getCoordinator().getEventBus() .fireEventToListenersOf(event, OresHelper.lookupType(BusinessGroup.class)); } } @Override public void removeResource(RepositoryEntryRef re) { SearchBusinessGroupParams params = new SearchBusinessGroupParams(); List<BusinessGroup> groups = findBusinessGroups(params, re, 0, -1); removeResourceFrom(groups, re); } @Override public List<RepositoryEntry> findRepositoryEntries(Collection<BusinessGroup> groups, int firstResult, int maxResults) { return businessGroupRelationDAO.findRepositoryEntries(groups, firstResult, maxResults); } @Override public List<RepositoryEntryShort> findShortRepositoryEntries(Collection<BusinessGroupShort> groups, int firstResult, int maxResults) { return businessGroupRelationDAO.findShortRepositoryEntries(groups, firstResult, maxResults); } @Override public List<BGRepositoryEntryRelation> findRelationToRepositoryEntries(Collection<Long> groupKeys, int firstResult, int maxResults) { return businessGroupRelationDAO.findRelationToRepositoryEntries(groupKeys, firstResult, maxResults); } @Override public Group getGroup(BusinessGroup group) { return businessGroupRelationDAO.getGroup(group); } @Override public boolean hasRoles(IdentityRef identity, BusinessGroupRef businessGroup, String role) { return businessGroupRelationDAO.hasRole(identity, businessGroup, role); } @Override public List<Identity> getMembers(BusinessGroup businessGroup, String... roles) { return businessGroupRelationDAO.getMembers(businessGroup, roles); } @Override public List<Identity> getMembers(List<BusinessGroup> businessGroups, String... roles) { List<Identity> ids = new ArrayList<>(); for(BusinessGroup businessGroup:businessGroups) { ids.addAll(businessGroupRelationDAO.getMembers(businessGroup, roles)); } return ids; } @Override public int countMembers(BusinessGroup businessGroup, String... roles) { return businessGroupRelationDAO.countRoles(businessGroup, roles); } @Override public boolean isIdentityInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup) { if(businessGroup == null || identity == null) return false; List<String> roles = businessGroupRelationDAO.getRoles(identity, businessGroup); if(roles == null || roles.isEmpty() || (roles.size() == 1 && GroupRoles.waiting.name().equals(roles.get(0)))) { return false; } return roles.size() > 0; } @Override public List<String> getIdentityRolesInBusinessGroup(IdentityRef identity, BusinessGroupRef businessGroup) { return businessGroupRelationDAO.getRoles(identity, businessGroup); } @Override public List<BusinessGroupMembership> getBusinessGroupsMembership(Collection<BusinessGroup> businessGroups) { return businessGroupDAO.getBusinessGroupsMembership(businessGroups); } @Override public List<BusinessGroupMembership> getBusinessGroupMembership(Collection<Long> businessGroups, Identity... identity) { List<BusinessGroupMembershipViewImpl> views = businessGroupDAO.getMembershipInfoInBusinessGroups(businessGroups, identity); Map<IdentityGroupKey, BusinessGroupMembershipImpl> memberships = new HashMap<IdentityGroupKey, BusinessGroupMembershipImpl>(); for(BusinessGroupMembershipViewImpl membership: views) { if(GroupRoles.coach.name().equals(membership.getRole())) { Long groupKey = membership.getGroupKey(); IdentityGroupKey key = new IdentityGroupKey(membership.getIdentityKey(), groupKey); if(!memberships.containsKey(key)) { memberships.put(key, new BusinessGroupMembershipImpl(membership.getIdentityKey(), groupKey)); } BusinessGroupMembershipImpl mb = memberships.get(key); mb.setOwner(true); mb.setCreationDate(membership.getCreationDate()); mb.setLastModified(membership.getLastModified()); } if(GroupRoles.participant.name().equals(membership.getRole())) { Long groupKey = membership.getGroupKey(); IdentityGroupKey key = new IdentityGroupKey(membership.getIdentityKey(), groupKey); if(!memberships.containsKey(key)) { memberships.put(key, new BusinessGroupMembershipImpl(membership.getIdentityKey(), groupKey)); } BusinessGroupMembershipImpl mb = memberships.get(key); mb.setParticipant(true); mb.setCreationDate(membership.getCreationDate()); mb.setLastModified(membership.getLastModified()); } if(GroupRoles.waiting.name().equals(membership.getRole())) { Long groupKey = membership.getGroupKey(); IdentityGroupKey key = new IdentityGroupKey(membership.getIdentityKey(), groupKey); if(!memberships.containsKey(key)) { memberships.put(key, new BusinessGroupMembershipImpl(membership.getIdentityKey(), groupKey)); } BusinessGroupMembershipImpl mb = memberships.get(key); mb.setWaiting(true); mb.setCreationDate(membership.getCreationDate()); mb.setLastModified(membership.getLastModified()); } } return new ArrayList<BusinessGroupMembership>(memberships.values()); } @Override public boolean isIdentityInBusinessGroup(Identity identity, Long groupKey, boolean ownedById, boolean attendedById, RepositoryEntryRef resource) { return businessGroupRelationDAO.isIdentityInBusinessGroup(identity, groupKey, ownedById, attendedById, resource); } @Override public List<Identity> getIdentitiesWithRole(String role) { return businessGroupRelationDAO.getIdentitiesWithRole(role); } @Override public void exportGroups(List<BusinessGroup> groups, List<BGArea> areas, File fExportFile, BusinessGroupEnvironment env, boolean runtimeDatas, boolean backwardsCompatible) { BusinessGroupImportExport exporter = new BusinessGroupImportExport(dbInstance, areaManager, this, groupModule); exporter.exportGroups(groups, areas, fExportFile, env, runtimeDatas, backwardsCompatible); } @Override public BusinessGroupEnvironment importGroups(RepositoryEntry re, File fGroupExportXML) { BusinessGroupImportExport importer = new BusinessGroupImportExport(dbInstance, areaManager, this, groupModule); return importer.importGroups(re, fGroupExportXML); } @Override public void archiveGroups(List<BusinessGroup> groups, File exportFile) { BusinessGroupArchiver archiver = new BusinessGroupArchiver(dbInstance); archiver.archiveGroups(groups, exportFile); } }