/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.course.nodes.projectbroker.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.olat.basesecurity.BaseSecurity;
import org.olat.basesecurity.GroupRoles;
import org.olat.basesecurity.SecurityGroup;
import org.olat.core.commons.persistence.DB;
import org.olat.core.gui.UserRequest;
import org.olat.core.id.Identity;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.coordinate.SyncerCallback;
import org.olat.core.util.event.MultiUserEvent;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.groupsandrights.CourseGroupManager;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.ProjectBrokerCourseNode;
import org.olat.course.nodes.projectbroker.datamodel.Project;
import org.olat.course.properties.CoursePropertyManager;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.group.BusinessGroup;
import org.olat.group.BusinessGroupAddResponse;
import org.olat.group.BusinessGroupService;
import org.olat.group.ui.edit.BusinessGroupModifiedEvent;
import org.olat.properties.Property;
import org.olat.repository.RepositoryEntry;
import org.olat.resource.OLATResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* @author guretzki
*/
@Service
public class ProjectGroupManagerImpl implements ProjectGroupManager {
private static final OLog log = Tracing.createLoggerFor(ProjectGroupManagerImpl.class);
@Autowired
private DB dbInstance;
@Autowired
private ProjectDAO projectDao;
@Autowired
private BaseSecurity securityManager;
@Autowired
private ProjectBrokerManager projectBrokerManager;
@Autowired
private BusinessGroupService businessGroupService;
//////////////////////
// ACCOUNT MANAGEMENT
//////////////////////
@Override
public Long getAccountManagerGroupKey(CoursePropertyManager cpm, CourseNode courseNode) {
Property accountManagerGroupProperty = cpm.findCourseNodeProperty(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY);
if (accountManagerGroupProperty != null) {
return accountManagerGroupProperty.getLongValue();
}
return null;
}
@Override
public BusinessGroup getAccountManagerGroupFor(CoursePropertyManager cpm, CourseNode courseNode, ICourse course, String groupName, String groupDescription, Identity identity) {
BusinessGroup accountManagerGroup = null;
try {
Long groupKey = null;
Property accountManagerGroupProperty = cpm.findCourseNodeProperty(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY);
// Check if account-manager-group-key-property already exist
if (accountManagerGroupProperty != null) {
groupKey = accountManagerGroupProperty.getLongValue();
log.debug("accountManagerGroupProperty=" + accountManagerGroupProperty + " groupKey=" + groupKey);
}
log.debug("groupKey=" + groupKey);
if (groupKey != null) {
accountManagerGroup = businessGroupService.loadBusinessGroup(groupKey);
log.debug("load businessgroup=" + accountManagerGroup);
if (accountManagerGroup != null) {
return accountManagerGroup;
} else {
if (accountManagerGroupProperty != null) {
cpm.deleteProperty(accountManagerGroupProperty);
}
groupKey = null;
log.warn("ProjectBroker: Account-manager does no longer exist, create a new one", null);
}
} else {
log.debug("No group for project-broker exist => create a new one");
RepositoryEntry re = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
accountManagerGroup = businessGroupService.createBusinessGroup(identity, groupName, groupDescription, -1, -1, false, false, re);
int i = 2;
while (accountManagerGroup == null) {
// group with this name exist already, try another name
accountManagerGroup = businessGroupService.createBusinessGroup(identity, groupName + " _" + i, groupDescription, -1, -1, false, false, re);
i++;
}
log.debug("createAndPersistBusinessGroup businessgroup=" + accountManagerGroup);
if (accountManagerGroupProperty != null) {
accountManagerGroupProperty.setLongValue(accountManagerGroup.getKey());
cpm.updateProperty(accountManagerGroupProperty);
} else {
saveAccountManagerGroupKey(accountManagerGroup.getKey(), cpm, courseNode);
}
log.debug("created account-manager default businessgroup=" + accountManagerGroup);
}
} catch (AssertException e) {
log.error("", e);
if(tryToRepareAccountManagerProperty(cpm, courseNode)) {
accountManagerGroup = getAccountManagerGroupFor(cpm, courseNode, course, groupName, groupDescription, identity);
}
}
return accountManagerGroup;
}
@Override
public void saveAccountManagerGroupKey(Long accountManagerGroupKey, CoursePropertyManager cpm, CourseNode courseNode) {
Property accountManagerGroupKeyProperty = cpm.createCourseNodePropertyInstance(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY, null, accountManagerGroupKey, null, null);
cpm.saveProperty(accountManagerGroupKeyProperty);
log.debug("saveAccountManagerGroupKey accountManagerGroupKey=" + accountManagerGroupKey);
}
@Override
public boolean isAccountManager(Identity identity, CoursePropertyManager cpm, CourseNode courseNode) {
try {
Property accountManagerGroupProperty = cpm.findCourseNodeProperty(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY);
if (accountManagerGroupProperty != null) {
Long groupKey = accountManagerGroupProperty.getLongValue();
BusinessGroup accountManagerGroup = businessGroupService.loadBusinessGroup(groupKey);
if (accountManagerGroup != null) {
return isAccountManager(identity, accountManagerGroup);
}
}
} catch (AssertException e) {//detected multiple properties
log.error("", e);
if(tryToRepareAccountManagerProperty(cpm, courseNode)) {
return isAccountManager(identity, cpm, courseNode);
}
}
return false;
}
private boolean tryToRepareAccountManagerProperty(CoursePropertyManager cpm, CourseNode courseNode) {
List<Property> properties = cpm.findCourseNodeProperties(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY);
if(properties.size() > 1) {
Collections.sort(properties, (p1, p2) -> { return p1.getCreationDate().compareTo(p2.getCreationDate()); });
for(int i=1; i<properties.size(); i++) {
cpm.deleteProperty(properties.get(i));
}
dbInstance.commit();
}
return false;
}
public void deleteAccountManagerGroup( CoursePropertyManager cpm, CourseNode courseNode) {
log.debug("deleteAccountManagerGroup start...");
Property accountManagerGroupProperty = cpm.findCourseNodeProperty(courseNode, null, null, ProjectBrokerCourseNode.CONF_ACCOUNTMANAGER_GROUP_KEY);
if (accountManagerGroupProperty != null) {
Long groupKey = accountManagerGroupProperty.getLongValue();
if (groupKey != null) {
BusinessGroup accountManagerGroup = businessGroupService.loadBusinessGroup(groupKey);
if (accountManagerGroup != null) {
BusinessGroupService bgs = businessGroupService;
bgs.deleteBusinessGroup(accountManagerGroup);
log.audit("ProjectBroker: Deleted accountManagerGroup=" + accountManagerGroup);
} else {
log.debug("deleteAccountManagerGroup: accountManagerGroup=" + accountManagerGroup + " has already been deleted");
}
}
cpm.deleteProperty(accountManagerGroupProperty);
log.debug("deleteAccountManagerGroup: deleted accountManagerGroupProperty=" + accountManagerGroupProperty );
} else {
log.debug("deleteAccountManagerGroup: found no accountManagerGroup-key");
}
}
@Override
public BusinessGroup updateAccountManagerGroupName(Identity ureqIdentity, String groupName, String groupDescription, BusinessGroup accountManagerGroup) {
// group could have been deleted, see FXOLAT-295
if (accountManagerGroup != null){
BusinessGroupService bgs = businessGroupService;
BusinessGroup reloadedBusinessGroup = bgs.loadBusinessGroup(accountManagerGroup);
return bgs.updateBusinessGroup(ureqIdentity, reloadedBusinessGroup, groupName, groupDescription,
reloadedBusinessGroup.getExternalId(), reloadedBusinessGroup.getManagedFlagsString(),
reloadedBusinessGroup.getMinParticipants(), reloadedBusinessGroup.getMaxParticipants());
}
return null;
}
////////////////////////////
// PROJECT GROUP MANAGEMENT
////////////////////////////
@Override
public BusinessGroup createProjectGroupFor(Long projectBrokerId, Identity identity, String groupName, String groupDescription, Long courseId) {
CourseGroupManager cgm = CourseFactory.loadCourse(courseId).getCourseEnvironment().getCourseGroupManager();
RepositoryEntry re = cgm.getCourseEntry();
log.debug("createProjectGroupFor groupName=" + groupName);
BusinessGroup projectGroup = businessGroupService.createBusinessGroup(identity, groupName, groupDescription, -1, -1, false, false, re);
// projectGroup could be null when a group with name already exists
int counter = 2;
while (projectGroup == null) {
// name already exist try another one
String newGroupName = groupName + " _" + counter ;
projectGroup = businessGroupService.createBusinessGroup(identity, newGroupName, groupDescription, -1, -1, false, false, re);
counter++;
}
log.debug("Created a new projectGroup=" + projectGroup);
return projectGroup;
}
@Override
public void deleteProjectGroupFor(Project project) {
BusinessGroupService bgs = businessGroupService;
bgs.deleteBusinessGroup(project.getProjectGroup());
}
/**
* Change group-name and description. Check if new group-name does not already exist in the course-group-context.
* If the goup-name already exist, it will be automatically try another one with suffix e.g. ' _2'
* @see org.olat.course.nodes.projectbroker.service.ProjectGroupManager#changeProjectGroupName(org.olat.group.BusinessGroup, java.lang.String, java.lang.String)
*/
@Override
public BusinessGroup changeProjectGroupName(Identity ureqIdentity, BusinessGroup projectGroup, String groupName, String groupDescription, OLATResource courseResource) {
BusinessGroupService bgs = businessGroupService;
BusinessGroup reloadedBusinessGroup = bgs.loadBusinessGroup(projectGroup);
return bgs.updateBusinessGroup(ureqIdentity, reloadedBusinessGroup, groupName, groupDescription,
reloadedBusinessGroup.getExternalId(), reloadedBusinessGroup.getManagedFlagsString(),
reloadedBusinessGroup.getMinParticipants(), reloadedBusinessGroup.getMaxParticipants());
}
public List<Identity> addCandidates(final List<Identity> addIdentities, final Project project) {
//TODO gsync
List<Identity> addedIdentities = CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(project.getProjectGroup(), new SyncerCallback<List<Identity>>(){
public List<Identity> execute() {
List<Identity> addedIdentityList = new ArrayList<Identity>();
for (Identity identity : addIdentities) {
if (!securityManager.isIdentityInSecurityGroup(identity, project.getCandidateGroup()) ) {
securityManager.addIdentityToSecurityGroup(identity, project.getCandidateGroup());
addedIdentityList.add(identity);
log.audit("ProjectBroker: Add user as candidate, identity=" + identity);
}
// fireEvents ?
}
return addedIdentityList;
}
});// end of doInSync
return addedIdentities;
}
public void removeCandidates(final List<Identity> addIdentities, final Project project) {
//TODO gsync
CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(project.getProjectGroup(), new SyncerCallback<Boolean>(){
public Boolean execute() {
Project reloadedProject = (Project) dbInstance.loadObject(project, true);
for (Identity identity : addIdentities) {
securityManager.removeIdentityFromSecurityGroup(identity, reloadedProject.getCandidateGroup());
log.audit("ProjectBroker: Remove user as candidate, identity=" + identity);
// fireEvents ?
}
return Boolean.TRUE;
}
});// end of doInSync
}
@Override
public BusinessGroupAddResponse acceptCandidates(final List<Identity> identities, final Project project, final Identity actionIdentity, final boolean autoSignOut, final boolean isAcceptSelectionManually) {
final Project reloadedProject = projectDao.loadProject(project.getKey());
final BusinessGroupAddResponse response = new BusinessGroupAddResponse();
BusinessGroupAddResponse state = businessGroupService.addParticipants(actionIdentity, null, identities, reloadedProject.getProjectGroup(), null);
response.getAddedIdentities().addAll(state.getAddedIdentities());
response.getIdentitiesAlreadyInGroup().addAll(state.getIdentitiesAlreadyInGroup());
Boolean result = CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(project.getProjectGroup(), new SyncerCallback<Boolean>(){
@Override
public Boolean execute() {
for (final Identity identity : identities) {
if (businessGroupService.hasRoles(identity, reloadedProject.getProjectGroup(), GroupRoles.participant.name())) {
securityManager.removeIdentityFromSecurityGroup(identity, reloadedProject.getCandidateGroup());
log.audit("ProjectBroker: Accept candidate, identity=" + identity + " project=" + reloadedProject);
}
}
return Boolean.TRUE;
}
});// end of doInSync
if (autoSignOut && result.booleanValue()) {
projectBrokerManager.signOutFormAllCandidateList(response.getAddedIdentities(), reloadedProject.getProjectBroker().getKey());
}
if (isAcceptSelectionManually && (reloadedProject.getMaxMembers() != Project.MAX_MEMBERS_UNLIMITED)
&& reloadedProject.getSelectedPlaces() >= reloadedProject.getMaxMembers()) {
projectBrokerManager.setProjectState(reloadedProject, Project.STATE_ASSIGNED);
log.info("ProjectBroker: Accept candidate, change project-state=" + Project.STATE_ASSIGNED);
}
return response;
}
@Override
public void sendGroupChangeEvent(Project project, Long courseResourceableId, Identity identity) {
ICourse course = CourseFactory.loadCourse(courseResourceableId);
RepositoryEntry ores = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
MultiUserEvent modifiedEvent = new BusinessGroupModifiedEvent(BusinessGroupModifiedEvent.IDENTITY_ADDED_EVENT, project.getProjectGroup(), identity);
CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(modifiedEvent, ores);
}
public boolean isProjectManager(Identity identity, Project project) {
return businessGroupService.hasRoles(identity, project.getProjectGroup(), GroupRoles.coach.name());
}
public boolean isProjectManagerOrAdministrator(UserRequest ureq, CourseEnvironment courseEnv, Project project) {
return ureq.getUserSession().getRoles().isOLATAdmin()
|| isProjectManager(ureq.getIdentity(), project)
|| courseEnv.getCourseGroupManager().isIdentityCourseAdministrator(ureq.getIdentity());
}
public boolean isProjectParticipant(Identity identity, Project project) {
return businessGroupService.hasRoles(identity, project.getProjectGroup(), GroupRoles.participant.name());
}
public boolean isProjectCandidate(Identity identity, Project project) {
return securityManager.isIdentityInSecurityGroup(identity, project.getCandidateGroup());
}
@Override
public BusinessGroup setProjectGroupMaxMembers(Identity ureqIdentity, BusinessGroup projectGroup, int maxMembers ) {
BusinessGroupService bgs = businessGroupService;
BusinessGroup reloadedBusinessGroup = bgs.loadBusinessGroup(projectGroup);
log.debug("ProjectGroup.name=" + reloadedBusinessGroup.getName() + " setMaxParticipants=" + maxMembers);
return bgs.updateBusinessGroup(ureqIdentity, reloadedBusinessGroup, reloadedBusinessGroup.getName(),
reloadedBusinessGroup.getDescription(), reloadedBusinessGroup.getExternalId(), reloadedBusinessGroup.getManagedFlagsString(),
reloadedBusinessGroup.getMinParticipants(), maxMembers);
}
///////////////////
// PRIVATE METHODS
///////////////////
private boolean isAccountManager(Identity identity, BusinessGroup businessGroup) {
if (businessGroup == null) {
return false;
}
return businessGroupService.hasRoles(identity, businessGroup, GroupRoles.participant.name())
|| businessGroupService.hasRoles(identity, businessGroup, GroupRoles.coach.name());
}
@Override
public void acceptAllCandidates(Long projectBrokerId, Identity actionIdentity, boolean autoSignOut, boolean isAcceptSelectionManually) {
// loop over all project
List<Project> projectList = projectBrokerManager.getProjectListBy(projectBrokerId);
for (Iterator<Project> iterator = projectList.iterator(); iterator.hasNext();) {
Project project = iterator.next();
List<Identity> candidates = securityManager.getIdentitiesOfSecurityGroup(project.getCandidateGroup());
if (!candidates.isEmpty()) {
log.audit("ProjectBroker: Accept ALL candidates, project=" + project);
acceptCandidates(candidates, project, actionIdentity, autoSignOut, isAcceptSelectionManually);
}
}
}
@Override
public boolean hasProjectBrokerAnyCandidates(Long projectBrokerId) {
List<Project> projectList = projectBrokerManager.getProjectListBy(projectBrokerId);
for (Iterator<Project> iterator = projectList.iterator(); iterator.hasNext();) {
Project project = iterator.next();
List<Identity> candidates = securityManager.getIdentitiesOfSecurityGroup(project.getCandidateGroup());
if (!candidates.isEmpty()) {
return true;
}
}
return false;
}
@Override
public boolean isCandidateListEmpty(SecurityGroup candidateGroup) {
List<Identity> candidates = securityManager.getIdentitiesOfSecurityGroup(candidateGroup);
return candidates.isEmpty();
}
@Override
public boolean isDeselectionAllowed(Project project){
return project.getProjectGroup().isAllowToLeave();
}
@Override
public void setDeselectionAllowed(Project project, boolean allow){
project.getProjectGroup().setAllowToLeave(allow);
businessGroupService.updateAllowToLeaveBusinessGroup(project.getProjectGroup(), allow);
}
}