/** * <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.ui.main; import java.util.ArrayList; 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.concurrent.atomic.AtomicInteger; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.SearchIdentityParams; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.commons.persistence.PersistenceHelper; import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableSearchEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; 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.CloseableCalloutWindowController; 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.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; import org.olat.core.id.UserConstants; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.mail.ContactList; import org.olat.core.util.mail.ContactMessage; import org.olat.core.util.mail.MailPackage; import org.olat.core.util.resource.OresHelper; import org.olat.core.util.session.UserSessionManager; import org.olat.course.assessment.manager.UserCourseInformationsManager; import org.olat.course.member.MemberListController; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupManagedFlag; import org.olat.group.BusinessGroupMembership; import org.olat.group.BusinessGroupModule; import org.olat.group.BusinessGroupService; import org.olat.group.BusinessGroupShort; import org.olat.group.model.BusinessGroupMembershipChange; import org.olat.group.ui.main.MemberListTableModel.Cols; import org.olat.instantMessaging.InstantMessagingModule; import org.olat.instantMessaging.InstantMessagingService; import org.olat.instantMessaging.OpenInstantMessageEvent; import org.olat.instantMessaging.model.Buddy; import org.olat.instantMessaging.model.Presence; import org.olat.modules.co.ContactFormController; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryManagedFlag; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryService; import org.olat.repository.model.RepositoryEntryMembership; import org.olat.repository.model.RepositoryEntryPermissionChangeEvent; import org.olat.resource.OLATResource; import org.olat.resource.accesscontrol.ACService; import org.olat.resource.accesscontrol.ResourceReservation; import org.olat.user.UserInfoMainController; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; import org.springframework.beans.factory.annotation.Autowired; /** * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public abstract class AbstractMemberListController extends FormBasicController implements Activateable2 { protected static final String USER_PROPS_ID = MemberListController.class.getCanonicalName(); public static final int USER_PROPS_OFFSET = 500; public static final String TABLE_ACTION_EDIT = "tbl_edit"; public static final String TABLE_ACTION_MAIL = "tbl_mail"; public static final String TABLE_ACTION_REMOVE = "tbl_remove"; public static final String TABLE_ACTION_GRADUATE = "tbl_graduate"; public static final String TABLE_ACTION_IM = "tbl_im"; public static final String TABLE_ACTION_HOME = "tbl_home"; public static final String TABLE_ACTION_CONTACT = "tbl_contact"; public static final String TABLE_ACTION_ASSESSMENT = "tbl_assessment"; protected FlexiTableElement membersTable; protected MemberListTableModel memberListModel; protected final TooledStackedPanel toolbarPanel; private FormLink editButton, mailButton, removeButton; private ToolsController toolsCtrl; protected CloseableModalController cmc; private ContactFormController contactCtrl; private DialogBoxController confirmSendMailBox; private UserInfoMainController visitingCardCtrl; private EditMembershipController editMembersCtrl; private MemberLeaveConfirmationController leaveDialogBox; private CloseableCalloutWindowController toolsCalloutCtrl; private EditSingleMembershipController editSingleMemberCtrl; private final List<UserPropertyHandler> userPropertyHandlers; private final AtomicInteger counter = new AtomicInteger(); protected final RepositoryEntry repoEntry; private final BusinessGroup businessGroup; private final boolean isLastVisitVisible; private final boolean isAdministrativeUser; private final boolean chatEnabled; private final boolean readOnly; private boolean overrideManaged = false; private final boolean globallyManaged; @Autowired protected UserManager userManager; @Autowired protected BaseSecurity securityManager; @Autowired private BaseSecurityModule securityModule; @Autowired private RepositoryService repositoryService; @Autowired private RepositoryManager repositoryManager; @Autowired private BusinessGroupService businessGroupService; @Autowired private UserCourseInformationsManager userInfosMgr; @Autowired private BusinessGroupModule groupModule; @Autowired private ACService acService; @Autowired private InstantMessagingModule imModule; @Autowired private InstantMessagingService imService; @Autowired private UserSessionManager sessionManager; public AbstractMemberListController(UserRequest ureq, WindowControl wControl, RepositoryEntry repoEntry, String page,boolean readOnly, TooledStackedPanel stackPanel) { this(ureq, wControl, repoEntry, null, page, readOnly, stackPanel, Util.createPackageTranslator(AbstractMemberListController.class, ureq.getLocale())); } public AbstractMemberListController(UserRequest ureq, WindowControl wControl, BusinessGroup group, String page, boolean readOnly, TooledStackedPanel stackPanel) { this(ureq, wControl, null, group, page, readOnly, stackPanel, Util.createPackageTranslator(AbstractMemberListController.class, ureq.getLocale())); } protected AbstractMemberListController(UserRequest ureq, WindowControl wControl, RepositoryEntry repoEntry, BusinessGroup group, String page, boolean readOnly, TooledStackedPanel stackPanel, Translator translator) { super(ureq, wControl, page, Util.createPackageTranslator(UserPropertyHandler.class, ureq.getLocale(), translator)); this.businessGroup = group; this.repoEntry = repoEntry; this.toolbarPanel = stackPanel; this.readOnly = readOnly; globallyManaged = calcGloballyManaged(); Roles roles = ureq.getUserSession().getRoles(); chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled() && !readOnly; isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles); isLastVisitVisible = securityModule.isUserLastVisitVisible(roles); userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, isAdministrativeUser); initForm(ureq); } public void overrideManaged(UserRequest ureq, boolean override) { if(ureq.getUserSession().getRoles().isOLATAdmin()) { overrideManaged = override; editButton.setVisible((!globallyManaged || overrideManaged) && !readOnly); removeButton.setVisible((!globallyManaged || overrideManaged) && !readOnly); flc.setDirty(true); } } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); SortKey defaultSortKey = initColumns(columnsModel); memberListModel = new MemberListTableModel(columnsModel, imModule.isOnlineStatusEnabled()); membersTable = uifactory.addTableElement(getWindowControl(), "memberList", memberListModel, 20, false, getTranslator(), formLayout); membersTable.setMultiSelect(true); membersTable.setEmtpyTableMessageKey("nomembers"); membersTable.setAndLoadPersistedPreferences(ureq, this.getClass().getSimpleName()); membersTable.setSearchEnabled(true); membersTable.setExportEnabled(true); membersTable.setSelectAllEnable(true); membersTable.setElementCssClass("o_sel_member_list"); if(defaultSortKey != null) { FlexiTableSortOptions options = new FlexiTableSortOptions(); options.setDefaultOrderBy(defaultSortKey); membersTable.setSortSettings(options); } editButton = uifactory.addFormLink("edit.members", formLayout, Link.BUTTON); editButton.setVisible((!globallyManaged || overrideManaged) && !readOnly); mailButton = uifactory.addFormLink("table.header.mail", formLayout, Link.BUTTON); removeButton = uifactory.addFormLink("table.header.remove", formLayout, Link.BUTTON); removeButton.setVisible((!globallyManaged || overrideManaged) && !readOnly); } private boolean calcGloballyManaged() { boolean managed = true; if(businessGroup != null) { managed &= BusinessGroupManagedFlag.isManaged(businessGroup, BusinessGroupManagedFlag.membersmanagement); } if(repoEntry != null) { boolean managedEntry = RepositoryEntryManagedFlag.isManaged(repoEntry, RepositoryEntryManagedFlag.membersmanagement); managed &= managedEntry; List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, repoEntry, 0, -1); for(BusinessGroup group:groups) { managed &= BusinessGroupManagedFlag.isManaged(group, BusinessGroupManagedFlag.membersmanagement); } } return managed; } @Override protected void doDispose() { // } private SortKey initColumns(FlexiTableColumnModel columnsModel) { SortKey defaultSortKey = null; String editAction = readOnly ? null : TABLE_ACTION_EDIT; if(chatEnabled) { DefaultFlexiColumnModel chatCol = new DefaultFlexiColumnModel(Cols.online.i18n(), Cols.online.ordinal()); chatCol.setExportable(false); columnsModel.addFlexiColumnModel(chatCol); } if(isAdministrativeUser) { FlexiCellRenderer renderer = new StaticFlexiCellRenderer(editAction, new TextFlexiCellRenderer()); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.username.i18n(), Cols.username.ordinal(), editAction, true, Cols.username.name(), renderer)); defaultSortKey = new SortKey(Cols.username.name(), true); } int colPos = USER_PROPS_OFFSET; for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { if (userPropertyHandler == null) continue; String propName = userPropertyHandler.getName(); boolean visible = userManager.isMandatoryUserProperty(USER_PROPS_ID , userPropertyHandler); FlexiColumnModel col; if(UserConstants.FIRSTNAME.equals(propName) || UserConstants.LASTNAME.equals(propName)) { col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, editAction, true, propName, new StaticFlexiCellRenderer(editAction, new TextFlexiCellRenderer())); } else { col = new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, true, propName); } columnsModel.addFlexiColumnModel(col); colPos++; if(defaultSortKey == null) { defaultSortKey = new SortKey(propName, true); } } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), true, Cols.firstTime.name())); if(isLastVisitVisible) { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), true, Cols.lastTime.name())); } CourseRoleCellRenderer roleRenderer = new CourseRoleCellRenderer(getLocale()); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.role.i18n(), Cols.role.ordinal(), true, Cols.role.name(), roleRenderer)); if(repoEntry != null) { GroupCellRenderer groupRenderer = new GroupCellRenderer(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.groups.i18n(), Cols.groups.ordinal(), true, Cols.groups.name(), groupRenderer)); } DefaultFlexiColumnModel toolsCol = new DefaultFlexiColumnModel(Cols.tools.i18n(), Cols.tools.ordinal()); toolsCol.setExportable(false); toolsCol.setAlwaysVisible(true); columnsModel.addFlexiColumnModel(toolsCol); return defaultSortKey; } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { // } @Override protected void formOK(UserRequest ureq) { // } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source == membersTable) { if(event instanceof SelectionEvent) { SelectionEvent se = (SelectionEvent)event; String cmd = se.getCommand(); MemberView row = memberListModel.getObject(se.getIndex()); if(TABLE_ACTION_IM.equals(cmd)) { doIm(ureq, row); } else if(TABLE_ACTION_EDIT.equals(cmd)) { openEdit(ureq, row); } } else if(event instanceof FlexiTableSearchEvent) { String cmd = event.getCommand(); if(FlexiTableSearchEvent.SEARCH.equals(event.getCommand())) { FlexiTableSearchEvent se = (FlexiTableSearchEvent)event; String search = se.getSearch(); doSearch(search); } else if(FlexiTableSearchEvent.QUICK_SEARCH.equals(event.getCommand())) { FlexiTableSearchEvent se = (FlexiTableSearchEvent)event; String search = se.getSearch(); doSearch(search); } else if(FlexiTableSearchEvent.RESET.getCommand().equals(cmd)) { doResetSearch(); } } } else if(editButton == source) { List<MemberView> selectedItems = getMultiSelectedRows(); openEdit(ureq, selectedItems); } else if(mailButton == source) { List<MemberView> selectedItems = getMultiSelectedRows(); doSendMail(ureq, selectedItems); } else if(removeButton == source) { List<MemberView> selectedItems = getMultiSelectedRows(); confirmDelete(ureq, selectedItems); } else if(source instanceof FormLink) { FormLink link = (FormLink)source; String cmd = link.getCmd(); if("tools".equals(cmd)) { MemberView row = (MemberView)link.getUserObject(); doOpenTools(ureq, row, link); } else if("im".equals(cmd)) { MemberView row = (MemberView)link.getUserObject(); doIm(ureq, row); } } super.formInnerEvent(ureq, source, event); } private List<MemberView> getMultiSelectedRows() { Set<Integer> selections = membersTable.getMultiSelectedIndex(); List<MemberView> rows = new ArrayList<>(selections.size()); if(selections.isEmpty()) { //do nothing } else { for(Integer i:selections) { int index = i.intValue(); if(index >= 0 && index < memberListModel.getRowCount()) { MemberView row = memberListModel.getObject(index); if(row != null) { rows.add(row); } } } } return rows; } @Override protected void event(UserRequest ureq, Controller source, Event event) { if (source == leaveDialogBox) { if (Event.DONE_EVENT == event) { List<Identity> members = leaveDialogBox.getIdentities(); doLeave(members, leaveDialogBox.isSendMail()); reloadModel(); } cmc.deactivate(); cleanUpPopups(); } else if(source == editMembersCtrl) { cmc.deactivate(); if(event instanceof MemberPermissionChangeEvent) { MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)event; doConfirmChangePermission(ureq, e, editMembersCtrl.getMembers()); } } else if(source == editSingleMemberCtrl) { cmc.deactivate(); cleanUpPopups(); if(event instanceof MemberPermissionChangeEvent) { MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)event; doConfirmChangePermission(ureq, e, null); } } else if(confirmSendMailBox == source) { boolean sendMail = DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event); MailConfirmation confirmation = (MailConfirmation)confirmSendMailBox.getUserObject(); MemberPermissionChangeEvent e =confirmation.getE(); if(e.getMember() != null) { doChangePermission(ureq, e, sendMail); } else { doChangePermission(ureq, e, confirmation.getMembers(), sendMail); } } else if (source == contactCtrl) { if(cmc != null) { cmc.deactivate(); } else { toolbarPanel.popController(contactCtrl); } cleanUpPopups(); } else if(toolsCtrl == source) { if(event == Event.DONE_EVENT) { toolsCalloutCtrl.deactivate(); cleanUpPopups(); } } else if (source == cmc) { cleanUpPopups(); } } /** * Aggressive clean up all popup controllers */ protected void cleanUpPopups() { removeAsListenerAndDispose(cmc); removeAsListenerAndDispose(editMembersCtrl); removeAsListenerAndDispose(editSingleMemberCtrl); removeAsListenerAndDispose(leaveDialogBox); removeAsListenerAndDispose(contactCtrl); cmc = null; contactCtrl = null; leaveDialogBox = null; editMembersCtrl = null; editSingleMemberCtrl = null; } protected void confirmDelete(UserRequest ureq, List<MemberView> members) { if(members.isEmpty()) { showWarning("error.select.one.user"); } else { int numOfOwners = repoEntry == null ? businessGroupService.countMembers(businessGroup, GroupRoles.coach.name()) : repositoryService.countMembers(repoEntry, GroupRoles.owner.name()); int numOfRemovedOwner = 0; List<Long> identityKeys = new ArrayList<Long>(); for(MemberView member:members) { identityKeys.add(member.getIdentityKey()); if(member.getMembership().isOwner()) { numOfRemovedOwner++; } } if(numOfRemovedOwner == 0 || numOfOwners - numOfRemovedOwner > 0) { List<Identity> ids = securityManager.loadIdentityByKeys(identityKeys); leaveDialogBox = new MemberLeaveConfirmationController(ureq, getWindowControl(), ids); listenTo(leaveDialogBox); cmc = new CloseableModalController(getWindowControl(), translate("close"), leaveDialogBox.getInitialComponent(), true, translate("edit.member")); cmc.activate(); listenTo(cmc); } else { showWarning("error.atleastone"); } } } protected void openEdit(UserRequest ureq, MemberView member) { if(editSingleMemberCtrl != null) return; Identity identity = securityManager.loadIdentityByKey(member.getIdentityKey()); editSingleMemberCtrl = new EditSingleMembershipController(ureq, getWindowControl(), identity, repoEntry, businessGroup, false, overrideManaged); listenTo(editSingleMemberCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), editSingleMemberCtrl.getInitialComponent(), true, translate("edit.member")); cmc.activate(); listenTo(cmc); } protected void openEdit(UserRequest ureq, List<MemberView> members) { if(members.isEmpty()) { showWarning("error.select.one.user"); } else { List<Long> identityKeys = getMemberKeys(members); List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys); if(identities.size() == 1) { editSingleMemberCtrl = new EditSingleMembershipController(ureq, getWindowControl(), identities.get(0), repoEntry, businessGroup, false, overrideManaged); listenTo(editSingleMemberCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), editSingleMemberCtrl.getInitialComponent(), true, translate("edit.member")); } else { editMembersCtrl = new EditMembershipController(ureq, getWindowControl(), identities, repoEntry, businessGroup, overrideManaged); listenTo(editMembersCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), editMembersCtrl.getInitialComponent(), true, translate("edit.member")); } cmc.activate(); listenTo(cmc); } } protected void doSearch(String search) { getSearchParams().setSearchString(search); reloadModel(); } protected void doResetSearch() { getSearchParams().setSearchString(null); reloadModel(); } private void doOpenTools(UserRequest ureq, MemberView row, FormLink link) { removeAsListenerAndDispose(toolsCtrl); removeAsListenerAndDispose(toolsCalloutCtrl); toolsCtrl = new ToolsController(ureq, getWindowControl(), row); listenTo(toolsCtrl); toolsCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), toolsCtrl.getInitialComponent(), link.getFormDispatchId(), "", true, ""); listenTo(toolsCalloutCtrl); toolsCalloutCtrl.activate(); } /** * Open private chat * @param ureq * @param member */ protected void doIm(UserRequest ureq, MemberView member) { Buddy buddy = imService.getBuddyById(member.getIdentityKey()); OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy); ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES); } protected void doConfirmChangePermission(UserRequest ureq, MemberPermissionChangeEvent e, List<Identity> members) { 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 = groupModule.isMandatoryEnrolmentEmail(ureq.getUserSession().getRoles()); if(mailMandatory) { if(members == null) { doChangePermission(ureq, e, true); } else { doChangePermission(ureq, e, members, true); } } else { confirmSendMailBox = activateYesNoDialog(ureq, null, translate("dialog.modal.bg.send.mail"), confirmSendMailBox); confirmSendMailBox.setUserObject(new MailConfirmation(e, members)); } } protected void doChangePermission(UserRequest ureq, MemberPermissionChangeEvent e, boolean sendMail) { MailPackage mailing = new MailPackage(sendMail); if(repoEntry != null) { List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e); repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, 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(); reloadModel(); } protected void doChangePermission(UserRequest ureq, MemberPermissionChangeEvent changes, List<Identity> members, boolean sendMail) { MailPackage mailing = new MailPackage(sendMail); if(repoEntry != null) { List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members); repositoryManager.updateRepositoryEntryMemberships(getIdentity(), ureq.getUserSession().getRoles(), repoEntry, repoChanges, mailing); } //commit all changes to the group memberships List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members); businessGroupService.updateMemberships(getIdentity(), allModifications, mailing); reloadModel(); } protected void doLeave(List<Identity> members, boolean sendMail) { MailPackage mailing = new MailPackage(sendMail); if(repoEntry != null) { businessGroupService.removeMembers(getIdentity(), members, repoEntry.getOlatResource(), mailing); repositoryManager.removeMembers(getIdentity(), members, repoEntry, mailing); } else { businessGroupService.removeMembers(getIdentity(), members, businessGroup.getResource(), mailing); } reloadModel(); } protected void doSendMail(UserRequest ureq, List<MemberView> members) { List<Long> identityKeys = getMemberKeys(members); List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys); if(identities.isEmpty()) { showWarning("error.msg.send.no.rcps"); return; } ContactMessage contactMessage = new ContactMessage(getIdentity()); String name = repoEntry != null ? repoEntry.getDisplayname() : businessGroup.getName(); ContactList contactList = new ContactList(name); contactList.addAllIdentites(identities); contactMessage.addEmailTo(contactList); contactCtrl = new ContactFormController(ureq, getWindowControl(), true, false, false, contactMessage); listenTo(contactCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), contactCtrl.getInitialComponent(), true, translate("mail.member")); cmc.activate(); listenTo(cmc); } protected void doGraduate(List<MemberView> members) { if(businessGroup != null) { List<Long> identityKeys = getMemberKeys(members); List<Identity> identitiesToGraduate = securityManager.loadIdentityByKeys(identityKeys); businessGroupService.moveIdentityFromWaitingListToParticipant(getIdentity(), identitiesToGraduate, businessGroup, null); } else { Map<Long, BusinessGroup> groupsMap = new HashMap<>(); Map<BusinessGroup, List<Identity>> graduatesMap = new HashMap<>(); for(MemberView member:members) { List<BusinessGroupShort> groups = member.getGroups(); if(groups != null && groups.size() > 0) { Identity memberIdentity = securityManager.loadIdentityByKey(member.getIdentityKey()); for(BusinessGroupShort group:groups) { if(businessGroupService.hasRoles(memberIdentity, group, GroupRoles.waiting.name())) { BusinessGroup fullGroup = groupsMap.get(group.getKey()); if(fullGroup == null) { fullGroup = businessGroupService.loadBusinessGroup(group.getKey()); groupsMap.put(group.getKey(), fullGroup); } List<Identity> identitiesToGraduate = graduatesMap.get(fullGroup); if(identitiesToGraduate == null) { identitiesToGraduate = new ArrayList<>(); graduatesMap.put(fullGroup, identitiesToGraduate); } identitiesToGraduate.add(memberIdentity); } } } } for(Map.Entry<BusinessGroup, List<Identity>> entry:graduatesMap.entrySet()) { BusinessGroup fullGroup = entry.getKey(); List<Identity> identitiesToGraduate = entry.getValue(); businessGroupService.moveIdentityFromWaitingListToParticipant(getIdentity(), identitiesToGraduate, fullGroup, null); } } reloadModel(); } protected void doOpenVisitingCard(UserRequest ureq, MemberView member) { removeAsListenerAndDispose(visitingCardCtrl); Identity choosenIdentity = securityManager.loadIdentityByKey(member.getIdentityKey()); visitingCardCtrl = new UserInfoMainController(ureq, getWindowControl(), choosenIdentity, false, false); listenTo(visitingCardCtrl); String fullname = userManager.getUserDisplayName(choosenIdentity); toolbarPanel.pushController(fullname, visitingCardCtrl); } protected void doOpenContact(UserRequest ureq, MemberView member) { removeAsListenerAndDispose(contactCtrl); Identity choosenIdentity = securityManager.loadIdentityByKey(member.getIdentityKey()); String fullname = userManager.getUserDisplayName(choosenIdentity); ContactMessage cmsg = new ContactMessage(ureq.getIdentity()); ContactList emailList = new ContactList(fullname); emailList.add(choosenIdentity); cmsg.addEmailTo(emailList); OLATResourceable ores = OresHelper.createOLATResourceableType("Contact"); WindowControl bwControl = addToHistory(ureq, ores, null); contactCtrl = new ContactFormController(ureq, bwControl, true, false, false, cmsg); listenTo(contactCtrl); toolbarPanel.pushController(fullname, contactCtrl); } protected abstract void doOpenAssessmentTool(UserRequest ureq, MemberView member); protected List<Long> getMemberKeys(List<MemberView> members) { List<Long> keys = new ArrayList<Long>(members.size()); if(members != null && !members.isEmpty()) { for(MemberView member:members) { keys.add(member.getIdentityKey()); } } return keys; } protected abstract SearchMembersParams getSearchParams(); public void reloadModel() { updateTableModel(getSearchParams()); } protected List<MemberView> updateTableModel(SearchMembersParams params) { //course membership boolean managedMembersRepo = RepositoryEntryManagedFlag.isManaged(repoEntry, RepositoryEntryManagedFlag.membersmanagement); List<RepositoryEntryMembership> repoMemberships = repoEntry == null ? Collections.<RepositoryEntryMembership>emptyList() : repositoryManager.getRepositoryEntryMembership(repoEntry); //groups membership List<BusinessGroup> groups = repoEntry == null ? Collections.singletonList(businessGroup) : businessGroupService.findBusinessGroups(null, repoEntry, 0, -1); List<Long> groupKeys = new ArrayList<Long>(); Map<Long,BusinessGroupShort> keyToGroupMap = new HashMap<>(); for(BusinessGroup group:groups) { groupKeys.add(group.getKey()); keyToGroupMap.put(group.getKey(), group); } List<BusinessGroupMembership> memberships = groups.isEmpty() ? Collections.<BusinessGroupMembership>emptyList() : businessGroupService.getBusinessGroupsMembership(groups); //get identities Set<Long> identityKeys = new HashSet<>(); for(RepositoryEntryMembership membership: repoMemberships) { identityKeys.add(membership.getIdentityKey()); } for(BusinessGroupMembership membership:memberships) { identityKeys.add(membership.getIdentityKey()); } List<Identity> identities; if(identityKeys.isEmpty()) { identities = new ArrayList<>(0); } else { identities = filterIdentities(params, identityKeys); } Map<Long,MemberView> keyToMemberMap = new HashMap<>(); List<MemberView> memberList = new ArrayList<>(); Locale locale = getLocale(); //reservations if(params.isPending()) { List<OLATResource> resourcesForReservations = new ArrayList<>(); if(repoEntry != null) { resourcesForReservations.add(repoEntry.getOlatResource()); } for(BusinessGroup group:groups) { resourcesForReservations.add(group.getResource()); } List<ResourceReservation> reservations = acService.getReservations(resourcesForReservations); List<Long> pendingIdentityKeys = new ArrayList<>(reservations.size()); for(ResourceReservation reservation:reservations) { pendingIdentityKeys.add(reservation.getIdentity().getKey()); } if(StringHelper.containsNonWhitespace(params.getSearchString()) || StringHelper.containsNonWhitespace(params.getLogin()) || (params.getUserPropertiesSearch() != null && !params.getUserPropertiesSearch().isEmpty())) { List<Identity> pendingIdentities = filterIdentities(params, pendingIdentityKeys); pendingIdentityKeys.retainAll(PersistenceHelper.toKeys(pendingIdentities)); } for(ResourceReservation reservation:reservations) { Identity identity = reservation.getIdentity(); if(pendingIdentityKeys.contains(identity.getKey())) { MemberView member = new MemberView(identity, userPropertyHandlers, locale); member.getMembership().setPending(true); memberList.add(member); forgeLinks(member); keyToMemberMap.put(identity.getKey(), member); } } } Long me = getIdentity().getKey(); Set<Long> loadStatus = new HashSet<>(); for(Identity identity:identities) { MemberView member = new MemberView(identity, userPropertyHandlers, locale); if(chatEnabled) { if(identity.getKey().equals(me)) { member.setOnlineStatus("me"); } else if(sessionManager.isOnline(identity.getKey())) { loadStatus.add(identity.getKey()); } else { member.setOnlineStatus(Presence.unavailable.name()); } } memberList.add(member); forgeLinks(member); keyToMemberMap.put(identity.getKey(), member); } if(loadStatus.size() > 0) { List<Long> statusToLoadList = new ArrayList<>(loadStatus); Map<Long,String> statusMap = imService.getBuddyStatus(statusToLoadList); for(Long toLoad:statusToLoadList) { String status = statusMap.get(toLoad); MemberView member = keyToMemberMap.get(toLoad); if(status == null) { member.setOnlineStatus(Presence.available.name()); } else { member.setOnlineStatus(status); } } } for(BusinessGroupMembership membership:memberships) { Long identityKey = membership.getIdentityKey(); MemberView memberView = keyToMemberMap.get(identityKey); if(memberView != null) { memberView.setFirstTime(membership.getCreationDate()); memberView.setLastTime(membership.getLastModified()); if(membership.isOwner()) { memberView.getMembership().setGroupTutor(true); } if(membership.isParticipant()) { memberView.getMembership().setGroupParticipant(true); } if(membership.isWaiting()) { memberView.getMembership().setGroupWaiting(true); } Long groupKey = membership.getGroupKey(); BusinessGroupShort group = keyToGroupMap.get(groupKey); memberView.addGroup(group); } } for(RepositoryEntryMembership membership:repoMemberships) { Long identityKey = membership.getIdentityKey(); MemberView memberView = keyToMemberMap.get(identityKey); if(memberView != null) { memberView.setFirstTime(membership.getCreationDate()); memberView.setLastTime(membership.getLastModified()); memberView.getMembership().setManagedMembersRepo(managedMembersRepo); if(membership.isOwner()) { memberView.getMembership().setRepoOwner(true); } if(membership.isCoach()) { memberView.getMembership().setRepoTutor(true); } if(membership.isParticipant()) { memberView.getMembership().setRepoParticipant(true); } } } if(repoEntry != null) { Map<Long,Date> lastLaunchDates = userInfosMgr.getRecentLaunchDates(repoEntry.getOlatResource()); for(MemberView memberView:keyToMemberMap.values()) { Long identityKey = memberView.getIdentityKey(); Date date = lastLaunchDates.get(identityKey); memberView.setLastTime(date); } } //the order of the filter is important filterByRoles(memberList, params); filterByOrigin(memberList, params); memberListModel.setObjects(memberList); membersTable.reset(true, true, true); return memberList; } private List<Identity> filterIdentities(SearchMembersParams params, Collection<Long> identityKeys) { SearchIdentityParams idParams = new SearchIdentityParams(); if(StringHelper.containsNonWhitespace(params.getSearchString())) { String searchString = params.getSearchString(); Map<String,String> propertiesSearch = new HashMap<>(); for(UserPropertyHandler handler:userPropertyHandlers) { propertiesSearch.put(handler.getName(), searchString); } idParams.setLogin(searchString); idParams.setUserProperties(propertiesSearch); } else { if(params.getUserPropertiesSearch() != null && !params.getUserPropertiesSearch().isEmpty()) { idParams.setUserProperties(params.getUserPropertiesSearch()); } if(StringHelper.containsNonWhitespace(params.getLogin())) { idParams.setLogin(params.getLogin()); } } List<Long> identityKeyList = new ArrayList<>(identityKeys); List<Identity> identities = new ArrayList<>(identityKeyList.size()); int count = 0; int batch = 500; do { int toIndex = Math.min(count + batch, identityKeyList.size()); List<Long> toLoad = identityKeyList.subList(count, toIndex); idParams.setIdentityKeys(toLoad); List<Identity> batchOfIdentities = securityManager.getIdentitiesByPowerSearch(idParams, 0, -1); identities.addAll(batchOfIdentities); count += batch; } while(count < identityKeyList.size()); return identities; } protected void forgeLinks(MemberView row) { FormLink toolsLink = uifactory.addFormLink("tools_" + counter.incrementAndGet(), "tools", "", null, null, Link.NONTRANSLATED); toolsLink.setIconLeftCSS("o_icon o_icon_actions o_icon-lg"); toolsLink.setUserObject(row); row.setToolsLink(toolsLink); FormLink chatLink = uifactory.addFormLink("tools_" + counter.incrementAndGet(), "im", "", null, null, Link.NONTRANSLATED); chatLink.setIconLeftCSS("o_icon o_icon_status_unavailable"); chatLink.setUserObject(row); row.setChatLink(chatLink); } private void filterByOrigin(List<MemberView> memberList, SearchMembersParams params) { if(params.isGroupOrigin() && params.isRepoOrigin()) { //do nothing not very useful :-) } else if(params.isGroupOrigin()) { for(Iterator<MemberView> it=memberList.iterator(); it.hasNext(); ) { CourseMembership m = it.next().getMembership(); if(!m.isGroupTutor() && !m.isGroupParticipant() && !m.isGroupWaiting()) { it.remove(); } } } else if(params.isRepoOrigin()) { for(Iterator<MemberView> it=memberList.iterator(); it.hasNext(); ) { CourseMembership m = it.next().getMembership(); if(!m.isRepoOwner() && !m.isRepoTutor() && !m.isRepoParticipant()) { it.remove(); } } } } /** * This filter method preserve the multiple roles of a member. If we want only the waiting list but * a member is in the waiting list and owner of the course, we want it to know. * @param memberList * @param params * @return */ private void filterByRoles(List<MemberView> memberList, SearchMembersParams params) { List<MemberView> members = new ArrayList<MemberView>(memberList); if(params.isRepoOwners()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isRepoOwner()) { it.remove(); } } } if(params.isRepoTutors()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isRepoTutor()) { it.remove(); } } } if(params.isRepoParticipants()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isRepoParticipant()) { it.remove(); } } } if(params.isGroupTutors()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isGroupTutor()) { it.remove(); } } } if(params.isGroupParticipants()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isGroupParticipant()) { it.remove(); } } } if(params.isGroupWaitingList()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isGroupWaiting()) { it.remove(); } } } if(params.isPending()) { for(Iterator<MemberView> it=members.iterator(); it.hasNext(); ) { if(it.next().getMembership().isPending()) { it.remove(); } } } memberList.removeAll(members); } private class MailConfirmation { private final List<Identity> members; private final MemberPermissionChangeEvent e; public MailConfirmation(MemberPermissionChangeEvent e, List<Identity> members) { this.e = e; this.members = members; } public List<Identity> getMembers() { return members; } public MemberPermissionChangeEvent getE() { return e; } } private class ToolsController extends BasicController { private final MemberView row; private final VelocityContainer mainVC; public ToolsController(UserRequest ureq, WindowControl wControl, MemberView row) { super(ureq, wControl); this.row = row; mainVC = createVelocityContainer("tools"); List<String> links = new ArrayList<>(); //links addLink("home", TABLE_ACTION_HOME, "o_icon o_icon_home", links); addLink("contact", TABLE_ACTION_CONTACT, "o_icon o_icon_mail", links); if(repoEntry != null && "CourseModule".equals(repoEntry.getOlatResource().getResourceableTypeName())) { addLink("assessment", TABLE_ACTION_ASSESSMENT, "o_icon o_icon_certificate", links); } links.add("-"); if(row.getMembership().isGroupWaiting() && !readOnly) { addLink("table.header.graduate", TABLE_ACTION_GRADUATE, "o_icon o_icon_graduate", links); } if(!readOnly) { addLink("edit.member", TABLE_ACTION_EDIT, "o_icon o_icon_edit", links); } if(!globallyManaged || overrideManaged) { addLink("table.header.remove", TABLE_ACTION_REMOVE, "o_icon o_icon_remove", links); } mainVC.contextPut("links", links); putInitialPanel(mainVC); } private void addLink(String name, String cmd, String iconCSS, List<String> links) { Link link = LinkFactory.createLink(name, cmd, getTranslator(), mainVC, this, Link.LINK); if(iconCSS != null) { link.setIconLeftCSS(iconCSS); } mainVC.put(name, link); links.add(name); } @Override protected void doDispose() { // } @Override protected void event(UserRequest ureq, Component source, Event event) { fireEvent(ureq, Event.DONE_EVENT); if(source instanceof Link) { Link link = (Link)source; String cmd = link.getCommand(); if(TABLE_ACTION_GRADUATE.equals(cmd)) { doGraduate(Collections.singletonList(row)); } else if(TABLE_ACTION_EDIT.equals(cmd)) { openEdit(ureq, row); } else if(TABLE_ACTION_REMOVE.equals(cmd)) { confirmDelete(ureq, Collections.singletonList(row)); } else if(TABLE_ACTION_HOME.equals(cmd)) { doOpenVisitingCard(ureq, row); } else if(TABLE_ACTION_CONTACT.equals(cmd)) { doOpenContact(ureq, row); } else if(TABLE_ACTION_ASSESSMENT.equals(cmd)) { doOpenAssessmentTool(ureq, row); } } } } }