/** * <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.admin.user.course; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.olat.NewControllerFactory; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.GroupRoles; 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.table.BooleanColumnDescriptor; import org.olat.core.gui.components.table.ColumnDescriptor; import org.olat.core.gui.components.table.CustomCellRenderer; import org.olat.core.gui.components.table.CustomRenderColumnDescriptor; import org.olat.core.gui.components.table.DefaultColumnDescriptor; import org.olat.core.gui.components.table.DefaultTableDataModel; import org.olat.core.gui.components.table.StaticColumnDescriptor; import org.olat.core.gui.components.table.Table; import org.olat.core.gui.components.table.TableController; import org.olat.core.gui.components.table.TableEvent; import org.olat.core.gui.components.table.TableGuiConfiguration; import org.olat.core.gui.components.table.TableMultiSelectEvent; 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.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.render.Renderer; import org.olat.core.gui.render.StringOutput; import org.olat.core.id.Identity; import org.olat.core.util.Util; import org.olat.core.util.mail.MailHelper; import org.olat.core.util.mail.MailPackage; import org.olat.core.util.mail.MailerResult; import org.olat.course.CourseModule; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupManagedFlag; import org.olat.group.BusinessGroupMembership; import org.olat.group.BusinessGroupService; import org.olat.group.BusinessGroupShort; import org.olat.group.model.BGRepositoryEntryRelation; import org.olat.group.ui.main.CourseMembership; import org.olat.group.ui.main.CourseMembershipComparator; import org.olat.group.ui.main.EditSingleMembershipController; import org.olat.group.ui.main.MemberPermissionChangeEvent; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryManagedFlag; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryModule; import org.olat.repository.RepositoryService; import org.olat.repository.controllers.ReferencableEntriesSearchController; import org.olat.repository.controllers.RepositoryEntryFilter; import org.olat.repository.controllers.RepositorySearchController.Can; import org.olat.repository.model.RepositoryEntryMembership; import org.olat.repository.model.RepositoryEntryPermissionChangeEvent; import org.olat.repository.ui.RepositoryEntryIconRenderer; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 23.09.2013<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class CourseOverviewController extends BasicController { private static final String CONFIG = CourseOverviewController.class.getName(); private static final String TABLE_ACTION_LAUNCH = "reTblLaunch"; private static final String TABLE_ACTION_EDIT = "edit"; private static final String TABLE_ACTION_UNSUBSCRIBE = "unsubscribe"; private final VelocityContainer vc; private final Link addAsOwner, addAsTutor, addAsParticipant; private TableController courseListCtr; private MembershipDataModel tableDataModel; private final CourseMembershipComparator membershipComparator = new CourseMembershipComparator(); private CloseableModalController cmc; private DialogBoxController confirmSendMailBox; private CourseLeaveDialogBoxController removeFromCourseDlg; private ReferencableEntriesSearchController repoSearchCtr; private EditSingleMembershipController editSingleMemberCtrl; private final Identity editedIdentity; @Autowired private BaseSecurityModule securityModule; @Autowired private RepositoryModule repositoryModule; @Autowired private RepositoryManager repositoryManager; @Autowired private RepositoryService repositoryService; @Autowired private BusinessGroupService businessGroupService; public CourseOverviewController(UserRequest ureq, WindowControl wControl, Identity identity) { super(ureq, wControl, Util.createPackageTranslator(CourseMembership.class, ureq.getLocale())); this.setTranslator(Util.createPackageTranslator(RepositoryService.class, getLocale(), getTranslator())); editedIdentity = identity; boolean isLastVisitVisible = securityModule.isUserLastVisitVisible(ureq.getUserSession().getRoles()); vc = createVelocityContainer("courseoverview"); TableGuiConfiguration config = new TableGuiConfiguration(); config.setPreferencesOffered(true, CONFIG); courseListCtr = new TableController(config, ureq, wControl, getTranslator()); listenTo(courseListCtr); courseListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(MSCols.key.i18n(), MSCols.key.ordinal(), TABLE_ACTION_LAUNCH, getLocale())); courseListCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(MSCols.entry.i18n(), MSCols.entry.ordinal(), null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, new RepositoryEntryIconRenderer())); courseListCtr.addColumnDescriptor(new DefaultColumnDescriptor(MSCols.title.i18n(), MSCols.title.ordinal(), TABLE_ACTION_LAUNCH, getLocale())); if(repositoryModule.isManagedRepositoryEntries()) { courseListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(MSCols.externalId.i18n(), MSCols.externalId.ordinal(), TABLE_ACTION_LAUNCH, getLocale())); } courseListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(MSCols.externalRef.i18n(), MSCols.externalRef.ordinal(), TABLE_ACTION_LAUNCH, getLocale())); CustomCellRenderer roleRenderer = new CourseRoleCellRenderer(); courseListCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(MSCols.role.i18n(), MSCols.role.ordinal(), null, getLocale(), ColumnDescriptor.ALIGNMENT_LEFT, roleRenderer){ @Override public int compareTo(int rowa, int rowb) { CourseMemberView cmv1 = (CourseMemberView)table.getTableDataModel().getValueAt(rowa,dataColumn); CourseMemberView cmv2 = (CourseMemberView)table.getTableDataModel().getValueAt(rowb,dataColumn); if(cmv1 == null || cmv1.getMembership() == null) { return -1; } else if(cmv2 == null || cmv2.getMembership() == null) { return 1; } return membershipComparator.compare(cmv1.getMembership(), cmv2.getMembership()); } }); courseListCtr.addColumnDescriptor(new DefaultColumnDescriptor(MSCols.firstTime.i18n(), MSCols.firstTime.ordinal(), null, getLocale())); if(isLastVisitVisible) { courseListCtr.addColumnDescriptor(new DefaultColumnDescriptor(MSCols.lastTime.i18n(), MSCols.lastTime.ordinal(), null, getLocale())); } courseListCtr.addColumnDescriptor(new StaticColumnDescriptor(TABLE_ACTION_EDIT, "table.header.edit", translate("table.header.edit"))); courseListCtr.addColumnDescriptor(new BooleanColumnDescriptor(MSCols.allowLeave.i18n(), MSCols.allowLeave.ordinal(), TABLE_ACTION_UNSUBSCRIBE, translate("table.header.leave"), null)); courseListCtr.setMultiSelect(true); courseListCtr.addMultiSelectAction("table.leave", TABLE_ACTION_UNSUBSCRIBE); tableDataModel = new MembershipDataModel(); courseListCtr.setTableDataModel(tableDataModel); updateModel(); addAsOwner = LinkFactory.createButton("add.course.owner", vc, this); addAsTutor = LinkFactory.createButton("add.course.tutor", vc, this); addAsParticipant = LinkFactory.createButton("add.course.participant", vc, this); vc.put("table.courses", courseListCtr.getInitialComponent()); putInitialPanel(vc); } private void updateModel() { //course membership List<RepositoryEntryMembership> memberships = repositoryManager.getRepositoryEntryMembership(null, editedIdentity); //group membership List<BusinessGroupMembership> groupMemberships = businessGroupService.getBusinessGroupMembership(Collections.<Long>emptyList(), editedIdentity); Collection<Long> groupKeys = new ArrayList<Long>(groupMemberships.size()); for(BusinessGroupMembership membership: groupMemberships) { groupKeys.add(membership.getGroupKey()); } //relation to course List<BGRepositoryEntryRelation> relations = businessGroupService.findRelationToRepositoryEntries(groupKeys, 0, -1); Map<Long,List<Long>> groupKeyToRepoKeyMap = new HashMap<>(); for(BGRepositoryEntryRelation relation:relations) { if(groupKeyToRepoKeyMap.containsKey(relation.getGroupKey())) { groupKeyToRepoKeyMap.get(relation.getGroupKey()).add(relation.getRepositoryEntryKey()); } else { List<Long> repoEntryKeys = new ArrayList<>(2); repoEntryKeys.add(relation.getRepositoryEntryKey()); groupKeyToRepoKeyMap.put(relation.getGroupKey(), repoEntryKeys); } } Map<Long,CourseMemberView> repoKeyToViewMap = new HashMap<Long,CourseMemberView>(); for(RepositoryEntryMembership membership: memberships) { Long repoKey = membership.getRepoKey(); CourseMemberView memberView; if(repoKeyToViewMap.containsKey(repoKey)) { memberView = repoKeyToViewMap.get(repoKey); } else { memberView = new CourseMemberView(repoKey); repoKeyToViewMap.put(repoKey, memberView); } memberView.setFirstTime(membership.getCreationDate()); if(memberView.getLastTime() == null || (memberView.getLastTime() != null && membership.getLastModified() != null && membership.getLastModified().after(memberView.getLastTime()))) { memberView.setLastTime(membership.getLastModified()); } //add the roles if(!memberView.getMembership().isRepoOwner()) { memberView.getMembership().setRepoOwner(membership.isOwner()); } if(!memberView.getMembership().isRepoTutor()) { memberView.getMembership().setRepoTutor(membership.isCoach()); } if(!memberView.getMembership().isRepoParticipant()) { memberView.getMembership().setRepoParticipant(membership.isParticipant()); } } List<BusinessGroupShort> groups = businessGroupService.loadShortBusinessGroups(groupKeys); Map<Long,BusinessGroupShort> groupKeyToGroupMap = new HashMap<Long,BusinessGroupShort>(); for(BusinessGroupShort group:groups) { groupKeyToGroupMap.put(group.getKey(), group); } for(BusinessGroupMembership membership: groupMemberships) { List<Long> repoKeys; if(groupKeyToRepoKeyMap.containsKey(membership.getGroupKey())) { repoKeys = groupKeyToRepoKeyMap.get(membership.getGroupKey()); } else { continue; } BusinessGroupShort group = groupKeyToGroupMap.get(membership.getGroupKey()); if(group == null) { continue; } for(Long repoKey:repoKeys) { CourseMemberView memberView; if(repoKeyToViewMap.containsKey(repoKey)) { memberView = repoKeyToViewMap.get(repoKey); } else { memberView = new CourseMemberView(repoKey); repoKeyToViewMap.put(repoKey, memberView); } memberView.addGroup(group); memberView.setFirstTime(membership.getCreationDate()); if(memberView.getLastTime() == null || ( memberView.getLastTime() != null && membership.getLastModified() != null && membership.getLastModified().after(memberView.getLastTime()))) { memberView.setLastTime(membership.getLastModified()); } switch(membership.getMembership()) { case owner: memberView.getMembership().setGroupTutor(true); break; case participant: memberView.getMembership().setGroupParticipant(true); break; case waiting: memberView.getMembership().setGroupWaiting(true); break; } } } List<RepositoryEntry> entries = repositoryManager.lookupRepositoryEntries(repoKeyToViewMap.keySet()); Map<Long,RepositoryEntry> entryKeyToRepoEntryMap = new HashMap<Long,RepositoryEntry>(); for(RepositoryEntry entry:entries) { entryKeyToRepoEntryMap.put(entry.getKey(), entry); } for(CourseMemberView memberView:repoKeyToViewMap.values()) { RepositoryEntry entry = entryKeyToRepoEntryMap.get(memberView.getRepoKey()); if(entry != null) { memberView.setEntry(entry); boolean managedMembersRepo = RepositoryEntryManagedFlag.isManaged(entry, RepositoryEntryManagedFlag.membersmanagement); memberView.getMembership().setManagedMembersRepo(managedMembersRepo); } } List<CourseMemberView> views = new ArrayList<CourseMemberView>(repoKeyToViewMap.values()); tableDataModel.setObjects(views); courseListCtr.modelChanged(); } @Override protected void doDispose() { // } @Override protected void event(UserRequest ureq, Component source, Event event) { if(source == addAsOwner) { doSearchRepoEntries(ureq, SearchType.owner, translate("add.course.owner")); } else if(source == addAsTutor) { doSearchRepoEntries(ureq, SearchType.tutor, translate("add.course.tutor")); } else if(source == addAsParticipant) { doSearchRepoEntries(ureq, SearchType.participant, translate("add.course.participant")); } } @Override protected void event(UserRequest ureq, Controller source, Event event) { if (source == courseListCtr){ if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) { TableEvent te = (TableEvent) event; CourseMemberView item = tableDataModel.getObject(te.getRowId()); if (TABLE_ACTION_LAUNCH.equals(te.getActionId())) { launch(ureq, item.getRepoKey()); } else if (TABLE_ACTION_UNSUBSCRIBE.equals(te.getActionId())){ doLeave(ureq, Collections.singletonList(item)); } else if (TABLE_ACTION_EDIT.equals(te.getActionId())){ doOpenEdit(ureq, item); } } else if (event instanceof TableMultiSelectEvent) { TableMultiSelectEvent mse = (TableMultiSelectEvent)event; List<CourseMemberView> items = tableDataModel.getObjects(mse.getSelection()); if (TABLE_ACTION_UNSUBSCRIBE.equals(mse.getAction())){ doLeave(ureq, items); } } } else if(source == repoSearchCtr) { SearchType type = (SearchType)repoSearchCtr.getUserObject(); if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { // repository search controller done RepositoryEntry re = repoSearchCtr.getSelectedEntry(); removeAsListenerAndDispose(repoSearchCtr); cmc.deactivate(); doConfirmSendEmail(ureq, Collections.singletonList(re), type); } else if(event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRIES_SELECTED) { // repository search controller done List<RepositoryEntry> res = repoSearchCtr.getSelectedEntries(); removeAsListenerAndDispose(repoSearchCtr); cmc.deactivate(); if (res != null && !res.isEmpty()) { doConfirmSendEmail(ureq, res, type); } } } else if(source == editSingleMemberCtrl) { cmc.deactivate(); if(event instanceof MemberPermissionChangeEvent) { MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)event; RepositoryEntry re = editSingleMemberCtrl.getRepositoryEntry(); doConfirmChangePermission(ureq, e, re); } } else if(source == confirmSendMailBox) { boolean sendMail = DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event); Object confirmation = confirmSendMailBox.getUserObject(); if(confirmation instanceof ConfirmAdd) { ConfirmAdd addInfos = (ConfirmAdd)confirmation; doAddToRepositoryEntry(ureq, addInfos.getEntries(), addInfos.getType(), sendMail); } else if(confirmation instanceof ConfirmEdit) { ConfirmEdit editInfos = (ConfirmEdit)confirmation; doChangePermission(ureq, editInfos.getChangeEvent(), editInfos.getEntry(), sendMail); } updateModel(); } else if(source == removeFromCourseDlg) { if(event == Event.DONE_EVENT) { boolean sendMail = removeFromCourseDlg.isSendMail(); List<BusinessGroup> groupsToDelete = removeFromCourseDlg.getGroupsToDelete(); List<BusinessGroup> groupsToLeave = removeFromCourseDlg.getGroupsToLeave(); List<RepositoryEntry> repoEntriesToLeave = removeFromCourseDlg.getRepoEntriesToLeave(); removeUserFromCourse(ureq, repoEntriesToLeave, groupsToLeave, groupsToDelete, sendMail); } cmc.deactivate(); cleanUp(); } else if(source == cmc) { cleanUp(); } } private void cleanUp() { removeAsListenerAndDispose(removeFromCourseDlg); removeAsListenerAndDispose(repoSearchCtr); removeAsListenerAndDispose(cmc); removeFromCourseDlg = null; repoSearchCtr = null; cmc = null; } private void launch(UserRequest ureq, Long repoKey) { NewControllerFactory.getInstance().launch("[RepositoryEntry:" + repoKey + "]", ureq, getWindowControl()); } private void doOpenEdit(UserRequest ureq, CourseMemberView member) { RepositoryEntry repoEntry = repositoryManager.lookupRepositoryEntry(member.getRepoKey()); editSingleMemberCtrl = new EditSingleMembershipController(ureq, getWindowControl(), editedIdentity, repoEntry, null, true, false); listenTo(editSingleMemberCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), editSingleMemberCtrl.getInitialComponent(), true, translate("edit.member")); cmc.activate(); listenTo(cmc); } private void doConfirmChangePermission(UserRequest ureq, MemberPermissionChangeEvent e, RepositoryEntry re) { boolean groupChangesEmpty = e.getGroupChanges() == null || e.getGroupChanges().isEmpty(); boolean repoChangesEmpty = e.getRepoOwner() == null && e.getRepoParticipant() == null && e.getRepoTutor() == null; if(groupChangesEmpty && repoChangesEmpty) { //nothing to do return; } boolean mailMandatory = repositoryModule.isMandatoryEnrolmentEmail(ureq.getUserSession().getRoles()); if(mailMandatory) { doChangePermission(ureq, e, re, true); } else { confirmSendMailBox = activateYesNoDialog(ureq, null, translate("dialog.modal.bg.send.mail"), confirmSendMailBox); confirmSendMailBox.setUserObject(new ConfirmEdit(e, re)); } } private void doChangePermission(UserRequest ureq, MemberPermissionChangeEvent e, RepositoryEntry re, boolean sendMail) { MailPackage mailing = new MailPackage(sendMail); if(re != null) { List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e); repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), re, changes, mailing); } businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges(), mailing); //make sure all is committed before loading the model again (I see issues without) DBFactory.getInstance().commitAndCloseSession(); updateModel(); } private void doSearchRepoEntries(UserRequest ureq, SearchType type, String title) { removeAsListenerAndDispose(repoSearchCtr); removeAsListenerAndDispose(cmc); RepositoryEntryFilter filter = new ManagedEntryfilter(); repoSearchCtr = new ReferencableEntriesSearchController(getWindowControl(), ureq, new String[]{ CourseModule.getCourseTypeName() }, filter, translate("choose"), false, false, true, true, Can.all); repoSearchCtr.setUserObject(type); listenTo(repoSearchCtr); cmc = new CloseableModalController(getWindowControl(), translate("close"), repoSearchCtr.getInitialComponent(), true, title); listenTo(cmc); cmc.activate(); } private void doConfirmSendEmail(UserRequest ureq, Collection<RepositoryEntry> res, SearchType type) { boolean mailMandatory = repositoryModule.isMandatoryEnrolmentEmail(ureq.getUserSession().getRoles()); if(mailMandatory) { doAddToRepositoryEntry(ureq, res, type, false); updateModel(); } else { confirmSendMailBox = activateYesNoDialog(ureq, null, translate("dialog.modal.bg.send.mail"), confirmSendMailBox); confirmSendMailBox.setUserObject(new ConfirmAdd(type, res)); } } private void doAddToRepositoryEntry(UserRequest ureq, Collection<RepositoryEntry> res, SearchType type, boolean sendEmail) { //commit changes to the repository entry MailerResult result = new MailerResult(); MailPackage reMailing = new MailPackage(result, getWindowControl().getBusinessControl().getAsString(), sendEmail); RepositoryEntryPermissionChangeEvent changeEvent = new RepositoryEntryPermissionChangeEvent(editedIdentity); switch(type) { case owner: changeEvent.setRepoOwner(Boolean.TRUE); break; case tutor: changeEvent.setRepoTutor(Boolean.TRUE); break; case participant: changeEvent.setRepoParticipant(Boolean.TRUE); break; } List<RepositoryEntryPermissionChangeEvent> repoChanges = Collections.singletonList(changeEvent); for(RepositoryEntry repoEntry:res) { repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, reMailing); } //make sure all is committed before loading the model again (I see issues without) DBFactory.getInstance().commitAndCloseSession(); MailHelper.printErrorsAndWarnings(result, getWindowControl(), getLocale()); } /** * The method check the managed flags * @param ureq * @param views */ private void doLeave(UserRequest ureq, Collection<CourseMemberView> views) { List<Long> groupKeys = new ArrayList<Long>(); List<RepositoryEntry> repoEntryToLeave = new ArrayList<RepositoryEntry>(); for(CourseMemberView view:views) { for(BusinessGroupShort group:view.getGroups()) { if(!BusinessGroupManagedFlag.isManaged(group.getManagedFlags(), BusinessGroupManagedFlag.membersmanagement)) { groupKeys.add(group.getKey()); } } RepositoryEntry re = repositoryManager.lookupRepositoryEntry(view.getRepoKey()); if(!RepositoryEntryManagedFlag.isManaged(re, RepositoryEntryManagedFlag.membersmanagement)) { repoEntryToLeave.add(re); if(view.getMembership().isRepoOwner()) { int numOfOwners = repositoryService.countMembers(re, GroupRoles.owner.name()); if(numOfOwners == 1) { showError("error.atleastone", view.getDisplayName()); return;//break the process } } } } List<BusinessGroup> groupsToLeave = businessGroupService.loadBusinessGroups(groupKeys); List<BusinessGroup> groupsToDelete = new ArrayList<BusinessGroup>(1); for(BusinessGroup group:groupsToLeave) { int numOfOwners = businessGroupService.countMembers(group, GroupRoles.coach.name()); int numOfParticipants = businessGroupService.countMembers(group, GroupRoles.participant.name()); if ((numOfOwners == 1 && numOfParticipants == 0) || (numOfOwners == 0 && numOfParticipants == 1)) { groupsToDelete.add(group); } } removeFromCourseDlg = new CourseLeaveDialogBoxController(ureq, getWindowControl(), editedIdentity, repoEntryToLeave, groupsToLeave, groupsToDelete); listenTo(removeFromCourseDlg); cmc = new CloseableModalController(getWindowControl(), translate("close"), removeFromCourseDlg.getInitialComponent(), true, translate("unsubscribe.title")); cmc.activate(); listenTo(cmc); } private void removeUserFromCourse(UserRequest ureq, List<RepositoryEntry> repoEntriesToLeave, List<BusinessGroup> groupsToLeave, List<BusinessGroup> groupsToDelete, boolean doSendMail) { List<Identity> membersToRemove = Collections.singletonList(editedIdentity); for(BusinessGroup group:groupsToLeave) { if (groupsToDelete.contains(group)) { // really delete the group as it has no more owners/participants if(doSendMail) { String businessPath = getWindowControl().getBusinessControl().getAsString(); businessGroupService.deleteBusinessGroupWithMail(group, businessPath, getIdentity(), getLocale()); } else { businessGroupService.deleteBusinessGroup(group); } } else { // 1) remove as owner if (businessGroupService.hasRoles(editedIdentity, group, GroupRoles.coach.name())) { businessGroupService.removeOwners(ureq.getIdentity(), membersToRemove, group); } MailPackage mailing = new MailPackage(doSendMail); // 2) remove as participant businessGroupService.removeParticipants(getIdentity(), membersToRemove, group, mailing); MailHelper.printErrorsAndWarnings(mailing.getResult(), getWindowControl(), getLocale()); } } for(RepositoryEntry repoEntry:repoEntriesToLeave) { if(isRepoMember(repoEntry)) { MailPackage mailing = new MailPackage(doSendMail); repositoryManager.removeMembers(getIdentity(), membersToRemove, repoEntry, mailing); } } updateModel(); StringBuilder groupNames = new StringBuilder(); for(BusinessGroup group:groupsToLeave) { if(groupNames.length() > 0) groupNames.append(", "); groupNames.append(group.getName()); } showInfo("unsubscribe.successful"); } private boolean isRepoMember(RepositoryEntry repoEntry) { for(CourseMemberView view:tableDataModel.getObjects()) { if(repoEntry.getKey().equals(view.getRepoKey())) { CourseMembership membership = view.getMembership(); return membership.isRepoOwner() || membership.isRepoTutor() || membership.isRepoParticipant(); } } return false; } private enum SearchType { owner, tutor, participant } public enum MSCols { key("table.header.key"), entry("table.header.typeimg"), title("cif.displayname"), externalId("table.header.externalid"), externalRef("table.header.externalref"), role("table.header.role"), lastTime("table.header.lastTime"), firstTime("table.header.firstTime"), allowLeave("table.header.leave"); private final String i18n; private MSCols(String i18n) { this.i18n = i18n; } public String i18n() { return i18n; } } private static class ConfirmAdd { private final SearchType type; private final Collection<RepositoryEntry> entries; public ConfirmAdd(SearchType type, Collection<RepositoryEntry> entries) { this.type = type; this.entries = entries; } public SearchType getType() { return type; } public Collection<RepositoryEntry> getEntries() { return entries; } } private class ConfirmEdit { private final RepositoryEntry entry; private final MemberPermissionChangeEvent e; public ConfirmEdit(MemberPermissionChangeEvent e, RepositoryEntry entry) { this.e = e; this.entry = entry; } public RepositoryEntry getEntry() { return entry; } public MemberPermissionChangeEvent getChangeEvent() { return e; } } private static class MembershipDataModel extends DefaultTableDataModel<CourseMemberView> { public MembershipDataModel() { super(Collections.<CourseMemberView>emptyList()); } @Override public int getColumnCount() { return 4; } @Override public Object getValueAt(int row, int col) { CourseMemberView view = getObject(row); switch(MSCols.values()[col]) { case key: return view.getRepoKey(); case entry: return view.getEntry(); case title: return view.getDisplayName(); case externalId: return view.getExternalId(); case externalRef: return view.getExternalRef(); case role: return view; case firstTime: return view.getFirstTime(); case lastTime: return view.getLastTime(); case allowLeave: return view.isFullyManaged() ? Boolean.FALSE : Boolean.TRUE; default: return "ERROR"; } } } private class CourseMemberView { private final Long repoKey; private RepositoryEntry entry; private Date firstTime; private Date lastTime; private final CourseMembership membership = new CourseMembership(); private List<BusinessGroupShort> groups; public CourseMemberView(Long repoKey) { this.repoKey = repoKey; } public Long getRepoKey() { return repoKey; } public String getDisplayName() { return entry == null ? "???" : entry.getDisplayname(); } public String getExternalId() { return entry == null ? "" : entry.getExternalId(); } public String getExternalRef() { return entry == null ? "" : entry.getExternalRef(); } public RepositoryEntry getEntry() { return entry; } public void setEntry(RepositoryEntry entry) { this.entry = entry; } public Date getFirstTime() { return firstTime; } public void setFirstTime(Date firstTime) { if(firstTime == null) return; if(this.firstTime == null || this.firstTime.compareTo(firstTime) > 0) { this.firstTime = firstTime; } } public Date getLastTime() { return lastTime; } public void setLastTime(Date lastTime) { if(lastTime == null) return; if(this.lastTime == null || this.lastTime.compareTo(lastTime) < 0) { this.lastTime = lastTime; } } public void addGroup(BusinessGroupShort group) { if(groups == null) groups = new ArrayList<BusinessGroupShort>(); groups.add(group); } public CourseMembership getMembership() { return membership; } public List<BusinessGroupShort> getGroups() { return groups == null ? Collections.<BusinessGroupShort>emptyList() : new ArrayList<BusinessGroupShort>(groups); } public boolean isFullyManaged() { if(membership != null && !membership.isManagedMembersRepo() && (membership.isRepoOwner() || membership.isRepoTutor() || membership.isRepoParticipant())) { return false; } if(groups != null) { for(BusinessGroupShort group:groups) { if(!BusinessGroupManagedFlag.isManaged(group.getManagedFlags(), BusinessGroupManagedFlag.membersmanagement)) { return false; } } } return true; } } private class CourseRoleCellRenderer implements CustomCellRenderer { @Override public void render(StringOutput sb, Renderer renderer, Object val, Locale locale, int alignment, String action) { if (val instanceof CourseMemberView) { CourseMemberView membership = (CourseMemberView)val; boolean and = false; if(membership.getMembership().isRepoOwner()) { and = and(sb, and); sb.append(translate("role.repo.owner")); } if(membership.getMembership().isRepoTutor()) { and = and(sb, and); sb.append(translate("role.repo.tutor")); } if(membership.getMembership().isRepoParticipant()) { and = and(sb, and); sb.append(translate("role.repo.participant")); } if(membership.getMembership().isGroupTutor()) { and = and(sb, and); sb.append(translate("role.group.tutor")); } if(membership.getMembership().isGroupParticipant()) { and = and(sb, and); sb.append(translate("role.group.participant")); } if(membership.getMembership().isGroupWaiting()) { and = and(sb, and); sb.append(translate("role.group.waiting")); } } } private final boolean and(StringOutput sb, boolean and) { if(and) sb.append(", "); return true; } } private static class ManagedEntryfilter implements RepositoryEntryFilter { @Override public boolean accept(RepositoryEntry re) { return !RepositoryEntryManagedFlag.isManaged(re, RepositoryEntryManagedFlag.membersmanagement); } } }