From cd8c20cca500c20233cd0e4d641a0ab5606ac872 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 26 Jul 2012 15:10:21 +0200
Subject: [PATCH] OO-291: add first / last visit in a group

---
 .../org/olat/basesecurity/BaseSecurity.java   |  7 ++++
 .../basesecurity/BaseSecurityManager.java     | 21 ++++++++++++
 .../olat/group/BusinessGroupMembership.java   |  8 ++---
 .../org/olat/group/BusinessGroupService.java  |  2 +-
 .../group/context/BGContext2Resource.java     |  3 +-
 .../org/olat/group/context/BGContextImpl.java |  1 +
 .../olat/group/manager/BusinessGroupDAO.java  |  1 -
 .../manager/BusinessGroupServiceImpl.java     | 15 ++++++++-
 .../java/org/olat/group/site/GroupsSite.java  |  5 +--
 .../org/olat/group/site/GroupsSiteDef.java    | 21 ------------
 .../group/ui/BusinessGroupFormController.java |  1 -
 .../BusinessGroupTableModelWithMaxSize.java   |  5 ++-
 .../group/ui/area/BGAreaEditController.java   |  4 +--
 .../olat/group/ui/area/BGAreaTableModel.java  | 16 ++-------
 .../group/ui/area/GroupsToAreaDataModel.java  | 18 +++-------
 .../ui/edit/BusinessGroupEditController.java  |  2 +-
 .../ui/edit/BusinessGroupModifiedEvent.java   |  1 +
 .../AbstractBusinessGroupListController.java  |  4 +--
 .../main/AdminBusinessGroupsController.java   |  2 ++
 .../ui/main/BusinessGroupListController.java  |  4 ++-
 .../main/BusinessGroupTableModelWithType.java | 33 +++++++++++++++----
 .../ui/main/OpenBusinessGroupsController.java |  4 ++-
 .../ui/main/_i18n/LocalStrings_de.properties  |  4 ++-
 .../ui/management/BGManagementController.java |  2 +-
 .../run/BusinessGroupMainRunController.java   |  2 +-
 .../run/BusinessGroupSendToChooserForm.java   | 13 +++-----
 .../ui/wizard/MemberListWizardController.java |  4 +--
 27 files changed, 113 insertions(+), 90 deletions(-)

diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java
index f03693699d5..8596cb92754 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurity.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java
@@ -88,6 +88,13 @@ public interface BaseSecurity {
 	 * @return true if the identity is in the group
 	 */
 	public boolean isIdentityInSecurityGroup(Identity identity, SecurityGroup secGroup);
+	
+	/**
+	 * Change the last modificaiton date of the membership
+	 * @param identity
+	 * @param secGroups
+	 */
+	public void touchMembership(Identity identity, List<SecurityGroup> secGroups);
 
 	/**
 	 * search
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index 09ea736d7ae..68f3e193080 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -49,6 +49,7 @@ import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.commons.persistence.DBQuery;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
+import org.olat.core.id.ModifiedInfo;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
 import org.olat.core.id.User;
@@ -450,6 +451,26 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		return (cntL.longValue() == 1);
 	}
 
+	@Override
+	public void touchMembership(Identity identity, List<SecurityGroup> secGroups) {
+		if (secGroups == null || secGroups.isEmpty()) return;
+		
+		StringBuilder sb = new StringBuilder();
+		sb.append("select sgmsi from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
+		  .append("where sgmsi.identity.key=:identityKey and sgmsi.securityGroup in (:securityGroups)");
+		
+		List<ModifiedInfo> infos = DBFactory.getInstance().getCurrentEntityManager()
+				.createQuery(sb.toString(), ModifiedInfo.class)
+				.setParameter("identityKey", identity.getKey())
+				.setParameter("securityGroups", secGroups)
+				.getResultList();
+		
+		for(ModifiedInfo info:infos) {
+			info.setLastModified(new Date());
+			DBFactory.getInstance().getCurrentEntityManager().merge(info);
+		}
+	}
+
 	/**
 	 * @see org.olat.basesecurity.Manager#createAndPersistSecurityGroup()
 	 */
diff --git a/src/main/java/org/olat/group/BusinessGroupMembership.java b/src/main/java/org/olat/group/BusinessGroupMembership.java
index 17147eac47f..1c856ad2d5d 100644
--- a/src/main/java/org/olat/group/BusinessGroupMembership.java
+++ b/src/main/java/org/olat/group/BusinessGroupMembership.java
@@ -19,8 +19,7 @@
  */
 package org.olat.group;
 
-import java.util.Date;
-
+import org.olat.core.id.CreateInfo;
 import org.olat.core.id.ModifiedInfo;
 import org.olat.group.model.BGMembership;
 
@@ -28,13 +27,10 @@ import org.olat.group.model.BGMembership;
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
-public interface BusinessGroupMembership extends ModifiedInfo {
-
+public interface BusinessGroupMembership extends CreateInfo, ModifiedInfo {
 
 	public Long getIdentityKey();
 
-	public Date getLastModified();
-
 	public Long getOwnerGroupKey();
 
 	public Long getParticipantGroupKey();
diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java
index 4ebc474e811..a882c4d412b 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -123,7 +123,7 @@ public interface BusinessGroupService {
 	 * @param group
 	 * @return
 	 */
-	public BusinessGroup setLastUsageFor(BusinessGroup group);
+	public BusinessGroup setLastUsageFor(Identity identity, BusinessGroup group);
 	
 	/**
 	 * Reload the business group
diff --git a/src/main/java/org/olat/group/context/BGContext2Resource.java b/src/main/java/org/olat/group/context/BGContext2Resource.java
index 8704066cfca..efe5d3bbda5 100644
--- a/src/main/java/org/olat/group/context/BGContext2Resource.java
+++ b/src/main/java/org/olat/group/context/BGContext2Resource.java
@@ -35,7 +35,8 @@ import org.olat.resource.OLATResource;
  * @author gnaegi
  */
 public class BGContext2Resource extends PersistentObject {
-
+	private static final long serialVersionUID = -8038136432971186300L;
+	
 	private BGContextImpl groupContext;
 	private OLATResource resource;
 
diff --git a/src/main/java/org/olat/group/context/BGContextImpl.java b/src/main/java/org/olat/group/context/BGContextImpl.java
index 4adcaa2464d..3e084bba92c 100644
--- a/src/main/java/org/olat/group/context/BGContextImpl.java
+++ b/src/main/java/org/olat/group/context/BGContextImpl.java
@@ -38,6 +38,7 @@ import org.olat.core.logging.AssertException;
  * @author gnaegi
  */
 public class BGContextImpl extends PersistentObject {
+	private static final long serialVersionUID = -8242401346721569801L;
 
 	private static final int GROUPTYPE_MAXLENGTH = 15;
 
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
index 430f5f39e45..63a028dafb3 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
@@ -234,7 +234,6 @@ public class BusinessGroupDAO {
 		List<BusinessGroupMembership> res = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), BusinessGroupMembership.class)
 				.setParameter("groupKeys", groupKeys)
 				.setParameter("identId", identity.getKey())
-				.setHint("org.hibernate.cacheable", Boolean.TRUE)
 				.getResultList();
 		return res;
 	}
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index b8f98fa3785..87adadbad3b 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -254,12 +254,25 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 
 	@Override
 	@Transactional
-	public BusinessGroup setLastUsageFor(final BusinessGroup group) {
+	public BusinessGroup setLastUsageFor(final Identity identity, final BusinessGroup group) {
 		return CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(group, new SyncerCallback<BusinessGroup>() {
 			public BusinessGroup execute() {
 				try {
 					BusinessGroup reloadedBusinessGroup = loadBusinessGroup(group);
 					reloadedBusinessGroup.setLastUsage(new Date());
+					if(identity != null) {
+						List<SecurityGroup> secGroups = new ArrayList<SecurityGroup>();
+						if(group.getOwnerGroup() != null) {
+							secGroups.add(group.getOwnerGroup());
+						}
+						if(group.getPartipiciantGroup() != null) {
+							secGroups.add(group.getPartipiciantGroup());
+						}
+						if(group.getWaitingGroup() != null) {
+							secGroups.add(group.getWaitingGroup());
+						}
+						securityManager.touchMembership(identity, secGroups);
+					}
 					return businessGroupDAO.merge(reloadedBusinessGroup);
 				} catch(DBRuntimeException e) {
 					if(e.getCause() instanceof ObjectNotFoundException) {
diff --git a/src/main/java/org/olat/group/site/GroupsSite.java b/src/main/java/org/olat/group/site/GroupsSite.java
index cda4a192e7d..1eb086b5b47 100644
--- a/src/main/java/org/olat/group/site/GroupsSite.java
+++ b/src/main/java/org/olat/group/site/GroupsSite.java
@@ -35,7 +35,6 @@ import org.olat.core.gui.control.generic.layout.MainLayoutController;
 import org.olat.core.gui.control.navigation.DefaultNavElement;
 import org.olat.core.gui.control.navigation.NavElement;
 import org.olat.core.gui.control.navigation.SiteInstance;
-import org.olat.core.gui.translator.PackageTranslator;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.context.BusinessControlFactory;
@@ -53,8 +52,6 @@ public class GroupsSite implements SiteInstance {
 	private static final OLATResourceable ORES_GROUPS = OresHelper.createOLATResourceableInstance("BGMainController", 0l);
 
 	// refer to the definitions in org.olat
-	private static final String PACKAGE = Util.getPackageName(BaseChiefController.class);
-
 	private NavElement origNavElem;
 	private NavElement curNavElem;
 
@@ -62,7 +59,7 @@ public class GroupsSite implements SiteInstance {
 	 * 
 	 */
 	public GroupsSite(Locale loc) {
-		Translator trans = new PackageTranslator(PACKAGE, loc);
+		Translator trans = Util.createPackageTranslator(BaseChiefController.class, loc);
 		origNavElem = new DefaultNavElement(trans.translate("topnav.buddygroups"), trans.translate("topnav.buddygroups.alt"), "o_site_groups");
 		origNavElem.setAccessKey("g".charAt(0));
 		curNavElem = new DefaultNavElement(origNavElem);
diff --git a/src/main/java/org/olat/group/site/GroupsSiteDef.java b/src/main/java/org/olat/group/site/GroupsSiteDef.java
index a1f4bcbba94..006862abeb3 100644
--- a/src/main/java/org/olat/group/site/GroupsSiteDef.java
+++ b/src/main/java/org/olat/group/site/GroupsSiteDef.java
@@ -25,9 +25,6 @@
 
 package org.olat.group.site;
 
-import java.util.List;
-
-import org.olat.core.extensions.ExtensionResource;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.navigation.AbstractSiteDefinition;
@@ -47,7 +44,6 @@ public class GroupsSiteDef extends AbstractSiteDefinition implements SiteDefinit
 	 */
 	public GroupsSiteDef() {
 		super();
-		// TODO Auto-generated constructor stub
 	}
 
 	/**
@@ -57,22 +53,6 @@ public class GroupsSiteDef extends AbstractSiteDefinition implements SiteDefinit
 		return "groupssite";
 	}
 
-	/**
-	 * @see org.olat.core.extensions.OLATExtension#getExtensionResources()
-	 */
-	public List getExtensionResources() {
-		// no ressources, part of main css
-		return null;
-	}
-
-	/**
-	 * @see org.olat.core.extensions.OLATExtension#getExtensionCSS()
-	 */
-	public ExtensionResource getExtensionCSS() {
-		// no ressources, part of main css
-		return null;
-	}
-
 	/**
 	 * @see org.olat.navigation.SiteDefinition#createSite(org.olat.core.gui.UserRequest,
 	 *      org.olat.core.gui.control.WindowControl)
@@ -85,5 +65,4 @@ public class GroupsSiteDef extends AbstractSiteDefinition implements SiteDefinit
 		}
 		return si;
 	}
-
 }
diff --git a/src/main/java/org/olat/group/ui/BusinessGroupFormController.java b/src/main/java/org/olat/group/ui/BusinessGroupFormController.java
index 1033ce0b7ee..1aeaea92faf 100644
--- a/src/main/java/org/olat/group/ui/BusinessGroupFormController.java
+++ b/src/main/java/org/olat/group/ui/BusinessGroupFormController.java
@@ -26,7 +26,6 @@ import java.util.Set;
 
 import org.olat.basesecurity.BaseSecurityManager;
 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.MultipleSelectionElement;
 import org.olat.core.gui.components.form.flexible.elements.RichTextElement;
diff --git a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java b/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java
index 2b9a88c249b..912626f7528 100644
--- a/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java
+++ b/src/main/java/org/olat/group/ui/BusinessGroupTableModelWithMaxSize.java
@@ -33,6 +33,7 @@ import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
 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.filter.FilterFactory;
@@ -49,6 +50,8 @@ import org.olat.group.BusinessGroupService;
  * @author gnaegi
  */
 public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<BusinessGroup> {
+	private static final OLog log = Tracing.createLoggerFor(BusinessGroupTableModelWithMaxSize.class);
+	
 	private static final int COLUMN_COUNT = 7;
 	private List<Integer> members;
 	private Translator trans;
@@ -107,7 +110,7 @@ public class BusinessGroupTableModelWithMaxSize extends DefaultTableDataModel<Bu
 				buf.append(trans.translate("grouplist.table.partipiciant.delimiter"));
 				buf.append(businessGroup.getMaxParticipants());
 				if(numbParts>businessGroup.getMaxParticipants()) {
-				  Tracing.logInfo("Group overflow detected for the group: " + businessGroup + ", participants: " + numbParts + " maxParticipamts: " + businessGroup.getMaxParticipants(), BusinessGroupTableModelWithMaxSize.class);
+				  log.info("Group overflow detected for the group: " + businessGroup + ", participants: " + numbParts + " maxParticipamts: " + businessGroup.getMaxParticipants());
 				}
 				return buf.toString();
 			case 3:
diff --git a/src/main/java/org/olat/group/ui/area/BGAreaEditController.java b/src/main/java/org/olat/group/ui/area/BGAreaEditController.java
index d96d26adf83..bd7a0939197 100644
--- a/src/main/java/org/olat/group/ui/area/BGAreaEditController.java
+++ b/src/main/java/org/olat/group/ui/area/BGAreaEditController.java
@@ -204,7 +204,7 @@ public class BGAreaEditController extends BasicController {
 		// 1) add groups to area
 		List<Integer> addedGroups = groupsChoice.getAddedRows();
 		for (Integer position:addedGroups) {
-			BusinessGroup group = groupsDataModel.getGroup(position.intValue());
+			BusinessGroup group = groupsDataModel.getObject(position.intValue());
 			// refresh group to prevent stale object exception and context proxy
 			// issues
 			group = CoreSpringFactory.getImpl(BusinessGroupService.class).loadBusinessGroup(group);
@@ -217,7 +217,7 @@ public class BGAreaEditController extends BasicController {
 		// 2) remove groups from area
 		List<Integer> removedGroups = groupsChoice.getRemovedRows();
 		for (Integer position:removedGroups) {
-			BusinessGroup group = groupsDataModel.getGroup(position.intValue());
+			BusinessGroup group = groupsDataModel.getObject(position.intValue());
 			areaManager.removeBGFromArea(group, area);
 			this.inAreaGroups.remove(group);
 		}
diff --git a/src/main/java/org/olat/group/ui/area/BGAreaTableModel.java b/src/main/java/org/olat/group/ui/area/BGAreaTableModel.java
index f01df728c34..4983b73eb03 100644
--- a/src/main/java/org/olat/group/ui/area/BGAreaTableModel.java
+++ b/src/main/java/org/olat/group/ui/area/BGAreaTableModel.java
@@ -29,7 +29,6 @@ import java.util.List;
 
 import org.apache.commons.lang.StringEscapeUtils;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
-import org.olat.core.gui.components.table.TableDataModel;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.Formatter;
 import org.olat.core.util.filter.FilterFactory;
@@ -41,7 +40,7 @@ import org.olat.group.area.BGArea;
  * 
  * @author gnaegi
  */
-public class BGAreaTableModel extends DefaultTableDataModel implements TableDataModel {
+public class BGAreaTableModel extends DefaultTableDataModel<BGArea> {
 	private static final int COLUMN_COUNT = 3;
 
 	// package-local to avoid synthetic accessor method.
@@ -51,7 +50,7 @@ public class BGAreaTableModel extends DefaultTableDataModel implements TableData
 	 * @param owned list of group areas
 	 * @param translator
 	 */
-	public BGAreaTableModel(List owned, Translator translator) {
+	public BGAreaTableModel(List<BGArea> owned, Translator translator) {
 		super(owned);
 		this.translator = translator;
 	}
@@ -67,7 +66,7 @@ public class BGAreaTableModel extends DefaultTableDataModel implements TableData
 	 * @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int)
 	 */
 	public Object getValueAt(int row, int col) {
-		BGArea area = (BGArea) objects.get(row);
+		BGArea area = getObject(row);
 		switch (col) {
 			case 0:
 				return StringEscapeUtils.escapeHtml(area.getName()).toString();
@@ -80,13 +79,4 @@ public class BGAreaTableModel extends DefaultTableDataModel implements TableData
 				return "ERROR";
 		}
 	}
-
-	/**
-	 * @param row
-	 * @return the area at this position
-	 */
-	public BGArea getBGAreaAt(int row) {
-		return (BGArea) objects.get(row);
-	}
-
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/group/ui/area/GroupsToAreaDataModel.java b/src/main/java/org/olat/group/ui/area/GroupsToAreaDataModel.java
index 2c9da09fd8c..234004cf194 100644
--- a/src/main/java/org/olat/group/ui/area/GroupsToAreaDataModel.java
+++ b/src/main/java/org/olat/group/ui/area/GroupsToAreaDataModel.java
@@ -36,8 +36,8 @@ import org.olat.group.BusinessGroup;
  * 
  * @author gnaegi
  */
-public class GroupsToAreaDataModel extends DefaultTableDataModel {
-	List inAreaGroups;
+public class GroupsToAreaDataModel extends DefaultTableDataModel<BusinessGroup> {
+	List<BusinessGroup> inAreaGroups;
 
 	/**
 	 * Constructor for the GroupsToAreaDataModel
@@ -46,7 +46,7 @@ public class GroupsToAreaDataModel extends DefaultTableDataModel {
 	 * @param inAreaGroups All groups that are associated to the group area. The
 	 *          checked rows.
 	 */
-	public GroupsToAreaDataModel(List allGroups, List inAreaGroups) {
+	public GroupsToAreaDataModel(List<BusinessGroup> allGroups, List<BusinessGroup> inAreaGroups) {
 		super(allGroups);
 		this.inAreaGroups = inAreaGroups;
 	}
@@ -63,19 +63,11 @@ public class GroupsToAreaDataModel extends DefaultTableDataModel {
 	 */
 	public Object getValueAt(int row, int col) {
 		if (col == 0) {
-			return inAreaGroups.contains(getGroup(row)) ? Boolean.TRUE : Boolean.FALSE;
+			return inAreaGroups.contains(getObject(row)) ? Boolean.TRUE : Boolean.FALSE;
 		} else if (col == 1) {
-			return getGroup(row).getName();
+			return getObject(row).getName();
 		} else {
 			return "ERROR";
 		}
 	}
-
-	/**
-	 * @param row
-	 * @return the group at the given position
-	 */
-	public BusinessGroup getGroup(int row) {
-		return (BusinessGroup) super.getObject(row);
-	}
 }
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
index 31ed138e558..2b9da4e526e 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
@@ -175,7 +175,7 @@ public class BusinessGroupEditController extends BasicController implements Cont
 		if (lockEntry.isSuccess()) {
 			// reload group to minimize stale object exception and update last usage
 			// timestamp
-			currBusinessGroup = businessGroupService.setLastUsageFor(businessGroup);
+			currBusinessGroup = businessGroupService.setLastUsageFor(getIdentity(), businessGroup);
 			if(currBusinessGroup == null) {
 				VelocityContainer vc = createVelocityContainer("deleted");
 				vc.contextPut("name", businessGroup.getName());
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
index c30ad601a9c..7ab10e3aa42 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupModifiedEvent.java
@@ -51,6 +51,7 @@ import org.olat.repository.RepositoryEntry;
  */
 public class BusinessGroupModifiedEvent extends MultiUserEvent {
 
+	private static final long serialVersionUID = 6234290505358324180L;
 	/** event: group has been modified */
 	public static final String CONFIGURATION_MODIFIED_EVENT = "configuration.modified.event";
 	/** event: an identity has been added to the group */
diff --git a/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java b/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
index 10e2080746e..fa13f140606 100644
--- a/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
+++ b/src/main/java/org/olat/group/ui/main/AbstractBusinessGroupListController.java
@@ -148,7 +148,7 @@ abstract class AbstractBusinessGroupListController extends BasicController {
 		listenTo(groupListCtr);
 
 		int numOfColumns = initColumns();
-		groupListModel = new BusinessGroupTableModelWithType(new ArrayList<BGTableItem>(), getTranslator(), numOfColumns);
+		groupListModel = new BusinessGroupTableModelWithType(getTranslator(), numOfColumns);
 		groupListCtr.setTableDataModel(groupListModel);
 
 		mainVC.put("groupList", groupListCtr.getInitialComponent());
@@ -712,7 +712,7 @@ abstract class AbstractBusinessGroupListController extends BasicController {
 			items.add(tableItem);
 		}
 		
-		groupListModel.setEntries(items);
+		groupListModel.setEntries(items, memberships);
 		groupListCtr.modelChanged();
 		return groups;
 	}
diff --git a/src/main/java/org/olat/group/ui/main/AdminBusinessGroupsController.java b/src/main/java/org/olat/group/ui/main/AdminBusinessGroupsController.java
index 2e138ef432c..0578d9e0132 100644
--- a/src/main/java/org/olat/group/ui/main/AdminBusinessGroupsController.java
+++ b/src/main/java/org/olat/group/ui/main/AdminBusinessGroupsController.java
@@ -73,6 +73,8 @@ public class AdminBusinessGroupsController extends AbstractBusinessGroupListCont
 		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.description.i18n(), Cols.description.ordinal(), null, getLocale()));
 		CustomCellRenderer resourcesRenderer = new BGResourcesCellRenderer(this, mainVC, getTranslator());
 		groupListCtr.addColumnDescriptor(false, new CustomRenderColumnDescriptor(Cols.resources.i18n(), Cols.resources.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, resourcesRenderer));
+		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), null, getLocale()));
 		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.lastUsage.i18n(), Cols.lastUsage.ordinal(), null, getLocale()));
 		CustomCellRenderer roleRenderer = new BGRoleCellRenderer(getLocale());
 		groupListCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Cols.role.i18n(), Cols.role.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, roleRenderer));
diff --git a/src/main/java/org/olat/group/ui/main/BusinessGroupListController.java b/src/main/java/org/olat/group/ui/main/BusinessGroupListController.java
index 98ba79346be..15fc0a807e1 100644
--- a/src/main/java/org/olat/group/ui/main/BusinessGroupListController.java
+++ b/src/main/java/org/olat/group/ui/main/BusinessGroupListController.java
@@ -97,7 +97,9 @@ public class BusinessGroupListController extends AbstractBusinessGroupListContro
 		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.description.i18n(), Cols.description.ordinal(), null, getLocale()));
 		CustomCellRenderer resourcesRenderer = new BGResourcesCellRenderer(this, mainVC, getTranslator());
 		groupListCtr.addColumnDescriptor(false, new CustomRenderColumnDescriptor(Cols.resources.i18n(), Cols.resources.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, resourcesRenderer));
-		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.lastUsage.i18n(), Cols.lastUsage.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.lastUsage.i18n(), Cols.lastUsage.ordinal(), null, getLocale()));
 		CustomCellRenderer roleRenderer = new BGRoleCellRenderer(getLocale());
 		groupListCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Cols.role.i18n(), Cols.role.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, roleRenderer));
 		groupListCtr.addColumnDescriptor(false, new BooleanColumnDescriptor(Cols.allowLeave.i18n(), Cols.allowLeave.ordinal(), TABLE_ACTION_LEAVE, translate("table.header.leave"), null));
diff --git a/src/main/java/org/olat/group/ui/main/BusinessGroupTableModelWithType.java b/src/main/java/org/olat/group/ui/main/BusinessGroupTableModelWithType.java
index 61073244bb9..fc298016fe2 100644
--- a/src/main/java/org/olat/group/ui/main/BusinessGroupTableModelWithType.java
+++ b/src/main/java/org/olat/group/ui/main/BusinessGroupTableModelWithType.java
@@ -25,8 +25,9 @@
 
 package org.olat.group.ui.main;
 
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.lang.StringEscapeUtils;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
@@ -34,6 +35,7 @@ import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.Formatter;
 import org.olat.core.util.filter.FilterFactory;
 import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupMembership;
 
 /**
  * @author gnaegi
@@ -41,12 +43,14 @@ import org.olat.group.BusinessGroup;
 public class BusinessGroupTableModelWithType extends DefaultTableDataModel<BGTableItem> {
 	private final int columnCount;
 	private Translator trans;
+	
+	private Map<Long, BusinessGroupMembership> memberships;
 
 	/**
 	 * @param owned list of business groups
 	 */
-	public BusinessGroupTableModelWithType(List<BGTableItem> owned, Translator trans, int columnCount) {
-		super(owned);
+	public BusinessGroupTableModelWithType(Translator trans, int columnCount) {
+		super(new ArrayList<BGTableItem>());
 		this.trans = trans;
 		//fxdiff VCRP-1,2: access control of resources
 		this.columnCount = columnCount;
@@ -102,6 +106,18 @@ public class BusinessGroupTableModelWithType extends DefaultTableDataModel<BGTab
 				return wrapped.getBusinessGroup().getLastUsage();
 			case role:
 				return wrapped.getMembership();
+			case firstTime:
+				if(memberships != null) {
+					BusinessGroupMembership membership = memberships.get(businessGroup.getKey());
+					return membership == null ? null : membership.getCreationDate();
+				}
+				return null;
+			case lastTime:
+				if(memberships != null) {
+					BusinessGroupMembership membership = memberships.get(businessGroup.getKey());
+					return membership == null ? null : membership.getLastModified();
+				}
+				return null;
 			default:
 				return "ERROR";
 		}
@@ -110,14 +126,15 @@ public class BusinessGroupTableModelWithType extends DefaultTableDataModel<BGTab
 	@Override
 	//fxdiff VCRP-1,2: access control of resources
 	public Object createCopyWithEmptyList() {
-		return new BusinessGroupTableModelWithType(Collections.<BGTableItem>emptyList(), trans, columnCount);
+		return new BusinessGroupTableModelWithType(trans, columnCount);
 	}
 
 	/**
 	 * @param owned
 	 */
-	public void setEntries(List<BGTableItem> owned) {
-		this.objects = owned;
+	public void setEntries(List<BGTableItem> owned, Map<Long, BusinessGroupMembership> memberships) {
+		setObjects(owned);
+		this.memberships = memberships;
 	}
 
 	/**
@@ -151,7 +168,9 @@ public class BusinessGroupTableModelWithType extends DefaultTableDataModel<BGTab
 		accessTypes("table.header.ac"),
 		mark("table.header.mark"),
 		lastUsage("table.header.lastUsage"),
-		role("table.header.role");
+		role("table.header.role"),
+		firstTime("table.header.firstTime"),
+		lastTime("table.header.lastTime");
 		
 		private final String i18n;
 		
diff --git a/src/main/java/org/olat/group/ui/main/OpenBusinessGroupsController.java b/src/main/java/org/olat/group/ui/main/OpenBusinessGroupsController.java
index 9e8190aaf2d..33b04cca3ad 100644
--- a/src/main/java/org/olat/group/ui/main/OpenBusinessGroupsController.java
+++ b/src/main/java/org/olat/group/ui/main/OpenBusinessGroupsController.java
@@ -81,7 +81,9 @@ public class OpenBusinessGroupsController extends AbstractBusinessGroupListContr
 		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.description.i18n(), Cols.description.ordinal(), null, getLocale()));
 		CustomCellRenderer resourcesRenderer = new BGResourcesCellRenderer(this, mainVC, getTranslator());
 		groupListCtr.addColumnDescriptor(false, new CustomRenderColumnDescriptor(Cols.resources.i18n(), Cols.resources.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, resourcesRenderer));
-		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.lastUsage.i18n(), Cols.lastUsage.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), null, getLocale()));
+		groupListCtr.addColumnDescriptor(false, new DefaultColumnDescriptor(Cols.lastUsage.i18n(), Cols.lastUsage.ordinal(), null, getLocale()));
 		CustomCellRenderer roleRenderer = new BGRoleCellRenderer(getLocale());
 		groupListCtr.addColumnDescriptor(new CustomRenderColumnDescriptor(Cols.role.i18n(), Cols.role.ordinal(), null, getLocale(),  ColumnDescriptor.ALIGNMENT_LEFT, roleRenderer));
 		groupListCtr.addColumnDescriptor(false, new BooleanColumnDescriptor(Cols.allowLeave.i18n(), Cols.allowLeave.ordinal(), TABLE_ACTION_LEAVE, translate("table.header.leave"), null));
diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties
index 0aadf6f7141..0ee10cbcc71 100644
--- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties
@@ -67,12 +67,14 @@ table.header.bgname=Name
 table.header.delete=L\u00F6schen
 table.header.description=Beschreibung
 table.header.edit=\u00C4ndern
-table.header.lastUsage=Zuletzt ge\u00F6ffnet
+table.header.lastUsage=Zuletzt
 table.header.leave=Verlassen
 table.header.mark=Favorit
 table.header.type=Typ
 table.header.resources=Kurs
 table.header.role=Rolle
+table.header.firstTime=Eintritt
+table.header.lastTime=Zuletzt ge\u00F6ffnet
 table.access=Belegen
 table.delete=Löschen
 table.email=Email versenden
diff --git a/src/main/java/org/olat/group/ui/management/BGManagementController.java b/src/main/java/org/olat/group/ui/management/BGManagementController.java
index 2a4d937a299..7a6220c5410 100644
--- a/src/main/java/org/olat/group/ui/management/BGManagementController.java
+++ b/src/main/java/org/olat/group/ui/management/BGManagementController.java
@@ -536,7 +536,7 @@ public class BGManagementController extends MainLayoutBasicController implements
 				TableEvent te = (TableEvent) event;
 				String actionid = te.getActionId();
 				int rowid = te.getRowId();
-				this.currentArea = areaListModel.getBGAreaAt(rowid);
+				this.currentArea = areaListModel.getObject(rowid);
 				if (actionid.equals(CMD_AREA_EDIT)) {
 					doAreaEdit(ureq);
 				} else if (actionid.equals(CMD_AREA_DELETE)) {
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 88d884640c3..39b36d37dad 100644
--- a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
+++ b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java
@@ -237,7 +237,7 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im
 		 */
 		securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);
 		businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
-		businessGroup = businessGroupService.setLastUsageFor(bGroup);
+		businessGroup = businessGroupService.setLastUsageFor(getIdentity(), bGroup);
 		if(businessGroup == null) {
 			VelocityContainer vc = createVelocityContainer("deleted");
 			vc.contextPut("name", bGroup.getName());
diff --git a/src/main/java/org/olat/group/ui/run/BusinessGroupSendToChooserForm.java b/src/main/java/org/olat/group/ui/run/BusinessGroupSendToChooserForm.java
index 3f195696f87..0157dedc558 100644
--- a/src/main/java/org/olat/group/ui/run/BusinessGroupSendToChooserForm.java
+++ b/src/main/java/org/olat/group/ui/run/BusinessGroupSendToChooserForm.java
@@ -396,9 +396,9 @@ public class BusinessGroupSendToChooserForm extends FormBasicController {
 		if (multiSelectionElements == null) {
 			return new ArrayList<Long>();
 		}
-		Set selectedKeys = multiSelectionElements.getSelectedKeys();
+		Set<String> selectedKeys = multiSelectionElements.getSelectedKeys();
 		List<Long> selectedKeysLong = new ArrayList<Long>();
-		for (Object key : selectedKeys) {
+		for (String key : selectedKeys) {
 			selectedKeysLong.add(Long.parseLong(key.toString()));
 		}
 		return selectedKeysLong;
@@ -472,11 +472,8 @@ public class BusinessGroupSendToChooserForm extends FormBasicController {
 	}
 	
 	private void update() {
-		
-		List k;
-		
 		if ( (radioKeysOwners != null) && (multiSelectionOwnerKeys != null) ) {
-			k = Arrays.asList(radioKeysOwners);
+			List<String> k = Arrays.asList(radioKeysOwners);
 			multiSelectionOwnerKeys.setVisible(
 					k.contains(NLS_RADIO_CHOOSE) && 
 					radioButtonOwner.isSelected(k.indexOf(NLS_RADIO_CHOOSE))
@@ -484,7 +481,7 @@ public class BusinessGroupSendToChooserForm extends FormBasicController {
 		}
 		
 		if ( (radioKeysPartips != null) && (multiSelectionPartipKeys != null) ) {		
-			k = Arrays.asList(radioKeysPartips);
+			List<String> k = Arrays.asList(radioKeysPartips);
 			multiSelectionPartipKeys.setVisible(
 					k.contains(NLS_RADIO_CHOOSE) && 
 					radioButtonPartips.isSelected(k.indexOf(NLS_RADIO_CHOOSE))
@@ -493,7 +490,7 @@ public class BusinessGroupSendToChooserForm extends FormBasicController {
 		
 		if ( (radioButtonWaitings != null) && (multiSelectionWaitingKeys != null) ) {		
 			radioButtonWaitings.setVisible(showWaitingList);
-			k = Arrays.asList(radioKeysWaitings);
+			List<String> k = Arrays.asList(radioKeysWaitings);
 			multiSelectionWaitingKeys.setVisible(
 					showWaitingList &&
 					k.contains(NLS_RADIO_CHOOSE) && 
diff --git a/src/main/java/org/olat/group/ui/wizard/MemberListWizardController.java b/src/main/java/org/olat/group/ui/wizard/MemberListWizardController.java
index a7d61f6cc13..0a011ed5190 100644
--- a/src/main/java/org/olat/group/ui/wizard/MemberListWizardController.java
+++ b/src/main/java/org/olat/group/ui/wizard/MemberListWizardController.java
@@ -270,9 +270,9 @@ public class MemberListWizardController extends BasicController {
 					}
 				} else {
 					if (GROUPS_MEMBERS.equals(wizardType)) {						
-						this.setGroupList(getSelectedValues(groupsOrAreaChoice));
+						setGroupList(getSelectedValues(groupsOrAreaChoice));
 					} else if (AREAS_MEMBERS.equals(wizardType)) {						
-						this.setAreaList(getSelectedValues(groupsOrAreaChoice));				
+						setAreaList(getSelectedValues(groupsOrAreaChoice));				
 					}								
 					velocityContainer2.put("colsChoice", colsChoiceController.getInitialComponent());				
 					wizardController.setNextWizardStep(translate("memberlistwizard.colchoice"), velocityContainer2);					
-- 
GitLab