diff --git a/src/main/java/org/olat/_spring/mainContext.xml b/src/main/java/org/olat/_spring/mainContext.xml
index 006d5322469901e64ec1b8a25dceb44e5b084aa4..9f281a42f91f42cbd438ab468aaabba805ee34a9 100644
--- a/src/main/java/org/olat/_spring/mainContext.xml
+++ b/src/main/java/org/olat/_spring/mainContext.xml
@@ -8,7 +8,7 @@
         http://www.springframework.org/schema/context 
         http://www.springframework.org/schema/context/spring-context.xsd">
   
-	<context:component-scan base-package="org.olat.note,org.olat.social" />
+	<context:component-scan base-package="org.olat.note,org.olat.social,org.olat.commons.memberlist" />
 
 	<import resource="classpath:/org/olat/core/util/threadlog/_spring/threadlogCorecontext.xml"/>
 	<import resource="classpath:/org/olat/core/_spring/mainCorecontext.xml"/>
diff --git a/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java b/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..d33b004e60fbb2585ff7785e3cec2437cff90338
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java
@@ -0,0 +1,168 @@
+/**
+ * <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.commons.memberlist.manager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.id.Identity;
+import org.olat.core.util.Util;
+import org.olat.course.nodes.members.Member;
+import org.olat.course.nodes.members.MembersCourseNodeRunController;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupMembership;
+import org.olat.group.BusinessGroupService;
+import org.olat.group.ui.run.GroupMembersRunController;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.olat.repository.model.RepositoryEntryMembership;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * Initial Date: 28.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+@Service
+public class MembersExportManager {
+	
+	public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName();
+	
+	public static final int USER_PROPS_OFFSET = 500;
+	
+	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	protected BaseSecurity securityManager;
+	@Autowired
+	private BusinessGroupService businessGroupService;	
+	@Autowired
+	private UserManager userManager;
+	
+	public Map<Long,RepositoryEntryMembership> getRepoMembershipMap(RepositoryEntry repoEntry) {
+		List<RepositoryEntryMembership> repoMemberships = repositoryManager.getRepositoryEntryMembership(repoEntry);
+		Map<Long,RepositoryEntryMembership> map = new HashMap<>();
+		for (RepositoryEntryMembership membership : repoMemberships) {
+			map.put(membership.getIdentityKey(), membership);
+		}
+		return map;
+	}
+	
+	public Map<Long,BusinessGroupMembership> getGroupMembershipMap(List<BusinessGroup> groups) {
+		List<BusinessGroupMembership> groupMemberships = businessGroupService.getBusinessGroupsMembership(groups);
+		Map<Long,BusinessGroupMembership> map = new HashMap<>();
+		for (BusinessGroupMembership membership : groupMemberships) {
+			map.put(membership.getIdentityKey(), membership);
+		}
+		return map;
+	}
+	
+	private void putRoleToMember(List<Identity> rows, Map<Identity, StringBuilder> membersMap, Identity member, String role, Translator roleTranslator) {
+		if (membersMap.containsKey(member)) {
+			String roleString = roleTranslator.translate("members." + role);
+			StringBuilder roleBuilder = membersMap.get(member);
+			if (!roleBuilder.toString().contains(roleString)) {
+				roleBuilder.append(", ").append(roleString);				
+			}
+		} else {
+			membersMap.put(member, new StringBuilder(roleTranslator.translate("members." + role)));
+			rows.add(member);
+		}
+	}
+	
+	public MediaResource getXlsMediaResource(boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, 
+			List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting,			
+			Translator translator, List<UserPropertyHandler> userPropertyHandlers, RepositoryEntry repoEntry, BusinessGroup businessGroup) {//TODO
+		Map<Long,BusinessGroupMembership> groupmemberships;
+		Map<Long,RepositoryEntryMembership> repomemberships;
+		if (repoEntry == null) {
+			List<BusinessGroup> groups = new ArrayList<>(); 
+			groups.add(businessGroup);
+			groupmemberships = getGroupMembershipMap(groups);
+			repomemberships = new HashMap<>();
+		} else {
+			repomemberships = getRepoMembershipMap(repoEntry);
+			List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, repoEntry, 0, -1);
+			groupmemberships = getGroupMembershipMap(groups);
+		}	
+		
+		List<List<Identity>> roleMembers = new ArrayList<>();
+		if (showOwners) {
+			roleMembers.add(owners);
+		}
+		if (showCoaches) {
+			roleMembers.add(coaches);
+		}
+		if (showParticipants) {
+			roleMembers.add(participants);
+		}
+		if (showWaiting) {
+			roleMembers.add(waiting);
+		}
+		Translator repoTranslator = Util.createPackageTranslator(Member.class, translator.getLocale());
+		Translator groupTranslator = Util.createPackageTranslator(GroupMembersRunController.class, translator.getLocale());
+		Map<Identity, StringBuilder> membersMap = new HashMap<>();
+		List<Identity> rows = new ArrayList<>();
+		for (List<Identity> membersList : roleMembers) {
+			for (Identity member : membersList) {
+				Long memberKey = member.getKey();
+				if (repomemberships != null && !repomemberships.isEmpty() && repomemberships.containsKey(memberKey)) {
+					RepositoryEntryMembership repomembership = repomemberships.get(memberKey);
+					if (repomembership.isOwner()) {
+						putRoleToMember(rows, membersMap, member, "owners", repoTranslator);
+					}
+					if (repomembership.isCoach()) {
+						putRoleToMember(rows, membersMap, member, "coaches", repoTranslator);
+					} 
+					if (repomembership.isParticipant()) {
+						putRoleToMember(rows, membersMap, member, "participants", repoTranslator);
+					}
+				}
+				if (groupmemberships != null && !groupmemberships.isEmpty() && groupmemberships.containsKey(memberKey)) {
+					BusinessGroupMembership groupmembership = groupmemberships.get(memberKey);
+					if (groupmembership.isOwner()) {
+						putRoleToMember(rows, membersMap, member, "coaches", groupTranslator);
+					}
+					if (groupmembership.isParticipant()) {
+						putRoleToMember(rows, membersMap, member, "participants", groupTranslator);
+					} 
+					if (groupmembership.isWaiting()) {
+						putRoleToMember(rows, membersMap, member, "waiting", groupTranslator);
+					}
+				}
+			}			
+		}
+		
+		Translator handlerTranslator = userManager.getPropertyHandlerTranslator(translator);
+		XlsMembersExport exporter = new XlsMembersExport();
+		MediaResource resource = exporter.export(rows, membersMap, handlerTranslator, userPropertyHandlers);		
+		
+		return resource;
+	}
+
+}
diff --git a/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java b/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java
new file mode 100644
index 0000000000000000000000000000000000000000..54a992b018fe85fadfe72835ae2282f0303e8409
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java
@@ -0,0 +1,95 @@
+/**
+ * <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.commons.memberlist.manager;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.olat.admin.landingpages.ui.RulesDataModel;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.id.Identity;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.Formatter;
+import org.olat.core.util.Util;
+import org.olat.core.util.openxml.OpenXMLWorkbook;
+import org.olat.core.util.openxml.OpenXMLWorkbookResource;
+import org.olat.core.util.openxml.OpenXMLWorksheet;
+import org.olat.core.util.openxml.OpenXMLWorksheet.Row;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+
+/**
+ * Initial Date: 23.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+public class XlsMembersExport {
+	
+	private static final OLog log = Tracing.createLoggerFor(XlsMembersExport.class);
+
+
+	public MediaResource export(List<Identity> rows, Map<Identity, StringBuilder> members, Translator translator, List<UserPropertyHandler> userPropertyHandlers) {
+	
+		String label = "TableExport_"
+				+ Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()))
+				+ ".xlsx";
+		
+		return new OpenXMLWorkbookResource(label) {
+			@Override
+			protected void generate(OutputStream out) {
+				try (OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) {
+					OpenXMLWorksheet sheet = workbook.nextWorksheet();
+					createHeader(userPropertyHandlers, translator, sheet, workbook);
+					createData(members, rows, userPropertyHandlers, sheet);
+				} catch (IOException e) {
+					log.error("Unable to export xlsx", e);
+				}
+			}
+		};
+	}
+
+	private void createHeader(List<UserPropertyHandler> userPropertyHandlers, Translator translator, 
+			OpenXMLWorksheet sheet,	OpenXMLWorkbook workbook) {
+		Row headerRow = sheet.newRow();
+		for (int c = 0; c < userPropertyHandlers.size(); c++) {
+			UserPropertyHandler handler = userPropertyHandlers.get(c);
+			String header = translator.translate("form.name." + handler.getName());
+			headerRow.addCell(c, header, workbook.getStyles().getHeaderStyle());
+		}
+		Translator roleTranslator = Util.createPackageTranslator(RulesDataModel.class, translator.getLocale());
+		headerRow.addCell(userPropertyHandlers.size(), roleTranslator.translate("rules.role"));
+	}
+
+	private void createData(Map<Identity, StringBuilder> members, List<Identity> rows, List<UserPropertyHandler> userPropertyHandlers, OpenXMLWorksheet sheet) {
+		sheet.setHeaderRows(1);
+		for (int r = 0; r < rows.size(); r++) {
+			Row dataRow = sheet.newRow();
+			for (int c = 0; c < userPropertyHandlers.size(); c++) {
+				String value = userPropertyHandlers.get(c).getUserProperty(rows.get(r).getUser(), null);
+				dataRow.addCell(c, value, null);
+			}
+			dataRow.addCell(userPropertyHandlers.size(), members.get(rows.get(r)).toString());
+		}
+	}
+	
+}
diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java
new file mode 100644
index 0000000000000000000000000000000000000000..a885f67e64e0071efee07e436b51f8f420e6b77c
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java
@@ -0,0 +1,527 @@
+/**
+ * <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.commons.memberlist.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.olat.NewControllerFactory;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.commons.memberlist.manager.MembersExportManager;
+import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
+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.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.FormLayoutContainer;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.link.LinkPopupSettings;
+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.creator.ControllerCreator;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.helpers.Settings;
+import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
+import org.olat.core.id.Roles;
+import org.olat.core.id.UserConstants;
+import org.olat.core.id.context.BusinessControl;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
+import org.olat.core.util.session.UserSessionManager;
+import org.olat.course.nodes.members.Member;
+import org.olat.course.nodes.members.MembersCourseNodeRunController;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.group.BusinessGroup;
+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.user.DisplayPortraitManager;
+import org.olat.user.UserAvatarMapper;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Description:<br>
+ * The run controller show the list of members of the course
+ * 
+ * <P>
+ * Initial Date:  11 mars 2011 <br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ * @author fkiefer
+ */
+public class MembersAvatarDisplayRunController extends FormBasicController {
+	
+	private final List<UserPropertyHandler> userPropertyHandlers;
+	public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName();
+
+	private final CourseEnvironment courseEnv;
+	private final DisplayPortraitManager portraitManager;
+	private final String avatarBaseURL;
+	
+	private Link printLink;
+	private FormLink allEmailLink;
+	private FormLink downloadLink;
+	
+	private List<Member> ownerList;
+	private List<Member> coachList;
+	private List<Member> participantList;
+	private List<Member> waitingtList;
+	
+	private List<Identity> owners;
+	private List<Identity> coaches;
+	private List<Identity> participants;
+	private List<Identity> waiting;
+
+	private final boolean canEmail;
+	private final boolean showOwners;
+	private final boolean showCoaches;
+	private final boolean showParticipants;
+	private final boolean showWaiting;
+	private final boolean chatEnabled;
+	private final boolean editable;
+
+	
+	private FormBasicController mailCtrl;
+	private ContactFormController emailController;
+	private CloseableModalController cmc;
+	
+	private int count = 0;
+	private final boolean deduplicateList;
+	
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
+	private InstantMessagingModule imModule;
+	@Autowired
+	private InstantMessagingService imService;
+	@Autowired
+	private UserSessionManager sessionManager;
+	@Autowired
+	private MembersExportManager exportManager;
+
+	private BusinessGroup businessGroup;
+	private RepositoryEntry repoEntry;
+	
+	
+	public MembersAvatarDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup,
+			List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList,
+			boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) {
+		super(ureq, wControl, "members", translator);
+		setTranslator(translator);
+		
+		this.courseEnv = courseEnv;
+		this.businessGroup = businessGroup;
+		this.repoEntry = courseEnv != null ? courseEnv.getCourseGroupManager().getCourseEntry() : null;
+
+		userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false);
+		avatarBaseURL = registerCacheableMapper(ureq, "avatars-members", new UserAvatarMapper(true));
+		portraitManager = DisplayPortraitManager.getInstance();
+		chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled();
+		// lists
+		this.owners = owners;
+		this.coaches = coaches;
+		this.participants = participants;
+		this.waiting = waiting;
+		// flags
+		this.canEmail = canEmail;
+		this.showOwners = showOwners;
+		this.showCoaches = showCoaches;
+		this.showParticipants = showParticipants;
+		this.showWaiting = showWaiting;
+		this.deduplicateList = deduplicateList;
+		this.editable = editable;
+		
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {		
+		Comparator<Identity> idComparator = new IdentityComparator();
+		Collections.sort(owners, idComparator);
+		Collections.sort(coaches, idComparator);
+		Collections.sort(participants, idComparator);
+		Collections.sort(waiting, idComparator);
+		
+		if(canEmail) {
+			allEmailLink = uifactory.addFormLink("email", "members.email.title", null, formLayout, Link.BUTTON);
+			allEmailLink.setIconLeftCSS("o_icon o_icon_mail");
+		}
+		
+		IdentityEnvironment idEnv = ureq.getUserSession().getIdentityEnvironment();
+		Identity ownId = idEnv.getIdentity();
+		Roles roles = idEnv.getRoles();
+		boolean memberXlsEnabled = businessGroup != null ? businessGroup.isDownloadMembersLists() && !waiting.contains(ownId) : false;
+		if (editable && (roles.isOLATAdmin() || roles.isGroupManager() || owners.contains(ownId) || coaches.contains(ownId) || memberXlsEnabled)) {
+			downloadLink = uifactory.addFormLink("download", "members.download", null, formLayout, Link.BUTTON);
+			downloadLink.setIconLeftCSS("o_icon o_icon_download");
+			if(formLayout instanceof FormLayoutContainer) {
+				printLink = LinkFactory.createButton("print", ((FormLayoutContainer)formLayout).getFormItemComponent(), this);
+				printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg");
+				printLink.setPopup(new LinkPopupSettings(700, 500, "print-members"));
+				((FormLayoutContainer)formLayout).getFormItemComponent().put("print", printLink);
+			}
+		}
+
+		Set<Long> duplicateCatcher = deduplicateList ? new HashSet<Long>() : null;
+		ownerList = initFormMemberList("owners", owners, duplicateCatcher, formLayout, canEmail);
+		coachList = initFormMemberList("coaches", coaches, duplicateCatcher, formLayout, canEmail);
+		participantList = initFormMemberList("participants", participants, duplicateCatcher, formLayout, canEmail);
+		waitingtList = initFormMemberList("waiting", waiting, duplicateCatcher, formLayout, canEmail);
+		
+		if(formLayout instanceof FormLayoutContainer) {
+			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
+			layoutCont.contextPut("showOwners", showOwners);
+			layoutCont.contextPut("hasOwners", new Boolean(!ownerList.isEmpty()));
+			layoutCont.contextPut("showCoaches", showCoaches);
+			layoutCont.contextPut("hasCoaches", new Boolean(!coachList.isEmpty()));
+			layoutCont.contextPut("showParticipants", showParticipants);
+			layoutCont.contextPut("hasParticipants", new Boolean(!participantList.isEmpty()));
+			layoutCont.contextPut("showWaiting", showWaiting);
+			layoutCont.contextPut("hasWaiting", new Boolean(!waitingtList.isEmpty()));
+		}
+	}
+	
+	private List<Member> initFormMemberList(String name, List<Identity> ids, Set<Long> duplicateCatcher, FormItemContainer formLayout, boolean withEmail) {
+		String page = velocity_root + "/memberList.html";
+		
+		FormLayoutContainer container = FormLayoutContainer.createCustomFormLayout(name, getTranslator(), page);
+		formLayout.add(name, container);
+		container.setRootForm(mainForm);
+
+		List<Member> members = createMemberLinks(ids, duplicateCatcher, container, withEmail);
+		container.contextPut("members", members);
+		container.contextPut("avatarBaseURL", avatarBaseURL);
+		return members;
+	}
+	
+	protected List<Member> createMemberLinks(List<Identity> identities, Set<Long> duplicateCatcher, FormLayoutContainer formLayout, boolean withEmail) {
+		List<Member> members = new ArrayList<>();
+		for(Identity identity:identities) {
+			if(duplicateCatcher != null && duplicateCatcher.contains(identity.getKey())) continue;
+			
+			Member member = createMember(identity);
+			members.add(member);
+			
+			String guiId = Integer.toString(++count);
+			String fullname = StringHelper.escapeHtml(member.getFullName());
+			
+			FormLink idLink = uifactory.addFormLink("id_".concat(guiId), "id", fullname, null, formLayout, Link.NONTRANSLATED);
+			
+			idLink.setUserObject(member);
+			formLayout.add(idLink.getComponent().getComponentName(), idLink);
+			member.setIdLink(idLink);
+			
+			if(withEmail) {
+				FormLink emailLink = uifactory.addFormLink("mail_".concat(guiId), "mail", "", null, formLayout, Link.NONTRANSLATED);
+				emailLink.setUserObject(member);
+				emailLink.setIconLeftCSS("o_icon o_icon_mail o_icon-lg");
+				emailLink.setElementCssClass("o_mail");
+				formLayout.add(emailLink.getComponent().getComponentName(), emailLink);
+				member.setEmailLink(emailLink);
+			}
+			if(chatEnabled && editable) {
+				FormLink chatLink = uifactory.addFormLink("chat_".concat(guiId), "chat", "", null, formLayout, Link.NONTRANSLATED);
+				chatLink.setUserObject(member);
+				chatLink.setElementCssClass("o_chat");
+				formLayout.add(chatLink.getComponent().getComponentName(), chatLink);
+				member.setChatLink(chatLink);
+			}
+			
+			if(duplicateCatcher != null) {
+				duplicateCatcher.add(identity.getKey());
+			}
+		}
+		
+		if(chatEnabled && editable) {
+			Long me = getIdentity().getKey();
+			if(imModule.isOnlineStatusEnabled()) {
+				Map<Long,Member> loadStatus = new HashMap<>();
+				
+				for(Member member:members) {
+					if(member.getKey().equals(me)) {
+						member.getChatLink().setVisible(false);
+					} else if(sessionManager.isOnline(member.getKey())) {
+						loadStatus.put(member.getKey(), member);
+					} else {
+						member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable");
+					}
+				}
+				
+				if(loadStatus.size() > 0) {
+					List<Long> statusToLoadList = new ArrayList<>(loadStatus.keySet());
+					Map<Long,String> statusMap = imService.getBuddyStatus(statusToLoadList);
+					for(Long toLoad:statusToLoadList) {
+						String status = statusMap.get(toLoad);
+						Member member = loadStatus.get(toLoad);
+						if(status == null || Presence.available.name().equals(status)) {
+							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_available");
+						} else if(Presence.dnd.name().equals(status)) {
+							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_dnd");
+						} else {
+							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable");
+						}
+					}
+				}
+			} else {
+				for(Member member:members) {
+					if(member.getKey().equals(me)) {
+						member.getChatLink().setVisible(false);
+					} else {
+						member.getChatLink().setIconLeftCSS("o_icon o_icon_status_chat");
+					}
+				}
+			}
+		}
+		
+		return members;
+	}
+	
+	private Member createMember(Identity identity) {
+		boolean hasPortrait = portraitManager.hasPortrait(identity.getName());
+
+		String portraitCssClass;
+		String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH);
+		if ("male".equalsIgnoreCase(gender)) {
+			portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS;
+		} else if ("female".equalsIgnoreCase(gender)) {
+			portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS;
+		} else {
+			portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS;
+		}
+		String fullname = userManager.getUserDisplayName(identity);
+		return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+	
+	
+
+	@Override
+	public void event(UserRequest ureq, Component source, Event event) {
+		if(source == printLink) {
+			doPrint(ureq);
+		}
+		super.event(ureq, source, event);
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(source == allEmailLink) {
+			doEmail(ureq);
+		} else if (source == downloadLink) {
+			doExport(ureq);
+		} else if (source instanceof FormLink) {
+			FormLink link = (FormLink)source;
+			Object uobject = link.getUserObject();
+			if(uobject instanceof Member) {
+				Member member = (Member)uobject;
+				String cmd = link.getCmd();
+				if("id".equals(cmd)) {
+					doOpenHomePage(member, ureq);
+				} else if("mail".equals(cmd)) {
+					doSendEmailToMember(member, ureq);
+				} else if("chat".equals(cmd)) {
+					doOpenChat(member, ureq);
+				}
+			}	
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(source == cmc) {
+			cleanUp();
+		} else if (source == emailController) {
+			cmc.deactivate();
+			cleanUp();
+		} else if(source == mailCtrl) {
+			cmc.deactivate();
+			cleanUp();
+		}
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(emailController);
+		removeAsListenerAndDispose(mailCtrl);
+		removeAsListenerAndDispose(cmc);
+		emailController = null;
+		mailCtrl = null;
+		cmc = null;
+	}
+	
+	private void doEmail(UserRequest ureq) {
+		if(mailCtrl != null || cmc != null) return;
+		removeAsListenerAndDispose(cmc);
+		removeAsListenerAndDispose(mailCtrl);
+		mailCtrl = new MembersMailController(ureq, getWindowControl(), getTranslator(), courseEnv,
+				ownerList, coachList, participantList, waitingtList, createBodyTemplate());
+		listenTo(mailCtrl);
+		
+		String title = translate("members.email.title");
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title);
+		listenTo(cmc);
+		
+		cmc.activate();	
+	}
+	
+	private void doExport(UserRequest ureq) {
+		MediaResource resource = exportManager.getXlsMediaResource(showOwners, showCoaches, showParticipants, showWaiting, 
+				owners, coaches, participants, waiting, getTranslator(), userPropertyHandlers, repoEntry, businessGroup);
+		
+		ureq.getDispatchResult().setResultingMediaResource(resource);	
+	}
+	
+	private void doOpenChat(Member member, UserRequest ureq) {
+		Buddy buddy = imService.getBuddyById(member.getKey());
+		OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy);
+		ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES);
+	}
+	
+	private void doSendEmailToMember(Member member, UserRequest ureq) {
+		if (!editable) return;
+		ContactList memberList;
+		if (courseEnv == null) {
+			memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), businessGroup.getName() }));
+		} else {
+			memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), courseEnv.getCourseTitle() }));
+		}
+		Identity identity = securityManager.loadIdentityByKey(member.getKey());
+		memberList.add(identity);
+		doSendEmailToMember(memberList, ureq);
+	}
+
+	private void doSendEmailToMember(ContactList contactList, UserRequest ureq) {
+		if (contactList.getEmailsAsStrings().size() > 0) {
+			removeAsListenerAndDispose(cmc);
+			removeAsListenerAndDispose(emailController);
+			
+			ContactMessage cmsg = new ContactMessage(ureq.getIdentity());
+			cmsg.addEmailTo(contactList);
+			// preset body template from i18n
+			cmsg.setBodyText(createBodyTemplate());
+			emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg);
+			listenTo(emailController);
+			
+			String title = translate("members.email.title");
+			cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title);
+			listenTo(cmc);
+			cmc.activate();
+		}
+	}
+	
+	private String createBodyTemplate() {
+		if (courseEnv == null) {
+			String courseName = businessGroup.getName();
+			// Build REST URL to business group, use hack via group manager to access
+			StringBuilder courseLink = new StringBuilder();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/auth/BusinessGroup/").append(businessGroup.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});	
+		} else {
+			String courseName = courseEnv.getCourseTitle();
+			// Build REST URL to course element, use hack via group manager to access repo entry
+			StringBuilder courseLink = new StringBuilder();
+			RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/url/RepositoryEntry/").append(entry.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});		
+		}
+	}
+	
+	private void doOpenHomePage(Member member, UserRequest ureq) {
+		String url = "[HomePage:" + member.getKey() + "]";
+		BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url);
+		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
+		NewControllerFactory.getInstance().launch(ureq, bwControl);
+	}
+	
+	private void doPrint(UserRequest ureq) {
+		ControllerCreator printControllerCreator = new ControllerCreator() {
+			@Override
+			public Controller createController(UserRequest lureq, WindowControl lwControl) {
+				lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print");
+				return new MembersPrintController(lureq, lwControl, userPropertyHandlers, getTranslator(), ownerList, coachList,
+						participantList, waitingtList, showOwners, showCoaches, showParticipants, showWaiting, 
+						courseEnv != null ? courseEnv.getCourseTitle() : businessGroup.getName());
+			}					
+		};
+		ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator);
+		openInNewBrowserWindow(ureq, layoutCtrlr);
+	}
+	
+	public static class IdentityComparator implements Comparator<Identity> {
+
+		@Override
+		public int compare(Identity id1, Identity id2) {
+			if(id1 == null) return -1;
+			if(id2 == null) return 1;
+			
+			String l1 = id1.getUser().getProperty(UserConstants.LASTNAME, null);
+			String l2 = id2.getUser().getProperty(UserConstants.LASTNAME, null);
+			if(l1 == null) return -1;
+			if(l2 == null) return 1;
+			
+			int result = l1.compareToIgnoreCase(l2);
+			if(result == 0) {
+				String f1 = id1.getUser().getProperty(UserConstants.FIRSTNAME, null);
+				String f2 = id2.getUser().getProperty(UserConstants.FIRSTNAME, null);
+				if(f1 == null) return -1;
+				if(f2 == null) return 1;
+				result = f1.compareToIgnoreCase(f2);
+			}
+			return result;
+		}
+	}
+}
diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java
new file mode 100644
index 0000000000000000000000000000000000000000..81847dd9960eaf31b08cdb4cb6536551dece922e
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java
@@ -0,0 +1,184 @@
+/**
+ * <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.commons.memberlist.ui;
+
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+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.translator.Translator;
+import org.olat.core.id.Identity;
+import org.olat.core.util.prefs.Preferences;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.group.BusinessGroup;
+
+/**
+ * Initial Date: 28.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+public class MembersDisplayRunController extends BasicController {
+
+	private static final String GUIPREF_KEY_GROUPMEMBER = "groupmemberdisplay";
+	private static final String GUIPREF_KEY_COURSEMEMBER = "coursememberdisplay";
+	
+	private VelocityContainer mainVC;
+	private FormBasicController membersAvatarController;
+	
+	private MembersListDisplayRunController membersListController;
+	
+	private Link tableCustomLink, tableLink;
+	
+	private BusinessGroup businessGroup;
+	private CourseEnvironment courseEnv;
+	private String courseOrGroupIdentifier;
+	
+	private List<Identity> owners;
+	private List<Identity> coaches;
+	private List<Identity> participants;
+	private List<Identity> waiting;
+
+	private final boolean canEmail;
+	private final boolean showOwners;
+	private final boolean showCoaches;
+	private final boolean showParticipants;
+	private final boolean showWaiting;
+	private final boolean editable;
+	private final boolean deduplicateList;
+	
+	
+	public MembersDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup,
+			List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList,
+			boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) {
+		super(ureq, wControl);
+		setTranslator(translator);
+		this.courseOrGroupIdentifier = courseEnv == null ? GUIPREF_KEY_GROUPMEMBER + businessGroup.getKey()
+				: GUIPREF_KEY_COURSEMEMBER + courseEnv.getCourseGroupManager().getCourseEntry().getKey();
+		this.businessGroup = businessGroup;
+		this.courseEnv = courseEnv;
+		// lists
+		this.owners = owners;
+		this.coaches = coaches;
+		this.participants = participants;
+		this.waiting = waiting;
+		// flags
+		this.canEmail = canEmail;
+		this.showOwners = showOwners;
+		this.showCoaches = showCoaches;
+		this.showParticipants = showParticipants;
+		this.showWaiting = showWaiting;
+		this.deduplicateList = deduplicateList;
+		this.editable = editable;
+		
+		mainVC = createVelocityContainer("membersToggle");
+		
+		mainVC.contextPut("headline", businessGroup != null);
+		
+		tableCustomLink = LinkFactory.createLink(null, "tableCustomLink", "select.custom", "blank", getTranslator(), mainVC, this, Link.BUTTON);
+		tableCustomLink.setIconLeftCSS( "o_icon o_icon_table_custom o_icon-lg");
+		
+		tableLink = LinkFactory.createLink(null, "tableLink", "select.table", "blank", getTranslator(), mainVC, this, Link.BUTTON);
+		tableLink.setIconLeftCSS( "o_icon o_icon_table o_icon-lg");
+			
+		if (doLoadMemberListConfig(ureq)) {
+			doOpenPortraitView(ureq);
+		} else { 
+			doOpenListView(ureq, true);
+		}		
+		putInitialPanel(mainVC);		
+	}
+
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		super.event(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if (source == tableCustomLink) {
+			doOpenPortraitView(ureq);
+		} else if (source == tableLink) {
+			doOpenListView(ureq, true);
+		}
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	private void doOpenPortraitView(UserRequest ureq) {
+		if (membersAvatarController == null) {
+			membersAvatarController = new MembersAvatarDisplayRunController(ureq, getWindowControl(), getTranslator(), 
+					courseEnv, businessGroup, owners, coaches, participants, waiting, canEmail, deduplicateList, 
+					showOwners, showCoaches, showParticipants, showWaiting, editable);
+			listenTo(membersAvatarController);
+		}
+		mainVC.contextPut("portrait", Boolean.TRUE);
+		mainVC.put("portraitView", membersAvatarController.getInitialComponent());
+		tableCustomLink.setActive(true);
+		tableLink.setActive(false);
+		doUpdateMemberListConfig(ureq, true);
+	}
+	
+	private void doOpenListView(UserRequest ureq, boolean onClick) {
+		if (membersListController == null) {
+			membersListController = new MembersListDisplayRunController(ureq, getWindowControl(), getTranslator(), 
+					courseEnv, businessGroup, owners, coaches, participants, waiting, canEmail, deduplicateList,
+					showOwners, showCoaches, showParticipants, showWaiting, editable);
+			listenTo(membersListController);
+		}
+		mainVC.put("listView", membersListController.getInitialComponent());
+		mainVC.contextPut("portrait", Boolean.FALSE);
+		if (onClick) {
+			tableCustomLink.setActive(false);
+			tableLink.setActive(true);
+		}
+		doUpdateMemberListConfig(ureq, false);
+	}
+	
+	private boolean doLoadMemberListConfig(UserRequest ureq) {
+		Boolean showPortraitConfig = Boolean.TRUE;
+		if (ureq != null) {
+			Preferences guiPrefs = ureq.getUserSession().getGuiPreferences();
+			showPortraitConfig  = (Boolean) guiPrefs.get(MembersDisplayRunController.class, courseOrGroupIdentifier);
+			if (showPortraitConfig == null) {
+				showPortraitConfig = Boolean.TRUE;
+			}
+		}
+		return showPortraitConfig;
+	}
+	
+	private void doUpdateMemberListConfig(UserRequest ureq, boolean newValue) {
+		// save new config in GUI prefs
+		Preferences guiPrefs = ureq.getUserSession().getGuiPreferences();
+		if (guiPrefs != null) {
+			guiPrefs.putAndSave(MembersDisplayRunController.class, courseOrGroupIdentifier, Boolean.valueOf(newValue));
+		}
+	}
+}
diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java
new file mode 100644
index 0000000000000000000000000000000000000000..78c9887e565a627ce547e5b0ff1e211a41ce54ae
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java
@@ -0,0 +1,363 @@
+/**
+ * <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.commons.memberlist.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.olat.basesecurity.BaseSecurityModule;
+import org.olat.commons.memberlist.manager.MembersExportManager;
+import org.olat.commons.memberlist.ui.MembersAvatarDisplayRunController.IdentityComparator;
+import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.link.LinkPopupSettings;
+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.creator.ControllerCreator;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.media.MediaResource;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.helpers.Settings;
+import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
+import org.olat.core.id.Roles;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.Util;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
+import org.olat.course.nodes.members.Member;
+import org.olat.course.nodes.members.MembersCourseNodeRunController;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupMembership;
+import org.olat.group.ui.main.MemberListTableModel;
+import org.olat.group.ui.main.MemberView;
+import org.olat.modules.co.ContactFormController;
+import org.olat.repository.RepositoryEntry;
+import org.olat.user.DisplayPortraitManager;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+
+
+
+/**
+ * Initial Date: 29.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+public class MembersListDisplayRunController extends BasicController {
+	
+	public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName();
+	public static final int USER_PROPS_OFFSET = 500;
+	
+	private Link printLink;
+	private Link allEmailLink;
+	private Link downloadLink;
+	
+	private FormBasicController mailCtrl;
+	private ContactFormController emailController;
+	private CloseableModalController cmc;
+	
+	private List<Member> ownerList;
+	private List<Member> coachList;
+	private List<Member> participantList;
+	private List<Member> waitingtList;
+	
+	private List<Identity> owners;
+	private List<Identity> coaches;
+	private List<Identity> participants;
+	private List<Identity> waiting;
+
+	private final boolean showOwners;
+	private final boolean showCoaches;
+	private final boolean showParticipants;
+	private final boolean showWaiting;
+	
+	private final List<UserPropertyHandler> userPropertyHandlers;
+	private final CourseEnvironment courseEnv;
+	private final BusinessGroup businessGroup;
+	private final RepositoryEntry repoEntry;
+	
+	protected FlexiTableElement ownersTable, coachesTable, participantsTable, waitingTable;
+	protected MemberListTableModel ownersModel, coachesModel, participantsModel, waitingModel;
+	
+	private MembersTableController ownersTableCtrl, coachesTableCtrl, participantsTableCtrl, waitingTableCtrl;
+	
+	private Map<Long,BusinessGroupMembership> groupmemberships;
+	private	Map<Long,Date> recentLaunches, initialLaunches;
+
+	
+	private VelocityContainer mainVC;
+	
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private MembersExportManager exportManager;
+	@Autowired
+	private BaseSecurityModule securityModule;
+	@Autowired
+	private UserCourseInformationsManager userInfosMgr;
+
+	
+	public MembersListDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup,
+			List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList,
+			boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) {
+		super(ureq, wControl);
+		Translator fallback = userManager.getPropertyHandlerTranslator(getTranslator());		
+		setTranslator(Util.createPackageTranslator(translator, fallback, getLocale()));		
+		
+		mainVC = createVelocityContainer("membersTable");
+		
+		this.courseEnv = courseEnv;
+		this.businessGroup = businessGroup;
+		this.repoEntry = courseEnv != null ? courseEnv.getCourseGroupManager().getCourseEntry() : null;
+
+		userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false);
+		// lists
+		this.owners = owners;
+		this.coaches = coaches;
+		this.participants = participants;
+		this.waiting = waiting;
+		// flags
+		this.showOwners = showOwners;
+		this.showCoaches = showCoaches;
+		this.showParticipants = showParticipants;
+		this.showWaiting = showWaiting;
+		
+		if(canEmail) {
+			allEmailLink = LinkFactory.createLink(null, "email", "email.all", "members.email.title", getTranslator(), mainVC, this, Link.BUTTON);
+			allEmailLink.setIconLeftCSS("o_icon o_icon_mail");
+		}
+		
+		IdentityEnvironment idEnv = ureq.getUserSession().getIdentityEnvironment();
+		Identity ownId = idEnv.getIdentity();
+		Roles roles = idEnv.getRoles();
+		boolean memberXlsEnabled = businessGroup != null ? businessGroup.isDownloadMembersLists() && !waiting.contains(ownId) : false;
+		if (editable && (roles.isOLATAdmin() || roles.isGroupManager() || owners.contains(ownId) || coaches.contains(ownId) || memberXlsEnabled)) {
+			downloadLink = LinkFactory.createLink(null, "download", "download", "members.download", getTranslator(), mainVC, this, Link.BUTTON);
+			downloadLink.setIconLeftCSS("o_icon o_icon_download");
+			printLink = LinkFactory.createButton("print", mainVC, this);
+			printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg");
+			printLink.setPopup(new LinkPopupSettings(700, 500, "print-members"));
+		}
+		
+		Comparator<Identity> idComparator = new IdentityComparator();
+		Collections.sort(owners, idComparator);
+		ownerList = convertIdentitiesToMembers(owners);
+		Collections.sort(coaches, idComparator);
+		coachList = convertIdentitiesToMembers(coaches);
+		Collections.sort(participants, idComparator);
+		participantList = convertIdentitiesToMembers(participants);
+		Collections.sort(waiting, idComparator);
+		waitingtList = convertIdentitiesToMembers(waiting);
+		
+		Set<MemberView> duplicateCatcher = new HashSet<>();
+		boolean userLastTimeVisible = cacheGroupMemberships(ureq);
+		
+		if (showOwners && !owners.isEmpty()) {
+			ownersTableCtrl = new MembersTableController(ureq, wControl, owners, duplicateCatcher, recentLaunches, initialLaunches, 
+					userPropertyHandlers, groupmemberships,	repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), 
+					editable, canEmail, userLastTimeVisible);
+			listenTo(ownersTableCtrl);
+			mainVC.put("ownerList", ownersTableCtrl.getInitialComponent());
+		}
+		if (showCoaches && !coaches.isEmpty()) {
+			coachesTableCtrl = new MembersTableController(ureq, wControl, coaches, duplicateCatcher, recentLaunches, initialLaunches, 
+					userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable,
+					canEmail, userLastTimeVisible);
+			listenTo(coachesTableCtrl);
+			mainVC.put("coachList", coachesTableCtrl.getInitialComponent());
+		}
+		if (showParticipants && !participants.isEmpty()) {
+			participantsTableCtrl = new MembersTableController(ureq, wControl, participants, duplicateCatcher, recentLaunches, initialLaunches,
+					userPropertyHandlers, groupmemberships,	repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable,
+					canEmail, userLastTimeVisible);
+			listenTo(participantsTableCtrl);
+			mainVC.put("participantList", participantsTableCtrl.getInitialComponent());
+		}
+		if (showWaiting && !waiting.isEmpty()) {
+			waitingTableCtrl = new MembersTableController(ureq, wControl, waiting, duplicateCatcher, recentLaunches, initialLaunches,
+					userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable, 
+					canEmail, userLastTimeVisible);
+			listenTo(waitingTableCtrl);
+			mainVC.put("waitingList", waitingTableCtrl.getInitialComponent());
+		}
+		
+		putInitialPanel(mainVC);		
+	}
+
+
+	
+	@Override
+	public void event(UserRequest ureq, Component source, Event event) {
+		if (source == printLink) {
+			doPrint(ureq);
+		} else if(source == allEmailLink) {
+			doEmail(ureq);
+		} else if (source == downloadLink) {
+			doExport(ureq);
+		}
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(source == cmc) {
+			cleanUp();
+		} else if (source == emailController) {
+			cmc.deactivate();
+			cleanUp();
+		} else if(source == mailCtrl) {
+			cmc.deactivate();
+			cleanUp();
+		}
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(emailController);
+		removeAsListenerAndDispose(mailCtrl);
+		removeAsListenerAndDispose(cmc);
+		emailController = null;
+		mailCtrl = null;
+		cmc = null;
+	}
+	
+	private boolean cacheGroupMemberships (UserRequest ureq) {
+		boolean isUserLastVisitVisible = securityModule.isUserLastVisitVisible(ureq.getUserSession().getRoles());
+		if (isUserLastVisitVisible) {
+			if (businessGroup != null) {
+				List<BusinessGroup> groups = new ArrayList<>(); 
+				groups.add(businessGroup);
+				groupmemberships = exportManager.getGroupMembershipMap(groups);
+			}
+			if (repoEntry != null) {
+				initialLaunches = userInfosMgr.getInitialLaunchDates(repoEntry.getOlatResource().getResourceableId());
+				recentLaunches = userInfosMgr.getRecentLaunchDates(repoEntry.getOlatResource());			
+			}
+		}
+		return isUserLastVisitVisible;
+	}
+	
+
+
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+
+	
+	private void doEmail(UserRequest ureq) {
+		if(mailCtrl != null || cmc != null) return;
+		removeAsListenerAndDispose(cmc);
+		removeAsListenerAndDispose(mailCtrl);
+		mailCtrl = new MembersMailController(ureq, getWindowControl(), getTranslator(), courseEnv,
+				ownerList, coachList, participantList, waitingtList, createBodyTemplate());
+		listenTo(mailCtrl);
+		
+		String title = translate("members.email.title");
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title);
+		listenTo(cmc);
+		
+		cmc.activate();	
+	}
+	
+	private String createBodyTemplate() {
+		if (courseEnv == null) {
+			String courseName = businessGroup.getName();
+			// Build REST URL to business group, use hack via group manager to access
+			StringBuilder courseLink = new StringBuilder();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/auth/BusinessGroup/").append(businessGroup.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});	
+		} else {
+			String courseName = courseEnv.getCourseTitle();
+			// Build REST URL to course element, use hack via group manager to access repo entry
+			StringBuilder courseLink = new StringBuilder();
+			RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/url/RepositoryEntry/").append(entry.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});		
+		}
+	}
+	
+	
+	private void doExport(UserRequest ureq) {
+		MediaResource resource = exportManager.getXlsMediaResource(showOwners, showCoaches, showParticipants, showWaiting, 
+				owners, coaches, participants, waiting, getTranslator(), userPropertyHandlers, repoEntry, businessGroup);
+		
+		ureq.getDispatchResult().setResultingMediaResource(resource);
+	}
+	
+	private void doPrint(UserRequest ureq) {
+		ControllerCreator printControllerCreator = new ControllerCreator() {
+			@Override
+			public Controller createController(UserRequest lureq, WindowControl lwControl) {
+				lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print");
+				return new MembersPrintController(lureq, lwControl, userPropertyHandlers, getTranslator(), ownerList, coachList,
+						participantList, waitingtList, showOwners, showCoaches, showParticipants, showWaiting, 
+						courseEnv != null ? courseEnv.getCourseTitle() : businessGroup.getName());
+			}					
+		};
+		ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator);
+		openInNewBrowserWindow(ureq, layoutCtrlr);
+	}
+	
+	
+	private List<Member> convertIdentitiesToMembers(List<Identity> identities) {
+		List<Member> memberList = new ArrayList<>();
+		for (Identity identity : identities) {
+			Member member = createMember(identity);
+			memberList.add(member);
+		}
+		return memberList;
+	}
+	
+	private Member createMember(Identity identity) {		
+		boolean hasPortrait = DisplayPortraitManager.getInstance().hasPortrait(identity.getName());
+
+		String portraitCssClass;
+		String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH);
+		if ("male".equalsIgnoreCase(gender)) {
+			portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS;
+		} else if ("female".equalsIgnoreCase(gender)) {
+			portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS;
+		} else {
+			portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS;
+		}
+		String fullname = userManager.getUserDisplayName(identity);
+		return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass);
+	}
+}
diff --git a/src/main/java/org/olat/course/nodes/members/MembersMailController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java
similarity index 85%
rename from src/main/java/org/olat/course/nodes/members/MembersMailController.java
rename to src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java
index 11a810071e7b0bf51f49602b8b357b4ed8fcd3b1..0a5b6146794f8f6ee8fba82bac4e09935cba4256 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersMailController.java
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.course.nodes.members;
+package org.olat.commons.memberlist.ui;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -46,6 +46,7 @@ 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.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.translator.Translator;
 import org.olat.core.gui.util.CSSHelper;
 import org.olat.core.id.Identity;
 import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
@@ -63,6 +64,7 @@ import org.olat.core.util.mail.MailModule;
 import org.olat.core.util.mail.MailerResult;
 import org.olat.core.util.mail.ui.EMailIdentity;
 import org.olat.course.groupsandrights.CourseGroupManager;
+import org.olat.course.nodes.members.Member;
 import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryService;
@@ -83,7 +85,7 @@ public class MembersMailController extends FormBasicController {
 	private FileElement attachmentEl;
 	private FormLink addMemberButton;
 	private TextElement subjectEl, externalAddressesEl;
-	private MultipleSelectionElement ownerEl, coachEl, participantEl, individualEl, externalEl, copyFromEl;
+	private MultipleSelectionElement ownerEl, coachEl, participantEl, waitingEl, individualEl, externalEl, copyFromEl;
 	private FormLayoutContainer uploadCont, individualMemberCont;
 	
 	private CloseableModalController cmc;
@@ -96,7 +98,7 @@ public class MembersMailController extends FormBasicController {
 	private final int contactAttachmentMaxSizeInMb;
 	private final List<Member> selectedMembers = new ArrayList<>();
 	private final List<Attachment> attachments = new ArrayList<>();
-	private final List<Member> ownerList, coachList, participantList;
+	private final List<Member> ownerList, coachList, participantList, waitingList;
 	
 	@Autowired
 	private UserManager userManager;
@@ -109,14 +111,17 @@ public class MembersMailController extends FormBasicController {
 	@Autowired
 	private RepositoryService repositoryService;
 	
-	public MembersMailController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv,
-			List<Member> ownerList, List<Member> coachList, List<Member> participantList, String bodyTemplate) {
+	public MembersMailController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv,
+			List<Member> ownerList, List<Member> coachList, List<Member> participantList, List<Member> waitingList, String bodyTemplate) {
 		super(ureq, wControl, Util.createPackageTranslator(MailHelper.class, ureq.getLocale()));
+		setTranslator(Util.createPackageTranslator(translator, MailHelper.class, ureq.getLocale()));
+//		setTranslator(translator);
 		
 		this.courseEnv = courseEnv;
 		this.ownerList = ownerList;
 		this.coachList = coachList;
 		this.participantList = participantList;
+		this.waitingList = waitingList;
 		this.contactAttachmentMaxSizeInMb = mailModule.getMaxSizeForAttachement();
 		initForm(ureq);
 		
@@ -154,10 +159,16 @@ public class MembersMailController extends FormBasicController {
 			participantEl = uifactory.addCheckboxesHorizontal("contact.all.participants", to, formLayout, keys, values);
 			to = null;
 		}
+		if(waitingList != null && waitingList.size() > 0) {
+			String[] values = new String[] { translate("contact.all.waiting") };
+			waitingEl = uifactory.addCheckboxesHorizontal("contact.all.waiting", to, formLayout, keys, values);
+			to = null;
+		}
 		
 		if((ownerList != null && ownerList.size() > 0)
 				|| (coachList != null && coachList.size() > 0)
-				|| (participantList != null && participantList.size() > 0)) {
+				|| (participantList != null && participantList.size() > 0)
+				|| (waitingList != null && waitingList.size() > 0)) {
 			String[] values = new String[] { translate("contact.individual") };
 			individualEl = uifactory.addCheckboxesHorizontal("contact.individual", to, formLayout, keys, values);
 			individualEl.addActionListener(FormEvent.ONCHANGE);
@@ -350,6 +361,7 @@ public class MembersMailController extends FormBasicController {
 		List<Member> owners = ownerList;
 		List<Member> coaches = coachList;
 		List<Member> participants = participantList;
+		List<Member> waiting = waitingList;
 		if(ownerEl != null && ownerEl.isAtLeastSelected(1)) {
 			owners = null;
 		}
@@ -359,11 +371,15 @@ public class MembersMailController extends FormBasicController {
 		if(participantEl != null && participantEl.isAtLeastSelected(1)) {
 			participants = null;
 		}
+		if(waitingEl != null && waitingEl.isAtLeastSelected(1)) {
+			waiting = null;
+		}
 		
 		if(owners == null && coaches == null && participants == null) {
 			showWarning("already.all.selected");
 		} else {
-			selectMemberCtrl = new SelectMembersController(ureq, getWindowControl(), selectedMembers, owners, coaches, participants);
+			selectMemberCtrl = new SelectMembersController(ureq, getWindowControl(), getTranslator(), 
+					selectedMembers, owners, coaches, participants, waiting);
 			listenTo(selectMemberCtrl);
 			
 			String title = translate("select.members");
@@ -414,30 +430,59 @@ public class MembersMailController extends FormBasicController {
 	
 	private void doSend(UserRequest ureq) {
 		ContactList contactList = new ContactList("");
-		if(ownerEl != null && ownerEl.isAtLeastSelected(1)) {
-			RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry();
-			List<Identity> owners = repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name());
-			contactList.addAllIdentites(owners);
-		}
-		
-		if(coachEl != null && coachEl.isAtLeastSelected(1)) {
-			Set<Long> sendToWhatYouSee = new HashSet<>();
-			for(Member coach:coachList) {
-				sendToWhatYouSee.add(coach.getKey());
+		if (courseEnv == null) {
+			if(coachEl != null && coachEl.isAtLeastSelected(1)) {
+				List<Long> identityKeys = new ArrayList<>(coachList.size());
+				for(Member coach:coachList) {
+					identityKeys.add(coach.getKey());
+				}
+				List<Identity> coaches = securityManager.loadIdentityByKeys(identityKeys);
+				contactList.addAllIdentites(coaches);
 			}
-			CourseGroupManager cgm = courseEnv.getCourseGroupManager();
-			avoidInvisibleMember(cgm.getCoachesFromBusinessGroups(), contactList, sendToWhatYouSee);
-			avoidInvisibleMember(cgm.getCoaches(), contactList, sendToWhatYouSee);
-		}
-		
-		if(participantEl != null && participantEl.isAtLeastSelected(1)) {
-			Set<Long> sendToWhatYouSee = new HashSet<>();
-			for(Member participant:participantList) {
-				sendToWhatYouSee.add(participant.getKey());
+			
+			if(participantEl != null && participantEl.isAtLeastSelected(1)) {
+				List<Long> identityKeys = new ArrayList<>(participantList.size());
+				for(Member participant:participantList) {
+					identityKeys.add(participant.getKey());
+				}
+				List<Identity> participants = securityManager.loadIdentityByKeys(identityKeys);
+				contactList.addAllIdentites(participants);
+			}
+			
+			if(waitingEl != null && waitingEl.isAtLeastSelected(1)) {
+				List<Long> identityKeys = new ArrayList<>(waitingList.size());
+				for(Member waiter:waitingList) {
+					identityKeys.add(waiter.getKey());
+				}
+				List<Identity> waiters = securityManager.loadIdentityByKeys(identityKeys);
+				contactList.addAllIdentites(waiters);
+			}
+		} else {			
+			if(ownerEl != null && ownerEl.isAtLeastSelected(1)) {
+				RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry();
+				List<Identity> owners = repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name());
+				contactList.addAllIdentites(owners);
+			}
+			
+			if(coachEl != null && coachEl.isAtLeastSelected(1)) {
+				Set<Long> sendToWhatYouSee = new HashSet<>();
+				for(Member coach:coachList) {
+					sendToWhatYouSee.add(coach.getKey());
+				}
+				CourseGroupManager cgm = courseEnv.getCourseGroupManager();
+				avoidInvisibleMember(cgm.getCoachesFromBusinessGroups(), contactList, sendToWhatYouSee);
+				avoidInvisibleMember(cgm.getCoaches(), contactList, sendToWhatYouSee);
+			}
+			
+			if(participantEl != null && participantEl.isAtLeastSelected(1)) {
+				Set<Long> sendToWhatYouSee = new HashSet<>();
+				for(Member participant:participantList) {
+					sendToWhatYouSee.add(participant.getKey());
+				}
+				CourseGroupManager cgm = courseEnv.getCourseGroupManager();
+				avoidInvisibleMember(cgm.getParticipantsFromBusinessGroups(), contactList, sendToWhatYouSee);
+				avoidInvisibleMember(cgm.getParticipants(), contactList, sendToWhatYouSee);
 			}
-			CourseGroupManager cgm = courseEnv.getCourseGroupManager();
-			avoidInvisibleMember(cgm.getParticipantsFromBusinessGroups(), contactList, sendToWhatYouSee);
-			avoidInvisibleMember(cgm.getParticipants(), contactList, sendToWhatYouSee);
 		}
 		
 		if(individualEl != null && individualEl.isAtLeastSelected(1)
diff --git a/src/main/java/org/olat/course/nodes/members/MembersPrintController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java
similarity index 83%
rename from src/main/java/org/olat/course/nodes/members/MembersPrintController.java
rename to src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java
index 5379f30bfb82e9fe9286a9d6f73935a3fde1914d..6571af4de64dc8c06e74ba18f1d4de6b5ee2f720 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersPrintController.java
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.course.nodes.members;
+package org.olat.commons.memberlist.ui;
 
 import java.util.List;
 
@@ -35,9 +35,11 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
 import org.olat.core.gui.media.MediaResource;
 import org.olat.core.gui.media.NotFoundMediaResource;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.util.Util;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSMediaResource;
-import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.course.nodes.members.Member;
 import org.olat.user.DisplayPortraitManager;
 import org.olat.user.UserManager;
 import org.olat.user.propertyhandlers.UserPropertyHandler;
@@ -61,28 +63,32 @@ public class MembersPrintController extends BasicController {
 	@Autowired
 	private DisplayPortraitManager portraitManager;
 	
-	public MembersPrintController(UserRequest ureq, WindowControl wControl,
-			CourseEnvironment courseEnv, List<UserPropertyHandler> userPropertyHandlers,
-			List<Member> owners, List<Member> coaches, List<Member> participants) {
+	public MembersPrintController(UserRequest ureq, WindowControl wControl, List<UserPropertyHandler> userPropertyHandlers, 
+			Translator translator, List<Member> owners, List<Member> coaches, List<Member> participants, List<Member> waiting, 
+			boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, String title) {
 		super(ureq, wControl);
+		setTranslator(Util.createPackageTranslator(translator, getTranslator(), getLocale()));
 
 		avatarBaseURL = registerCacheableMapper(ureq, "avatars-members-high-quality", new UserAvatarHQMapper());
 		this.userPropertyHandlers = userPropertyHandlers;
 		
 		mainVC = createVelocityContainer("print");
-		mainVC.contextPut("courseTitle", courseEnv.getCourseTitle());
+		mainVC.contextPut("courseTitle", title);
 		mainVC.contextPut("avatarBaseURL", avatarBaseURL);
 		mainVC.contextPut("userPropertyHandlers", userPropertyHandlers);
 		
-		if(owners != null && owners.size() > 0) {
+		if(showOwners && owners != null && owners.size() > 0) {
 			initFormMemberList("owners", translate("members.owners"), owners);
 		}
-		if(coaches != null && coaches.size() > 0) {
+		if(showCoaches && coaches != null && coaches.size() > 0) {
 			initFormMemberList("coaches", translate("members.coaches"), coaches);
 		}
-		if(participants != null && participants.size() > 0) {
+		if(showParticipants && participants != null && participants.size() > 0) {
 			initFormMemberList("participants", translate("members.participants"), participants);
 		}
+		if(showWaiting && waiting != null && waiting.size() > 0) {
+			initFormMemberList("waiting", translate("members.waiting"), waiting);
+		}
 
 		MainPanel mainPanel = new MainPanel("membersPrintPanel");
 		mainPanel.setContent(mainVC);
diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java
new file mode 100644
index 0000000000000000000000000000000000000000..69d1aa322c98752a40193486e5fda32e6e2bbaa7
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java
@@ -0,0 +1,360 @@
+/**
+ * <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.commons.memberlist.ui;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.olat.NewControllerFactory;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.commons.persistence.SortKey;
+import org.olat.core.gui.UserRequest;
+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.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.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.control.Controller;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.helpers.Settings;
+import org.olat.core.id.Identity;
+import org.olat.core.id.UserConstants;
+import org.olat.core.id.context.BusinessControl;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
+import org.olat.course.nodes.members.MembersCourseNodeRunController;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupMembership;
+import org.olat.group.ui.main.MemberListTableModel;
+import org.olat.group.ui.main.MemberListTableModel.Cols;
+import org.olat.group.ui.main.MemberView;
+import org.olat.instantMessaging.InstantMessagingModule;
+import org.olat.instantMessaging.InstantMessagingService;
+import org.olat.instantMessaging.OpenInstantMessageEvent;
+import org.olat.instantMessaging.model.Buddy;
+import org.olat.modules.co.ContactFormController;
+import org.olat.repository.RepositoryEntry;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Initial Date: 30.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+public class MembersTableController extends FormBasicController {
+	
+	public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName();
+	public static final int USER_PROPS_OFFSET = 500;
+
+	protected FlexiTableElement membersTable;
+	protected MemberListTableModel membersModel;
+	
+	
+	private ContactFormController emailController;
+	private CloseableModalController cmc;
+
+	private final AtomicInteger counter = new AtomicInteger();
+	
+	private final boolean chatEnabled, canEmail, deduplicateList, editable, userLastTimeVisible;
+	
+	private final List<UserPropertyHandler> userPropertyHandlers;
+	private final List<MemberView> membersList;
+	private final RepositoryEntry repoEntry; 
+	private Set<MemberView> duplicateCatcher;
+
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private InstantMessagingModule imModule;
+	@Autowired
+	private InstantMessagingService imService;
+	@Autowired
+	private BaseSecurity securityManager;
+	
+	private BusinessGroup businessGroup;
+	private CourseEnvironment courseEnv;
+	
+	private int pageSize = 20;
+	
+	public MembersTableController(UserRequest ureq, WindowControl wControl, List<Identity> members, Set<MemberView> duplicateCatcher, Map<Long,Date> recentLaunches, Map<Long,Date> initialLaunches,
+			List<UserPropertyHandler> userPropertyHandlers, Map<Long,BusinessGroupMembership> groupmemberships, RepositoryEntry repoEntry, BusinessGroup businessGroup, 
+			CourseEnvironment courseEnv, boolean deduplicateList, Translator translator, boolean editable, boolean canEmail, boolean userLastTimeVisible) {
+		super(ureq, wControl, "table");
+		setTranslator(translator);
+		
+		chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled();
+		
+		this.userPropertyHandlers = userPropertyHandlers;
+		this.duplicateCatcher = duplicateCatcher;
+		this.repoEntry = repoEntry;
+		this.deduplicateList = deduplicateList;
+		this.editable = editable;
+		this.canEmail = canEmail;
+		this.userLastTimeVisible = userLastTimeVisible;
+		
+		this.businessGroup = businessGroup;
+		this.courseEnv = courseEnv;
+		
+		this.membersList = getMembersFromIdentity(ureq, members, groupmemberships, recentLaunches, initialLaunches);
+	
+		initForm(ureq);
+	}
+	
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		SortKey defaultSortKey = initColumns(columnsModel);		
+		membersModel = new MemberListTableModel(columnsModel, imModule.isOnlineStatusEnabled());
+		membersModel.setObjects(membersList);
+		membersTable = uifactory.addTableElement(getWindowControl(), "table", membersModel, pageSize, false, getTranslator(), formLayout);
+		membersTable.setEmtpyTableMessageKey("nomembers");
+		membersTable.setAndLoadPersistedPreferences(ureq, this.getClass().getSimpleName());
+		membersTable.setExportEnabled(false);
+		membersTable.setElementCssClass("o_sel_member_list");
+		
+		if(defaultSortKey != null) {
+			FlexiTableSortOptions options = new FlexiTableSortOptions();
+			options.setDefaultOrderBy(defaultSortKey);
+			membersTable.setSortSettings(options);
+		}
+	}
+	
+	@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();
+				if ("vcard".equals(cmd)) {
+					MemberView row = membersModel.getObject(se.getIndex());
+					doOpenHomePage(row, ureq);
+				} else if ("email".equals(cmd)) {
+					MemberView row = membersModel.getObject(se.getIndex());
+					doSendEmailToMember(row, ureq);
+				}
+			}	
+		} else if (source instanceof FormLink) {
+			FormLink link = (FormLink)source;
+			String cmd = link.getCmd();
+			MemberView row = (MemberView)link.getUserObject();
+			if ("im".equals(cmd)) {
+				doOpenChat(row, ureq);
+			} 
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(source == cmc) {
+			cleanUp();
+		} else if (source == emailController) {
+			cmc.deactivate();
+			cleanUp();
+		} 
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(emailController);
+		removeAsListenerAndDispose(cmc);
+		emailController = null;
+		cmc = null;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	private List<MemberView> getMembersFromIdentity(UserRequest ureq, List<Identity> identities, 
+			Map<Long,BusinessGroupMembership> groupmemberships,	Map<Long,Date> recentLaunches, Map<Long,Date> initialLaunches) {
+		if (!deduplicateList) {
+			duplicateCatcher = new HashSet<>();
+		}
+		List<MemberView> memberList = new ArrayList<>();		
+		for (Identity identity : identities) {
+			MemberView member = new MemberView(identity, userPropertyHandlers, getLocale());
+			if (userLastTimeVisible) {
+				if (repoEntry == null) {
+					BusinessGroupMembership groupmembership = groupmemberships.get(identity.getKey());
+					member.setFirstTime(groupmembership.getCreationDate());
+					member.setLastTime(groupmembership.getLastModified());
+				} else {
+					member.setFirstTime(initialLaunches.get(identity.getKey()));
+					member.setLastTime(recentLaunches.get(identity.getKey()));
+				}
+			}
+			if (!duplicateCatcher.contains(member)) {
+				memberList.add(member);
+				if (!identity.equals(ureq.getIdentity())){
+					forgeChatLink(member);
+				}
+			}
+			duplicateCatcher.add(member);
+		}
+		return memberList;
+	}
+	
+	protected void forgeChatLink(MemberView row) {
+		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 SortKey initColumns(FlexiTableColumnModel columnsModel) {
+		SortKey defaultSortKey = null;
+		String editAction = "vcard";
+		
+		if (chatEnabled && editable) {
+			DefaultFlexiColumnModel chatCol = new DefaultFlexiColumnModel(Cols.online.i18n(), Cols.online.ordinal());
+			chatCol.setExportable(false);
+			columnsModel.addFlexiColumnModel(chatCol);
+		}
+			
+		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);
+			String myEditAction = editAction;
+			FlexiColumnModel col;
+			if(UserConstants.FIRSTNAME.equals(propName) || UserConstants.LASTNAME.equals(propName) || UserConstants.EMAIL.equals(propName)) {
+				if (UserConstants.EMAIL.equals(propName)) {
+					myEditAction = "email";
+					if (!canEmail) {
+						continue;
+					}
+				}
+				col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(),
+						colPos, myEditAction, true, propName,
+						new StaticFlexiCellRenderer(myEditAction, new TextFlexiCellRenderer()));
+			} else {
+				col = new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, true, propName);
+			}
+			columnsModel.addFlexiColumnModel(col);
+			colPos++;
+			
+			if(defaultSortKey == null) {
+				defaultSortKey = new SortKey(propName, true);
+			}
+		}
+		if (userLastTimeVisible) {
+			columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), true, Cols.firstTime.name()));
+			columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), true, Cols.lastTime.name()));
+		}
+		return defaultSortKey;
+	}
+	
+	private void doSendEmailToMember(MemberView member, UserRequest ureq) {
+		if (!editable) return;
+		ContactList memberList;
+		Identity identity = securityManager.loadIdentityByKey(member.getIdentityKey());
+		String fullName = userManager.getUserDisplayName(identity);
+		if (courseEnv == null) {
+			memberList = new ContactList(translate("members.to", new String[]{ fullName, businessGroup.getName() }));
+		} else {
+			memberList = new ContactList(translate("members.to", new String[]{ fullName, courseEnv.getCourseTitle() }));
+		}
+		memberList.add(identity);
+		doSendEmailToMember(memberList, ureq);
+	}
+	
+	private void doSendEmailToMember(ContactList contactList, UserRequest ureq) {
+		if (contactList.getEmailsAsStrings().size() > 0) {
+			removeAsListenerAndDispose(cmc);
+			removeAsListenerAndDispose(emailController);
+			
+			ContactMessage cmsg = new ContactMessage(ureq.getIdentity());
+			cmsg.addEmailTo(contactList);
+			// preset body template from i18n
+			cmsg.setBodyText(createBodyTemplate());
+			emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg);
+			listenTo(emailController);
+			
+			String title = translate("members.email.title");
+			cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title);
+			listenTo(cmc);
+			cmc.activate();
+		}
+	}
+	
+	private String createBodyTemplate() {
+		if (courseEnv == null) {
+			String courseName = businessGroup.getName();
+			// Build REST URL to business group, use hack via group manager to access
+			StringBuilder courseLink = new StringBuilder();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/auth/BusinessGroup/").append(businessGroup.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});	
+		} else {
+			String courseName = courseEnv.getCourseTitle();
+			// Build REST URL to course element, use hack via group manager to access repo entry
+			StringBuilder courseLink = new StringBuilder();
+			RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry();
+			courseLink.append(Settings.getServerContextPathURI())
+				.append("/url/RepositoryEntry/").append(entry.getKey());
+			return translate("email.body.template", new String[]{courseName, courseLink.toString()});		
+		}
+	}
+	
+	private void doOpenHomePage(MemberView member, UserRequest ureq) {
+		String url = "[HomePage:" + member.getIdentityKey() + "]";
+		BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url);
+		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
+		NewControllerFactory.getInstance().launch(ureq, bwControl);
+	}
+	
+	private void doOpenChat(MemberView member, UserRequest ureq) {
+		Buddy buddy = imService.getBuddyById(member.getIdentityKey());
+		OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy);
+		ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES);
+	}
+
+}
diff --git a/src/main/java/org/olat/course/nodes/members/SelectMembersController.java b/src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java
similarity index 83%
rename from src/main/java/org/olat/course/nodes/members/SelectMembersController.java
rename to src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java
index 055d59dfe77cae1f7c8c1b9beba091094a035776..79a7f920911b14c17a5b446cf8dfa0a7506ce6d4 100644
--- a/src/main/java/org/olat/course/nodes/members/SelectMembersController.java
+++ b/src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.course.nodes.members;
+package org.olat.commons.memberlist.ui;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,26 +31,30 @@ import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
 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.translator.Translator;
+import org.olat.course.nodes.members.Member;
 
 /**
  * 
  * Initial date: 22.12.2015<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
+ * @author fkiefer
  */
 public class SelectMembersController extends FormBasicController {
 
-	private MultipleSelectionElement ownerEl, coachEl, participantEl;
-	private final List<Member> ownerList, coachList, participantList, preSelectedMembers;
+	private MultipleSelectionElement ownerEl, coachEl, participantEl, waitingEl;
+	private final List<Member> ownerList, coachList, participantList, waitingList, preSelectedMembers;
 	
 	private List<Member> selectedMembers = new ArrayList<>();
 	
-	public SelectMembersController(UserRequest ureq, WindowControl wControl, List<Member> preSelectedMembers,
-			List<Member> ownerList, List<Member> coachList, List<Member> participantList) {
+	public SelectMembersController(UserRequest ureq, WindowControl wControl, Translator translator,
+			List<Member> preSelectedMembers, List<Member> ownerList, List<Member> coachList, List<Member> participantList, List<Member> waitingList) {
 		super(ureq, wControl, LAYOUT_VERTICAL);
+		setTranslator(translator);
 		this.ownerList = ownerList;
 		this.coachList = coachList;
 		this.participantList = participantList;
+		this.waitingList = waitingList;
 		this.preSelectedMembers = preSelectedMembers;
 		initForm(ureq);
 	}
@@ -62,15 +66,19 @@ public class SelectMembersController extends FormBasicController {
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		if(ownerList != null && ownerList.size() > 0) {
-			ownerEl = makeSelection("owners", ownerList, formLayout);
+			ownerEl = makeSelection("members.owners", ownerList, formLayout);
 		}
 		
 		if(coachList != null && coachList.size() > 0) {
-			coachEl = makeSelection("coaches", coachList, formLayout);
+			coachEl = makeSelection("members.coaches", coachList, formLayout);
 		}
 		
 		if(participantList != null && participantList.size() > 0) {
-			participantEl = makeSelection("participants", participantList, formLayout);
+			participantEl = makeSelection("members.participants", participantList, formLayout);
+		}
+		
+		if(waitingList != null && waitingList.size() > 0) {
+			waitingEl = makeSelection("members.waiting", waitingList, formLayout);
 		}
 
 		FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
@@ -111,6 +119,7 @@ public class SelectMembersController extends FormBasicController {
 		selectMembers(ownerEl, ownerList);
 		selectMembers(coachEl, coachList);
 		selectMembers(participantEl, participantList);
+		selectMembers(waitingEl, waitingList);
 		fireEvent(ureq, Event.DONE_EVENT);
 	}
 	
diff --git a/src/main/java/org/olat/course/nodes/members/_content/individual_members.html b/src/main/java/org/olat/commons/memberlist/ui/_content/individual_members.html
similarity index 100%
rename from src/main/java/org/olat/course/nodes/members/_content/individual_members.html
rename to src/main/java/org/olat/commons/memberlist/ui/_content/individual_members.html
diff --git a/src/main/java/org/olat/course/nodes/members/_content/memberList.html b/src/main/java/org/olat/commons/memberlist/ui/_content/memberList.html
similarity index 100%
rename from src/main/java/org/olat/course/nodes/members/_content/memberList.html
rename to src/main/java/org/olat/commons/memberlist/ui/_content/memberList.html
diff --git a/src/main/java/org/olat/course/nodes/members/_content/members.html b/src/main/java/org/olat/commons/memberlist/ui/_content/members.html
similarity index 77%
rename from src/main/java/org/olat/course/nodes/members/_content/members.html
rename to src/main/java/org/olat/commons/memberlist/ui/_content/members.html
index 554b50f1cc4f1e69fe7d8311efc3557b9e0fb65e..bd5e933e8a1efd98187a949ed2ba8cd87c7446af 100644
--- a/src/main/java/org/olat/course/nodes/members/_content/members.html
+++ b/src/main/java/org/olat/commons/memberlist/ui/_content/members.html
@@ -2,6 +2,9 @@
 #if($r.available("email"))
 	$r.render("email")
 #end
+#if($r.available("download"))
+	$r.render("download")
+#end
 #if($r.available("print"))
 	$r.render("print")
 #end
@@ -30,4 +33,10 @@
 			#end
 		</div>
 	#end
+	#if($showWaiting && $hasWaiting)
+	<div class="clearfix o_block o_participants">
+		<h4>$r.translate("members.waiting")</h4>
+		$r.render("waiting")
+	</div>
+	#end
 </div>
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html b/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html
new file mode 100644
index 0000000000000000000000000000000000000000..e0a6e9c2b8156791f5fe17632cd3ddb8460cb940
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html
@@ -0,0 +1,37 @@
+<div class="pull-right">
+#if($r.available("email"))
+	$r.render("email")
+#end
+#if($r.available("download"))
+	$r.render("download")
+#end
+#if($r.available("print"))
+	$r.render("print")
+#end
+</div>
+<div class="clearfix">
+	#if ($r.available("ownerList"))
+		<h4>$r.translate("members.owners")</h4>
+		<div>
+			$r.render("ownerList")
+		</div>
+	#end
+	#if ($r.available("coachList"))
+		<h4>$r.translate("members.coaches")</h4>
+		<div>
+			$r.render("coachList")
+		</div>
+	#end
+	#if ($r.available("participantList"))
+		<h4>$r.translate("members.participants")</h4>
+		<div>
+			$r.render("participantList")
+		</div>
+	#end
+	#if ($r.available("waitingList"))
+		<h4>$r.translate("members.waiting")</h4>
+		<div>
+			$r.render("waitingList")
+		</div>
+	#end
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html b/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html
new file mode 100644
index 0000000000000000000000000000000000000000..00369b16f7ab61f07db2fa051746443f72c88cff
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html
@@ -0,0 +1,30 @@
+<div class="clearfix">
+	<div class="o_header_with_buttons">
+		#if ($headline)
+		<h2><i class="o_icon o_icon_group"> </i> $r.translate("userlist.title")</h2>
+		#end
+		<div class="o_button_group">
+			<div class="btn-group">
+				#if($r.available("tableCustomLink"))
+					$r.render("tableCustomLink")
+				#end
+				#if($r.available("tableLink"))
+					$r.render("tableLink")
+				#end
+			</div>
+		</div>
+	</div>
+	#if ($portrait)
+		#if ($r.available("portraitView"))
+			<div>
+				$r.render("portraitView")
+			</div>
+		#end
+	#else
+		#if ($r.available("listView"))
+			<div>
+				$r.render("listView")
+			</div>
+		#end
+	#end
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/nodes/members/_content/print.html b/src/main/java/org/olat/commons/memberlist/ui/_content/print.html
similarity index 96%
rename from src/main/java/org/olat/course/nodes/members/_content/print.html
rename to src/main/java/org/olat/commons/memberlist/ui/_content/print.html
index bfb27657acaa2d21fd5edef5d6ffd8bc73901e61..016919128017bb94214e2a67250a781af830dc2e 100644
--- a/src/main/java/org/olat/course/nodes/members/_content/print.html
+++ b/src/main/java/org/olat/commons/memberlist/ui/_content/print.html
@@ -12,6 +12,9 @@
 #if($r.available("participants"))
 	$r.render("participants")
 #end
+#if($r.available("waiting"))
+	$r.render("waiting")
+#end
 <script type='text/javascript'>
 /* <![CDATA[ */
 	jQuery(function() {
diff --git a/src/main/java/org/olat/course/nodes/members/_content/printList.html b/src/main/java/org/olat/commons/memberlist/ui/_content/printList.html
similarity index 100%
rename from src/main/java/org/olat/course/nodes/members/_content/printList.html
rename to src/main/java/org/olat/commons/memberlist/ui/_content/printList.html
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/table.html b/src/main/java/org/olat/commons/memberlist/ui/_content/table.html
new file mode 100644
index 0000000000000000000000000000000000000000..399d41789c3ea26a6195326eec73e7af3a5b0eb9
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_content/table.html
@@ -0,0 +1,5 @@
+#if ($r.available("table"))
+	<div>
+		$r.render("table")
+	</div>
+#end
\ No newline at end of file
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c1d6e89cbe9e68879482b2e8ff8a357bf7fdf164
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties
@@ -0,0 +1,5 @@
+#Tue Nov 13 17:14:15 CET 2012
+table.header.firstTime=Beitritt
+table.header.lastTime=Zuletzt besucht
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=Keine Mitglieder
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties
new file mode 100644
index 0000000000000000000000000000000000000000..3ffdcd55cddf5440d3d344fefa89c2fa91898c30
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties
@@ -0,0 +1,5 @@
+#Tue Feb 09 11:43:56 CET 2016
+table.header.firstTime=Registration
+table.header.lastTime=Last visit
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
\ No newline at end of file
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties
new file mode 100644
index 0000000000000000000000000000000000000000..3cc2cfc9907722855fe65b55d32d62b608ffb502
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties
@@ -0,0 +1,5 @@
+#Wed Jan 11 22:07:40 CET 2017
+table.header.firstTime=Admission
+table.header.lastTime=Derni\u00E8re visite
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties
new file mode 100644
index 0000000000000000000000000000000000000000..88b33f0a4412fc251c645870b96b64c5f0898920
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties
@@ -0,0 +1,5 @@
+#Wed Feb 01 13:37:33 CET 2017
+table.header.firstTime=Ammissione
+table.header.lastTime=Ultima visita
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties
new file mode 100644
index 0000000000000000000000000000000000000000..1943f12213f7ba925f55b6721abc8c694cab4d4e
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties
@@ -0,0 +1,5 @@
+#Wed Jan 15 13:44:33 CET 2014
+table.header.firstTime=Rejestracja
+table.header.lastTime=Ostatnia wizyta
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties
new file mode 100644
index 0000000000000000000000000000000000000000..4fe088176c71242206e40116f3b1e36ee72377dd
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties
@@ -0,0 +1,5 @@
+#Mon Jan 16 21:03:20 CET 2017
+table.header.firstTime=Entrando
+table.header.lastTime=\u00DAltima visita
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
\ No newline at end of file
diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties
new file mode 100644
index 0000000000000000000000000000000000000000..09613aad63e7b673746631cf30a1b46cff9de682
--- /dev/null
+++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties
@@ -0,0 +1,5 @@
+#Thu Oct 16 09:10:54 CEST 2014
+table.header.firstTime=\u52A0\u5165
+table.header.lastTime=\u4E0A\u6B21\u8BBF\u95EE
+table.header.online=$org.olat.group.ui.main\:table.header.online
+nomembers=No members
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/util/Util.java b/src/main/java/org/olat/core/util/Util.java
index 1cdb6217c04009ae5203b191f3e30419ea893f31..83a67bd18f02a59b79a5cc678833b87a44bad294 100644
--- a/src/main/java/org/olat/core/util/Util.java
+++ b/src/main/java/org/olat/core/util/Util.java
@@ -82,6 +82,25 @@ public class Util {
 		return createPackageTranslator(baseClass, locale, null);
 	}
 	
+	public static Translator createPackageTranslator(Class<?> baseClass, Class<?> fallbackClass, Locale locale) {
+		String fallbackpackage = Util.getPackageName(fallbackClass);
+		Translator fallback = new PackageTranslator(fallbackpackage, locale);
+		String transpackage = Util.getPackageName(baseClass);
+		Translator translator = new PackageTranslator(transpackage, locale, fallback);
+		return translator;
+	}
+	
+	public static Translator createPackageTranslator(Translator baseClass, Class<?> fallbackClass, Locale locale) {
+		String fallbackpackage = Util.getPackageName(fallbackClass);
+		Translator fallback = new PackageTranslator(fallbackpackage, locale);
+		Translator translator = new PackageTranslator(((PackageTranslator)baseClass).getPackageName(), locale, fallback);
+		return translator;
+	}
+	
+	public static Translator createPackageTranslator(Translator translator, Translator fallback, Locale locale) {
+		return new PackageTranslator(((PackageTranslator)translator).getPackageName(), locale, fallback);
+	}
+	
 	/**
 	 * 
 	 * returns a Translator for the given baseclass and locale
diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
index 09389eebb08c0d3747fc3a7b37a71016c84aefaa..2e9e07277f41bb1736d89e45fc4f9967385da877 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
+++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java
@@ -21,62 +21,26 @@ package org.olat.course.nodes.members;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
 
-import org.olat.NewControllerFactory;
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
+import org.olat.commons.memberlist.ui.MembersDisplayRunController;
 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.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.FormLayoutContainer;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.link.LinkPopupSettings;
-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.creator.ControllerCreator;
-import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
-import org.olat.core.helpers.Settings;
+import org.olat.core.gui.control.controller.BasicController;
 import org.olat.core.id.Identity;
-import org.olat.core.id.UserConstants;
-import org.olat.core.id.context.BusinessControl;
-import org.olat.core.id.context.BusinessControlFactory;
-import org.olat.core.util.StringHelper;
-import org.olat.core.util.mail.ContactList;
-import org.olat.core.util.mail.ContactMessage;
-import org.olat.core.util.session.UserSessionManager;
 import org.olat.course.groupsandrights.CourseGroupManager;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.MembersCourseNode;
 import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.group.BusinessGroupService;
-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.IModuleConfiguration;
 import org.olat.modules.ModuleConfiguration;
-import org.olat.modules.co.ContactFormController;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryService;
-import org.olat.user.DisplayPortraitManager;
-import org.olat.user.UserAvatarMapper;
-import org.olat.user.UserManager;
-import org.olat.user.propertyhandlers.UserPropertyHandler;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -87,63 +51,34 @@ import org.springframework.beans.factory.annotation.Autowired;
  * <P>
  * Initial Date:  11 mars 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ * @author fkiefer
  */
-public class MembersCourseNodeRunController extends FormBasicController {
+public class MembersCourseNodeRunController extends BasicController {
 	
-	private final List<UserPropertyHandler> userPropertyHandlers;
 	public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName();
-
-	private final CourseEnvironment courseEnv;
-	private final DisplayPortraitManager portraitManager;
-	private final String avatarBaseURL;
-	
-	private Link printLink;
-	private FormLink allEmailLink;
 	
-	private List<Member> ownerList;
-	private List<Member> coachList;
-	private List<Member> participantList;
-
-	private final boolean canEmail;
-	private final boolean showOwners;
-	private final boolean chatEnabled;
+	private CourseEnvironment courseEnv;
+	private boolean canEmail, deduplicateList, showOwners, showCoaches = false, showParticipants = false;
+	private List<Identity> owners, coaches, participants;
 
-	private MembersMailController mailCtrl;
-	private ContactFormController emailController;
-	private CloseableModalController cmc;
+	private MembersDisplayRunController membersDisplayRunController;
 	
-	private int count = 0;
-	private final boolean deduplicateList;
-	
-	@Autowired
-	private UserManager userManager;
-	@Autowired
-	private BaseSecurity securityManager;
 	@Autowired
 	private RepositoryService repositoryService;
 	@Autowired
-	private InstantMessagingModule imModule;
-	@Autowired
-	private InstantMessagingService imService;
-	@Autowired
-	private UserSessionManager sessionManager;
-	@Autowired
 	private BusinessGroupService businessGroupService;	
-
-	private final ModuleConfiguration config;
+	@Autowired
+	protected BaseSecurity securityManager;
 	
 	public MembersCourseNodeRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, ModuleConfiguration config) {
-		super(ureq, wControl, "members");
-
-		this.config = config;
-		userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false);
-
+		super(ureq, wControl);
+		
 		courseEnv = userCourseEnv.getCourseEnvironment();
-		avatarBaseURL = registerCacheableMapper(ureq, "avatars-members", new UserAvatarMapper(true));
-		portraitManager = DisplayPortraitManager.getInstance();
+		
+		this.coaches = new ArrayList<>();
+		this.participants = new ArrayList<>();
 
 		showOwners = config.getBooleanSafe(MembersCourseNode.CONFIG_KEY_SHOWOWNER);
-		chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled();
 		
 		MembersCourseNodeConfiguration nodeConfig = (MembersCourseNodeConfiguration)CourseNodeFactory.getInstance().getCourseNodeConfiguration("cmembers");
 		deduplicateList = nodeConfig.isDeduplicateList();
@@ -151,32 +86,15 @@ public class MembersCourseNodeRunController extends FormBasicController {
 		String emailFct = config.getStringValue(MembersCourseNode.CONFIG_KEY_EMAIL_FUNCTION, MembersCourseNode.EMAIL_FUNCTION_COACH_ADMIN);
 		canEmail = MembersCourseNode.EMAIL_FUNCTION_ALL.equals(emailFct) || userCourseEnv.isAdmin() || userCourseEnv.isCoach();
 		
-		initForm(ureq);
-	}
-
-	@Override
-	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		if(formLayout instanceof FormLayoutContainer) {
-			printLink = LinkFactory.createButton("print", ((FormLayoutContainer)formLayout).getFormItemComponent(), this);
-			printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg");
-			printLink.setPopup(new LinkPopupSettings(700, 500, "print-members"));
-			((FormLayoutContainer)formLayout).getFormItemComponent().put("print", printLink);
-		}
-
 		IModuleConfiguration membersFrag = IModuleConfiguration.fragment("members", config);
 		
-		List<Identity> owners;
 		if(showOwners) {
 			RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry();
 			owners = MembersHelpers.getOwners(repositoryService, courseRepositoryEntry);
 		} else {
 			owners = Collections.emptyList();
 		}
-
-		boolean showCoaches = false;
-		boolean showParticipants = false;
 		
-		List<Identity> coaches = new ArrayList<>();
 		if(membersFrag.anyTrue(MembersCourseNode.CONFIG_KEY_COACHES_ALL, MembersCourseNode.CONFIG_KEY_COACHES_COURSE)		
 				|| membersFrag.hasAnyOf(MembersCourseNode.CONFIG_KEY_COACHES_GROUP, MembersCourseNode.CONFIG_KEY_COACHES_AREA)) {
 			
@@ -186,7 +104,6 @@ public class MembersCourseNodeRunController extends FormBasicController {
 			showCoaches = true;
 		}
 		
-		List<Identity> participants = new ArrayList<>();
 		if(membersFrag.anyTrue(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_ALL, MembersCourseNode.CONFIG_KEY_PARTICIPANTS_COURSE)
 				|| membersFrag.hasAnyOf(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP, MembersCourseNode.CONFIG_KEY_PARTICIPANTS_AREA)) {
 			
@@ -195,307 +112,27 @@ public class MembersCourseNodeRunController extends FormBasicController {
 			
 			showParticipants = true;
 		}
-
-		Comparator<Identity> idComparator = new IdentityComparator();
-		Collections.sort(owners, idComparator);
-		Collections.sort(coaches, idComparator);
-		Collections.sort(participants, idComparator);
 		
-		if(canEmail) {
-			allEmailLink = uifactory.addFormLink("email", "members.email.title", null, formLayout, Link.BUTTON);
-			allEmailLink.setIconLeftCSS("o_icon o_icon_mail");
-		}
-
-		Set<Long> duplicateCatcher = deduplicateList ? new HashSet<Long>() : null;
-		ownerList = initFormMemberList("owners", owners, duplicateCatcher, formLayout, canEmail);
-		coachList = initFormMemberList("coaches", coaches, duplicateCatcher, formLayout, canEmail);
-		participantList = initFormMemberList("participants", participants, duplicateCatcher, formLayout, canEmail);
-		
-		if(formLayout instanceof FormLayoutContainer) {
-			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
-			layoutCont.contextPut("showOwners", showOwners);
-			layoutCont.contextPut("hasOwners", new Boolean(!ownerList.isEmpty()));
-			layoutCont.contextPut("showCoaches", showCoaches);
-			layoutCont.contextPut("hasCoaches", new Boolean(!coachList.isEmpty()));
-			layoutCont.contextPut("showParticipants", showParticipants);
-			layoutCont.contextPut("hasParticipants", new Boolean(!participantList.isEmpty()));
-		}
-	}
-	
-	private List<Member> initFormMemberList(String name, List<Identity> ids, Set<Long> duplicateCatcher, FormItemContainer formLayout, boolean withEmail) {
-		String page = velocity_root + "/memberList.html";
-		
-		FormLayoutContainer container = FormLayoutContainer.createCustomFormLayout(name, getTranslator(), page);
-		formLayout.add(name, container);
-		container.setRootForm(mainForm);
-
-		List<Member> members = createMemberLinks(ids, duplicateCatcher, container, withEmail);
-		container.contextPut("members", members);
-		container.contextPut("avatarBaseURL", avatarBaseURL);
-		return members;
-	}
-	
-	protected List<Member> createMemberLinks(List<Identity> identities, Set<Long> duplicateCatcher, FormLayoutContainer formLayout, boolean withEmail) {
-		List<Member> members = new ArrayList<>();
-		for(Identity identity:identities) {
-			if(duplicateCatcher != null && duplicateCatcher.contains(identity.getKey())) continue;
-			
-			Member member = createMember(identity);
-			members.add(member);
-			
-			String guiId = Integer.toString(++count);
-			String fullname = StringHelper.escapeHtml(member.getFullName());
-			
-			FormLink idLink = uifactory.addFormLink("id_".concat(guiId), "id", fullname, null, formLayout, Link.NONTRANSLATED);
-			
-			idLink.setUserObject(member);
-			formLayout.add(idLink.getComponent().getComponentName(), idLink);
-			member.setIdLink(idLink);
-			
-			if(withEmail) {
-				FormLink emailLink = uifactory.addFormLink("mail_".concat(guiId), "mail", "", null, formLayout, Link.NONTRANSLATED);
-				emailLink.setUserObject(member);
-				emailLink.setIconLeftCSS("o_icon o_icon_mail o_icon-lg");
-				emailLink.setElementCssClass("o_mail");
-				formLayout.add(emailLink.getComponent().getComponentName(), emailLink);
-				member.setEmailLink(emailLink);
-			}
-			if(chatEnabled) {
-				FormLink chatLink = uifactory.addFormLink("chat_".concat(guiId), "chat", "", null, formLayout, Link.NONTRANSLATED);
-				chatLink.setUserObject(member);
-				chatLink.setElementCssClass("o_chat");
-				formLayout.add(chatLink.getComponent().getComponentName(), chatLink);
-				member.setChatLink(chatLink);
-			}
-			
-			if(duplicateCatcher != null) {
-				duplicateCatcher.add(identity.getKey());
-			}
-		}
+		membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), courseEnv, null,
+				owners, coaches, participants, new ArrayList<>(), canEmail, deduplicateList, showOwners, showCoaches,
+				showParticipants, false, true);
+		listenTo(membersDisplayRunController);
 		
-		if(chatEnabled) {
-			Long me = getIdentity().getKey();
-			if(imModule.isOnlineStatusEnabled()) {
-				Map<Long,Member> loadStatus = new HashMap<>();
-				
-				for(Member member:members) {
-					if(member.getKey().equals(me)) {
-						member.getChatLink().setVisible(false);
-					} else if(sessionManager.isOnline(member.getKey())) {
-						loadStatus.put(member.getKey(), member);
-					} else {
-						member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable");
-					}
-				}
-				
-				if(loadStatus.size() > 0) {
-					List<Long> statusToLoadList = new ArrayList<>(loadStatus.keySet());
-					Map<Long,String> statusMap = imService.getBuddyStatus(statusToLoadList);
-					for(Long toLoad:statusToLoadList) {
-						String status = statusMap.get(toLoad);
-						Member member = loadStatus.get(toLoad);
-						if(status == null || Presence.available.name().equals(status)) {
-							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_available");
-						} else if(Presence.dnd.name().equals(status)) {
-							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_dnd");
-						} else {
-							member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable");
-						}
-					}
-				}
-			} else {
-				for(Member member:members) {
-					if(member.getKey().equals(me)) {
-						member.getChatLink().setVisible(false);
-					} else {
-						member.getChatLink().setIconLeftCSS("o_icon o_icon_status_chat");
-					}
-				}
-			}
-		}
-		
-		return members;
+		putInitialPanel(membersDisplayRunController.getInitialComponent());
 	}
 	
-	private Member createMember(Identity identity) {
-		boolean hasPortrait = portraitManager.hasPortrait(identity.getName());
 
-		String portraitCssClass;
-		String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH);
-		if ("male".equalsIgnoreCase(gender)) {
-			portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS;
-		} else if ("female".equalsIgnoreCase(gender)) {
-			portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS;
-		} else {
-			portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS;
-		}
-		String fullname = userManager.getUserDisplayName(identity);
-		return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass);
-	}
-	
 	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void formOK(UserRequest ureq) {
+	protected void event(UserRequest ureq, Component source, Event event) {
 		//
 	}
 	
-	
 
-	@Override
-	public void event(UserRequest ureq, Component source, Event event) {
-		if(source == printLink) {
-			doPrint(ureq);
-		}
-		super.event(ureq, source, event);
-	}
 
 	@Override
-	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if(source == allEmailLink) {
-			doEmail(ureq);
-		} else if(source instanceof FormLink) {
-			FormLink link = (FormLink)source;
-			Object uobject = link.getUserObject();
-			if(uobject instanceof Member) {
-				Member member = (Member)uobject;
-				String cmd = link.getCmd();
-				if("id".equals(cmd)) {
-					doOpenHomePage(member, ureq);
-				} else if("mail".equals(cmd)) {
-					doSendEmailToMember(member, ureq);
-				} else if("chat".equals(cmd)) {
-					doOpenChat(member, ureq);
-				}
-			}	
-		}
-		super.formInnerEvent(ureq, source, event);
-	}
-	
-	@Override
-	protected void event(UserRequest ureq, Controller source, Event event) {
-		if(source == cmc) {
-			cleanUp();
-		} else if (source == emailController) {
-			cmc.deactivate();
-			cleanUp();
-		} else if(source == mailCtrl) {
-			cmc.deactivate();
-			cleanUp();
-		}
-		super.event(ureq, source, event);
-	}
-	
-	private void cleanUp() {
-		removeAsListenerAndDispose(emailController);
-		removeAsListenerAndDispose(mailCtrl);
-		removeAsListenerAndDispose(cmc);
-		emailController = null;
-		mailCtrl = null;
-		cmc = null;
-	}
-	
-	private void doEmail(UserRequest ureq) {
-		if(mailCtrl != null || cmc != null) return;
-		removeAsListenerAndDispose(cmc);
-		removeAsListenerAndDispose(mailCtrl);
-		
-		mailCtrl = new MembersMailController(ureq, getWindowControl(), courseEnv, ownerList, coachList, participantList, createBodyTemplate());
-		listenTo(mailCtrl);
-		
-		String title = translate("members.email.title");
-		cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title);
-		listenTo(cmc);
-		
-		cmc.activate();	
-	}
-	
-	private void doOpenChat(Member member, UserRequest ureq) {
-		Buddy buddy = imService.getBuddyById(member.getKey());
-		OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy);
-		ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES);
-	}
-	
-	private void doSendEmailToMember(Member member, UserRequest ureq) {
-		ContactList memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), courseEnv.getCourseTitle() }));
-		Identity identity = securityManager.loadIdentityByKey(member.getKey());
-		memberList.add(identity);
-		doSendEmailToMember(memberList, ureq);
-	}
-
-	private void doSendEmailToMember(ContactList contactList, UserRequest ureq) {
-		if (contactList.getEmailsAsStrings().size() > 0) {
-			removeAsListenerAndDispose(cmc);
-			removeAsListenerAndDispose(emailController);
-			
-			ContactMessage cmsg = new ContactMessage(ureq.getIdentity());
-			cmsg.addEmailTo(contactList);
-			// preset body template from i18n
-			cmsg.setBodyText(createBodyTemplate());
-			emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg);
-			listenTo(emailController);
-			
-			String title = translate("members.email.title");
-			cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title);
-			listenTo(cmc);
-			cmc.activate();
-		}
-	}
-	
-	private String createBodyTemplate() {
-		String courseName = courseEnv.getCourseTitle();
-		// Build REST URL to course element, use hack via group manager to access repo entry
-		StringBuilder courseLink = new StringBuilder();
-		RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry();
-		courseLink.append(Settings.getServerContextPathURI())
-			.append("/url/RepositoryEntry/").append(entry.getKey());
-		return translate("email.body.template", new String[]{courseName, courseLink.toString()});		
-	}
-	
-	private void doOpenHomePage(Member member, UserRequest ureq) {
-		String url = "[HomePage:" + member.getKey() + "]";
-		BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url);
-		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
-		NewControllerFactory.getInstance().launch(ureq, bwControl);
-	}
-	
-	private void doPrint(UserRequest ureq) {
-		ControllerCreator printControllerCreator = new ControllerCreator() {
-			@Override
-			public Controller createController(UserRequest lureq, WindowControl lwControl) {
-				lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print");
-				return new MembersPrintController(lureq, lwControl, courseEnv, userPropertyHandlers,
-						ownerList, coachList, participantList);
-			}					
-		};
-		ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator);
-		openInNewBrowserWindow(ureq, layoutCtrlr);
-	}
-	
-	public static class IdentityComparator implements Comparator<Identity> {
-
-		@Override
-		public int compare(Identity id1, Identity id2) {
-			if(id1 == null) return -1;
-			if(id2 == null) return 1;
-			
-			String l1 = id1.getUser().getProperty(UserConstants.LASTNAME, null);
-			String l2 = id2.getUser().getProperty(UserConstants.LASTNAME, null);
-			if(l1 == null) return -1;
-			if(l2 == null) return 1;
-			
-			int result = l1.compareToIgnoreCase(l2);
-			if(result == 0) {
-				String f1 = id1.getUser().getProperty(UserConstants.FIRSTNAME, null);
-				String f2 = id2.getUser().getProperty(UserConstants.FIRSTNAME, null);
-				if(f1 == null) return -1;
-				if(f2 == null) return 1;
-				result = f1.compareToIgnoreCase(f2);
-			}
-			return result;
-		}
+	protected void doDispose() {
+		// nothing to dispose		
 	}
 }
+
+	
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
index 5beb12d2ac159988ca55249c1e70a3014488d919..decdbe1f021ed19c1527a82432d5644806054db2 100644
--- a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
+++ b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java
@@ -39,20 +39,20 @@ import org.olat.repository.RepositoryService;
  * <p>Initial date: May 20, 2016
  * @author lmihalkovic, http://www.frentix.com
  */
-/*public*/ class MembersHelpers {
+public class MembersHelpers {
 	private MembersHelpers() {
 		// CANNOT CREATE
 	}
 
 	// -----------------------------------------------------
 	
-	static List<Identity> getOwners(RepositoryService repositoryService, RepositoryEntry courseRepositoryEntry) {
+	public static List<Identity> getOwners(RepositoryService repositoryService, RepositoryEntry courseRepositoryEntry) {
 		return repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name());
 	}
 
 	// -----------------------------------------------------
 
-	static void addCoaches(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
+	public static void addCoaches(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
 	
 		if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_COACHES_GROUP)) {
 			String coachGroupNames = moduleConfiguration.val(MembersCourseNode.CONFIG_KEY_COACHES_GROUP);
@@ -81,24 +81,24 @@ import org.olat.repository.RepositoryService;
 		}
 	}
 	
-	static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
+	public static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
 		List<Identity> coaches = cgm.getCoachesFromAreas(areaKeys);
 		Set<Identity> coachesWithoutDuplicates = new HashSet<Identity>(coaches);
 		coaches = new ArrayList<Identity>(coachesWithoutDuplicates);
 		return coaches;
 	}
 	
-	static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
+	public static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
 		List<Identity> coaches = new ArrayList<Identity>(new HashSet<Identity>(cgm.getCoachesFromBusinessGroups(groupKeys)));
 		return coaches;
 	}
 	
-	static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) {
+	public static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) {
 		List<Identity> coaches = cgm.getCoaches();
 		return coaches;
 	}
 
-	static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) {
+	public static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) {
 		Set<Identity> uniq = new HashSet<Identity>();
 		{
 			List<Identity> coaches = cgm.getCoachesFromAreas();
@@ -113,7 +113,7 @@ import org.olat.repository.RepositoryService;
 	
 	// -----------------------------------------------------
 	
-	static void addParticipants(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
+	public static void addParticipants(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) {
 
 		if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP)) {
 			String participantGroupNames = moduleConfiguration.val(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP);
@@ -142,22 +142,22 @@ import org.olat.repository.RepositoryService;
 		}
 	}
 	
-	static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
+	public static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) {
 		List<Identity> participiants = cgm.getParticipantsFromAreas(areaKeys);
 		return participiants;
 	}
 	
-	static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
+	public static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) {
 		List<Identity> participiants = cgm.getParticipantsFromBusinessGroups(groupKeys);
 		return participiants;
 	}
 	
-	static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) {
+	public static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) {
 		List<Identity> participiants = cgm.getParticipants();
 		return participiants;
 	}
 	
-	static List<Identity> retrieveParticipantsFromCourseGroups(CourseGroupManager cgm) {
+	public static List<Identity> retrieveParticipantsFromCourseGroups(CourseGroupManager cgm) {
 		Set<Identity> uniq = new HashSet<Identity>();
 		{
 			List<Identity> participiants = cgm.getParticipantsFromAreas();
diff --git a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties
index 28e6465e986e5a3d7568611783a7cfa4aa6903a6..3bcdfa30079327a8865a13daa2d429c1a4257520 100644
--- a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties
@@ -30,6 +30,7 @@ members.info=W\u00E4hlen Sie welche Benutzergruppen in der Teilnehmerliste angez
 members.noParticipants.message=Diesem Kurs sind keine Lerngruppen mit Teilnehmern zugeordnet. Erstellen Sie eine Lerngruppe im Gruppenwerkzeug und f\u00FCgen Sie dort Teilnehmer hinzu.
 members.owners=Kursadministrator
 members.participants=Teilnehmer
+members.download=Herunterladen
 members.to=Teilnehmer "{0}" von Kurs "{1}"
 owners.to=Aministratoren von Kurs "{0}"
 pane.tab.accessibility=Zugang
@@ -40,3 +41,4 @@ select.members=Benutzer ausw\u00E4hlen
 title_info=Teilnehmerliste
 message.want.coaches=Betreuer
 message.want.participants=Teilnehmer
+blank=
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties
index 7646a7ab4f7efb089e5b91ea8d9cac3b3c34f7ee..8a4f3a68ac47f91d5d96a83108f1e4b19c7b94dc 100644
--- a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties
@@ -25,6 +25,7 @@ members.info=Select which user group should be shown in the participant list.
 members.noParticipants.message=This course has no learning groups with participants. Create a learning group in the group management tool and add a member there.
 members.owners=Course administrator
 members.participants=Participant
+members.download=Download
 members.to="{0}" of course "{1}"
 msg.send.ok=$org.olat.modules.co\:msg.send.ok
 error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok
@@ -41,3 +42,4 @@ title_info=Participant list
 
 message.want.coaches=Coaches
 message.want.participants=Participants
+blank=
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java b/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java
index c97f69a074a9cca21553a5dd68dee9df39833f45..78dd158e5b90699d6d92035220bb8b3cee40204c 100644
--- a/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java
+++ b/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java
@@ -19,53 +19,70 @@
  */
 package org.olat.group.ui.homepage;
 
-import org.olat.basesecurity.Group;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import org.olat.basesecurity.GroupRoles;
-import org.olat.basesecurity.ui.GroupController;
-import org.olat.core.CoreSpringFactory;
+import org.olat.commons.memberlist.ui.MembersDisplayRunController;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.velocity.VelocityContainer;
 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.id.Identity;
+import org.olat.core.util.Util;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupService;
+import org.olat.group.ui.run.GroupMembersRunController;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
  * Initial Date:  Aug 19, 2009 <br>
  * @author twuersch, www.frentix.com
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ * @author fkiefer
  */
 public class GroupMembersDisplayController extends BasicController {
 
 
-	private final VelocityContainer content;
+	private MembersDisplayRunController membersDisplayRunController;
+	
+	@Autowired
+	private BusinessGroupService businessGroupService;	
+	
 
 	public GroupMembersDisplayController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup) {
 		super(ureq, wControl);
+		setTranslator(Util.createPackageTranslator(GroupMembersRunController.class, getLocale()));
 		// display owners and participants
-		content = createVelocityContainer("groupmembersdisplay");
-		BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class);
-		Group group = bgs.getGroup(businessGroup);
-
-		if(businessGroup.isOwnersVisiblePublic()) {
-			GroupController groupOwnersController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.coach.name());
-			content.put("owners", groupOwnersController.getInitialComponent());
-			listenTo(groupOwnersController);
-		}
-		if(businessGroup.isParticipantsVisiblePublic()) {
-			GroupController groupParticipantsController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.participant.name());
-			content.put("participants", groupParticipantsController.getInitialComponent());
-			listenTo(groupParticipantsController);
+		
+		List<Identity> coaches, participants, waiting;
+		boolean showCoaches = businessGroup.isOwnersVisiblePublic();
+		if (showCoaches) {
+			coaches = businessGroupService.getMembers(businessGroup, GroupRoles.coach.name());			
+		} else {
+			coaches = Collections.emptyList();
 		}
-		if(businessGroup.isWaitingListVisiblePublic()) {
-			GroupController groupWaitingListController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.waiting.name());
-			content.put("waitingList", groupWaitingListController.getInitialComponent());
-			listenTo(groupWaitingListController);
+		boolean showParticipants = businessGroup.isParticipantsVisiblePublic();
+		if (showParticipants) {
+			participants = businessGroupService.getMembers(businessGroup, GroupRoles.participant.name());		
+		} else {
+			participants = Collections.emptyList();
 		}
-		putInitialPanel(content);
+		boolean showWaiting = businessGroup.isWaitingListVisiblePublic();
+		if (showWaiting) {
+			waiting = businessGroupService.getMembers(businessGroup, GroupRoles.waiting.name());
+		} else {
+			waiting = Collections.emptyList();
+		}	
+		
+		membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), null, businessGroup,
+				new ArrayList<>(), coaches, participants, waiting, false, true, false, showCoaches, showParticipants, showWaiting, false);
+		listenTo(membersDisplayRunController);
+		
+		putInitialPanel(membersDisplayRunController.getInitialComponent());	
 	}
 
 	@Override
@@ -75,6 +92,6 @@ public class GroupMembersDisplayController extends BasicController {
 
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
-	// Do nothing.
+		// events handled in child controller
 	}
 }
diff --git a/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html b/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html
deleted file mode 100644
index 26672b710fdc6c5475bdb8169f656415afecaf5e..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<h2><i class="o_icon o_icon_group"> </i> $r.translate("userlist.title")</h2>
-#if($r.available("owners"))
-	<h3>$r.translate("userlist.owners.title")</h3>
-	<div class="o_sel_group_coaches">
-		$r.render("owners")
-	</div>
-#end
-#if($r.available("participants"))
-	<h3>$r.translate("userlist.participants.title")</h3>
-	<div class="o_sel_group_participants">
-		$r.render("participants")
-	</div>
-#end
-#if($r.available("waitingList"))
-	<h3>$r.translate("userlist.waitingList.title")</h3>
-   	<div class="o_sel_group_waiting_list">
-		$r.render("waitingList")
-	</div>
-#end
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/main/MemberListTableModel.java b/src/main/java/org/olat/group/ui/main/MemberListTableModel.java
index 8471a9cc7a84daf591cbc8b0410f78a0afc1d6cf..9384373eefbc08476826df04c971bf4082703907 100644
--- a/src/main/java/org/olat/group/ui/main/MemberListTableModel.java
+++ b/src/main/java/org/olat/group/ui/main/MemberListTableModel.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.elements.FormLink;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
 import org.olat.instantMessaging.model.Presence;
@@ -102,7 +103,7 @@ public class MemberListTableModel extends DefaultFlexiTableDataModel<MemberView>
 		return new MemberListTableModel(getTableColumnModel(), onlineStatusEnabled);
 	}
 
-	public enum Cols {
+	public enum Cols implements FlexiSortableColumnDef{
 		username("table.header.login"),
 		firstTime("table.header.firstTime"),
 		lastTime("table.header.lastTime"),
@@ -120,5 +121,19 @@ public class MemberListTableModel extends DefaultFlexiTableDataModel<MemberView>
 		public String i18n() {
 			return i18n;
 		}
+
+		@Override
+		public String i18nHeaderKey() {
+			return i18n;
+		}
+
+		@Override
+		public boolean sortable() {
+			return true;
+		}
+		@Override
+		public String sortKey() {
+			return i18n;
+		}
 	}
 }
diff --git a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
index f7eddc2be214e49d3c9183665a79fda435f9d024..f52a68026d325b72089cc4b8ad965eed7536976e 100644
--- a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
+++ b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
@@ -31,9 +31,7 @@ import java.util.List;
 
 import org.olat.NewControllerFactory;
 import org.olat.basesecurity.BaseSecurityModule;
-import org.olat.basesecurity.Group;
 import org.olat.basesecurity.GroupRoles;
-import org.olat.basesecurity.ui.GroupController;
 import org.olat.collaboration.CollaborationTools;
 import org.olat.collaboration.CollaborationToolsFactory;
 import org.olat.commons.calendar.CalendarModule;
@@ -189,12 +187,9 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 	private BusinessGroupEditController bgEditCntrllr;
 	private Controller bgACHistoryCtrl;
 	private TableController resourcesCtr;
+	private GroupMembersRunController groupMembersToggleViewController;
 
 	private BusinessGroupSendToChooserForm sendToChooserForm;
-	
-	private GroupController gownersC;
-	private GroupController gparticipantsC;
-	private GroupController waitingListController;
 
 	/**
 	 * Business group administrator
@@ -852,42 +847,11 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 	}
 
 	private void doShowMembers(UserRequest ureq) {
-		VelocityContainer membersVc = createVelocityContainer("ownersandmembers");
-		// 1. show owners if configured with Owners
-		boolean downloadAllowed = businessGroup.isDownloadMembersLists();
-		Group group = businessGroupService.getGroup(businessGroup);
-		if (businessGroup.isOwnersVisibleIntern()) {
-			removeAsListenerAndDispose(gownersC);
-			gownersC = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.coach.name());
-			listenTo(gownersC);
-			membersVc.put("owners", gownersC.getInitialComponent());
-			membersVc.contextPut("showOwnerGroups", Boolean.TRUE);
-		} else {
-			membersVc.contextPut("showOwnerGroups", Boolean.FALSE);
-		}
-		// 2. show participants if configured with Participants
-		if (businessGroup.isParticipantsVisibleIntern()) {
-			removeAsListenerAndDispose(gparticipantsC);
-			gparticipantsC = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.participant.name());
-			listenTo(gparticipantsC);
-			
-			membersVc.put("participants", gparticipantsC.getInitialComponent());
-			membersVc.contextPut("showPartipsGroups", Boolean.TRUE);
-		} else {
-			membersVc.contextPut("showPartipsGroups", Boolean.FALSE);
-		}
-		// 3. show waiting-list if configured 
-		membersVc.contextPut("hasWaitingList", new Boolean(businessGroup.getWaitingListEnabled()) );
-		if (businessGroup.isWaitingListVisibleIntern()) {
-			removeAsListenerAndDispose(waitingListController);
-			waitingListController = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.waiting.name());
-			listenTo(waitingListController);
-			membersVc.put("waitingList", waitingListController.getInitialComponent());
-			membersVc.contextPut("showWaitingList", Boolean.TRUE);
-		} else {
-			membersVc.contextPut("showWaitingList", Boolean.FALSE);
-		}
-		mainPanel.setContent(membersVc);
+		CollaborationTools collabTools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(this.businessGroup);
+		boolean canEmail = collabTools.isToolEnabled(CollaborationTools.TOOL_CONTACT);
+		groupMembersToggleViewController = new GroupMembersRunController(ureq, getWindowControl(), businessGroup, canEmail);
+		listenTo(groupMembersToggleViewController);
+		mainPanel.setContent(groupMembersToggleViewController.getInitialComponent());
 		collabToolCtr = null;
 		addToHistory(ureq, ORES_TOOLMEMBERS, null);
 	}
diff --git a/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java b/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java
new file mode 100644
index 0000000000000000000000000000000000000000..33f35ee9e3fed0bf60dd9e035e077619a4f4afc2
--- /dev/null
+++ b/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java
@@ -0,0 +1,98 @@
+/**
+ * <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.run;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.basesecurity.GroupRoles;
+import org.olat.commons.memberlist.ui.MembersDisplayRunController;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+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.id.Identity;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Initial Date: 22.03.2017
+ * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com
+ */
+public class GroupMembersRunController extends BasicController {
+	
+	private MembersDisplayRunController membersDisplayRunController;
+	
+	@Autowired
+	private BusinessGroupService businessGroupService;	
+	
+	public GroupMembersRunController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup, boolean canEmail) {
+		super(ureq, wControl);
+		
+		List<Identity> coaches, participants, waiting;
+		boolean showCoaches = businessGroup.isOwnersVisibleIntern();
+		if (showCoaches) {
+			coaches = businessGroupService.getMembers(businessGroup, GroupRoles.coach.name());			
+		} else {
+			coaches = Collections.emptyList();
+		}
+		boolean showParticipants = businessGroup.isParticipantsVisibleIntern();
+		if (showParticipants) {
+			participants = businessGroupService.getMembers(businessGroup, GroupRoles.participant.name());		
+		} else {
+			participants = Collections.emptyList();
+		}
+		boolean showWaiting = businessGroup.isWaitingListVisibleIntern();
+		if (showWaiting) {
+			waiting = businessGroupService.getMembers(businessGroup, GroupRoles.waiting.name());
+		} else {
+			waiting = Collections.emptyList();
+		}	
+		membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), null, businessGroup,
+				new ArrayList<>(), coaches, participants, waiting, canEmail, true, false, showCoaches, showParticipants, showWaiting, true);
+		listenTo(membersDisplayRunController);
+		
+		putInitialPanel(membersDisplayRunController.getInitialComponent());		
+	}
+
+	
+
+	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		super.event(ureq, source, event);
+	}
+	
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+
+}
diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
index 7331919d4f6f69875a9cc6ead501755052c8fed7..b0d9f668220115b0b416143b58dbd53b14397bb0 100644
--- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties
@@ -76,3 +76,34 @@ userlist.show.no.owners.text=nicht sichtbar
 userlist.show.no.participants.text=nicht sichtbar 
 userlist.show.no.waitinglist.text=nicht sichtbar
 wizard.step1.send_option.member=Gruppenmitglieder und Gruppenbetreuer
+contact.all.coaches=Alle Gruppenbetreuer
+contact.all.participants=Alle Gruppenteilnehmer
+contact.all.waiting=Alle auf Warteliste
+contact.attachment=$org.olat.modules.co\:contact.attachment
+contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize
+contact.individual=Einzelne Teilnehmer
+contact.external=Externe E-Mail Adresse
+email.body.template=<p /><p /><p>Link zur Gruppe <strong>{0}</strong>:<br /><a href\='{1}'>{1}</a></p>
+email.from=Von
+email.send=Schicken
+email.function=Emailfunktion
+email.function.all=Emailfunktion f\u00FCr alle Benutzer freischalten
+email.function.coachAndAdmin=Emailfunktion nur f\u00FCr Betreuer und Administratoren freischalten
+yemail.title=E-Mail senden
+members.to=Teilnehmer "{0}" aus Gruppe "{1}"
+msg.send.ok=$org.olat.modules.co\:msg.send.ok
+error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok
+error.msg.send.invalid.rcps=$org.olat.modules.co\:error.msg.send.invalid.rcps
+error.msg.send.nok=$org.olat.modules.co\:error.msg.send.nok
+error.msg.send.553=$org.olat.modules.co\:error.msg.send.553
+blank=
+members.email.title=E-Mail versenden
+members.owners=Gruppenbesitzer
+members.coaches=Gruppenbetreuer
+members.participants=Gruppenteilnehmer
+members.waiting=Warteliste
+members.toggle=Portrait
+members.download=Herunterladen
+select.members=Benutzer ausw\u00E4hlen
+contact.external.list.example=xyz@olat.com, abc@frentix.com<br/>user@yourcompany.ch
+print.title=Fotobuch
diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
index bb38a432319d7946ec21707c61b7a4949ee8f747..ffc4544b71663415b946f928354c284074240457 100644
--- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties
@@ -75,4 +75,35 @@ userlist.show.no.participants.title=Participant
 userlist.show.no.waitinglist.text=not visible
 userlist.show.no.waitinglist.title=Waiting list
 userlist.title=Group members
-wizard.step1.send_option.member=Group members and group coaches
\ No newline at end of file
+wizard.step1.send_option.member=Group members and group coaches
+contact.all.coaches=All group coaches
+contact.all.participants=All group participants
+contact.all.waiting=All on waiting list
+contact.attachment=$org.olat.modules.co\:contact.attachment
+contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize
+contact.individual=Selected participants
+contact.external=External E-Mail address
+email.body.template=<p /><p /><p>Link to group <strong>{0}</strong>:<br /><a href\='{1}'>{1}</a></p>
+email.from=From
+email.send=Send
+email.function=Email function
+email.function.all=Enable email for all users
+email.function.coachAndAdmin=Enable email for all users for coaches and admins
+email.title=Send email
+members.to=Members "{0}" of group "{1}"
+msg.send.ok=$org.olat.modules.co\:msg.send.ok
+error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok
+error.msg.send.invalid.rcps=$org.olat.modules.co\:error.msg.send.invalid.rcps
+error.msg.send.nok=$org.olat.modules.co\:error.msg.send.nok
+error.msg.send.553=$org.olat.modules.co\:error.msg.send.553
+blank=
+members.email.title=Send e-mail
+members.owners=Group owners
+members.coaches=Group coaches
+members.participants=Group participants
+members.waiting=Waiting list
+members.toggle=Portrait
+members.download=Download
+select.members=Select members
+contact.external.list.example=xyz@olat.com, abc@frentix.com<br/>user@yourcompany.ch
+print.title=Face book
\ No newline at end of file