From fab5acbeee40825f2dead1ebc4a48c0e46bbb00a Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Tue, 18 Sep 2012 11:11:04 +0200
Subject: [PATCH] OO-291: implement the member info controller panel, fix the
 links, show the rights datas...

---
 .../member/AbstractMemberListController.java  |  91 +++++++++++--
 .../member/EditMembershipController.java      |  51 ++++++-
 .../course/member/MemberInfoController.java   | 128 ++++++++++++++----
 .../member/MemberPermissionChangeEvent.java   |  33 ++++-
 .../member/MembersOverviewController.java     |  15 +-
 .../course/member/_content/info_member.html   |   9 +-
 .../member/_i18n/LocalStrings_de.properties   |   4 +
 .../member/_i18n/LocalStrings_en.properties   |   3 +-
 .../member/_i18n/LocalStrings_fr.properties   |   3 +-
 .../org/olat/user/UserInfoMainController.java |  17 ++-
 .../_spring/userPropertiesContext.xml         |  14 ++
 11 files changed, 303 insertions(+), 65 deletions(-)

diff --git a/src/main/java/org/olat/course/member/AbstractMemberListController.java b/src/main/java/org/olat/course/member/AbstractMemberListController.java
index 1c08954ab73..13483cc5fd6 100644
--- a/src/main/java/org/olat/course/member/AbstractMemberListController.java
+++ b/src/main/java/org/olat/course/member/AbstractMemberListController.java
@@ -55,12 +55,15 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
 import org.olat.course.member.MemberListTableModel.Cols;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupMembership;
 import org.olat.group.BusinessGroupService;
 import org.olat.group.BusinessGroupShort;
 import org.olat.group.model.BusinessGroupMembershipChange;
+import org.olat.modules.co.ContactFormController;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.repository.model.RepositoryEntryMembership;
@@ -87,6 +90,7 @@ public abstract class AbstractMemberListController extends BasicController {
 	private DialogBoxController leaveDialogBox;
 	protected CloseableModalController cmc;
 	private EditMembershipController editMemberCtrl;
+	private ContactFormController contactCtrl;
 	private final List<UserPropertyHandler> userPropertyHandlers;
 
 	private final UserManager userManager;
@@ -176,9 +180,9 @@ public abstract class AbstractMemberListController extends BasicController {
 				if(TABLE_ACTION_REMOVE.equals(te.getAction())) {
 					confirmDelete(ureq, selectedItems);
 				} else if(TABLE_ACTION_EDIT.equals(te.getAction())) {
-					//TODO
+					openEdit(ureq, selectedItems);
 				}if(TABLE_ACTION_MAIL.equals(te.getAction())) {
-					//TODO
+					doSendMail(ureq, selectedItems);
 				}
 			}
 		} else if (source == leaveDialogBox) {
@@ -191,10 +195,16 @@ public abstract class AbstractMemberListController extends BasicController {
 		} else if(source == editMemberCtrl) {
 			if(event instanceof MemberPermissionChangeEvent) {
 				MemberPermissionChangeEvent e = (MemberPermissionChangeEvent)event;
-				doChangePermission(e);
+				if(e.getMember() != null) {
+					doChangePermission(e);
+				} else {
+					doChangePermission(e, editMemberCtrl.getMembers());
+				}
 			}
 			
-			//do something
+			cmc.deactivate();
+			cleanUpPopups();
+		} else if (source == contactCtrl) {
 			cmc.deactivate();
 			cleanUpPopups();
 		} else if (source == cmc) {
@@ -209,7 +219,9 @@ public abstract class AbstractMemberListController extends BasicController {
 		removeAsListenerAndDispose(cmc);
 		removeAsListenerAndDispose(editMemberCtrl);
 		removeAsListenerAndDispose(leaveDialogBox);
+		removeAsListenerAndDispose(contactCtrl);
 		cmc = null;
+		contactCtrl = null;
 		leaveDialogBox = null;
 		editMemberCtrl = null;
 	}
@@ -239,23 +251,45 @@ public abstract class AbstractMemberListController extends BasicController {
 		listenTo(cmc);
 	}
 	
+	protected void openEdit(UserRequest ureq, List<MemberView> members) {
+		List<Long> identityKeys = getMemberKeys(members);
+		List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys);
+		editMemberCtrl = new EditMembershipController(ureq, getWindowControl(), identities, repoEntry);
+		listenTo(editMemberCtrl);
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), editMemberCtrl.getInitialComponent(),
+				true, translate("edit.member"));
+		cmc.activate();
+		listenTo(cmc);
+	}
+	
 	protected void doChangePermission(MemberPermissionChangeEvent e) {
-		doChangeRepoPermission(e);
-		doChangeGroupPermissions(e.getGroupChanges());
+		List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList((RepositoryEntryPermissionChangeEvent)e);
+		repositoryManager.updateRepositoryEntryMembership(getIdentity(), repoEntry, changes);
+
+		businessGroupService.updateMemberships(getIdentity(), e.getGroupChanges());
+		
+		//TODO group mail
+		System.out.println("Send mails: ");
 		
 		//make sure all is committed before loading the model again (I see issues without)
 		DBFactory.getInstance().commitAndCloseSession();
 		reloadModel();
 	}
 	
-	private void doChangeRepoPermission(RepositoryEntryPermissionChangeEvent e) {
-		//first change repository permission
-		List<RepositoryEntryPermissionChangeEvent> changes = Collections.singletonList(e);
-		repositoryManager.updateRepositoryEntryMembership(getIdentity(), repoEntry, changes);
-	}
-	
-	private void doChangeGroupPermissions(List<BusinessGroupMembershipChange> changes) {
-		businessGroupService.updateMemberships(getIdentity(), changes);
+	protected void doChangePermission(MemberPermissionChangeEvent changes, List<Identity> members) {
+		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
+		repositoryManager.updateRepositoryEntryMembership(getIdentity(), repoEntry, repoChanges);
+
+		//commit all changes to the group memberships
+		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
+		businessGroupService.updateMemberships(getIdentity(), allModifications);
+		
+		//TODO group mail
+		System.out.println("Send mails: ");
+		
+		//make sure all is committed before loading the model again (I see issues without)
+		DBFactory.getInstance().commitAndCloseSession();
+		reloadModel();
 	}
 	
 	protected void doLeave(UserRequest ureq, List<Identity> members) {
@@ -263,6 +297,35 @@ public abstract class AbstractMemberListController extends BasicController {
 		businessGroupService.removeMembers(getIdentity(), members, repoEntry.getOlatResource());
 		reloadModel();
 	}
+	
+	protected void doSendMail(UserRequest ureq, List<MemberView> members) {
+		List<Long> identityKeys = getMemberKeys(members);
+		List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys);
+		
+		ContactMessage contactMessage = new ContactMessage(getIdentity());
+		ContactList contactList = new ContactList(repoEntry.getDisplayname());
+		contactList.addAllIdentites(identities);
+		contactMessage.addEmailTo(contactList);
+		
+		contactCtrl = new ContactFormController(ureq, getWindowControl(), false, true, false, false, contactMessage);
+		listenTo(contactCtrl);
+
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), contactCtrl.getInitialComponent(),
+				true, translate("mail.member"));
+		cmc.activate();
+		listenTo(cmc);
+		
+	}
+	
+	protected List<Long> getMemberKeys(List<MemberView> members) {
+		List<Long> keys = new ArrayList<Long>(members.size());
+		if(members != null && !members.isEmpty()) {
+			for(MemberView member:members) {
+				keys.add(member.getIdentityKey());
+			}
+		}
+		return keys;
+	}
 
 	protected abstract SearchMembersParams getSearchParams();
 	
diff --git a/src/main/java/org/olat/course/member/EditMembershipController.java b/src/main/java/org/olat/course/member/EditMembershipController.java
index d4726732abd..98b264cd55e 100644
--- a/src/main/java/org/olat/course/member/EditMembershipController.java
+++ b/src/main/java/org/olat/course/member/EditMembershipController.java
@@ -21,6 +21,7 @@ package org.olat.course.member;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -67,6 +68,7 @@ public class EditMembershipController extends FormBasicController {
 	private static final String[] repoRightsKeys = {"owner", "tutor", "participant"};
 	
 	private final Identity member;
+	private final List<Identity> members;
 	private List<RepositoryEntryMembership> memberships;
 	private List<BusinessGroupMembership> groupMemberships;
 	
@@ -81,6 +83,7 @@ public class EditMembershipController extends FormBasicController {
 			RepositoryEntry repoEntry) {
 		super(ureq, wControl, "edit_member");
 		this.member = member;
+		this.members = null;
 		this.repoEntry = repoEntry;
 		this.withButtons = true;
 		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
@@ -88,7 +91,44 @@ public class EditMembershipController extends FormBasicController {
 		
 		memberships = repositoryManager.getRepositoryEntryMembership(repoEntry, member);
 
-		infoController = new MemberInfoController(ureq, wControl, member);
+		infoController = new MemberInfoController(ureq, wControl, member, repoEntry);
+		initForm(ureq);
+		loadModel(member);
+		
+		Date membershipCreation = null;
+		if(memberships != null) {
+			for(RepositoryEntryMembership membership:memberships) {
+				Date creationDate = membership.getCreationDate();
+				if(creationDate != null && (membershipCreation == null || membershipCreation.compareTo(creationDate) > 0)) {
+					membershipCreation = creationDate;
+				}
+			}
+		}
+		
+		if(groupMemberships != null) {
+			for(BusinessGroupMembership membership:groupMemberships) {
+				Date creationDate = membership.getCreationDate();
+				if(creationDate != null && (membershipCreation == null || membershipCreation.compareTo(creationDate) > 0)) {
+					membershipCreation = creationDate;
+				}
+			}
+		}
+		infoController.setMembershipCreation(membershipCreation);
+	}
+	
+	public EditMembershipController(UserRequest ureq, WindowControl wControl, List<Identity> members,
+			RepositoryEntry repoEntry) {
+		super(ureq, wControl, "edit_member");
+		
+		this.member = null;
+		this.members = new ArrayList<Identity>(members);
+		this.repoEntry = repoEntry;
+		this.withButtons = true;
+		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
+		businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
+		
+		memberships = Collections.emptyList();
+
 		initForm(ureq);
 		loadModel(member);
 	}
@@ -98,6 +138,7 @@ public class EditMembershipController extends FormBasicController {
 		super(ureq, wControl, LAYOUT_CUSTOM, "edit_member", rootForm);
 		
 		this.member = null;
+		this.members = new ArrayList<Identity>(members);
 		this.repoEntry = repoEntry;
 		this.withButtons = false;
 		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
@@ -189,6 +230,14 @@ public class EditMembershipController extends FormBasicController {
 		//
 	}
 	
+	public Identity getMember() {
+		return member;
+	}
+
+	public List<Identity> getMembers() {
+		return members;
+	}
+
 	@Override
 	protected void formOK(UserRequest ureq) {
 		MemberPermissionChangeEvent e = new MemberPermissionChangeEvent(member);
diff --git a/src/main/java/org/olat/course/member/MemberInfoController.java b/src/main/java/org/olat/course/member/MemberInfoController.java
index d3e09f055c6..87d1bcdc2f7 100644
--- a/src/main/java/org/olat/course/member/MemberInfoController.java
+++ b/src/main/java/org/olat/course/member/MemberInfoController.java
@@ -19,47 +19,120 @@
  */
 package org.olat.course.member;
 
+import java.util.Date;
+import java.util.List;
+
 import org.olat.NewControllerFactory;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.velocity.VelocityContainer;
+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.elements.StaticTextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+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.control.controller.BasicController;
 import org.olat.core.id.Identity;
+import org.olat.core.util.Formatter;
+import org.olat.core.util.Util;
+import org.olat.course.assessment.UserCourseInformations;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
+import org.olat.repository.RepositoryEntry;
 import org.olat.user.DisplayPortraitController;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
 
 /**
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
-public class MemberInfoController extends BasicController {
+public class MemberInfoController extends FormBasicController {
+	
+	private FormLink homeLink, contactLink, assessmentLink;
+	private StaticTextElement membershipCreationEl;
+
+	private final Identity identity;
+	private final Long repoEntryKey;
+	private final UserCourseInformations courseInfos;
 	
-	private final Link homeLink, contactLink, assessmentLink;
-	private final VelocityContainer mainVC;
-	private Long identityKey;
+	private final UserManager userManager;
+	private final UserCourseInformationsManager efficiencyStatementManager;
 	
-	public MemberInfoController(UserRequest ureq, WindowControl wControl, Identity identity) {
-		super(ureq, wControl);
+	public MemberInfoController(UserRequest ureq, WindowControl wControl, Identity identity, RepositoryEntry repoEntry) {
+		super(ureq, wControl, "info_member", Util.createPackageTranslator(UserPropertyHandler.class, ureq.getLocale()));
+		
+		userManager = CoreSpringFactory.getImpl(UserManager.class);
+		efficiencyStatementManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+	
+		this.identity = identity;
+		repoEntryKey = repoEntry.getKey();
+		
+		courseInfos = efficiencyStatementManager.getUserCourseInformations(repoEntry.getOlatResource().getResourceableId(), identity);
+		
+		initForm(ureq);
+	}
 	
-		mainVC = createVelocityContainer("info_member");
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		if(formLayout instanceof FormLayoutContainer) {
+			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
 		
-		Controller dpc = new DisplayPortraitController(ureq, getWindowControl(), identity, true, false);
-		listenTo(dpc); // auto dispose
-		mainVC.put("image", dpc.getInitialComponent());
+			Controller dpc = new DisplayPortraitController(ureq, getWindowControl(), identity, true, false);
+			listenTo(dpc); // auto dispose
+			layoutCont.put("image", dpc.getInitialComponent());
+			layoutCont.contextPut("fullname", userManager.getUserDisplayName(identity.getUser()));
+		}
 		
+		//user properties
+		FormLayoutContainer userPropertiesContainer = FormLayoutContainer.createDefaultFormLayout("userProperties", getTranslator());
+		formLayout.add("userProperties", userPropertiesContainer);
 		
-		homeLink = LinkFactory.createButton("home",	mainVC, this);
-		homeLink.setCustomEnabledLinkCSS("b_link_left_icon b_link_to_home");
-		contactLink = LinkFactory.createButton("contact",	mainVC, this);
-		contactLink.setCustomEnabledLinkCSS("b_link_left_icon b_link_mail");
-		assessmentLink = LinkFactory.createButton("assessment",	mainVC, this);
-		assessmentLink.setCustomEnabledLinkCSS("b_link_left_icon b_link_assessment");
+		List<UserPropertyHandler> userPropertyHandlers = userManager.getUserPropertyHandlersFor(getClass().getCanonicalName(), false);
+		for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
+			if (userPropertyHandler == null) continue;
+
+			String propName = userPropertyHandler.getName();
+			String value = userPropertyHandler.getUserProperty(identity.getUser(), getLocale());
+			String key = userPropertyHandler.i18nFormElementLabelKey();
+			uifactory.addStaticTextElement("up_" + propName, key, value, userPropertiesContainer);
+		}
 
-		putInitialPanel(mainVC);
+		//course informations
+		FormLayoutContainer courseInfosContainer = FormLayoutContainer.createDefaultFormLayout("courseInfos", getTranslator());
+		formLayout.add("courseInfos", courseInfosContainer);
+		
+		Formatter formatter = Formatter.getInstance(getLocale());
+		
+		String lastVisit = null;
+		String numOfVisits = "0";
+		if(courseInfos != null) {
+			if(courseInfos.getRecentLaunch() != null) {
+				lastVisit = formatter.formatDate(courseInfos.getRecentLaunch());
+			}
+			if(courseInfos.getVisit() >= 0) {
+				numOfVisits = Integer.toString(courseInfos.getVisit());
+			}	
+		}
+		membershipCreationEl = uifactory.addStaticTextElement("firstTime", "course.membership.creation", "", courseInfosContainer);
+		uifactory.addStaticTextElement("lastTime", "course.lastTime", lastVisit, courseInfosContainer);
+		uifactory.addStaticTextElement("numOfVisits", "course.numOfVisits", numOfVisits, courseInfosContainer);
+		
+		//links
+		homeLink = uifactory.addFormLink("home", formLayout, "b_link_left_icon b_link_to_home");
+		formLayout.add("home", homeLink);
+		contactLink = uifactory.addFormLink("contact",	formLayout, "b_link_left_icon b_link_mail");
+		formLayout.add("contact", contactLink);
+		assessmentLink = uifactory.addFormLink("assessment",	formLayout, "b_link_left_icon b_link_assessment");
+		formLayout.add("assessment", assessmentLink);
+	}
+	
+	public void setMembershipCreation(Date date) {
+		if(date != null) {
+			Formatter formatter = Formatter.getInstance(getLocale());
+			membershipCreationEl.setValue(formatter.formatDate(date));
+		}
 	}
 	
 	@Override
@@ -68,16 +141,21 @@ public class MemberInfoController extends BasicController {
 	}
 
 	@Override
-	protected void event(UserRequest ureq, Component source, Event event) {
+	public void event(UserRequest ureq, Component source, Event event) {
 		if(source == homeLink) {
-			String businessPath = "[Identity:" + identityKey + "]";
+			String businessPath = "[Identity:" + identity.getKey() + "]";
 			NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl());
 		} else if (source == contactLink) {
-			String businessPath = "[Identity:" + identityKey + "][Contact:0]";
+			String businessPath = "[Identity:" + identity.getKey() + "][Contact:0]";
 			NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl());
 		} else if (source == assessmentLink) {
-			String businessPath = "[Identity:" + identityKey + "]";
+			String businessPath =  "[RepositoryEntry:" + repoEntryKey + "][assessmentTool:0][Identity:" + identity.getKey() + "]";
 			NewControllerFactory.getInstance().launch(businessPath, ureq, getWindowControl());	
 		}
 	}
+	
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/member/MemberPermissionChangeEvent.java b/src/main/java/org/olat/course/member/MemberPermissionChangeEvent.java
index 8e4cf916f50..5cd5b40d6d7 100644
--- a/src/main/java/org/olat/course/member/MemberPermissionChangeEvent.java
+++ b/src/main/java/org/olat/course/member/MemberPermissionChangeEvent.java
@@ -19,6 +19,8 @@
  */
 package org.olat.course.member;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.olat.core.id.Identity;
@@ -38,7 +40,6 @@ public class MemberPermissionChangeEvent extends RepositoryEntryPermissionChange
 		super(member);
 	}
 	
-
 	public List<BusinessGroupMembershipChange> getGroupChanges() {
 		return groupChanges;
 	}
@@ -46,4 +47,34 @@ public class MemberPermissionChangeEvent extends RepositoryEntryPermissionChange
 	public void setGroupChanges(List<BusinessGroupMembershipChange> changes) {
 		this.groupChanges = changes;
 	}
+	
+	public List<RepositoryEntryPermissionChangeEvent> generateRepositoryChanges(List<Identity> members) {
+		if(members == null || members.isEmpty()) {
+			return Collections.emptyList();
+		}
+		
+		List<RepositoryEntryPermissionChangeEvent> repoChanges = new ArrayList<RepositoryEntryPermissionChangeEvent>();
+		for(Identity member:members) {
+			repoChanges.add(new RepositoryEntryPermissionChangeEvent(member, this));
+		}
+		return repoChanges;
+	}
+	
+	public List<BusinessGroupMembershipChange> generateBusinessGroupMembershipChange(List<Identity> members) {
+		if(members == null || members.isEmpty()) {
+			return Collections.emptyList();
+		}
+		List<BusinessGroupMembershipChange> groupChanges = getGroupChanges();
+		if(groupChanges == null || groupChanges.isEmpty()) {
+			return Collections.emptyList();
+		}
+		
+		List<BusinessGroupMembershipChange> allModifications = new ArrayList<BusinessGroupMembershipChange>();
+		for(BusinessGroupMembershipChange groupChange:groupChanges) {
+			for(Identity member:members) {
+				allModifications.add(new BusinessGroupMembershipChange(member, groupChange));
+			}
+		}
+		return allModifications;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/member/MembersOverviewController.java b/src/main/java/org/olat/course/member/MembersOverviewController.java
index a006bf07a69..75bb2052b7c 100644
--- a/src/main/java/org/olat/course/member/MembersOverviewController.java
+++ b/src/main/java/org/olat/course/member/MembersOverviewController.java
@@ -19,7 +19,6 @@
  */
 package org.olat.course.member;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
@@ -212,23 +211,15 @@ public class MembersOverviewController extends BasicController implements Activa
 		
 		MemberPermissionChangeEvent changes = (MemberPermissionChangeEvent)runContext.get("permissions");
 		//commit changes to the repository entry
-		List<RepositoryEntryPermissionChangeEvent> repoChanges = new ArrayList<RepositoryEntryPermissionChangeEvent>();
-		for(Identity member:members) {
-			repoChanges.add(new RepositoryEntryPermissionChangeEvent(member, changes));
-		}
+		List<RepositoryEntryPermissionChangeEvent> repoChanges = changes.generateRepositoryChanges(members);
 		repositoryManager.updateRepositoryEntryMembership(getIdentity(), repoEntry, repoChanges);
 
 		//commit all changes to the group memberships
-		List<BusinessGroupMembershipChange> groupChanges = changes.getGroupChanges();
-		List<BusinessGroupMembershipChange> allModifications = new ArrayList<BusinessGroupMembershipChange>();
-		for(BusinessGroupMembershipChange groupChange:groupChanges) {
-			for(Identity member:members) {
-				allModifications.add(new BusinessGroupMembershipChange(member, groupChange));
-			}
-		}
+		List<BusinessGroupMembershipChange> allModifications = changes.generateBusinessGroupMembershipChange(members);
 		businessGroupService.updateMemberships(getIdentity(), allModifications);
 		
 		MailTemplate mailTemplate = (MailTemplate)runContext.get("mailTemplate");
+		//TODO group mail
 		System.out.println("Send mails: " + mailTemplate);
 	}
 	
diff --git a/src/main/java/org/olat/course/member/_content/info_member.html b/src/main/java/org/olat/course/member/_content/info_member.html
index d9aecff15ff..bbb3bbf2a85 100644
--- a/src/main/java/org/olat/course/member/_content/info_member.html
+++ b/src/main/java/org/olat/course/member/_content/info_member.html
@@ -2,14 +2,15 @@
 	<div class="b_c33l" style="width:110px;">
 		$r.render("image")
 	</div>
-	<div class="b_c50l">
+	<div class="b_c50l" style="width:45%">
 		<div class="b_subcl">
-			<h4>Fritz Müller</h4>
+			<h4>$fullname</h4>
+			$r.render("userProperties")
 		</div>
 	</div>
-	<div class="b_c33r">
+	<div class="b_c50r" style="width:45%">
 		<div class="b_subcr">
-			Kurz Beitritt
+			$r.render("courseInfos")
 		</div>
 	</div>
 </div>
diff --git a/src/main/java/org/olat/course/member/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/member/_i18n/LocalStrings_de.properties
index 8887f47b5d2..445f97bf869 100644
--- a/src/main/java/org/olat/course/member/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/member/_i18n/LocalStrings_de.properties
@@ -23,6 +23,10 @@ import.member=Mitglieder importieren
 edit.member=Mitglied bearbeiten
 edit.member.title=Mitgliederrechte Kurs "{0}"
 edit.member.groups=Gruppenmitgliedschaften
+mail.member=E-Mail
+course.membership.creation=Kurs Beitritt
+course.lastTime=Zuletzt geöfnnet
+course.numOfVisits=Anzahl Kursaufrufe
 home=Visitenkarte
 assessment=Bewertungswerkzeug
 dialog.modal.bg.leave.text=Wollen Sie wirklich dieser Person {0} von dem Kurs und alle Gruppe entfernen?
diff --git a/src/main/java/org/olat/course/member/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/member/_i18n/LocalStrings_en.properties
index 73db4840164..b53c307d368 100644
--- a/src/main/java/org/olat/course/member/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/member/_i18n/LocalStrings_en.properties
@@ -31,4 +31,5 @@ table.header.lastTime=Last visit
 table.header.role=Roles
 table.header.groups=Groups
 select.group=Select groups
-nomembers=There are no members matching your criteria.
\ No newline at end of file
+nomembers=There are no members matching your criteria.
+mail.member=E-Mail
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/member/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/member/_i18n/LocalStrings_fr.properties
index c257bfe1bb4..05e6ba5a4ff 100644
--- a/src/main/java/org/olat/course/member/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/course/member/_i18n/LocalStrings_fr.properties
@@ -31,4 +31,5 @@ table.header.lastTime=Derni\u00E8re visite
 table.header.role=R\u00D4les
 table.header.groups=Groupes
 select.group=S\u00E9lectionner des groupes
-nomembers.nogroup=Aucun membre correspondant \u00E0 vos crit\u00E8res n'a \u00E9t\u00E9 trouv\u00E9.
\ No newline at end of file
+nomembers.nogroup=Aucun membre correspondant \u00E0 vos crit\u00E8res n'a \u00E9t\u00E9 trouv\u00E9.
+mail.member=Courriel
\ No newline at end of file
diff --git a/src/main/java/org/olat/user/UserInfoMainController.java b/src/main/java/org/olat/user/UserInfoMainController.java
index c90cf8feb15..8ff91cceb06 100644
--- a/src/main/java/org/olat/user/UserInfoMainController.java
+++ b/src/main/java/org/olat/user/UserInfoMainController.java
@@ -109,6 +109,7 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 	private Controller portfolioController;
 	
 	private GenericTreeNode folderNode;
+	private GenericTreeNode contactNode;
 
 	/**
 	 * @param ureq
@@ -182,6 +183,10 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 			main.setContent(createComponent(ureq, cmd, chosenIdentity));
 			menuTree.setSelectedNode(folderNode);
 			folderRunController.activate(ureq, entries.subList(1, entries.size()), null);
+		} else if ("Contact".equals(type) && contactNode != null) {
+			String cmd = (String)contactNode.getUserObject();
+			main.setContent(createComponent(ureq, cmd, chosenIdentity));
+			menuTree.setSelectedNode(contactNode);
 		}
 	}
 
@@ -227,11 +232,11 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 			root.addChild(folderNode);
 		}	
 		if ( !isDeleted) {
-			gtn = new GenericTreeNode();
-			gtn.setTitle(translate("menu.contact"));
-			gtn.setUserObject(CMD_CONTACT);
-			gtn.setAltText(translate("menu.contact.alt"));
-			root.addChild(gtn);
+			contactNode = new GenericTreeNode();
+			contactNode.setTitle(translate("menu.contact"));
+			contactNode.setUserObject(CMD_CONTACT);
+			contactNode.setAltText(translate("menu.contact.alt"));
+			root.addChild(contactNode);
 		}
 		if ( !isDeleted && ! isInvitee) {
 			PortfolioModule portfolioModule = (PortfolioModule) CoreSpringFactory.getBean("portfolioModule");
@@ -271,7 +276,7 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 				calendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_WRITE);
 			else
 				calendarWrapper.setAccess(KalendarRenderWrapper.ACCESS_READ_ONLY);
-			List calendars = new ArrayList();
+			List<KalendarRenderWrapper> calendars = new ArrayList<KalendarRenderWrapper>();
 			calendars.add(calendarWrapper);
 			removeAsListenerAndDispose(calendarController);
 			calendarController = new WeeklyCalendarController(ureq, getWindowControl(), calendars,
diff --git a/src/main/java/org/olat/user/propertyhandlers/_spring/userPropertiesContext.xml b/src/main/java/org/olat/user/propertyhandlers/_spring/userPropertiesContext.xml
index 981a8413d4d..3fdac6bedcb 100644
--- a/src/main/java/org/olat/user/propertyhandlers/_spring/userPropertiesContext.xml
+++ b/src/main/java/org/olat/user/propertyhandlers/_spring/userPropertiesContext.xml
@@ -235,6 +235,20 @@
 					</bean>
 				</entry>
 				
+				<entry key="org.olat.course.member.MemberInfoController">
+					<!-- First name and last name are already shown -->
+					<bean class="org.olat.user.propertyhandlers.UserPropertyUsageContext">
+						<property name="propertyHandlers">
+							<list>
+								<ref bean="userPropertyEmail" />
+								<ref bean="userPropertyInstitutionalName" />
+								<ref bean="userPropertyInstitutionalUserIdentifier" />
+								<ref bean="userPropertyInstitutionalEmail" />
+							</list>
+						</property>
+					</bean>
+				</entry>
+				
 				<entry key="org.olat.admin.user.UsermanagerUserSearchForm">
 					<bean class="org.olat.user.propertyhandlers.UserPropertyUsageContext">
 						<property name="propertyHandlers">
-- 
GitLab