/**
* <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.course.member;
import java.util.List;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.segmentedview.SegmentViewComponent;
import org.olat.core.gui.components.segmentedview.SegmentViewEvent;
import org.olat.core.gui.components.segmentedview.SegmentViewFactory;
import org.olat.core.gui.components.stack.TooledStackedPanel;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.gui.control.generic.wizard.Step;
import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
import org.olat.core.gui.control.generic.wizard.StepsRunContext;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.mail.MailHelper;
import org.olat.core.util.mail.MailPackage;
import org.olat.core.util.mail.MailTemplate;
import org.olat.core.util.mail.MailerResult;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.member.wizard.ImportMember_1a_LoginListStep;
import org.olat.course.member.wizard.ImportMember_1b_ChooseMemberStep;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.group.BusinessGroupService;
import org.olat.group.model.BusinessGroupMembershipChange;
import org.olat.group.ui.main.AbstractMemberListController;
import org.olat.group.ui.main.DedupMembersConfirmationController;
import org.olat.group.ui.main.MemberPermissionChangeEvent;
import org.olat.group.ui.main.SearchMembersParams;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryManagedFlag;
import org.olat.repository.RepositoryManager;
import org.olat.repository.model.RepositoryEntryPermissionChangeEvent;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* The members overview.
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class MembersOverviewController extends BasicController implements Activateable2 {
private static final String SEG_ALL_MEMBERS = "All";
private static final String SEG_OWNERS_MEMBERS = "Owners";
private static final String SEG_TUTORS_MEMBERS = "Coaches";
private static final String SEG_PARTICIPANTS_MEMBERS = "Participants";
private static final String SEG_WAITING_MEMBERS = "Waiting";
private static final String SEG_SEARCH_MEMBERS = "Search";
private Link overrideLink, unOverrideLink;
private final Link allMembersLink, ownersLink, tutorsLink, participantsLink, waitingListLink, searchLink;
private final SegmentViewComponent segmentView;
private final VelocityContainer mainVC;
private final TooledStackedPanel toolbarPanel;
private AbstractMemberListController allMemberListCtrl;
private AbstractMemberListController ownersCtrl;
private AbstractMemberListController tutorsCtrl;
private AbstractMemberListController participantsCtrl;
private AbstractMemberListController waitingCtrl;
private AbstractMemberListController selectedCtrl;
private AbstractMemberListController searchCtrl;
private final Link importMemberLink, addMemberLink, dedupLink;
private CloseableModalController cmc;
private StepsMainRunController importMembersWizard;
private DedupMembersConfirmationController dedupCtrl;
private final boolean managed;
private boolean overrideManaged = false;
private final RepositoryEntry repoEntry;
private final UserCourseEnvironment coachCourseEnv;
@Autowired
private RepositoryManager repositoryManager;
@Autowired
private BusinessGroupService businessGroupService;
public MembersOverviewController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbarPanel,
RepositoryEntry repoEntry, UserCourseEnvironment coachCourseEnv) {
super(ureq, wControl);
this.repoEntry = repoEntry;
this.toolbarPanel = toolbarPanel;
this.coachCourseEnv = coachCourseEnv;
mainVC = createVelocityContainer("members_overview");
segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this);
allMembersLink = LinkFactory.createLink("members.all", mainVC, this);
segmentView.addSegment(allMembersLink, true);
ownersLink = LinkFactory.createLink("owners", mainVC, this);
segmentView.addSegment(ownersLink, false);
tutorsLink = LinkFactory.createLink("tutors", mainVC, this);
segmentView.addSegment(tutorsLink, false);
participantsLink = LinkFactory.createLink("participants", mainVC, this);
segmentView.addSegment(participantsLink, false);
waitingListLink = LinkFactory.createLink("waitinglist", mainVC, this);
segmentView.addSegment(waitingListLink, false);
searchLink = LinkFactory.createLink("search", mainVC, this);
segmentView.addSegment(searchLink, false);
selectedCtrl = updateAllMembers(ureq);
managed = RepositoryEntryManagedFlag.isManaged(repoEntry, RepositoryEntryManagedFlag.membersmanagement);
if(managed && ureq.getUserSession().getRoles().isOLATAdmin()) {
overrideLink = LinkFactory.createButton("override.member", mainVC, this);
overrideLink.setIconLeftCSS("o_icon o_icon-fw o_icon_refresh");
unOverrideLink = LinkFactory.createButton("unoverride.member", mainVC, this);
unOverrideLink.setIconLeftCSS("o_icon o_icon-fw o_icon_refresh");
unOverrideLink.setVisible(false);
}
addMemberLink = LinkFactory.createButton("add.member", mainVC, this);
addMemberLink.setIconLeftCSS("o_icon o_icon-fw o_icon_add_member");
addMemberLink.setElementCssClass("o_sel_course_add_member");
addMemberLink.setVisible(!managed && !coachCourseEnv.isCourseReadOnly());
mainVC.put("addMembers", addMemberLink);
importMemberLink = LinkFactory.createButton("import.member", mainVC, this);
importMemberLink.setIconLeftCSS("o_icon o_icon-fw o_icon_import");
importMemberLink.setElementCssClass("o_sel_course_import_members");
importMemberLink.setVisible(!managed && !coachCourseEnv.isCourseReadOnly());
mainVC.put("importMembers", importMemberLink);
dedupLink = LinkFactory.createButton("dedup.members", mainVC, this);
dedupLink.setIconLeftCSS("o_icon o_icon-fw o_icon_cleanup");
dedupLink.setVisible(!managed && !coachCourseEnv.isCourseReadOnly());
mainVC.put("dedupMembers", dedupLink);
putInitialPanel(mainVC);
}
@Override
protected void doDispose() {
//
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
ContextEntry currentEntry = entries.get(0);
String segment = currentEntry.getOLATResourceable().getResourceableTypeName();
List<ContextEntry> subEntries = entries.subList(1, entries.size());
if(SEG_ALL_MEMBERS.equals(segment)) {
updateAllMembers(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(allMembersLink);
} else if(SEG_OWNERS_MEMBERS.equals(segment)) {
updateOwners(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(ownersLink);
} else if(SEG_TUTORS_MEMBERS.equals(segment)) {
updateTutors(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(tutorsLink);
} else if(SEG_PARTICIPANTS_MEMBERS.equals(segment)) {
updateParticipants(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(participantsLink);
} else if(SEG_WAITING_MEMBERS.equals(segment)) {
updateWaitingList(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(waitingListLink);
} else if(SEG_SEARCH_MEMBERS.equals(segment)) {
updateSearch(ureq).activate(ureq, subEntries, currentEntry.getTransientState());
segmentView.select(searchLink);
}
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if(event instanceof SegmentViewEvent) {
SegmentViewEvent sve = (SegmentViewEvent)event;
String segmentCName = sve.getComponentName();
Component clickedLink = mainVC.getComponent(segmentCName);
if (clickedLink == allMembersLink) {
selectedCtrl = updateAllMembers(ureq);
} else if (clickedLink == ownersLink){
selectedCtrl = updateOwners(ureq);
} else if (clickedLink == tutorsLink){
selectedCtrl = updateTutors(ureq);
} else if (clickedLink == participantsLink) {
selectedCtrl = updateParticipants(ureq);
} else if (clickedLink == waitingListLink) {
selectedCtrl = updateWaitingList(ureq);
} else if (clickedLink == searchLink) {
updateSearch(ureq);
selectedCtrl = null;
}
} else if (source == addMemberLink) {
doChooseMembers(ureq);
} else if (source == importMemberLink) {
doImportMembers(ureq);
} else if (source == dedupLink) {
doDedupMembers(ureq);
} else if (source == overrideLink) {
doOverrideManagedResource(ureq);
} else if (source == unOverrideLink) {
doUnOverrideManagedResource(ureq);
}
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(source == importMembersWizard) {
if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
getWindowControl().pop();
removeAsListenerAndDispose(importMembersWizard);
importMembersWizard = null;
if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
if(selectedCtrl != null) {
selectedCtrl.reloadModel();
}
}
}
} else if(source == dedupCtrl) {
if(event == Event.DONE_EVENT) {
dedupMembers(ureq, dedupCtrl.isDedupCoaches(), dedupCtrl.isDedupParticipants());
}
cmc.deactivate();
cleanUp();
} else if (source == cmc) {
cleanUp();
}
super.event(ureq, source, event);
}
private void cleanUp() {
removeAsListenerAndDispose(dedupCtrl);
removeAsListenerAndDispose(cmc);
dedupCtrl = null;
cmc = null;
}
protected void reloadMembers() {
if(allMemberListCtrl != null) {
allMemberListCtrl.reloadModel();
}
if(ownersCtrl != null) {
ownersCtrl.reloadModel();
}
if(tutorsCtrl != null) {
tutorsCtrl.reloadModel();
}
if(participantsCtrl != null) {
participantsCtrl.reloadModel();
}
if(waitingCtrl != null) {
waitingCtrl.reloadModel();
}
if(selectedCtrl != null) {
selectedCtrl.reloadModel();
}
if(searchCtrl != null) {
searchCtrl.reloadModel();
}
}
private void doOverrideManagedResource(UserRequest ureq) {
overrideManagedResource(ureq, true);
}
private void doUnOverrideManagedResource(UserRequest ureq) {
overrideManagedResource(ureq, false);
}
private void overrideManagedResource(UserRequest ureq, boolean override) {
overrideManaged = override;
overrideLink.setVisible(!overrideManaged);
unOverrideLink.setVisible(overrideManaged);
addMemberLink.setVisible(overrideManaged);
importMemberLink.setVisible(overrideManaged);
dedupLink.setVisible(overrideManaged);
mainVC.setDirty(true);
if(allMemberListCtrl != null) {
allMemberListCtrl.overrideManaged(ureq, overrideManaged);
}
if(ownersCtrl != null) {
ownersCtrl.overrideManaged(ureq, overrideManaged);
}
if(tutorsCtrl != null) {
tutorsCtrl.overrideManaged(ureq, overrideManaged);
}
if(participantsCtrl != null) {
participantsCtrl.overrideManaged(ureq, overrideManaged);
}
if(waitingCtrl != null) {
waitingCtrl.overrideManaged(ureq, overrideManaged);
}
if(selectedCtrl != null) {
selectedCtrl.overrideManaged(ureq, overrideManaged);
}
if(searchCtrl != null) {
searchCtrl.overrideManaged(ureq, overrideManaged);
}
}
private void doChooseMembers(UserRequest ureq) {
removeAsListenerAndDispose(importMembersWizard);
Step start = new ImportMember_1b_ChooseMemberStep(ureq, repoEntry, null, overrideManaged);
StepRunnerCallback finish = new StepRunnerCallback() {
@Override
public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
addMembers(uureq, runContext);
return StepsMainRunController.DONE_MODIFIED;
}
};
importMembersWizard = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
translate("add.member"), "o_sel_course_member_import_1_wizard");
listenTo(importMembersWizard);
getWindowControl().pushAsModalDialog(importMembersWizard.getInitialComponent());
}
private void doImportMembers(UserRequest ureq) {
removeAsListenerAndDispose(importMembersWizard);
Step start = new ImportMember_1a_LoginListStep(ureq, repoEntry, null, overrideManaged);
StepRunnerCallback finish = new StepRunnerCallback() {
@Override
public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
addMembers(uureq, runContext);
if(runContext.containsKey("notFounds")) {
showWarning("user.notfound", runContext.get("notFounds").toString());
}
return StepsMainRunController.DONE_MODIFIED;
}
};
importMembersWizard = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
translate("import.member"), "o_sel_course_member_import_logins_wizard");
listenTo(importMembersWizard);
getWindowControl().pushAsModalDialog(importMembersWizard.getInitialComponent());
}
protected void addMembers(UserRequest ureq, StepsRunContext runContext) {
@SuppressWarnings("unchecked")
List<Identity> members = (List<Identity>)runContext.get("members");
MemberPermissionChangeEvent changes = (MemberPermissionChangeEvent)runContext.get("permissions");
MailTemplate template = (MailTemplate)runContext.get("mailTemplate");
//commit changes to the repository entry
MailerResult result = new MailerResult();
MailPackage reMailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), template != null);
List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing);
//commit all changes to the group memberships
List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
MailPackage mailing = new MailPackage(template, result, getWindowControl().getBusinessControl().getAsString(), template != null);
businessGroupService.updateMemberships(getIdentity(), allModifications, mailing);
MailHelper.printErrorsAndWarnings(result, getWindowControl(), getLocale());
switchToAllMembers(ureq);
}
protected void doDedupMembers(UserRequest ureq) {
int numOfDuplicate = businessGroupService.countDuplicateMembers(repoEntry, true, true);
if(numOfDuplicate <= 0) {
showInfo("dedup.members.notfound");
} else {
dedupCtrl = new DedupMembersConfirmationController(ureq, getWindowControl(), numOfDuplicate);
listenTo(dedupCtrl);
cmc = new CloseableModalController(getWindowControl(), translate("close"), dedupCtrl.getInitialComponent(),
true, translate("dedup.members"));
cmc.activate();
listenTo(cmc);
}
}
protected void dedupMembers(UserRequest ureq, boolean coaches, boolean participants) {
businessGroupService.dedupMembers(ureq.getIdentity(), repoEntry, coaches, participants);
showInfo("dedup.done");
if(selectedCtrl != null) {
selectedCtrl.reloadModel();
}
}
private void switchToAllMembers(UserRequest ureq) {
DBFactory.getInstance().commit();//make sure all is on the DB before reloading
if(selectedCtrl != null && selectedCtrl == allMemberListCtrl) {
allMemberListCtrl.reloadModel();
} else {
selectedCtrl = updateAllMembers(ureq);
segmentView.select(allMembersLink);
}
}
private AbstractMemberListController updateAllMembers(UserRequest ureq) {
if(allMemberListCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_ALL_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
SearchMembersParams searchParams = new SearchMembersParams(true, true, true, true, true, false, true);
allMemberListCtrl = new MemberListWithOriginFilterController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv, searchParams, null);
listenTo(allMemberListCtrl);
}
allMemberListCtrl.reloadModel();
allMemberListCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", allMemberListCtrl.getInitialComponent());
addToHistory(ureq, allMemberListCtrl);
return allMemberListCtrl;
}
private AbstractMemberListController updateOwners(UserRequest ureq) {
if(ownersCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_OWNERS_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
SearchMembersParams searchParams = new SearchMembersParams(true, false, false, false, false, false, false);
String infos = translate("owners.infos");
ownersCtrl = new MemberListController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv, searchParams, infos);
listenTo(ownersCtrl);
}
ownersCtrl.reloadModel();
ownersCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", ownersCtrl.getInitialComponent());
addToHistory(ureq, ownersCtrl);
return ownersCtrl;
}
private AbstractMemberListController updateTutors(UserRequest ureq) {
if(tutorsCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_TUTORS_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
SearchMembersParams searchParams = new SearchMembersParams(false, true, false, true, false, false, false);
String infos = translate("tutors.infos");
tutorsCtrl = new MemberListWithOriginFilterController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv, searchParams, infos);
listenTo(tutorsCtrl);
}
tutorsCtrl.reloadModel();
tutorsCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", tutorsCtrl.getInitialComponent());
addToHistory(ureq, tutorsCtrl);
return tutorsCtrl;
}
private AbstractMemberListController updateParticipants(UserRequest ureq) {
if(participantsCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_PARTICIPANTS_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
SearchMembersParams searchParams = new SearchMembersParams(false, false, true, false, true, false, true);
String infos = translate("participants.infos");
participantsCtrl = new MemberListWithOriginFilterController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv, searchParams, infos);
listenTo(participantsCtrl);
}
participantsCtrl.reloadModel();
participantsCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", participantsCtrl.getInitialComponent());
addToHistory(ureq, participantsCtrl);
return participantsCtrl;
}
private AbstractMemberListController updateWaitingList(UserRequest ureq) {
if(waitingCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_WAITING_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
SearchMembersParams searchParams = new SearchMembersParams(false, false, false, false, false, true, false);
String infos = translate("waiting.infos");
waitingCtrl = new MemberListController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv, searchParams, infos);
listenTo(waitingCtrl);
}
waitingCtrl.reloadModel();
waitingCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", waitingCtrl.getInitialComponent());
addToHistory(ureq, waitingCtrl);
return waitingCtrl;
}
private AbstractMemberListController updateSearch(UserRequest ureq) {
if(searchCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(SEG_SEARCH_MEMBERS, 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
searchCtrl = new MemberSearchController(ureq, bwControl, toolbarPanel, repoEntry, coachCourseEnv);
listenTo(searchCtrl);
}
searchCtrl.overrideManaged(ureq, overrideManaged);
mainVC.put("membersCmp", searchCtrl.getInitialComponent());
addToHistory(ureq, searchCtrl);
return searchCtrl;
}
}