diff --git a/src/main/java/org/olat/basesecurity/AuthHelper.java b/src/main/java/org/olat/basesecurity/AuthHelper.java
index 76a9a83726779edfb02bdb1d340c1fcf78105315..1352d2abf5a1bdc060f14178c46896787d4dbdc5 100644
--- a/src/main/java/org/olat/basesecurity/AuthHelper.java
+++ b/src/main/java/org/olat/basesecurity/AuthHelper.java
@@ -32,11 +32,11 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpSession;
 
+import org.olat.basesecurity.manager.GroupDAO;
 import org.olat.commons.rss.RSSUtil;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.BaseFullWebappController;
@@ -70,6 +70,7 @@ import org.olat.core.util.prefs.Preferences;
 import org.olat.core.util.session.UserSessionManager;
 import org.olat.login.AuthBFWCParts;
 import org.olat.login.GuestBFWCParts;
+import org.olat.portfolio.manager.InvitationDAO;
 import org.olat.user.UserManager;
 import org.olat.util.logging.activity.LoggingResourceable;
 
@@ -221,14 +222,16 @@ public class AuthHelper {
 	}
 	
 	public static int doInvitationLogin(String invitationToken, UserRequest ureq, Locale locale) {
-		boolean hasPolicies = BaseSecurityManager.getInstance().hasInvitationPolicies(invitationToken, new Date());
+		InvitationDAO invitationDao = CoreSpringFactory.getImpl(InvitationDAO.class);
+		boolean hasPolicies = invitationDao.hasInvitations(invitationToken, new Date());
 		if(!hasPolicies) {
 			return LOGIN_DENIED;
 		}
 		
 		UserManager um = UserManager.getInstance();
 		BaseSecurity securityManager = BaseSecurityManager.getInstance();
-		Invitation invitation = securityManager.findInvitation(invitationToken);
+		GroupDAO groupDao = CoreSpringFactory.getImpl(GroupDAO.class);
+		Invitation invitation = invitationDao.findInvitation(invitationToken);
 		if(invitation == null) {
 			return LOGIN_DENIED;
 		}
@@ -242,8 +245,8 @@ public class AuthHelper {
 				return LOGIN_DENIED;
 			} else {
 				//fxdiff FXOLAT-151: add eventually the identity to the security group
-				if(!securityManager.isIdentityInSecurityGroup(identity, invitation.getSecurityGroup())) {
-					securityManager.addIdentityToSecurityGroup(identity, invitation.getSecurityGroup());
+				if(!groupDao.hasRole(invitation.getBaseGroup(), identity, GroupRoles.invitee.name())) {
+					groupDao.addMembership(invitation.getBaseGroup(), identity, GroupRoles.invitee.name());
 					DBFactory.getInstance().commit();
 				}
 
@@ -262,13 +265,8 @@ public class AuthHelper {
 		} 
 		
 		//invitation ok -> create a temporary user
-		//TODO make an username beautifier???
-		String tempUsername = UUID.randomUUID().toString();
-		User user = UserManager.getInstance().createAndPersistUser(invitation.getFirstName(), invitation.getLastName(), invitation.getMail());
-		user.getPreferences().setLanguage(locale.toString());
-		Identity invited = securityManager.createAndPersistIdentity(tempUsername, user, null, null, null);
-		securityManager.addIdentityToSecurityGroup(invited, invitation.getSecurityGroup());
-		return doLogin(invited, BaseSecurityModule.getDefaultAuthProviderIdentifier(), ureq);
+		Identity invitee = invitationDao.createIdentityFrom(invitation, locale);
+		return doLogin(invitee, BaseSecurityModule.getDefaultAuthProviderIdentifier(), ureq);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java
index f56672dd8633013754e9f04152780b45b7ed2167..9e4917a688070726a8786613d90513cdf11389de 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurity.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java
@@ -62,21 +62,6 @@ public interface BaseSecurity {
 	 */
 	public boolean isIdentityPermittedOnResourceable(Identity identity, String permission, OLATResourceable olatResourceable);
 
-	/**
-	 * Return the list of "allowed to..."
-	 * @param identity
-	 * @param olatResourceable
-	 * @return
-	 */
-	public List<String> getIdentityPermissionOnresourceable(Identity identity, OLATResourceable olatResourceable);
-	
-	/**
-	 * 
-	 * @param permission
-	 * @param olatResourceableTypeName
-	 * @return
-	 */
-	public List<Identity> getIdentitiesWithPermissionWithOlatResourceableType(String permission, String olatResourceableTypeName);
 	
 	
 	/**
@@ -463,89 +448,11 @@ public interface BaseSecurity {
 	 * @return the newly created policy
 	 */
 	public Policy createAndPersistPolicy(SecurityGroup secGroup, String permission, OLATResourceable olatResourceable);
-	
-	public Policy createAndPersistPolicy(SecurityGroup secGroup, String permission, Date from, Date to, OLATResourceable olatResourceable);
-
-
-	/**
-	 * Creates and persist a policy for certain OLAT-resource (instead of OLAT-resourceable)
-	 * 
-	 * @param secGroup
-	 * @param permission
-	 * @param olatResource
-	 * @return the newly created policy
-	 */
-	public Policy createAndPersistPolicyWithResource(SecurityGroup secGroup, String permission, OLATResource olatResource);
-
-	
 
-	public Policy findPolicy(SecurityGroup secGroup, String permission, OLATResource olatResource);
-	
-	/**
-	 * Create and persist an invitation with its security group and security token.
-	 * @return
-	 */
-	public Invitation createAndPersistInvitation();
-	
-	/**
-	 * Update the invitation
-	 * @param invitation
-	 */
-	public void updateInvitation(Invitation invitation);
-	
-	/**
-	 * Is the invitation linked to any valid policies
-	 * @param token
-	 * @param atDate
-	 * @return
-	 */
-	public boolean hasInvitationPolicies(String token, Date atDate);
-	
-	/**
-	 * Find an invitation by its security group
-	 * @param secGroup
-	 * @return The invitation or null if not found
-	 */
-	public Invitation findInvitation(SecurityGroup secGroup);
-	
-	/**
-	 * Find an invitation by its security token
-	 * @param token
-	 * @return The invitation or null if not found
-	 */
-	public Invitation findInvitation(String token);
-	
-	/**
-	 * Check if the identity has an invitation, valid or not
-	 * @param identity
-	 * @return
-	 */
-	public boolean isIdentityInvited(Identity identity);
-	
-	/**
-	 * Delete an invitation
-	 * @param invitation
-	 */
-	public void deleteInvitation(Invitation invitation);
-	
-	/**
-	 * Clean up old invitation and set to deleted temporary users
-	 */
-	public void cleanUpInvitations();
-	
-	/**
-	 * @param secGroup
-	 * @param permission
-	 * @param olatResourceable
-	 */
-	public void deletePolicy(SecurityGroup secGroup, String permission, OLATResource olatResourceable);
-	
 	/**
 	 * Delete all policies of a resource
 	 */
 	public void deletePolicies(OLATResource olatResourceable);
-	
-	public boolean deletePolicies(Collection<SecurityGroup> secGroups, Collection<OLATResource> resources);
 
 	// some queries mainly for the group/groupcontext management
 	/**
@@ -554,29 +461,15 @@ public interface BaseSecurity {
 	 */
 	public List<Policy> getPoliciesOfSecurityGroup(SecurityGroup secGroup);
 	
+
 	/**
-	 * 
-	 * @param secGroups
+	 * Return the policies
+	 * @param resource The resource (mandatory)
+	 * @param securityGroup The securityGroup (optional)
 	 * @return
 	 */
-	public List<Policy> getPoliciesOfSecurityGroup(List<SecurityGroup> secGroups, OLATResource... resources);
-
-/**
- * Return the policies
- * @param resource The resource (mandatory)
- * @param securityGroup The securityGroup (optional)
- * @return
- */
 	public List<Policy> getPoliciesOfResource(OLATResource resource, SecurityGroup securityGroup);
 	
-	/**
-	 * Update the policy valid dates
-	 * @param policy
-	 * @param from
-	 * @param to
-	 */
-	public void updatePolicy(Policy policy, Date from, Date to);
-
 	/**
 	 * for debugging and info by the olat admins:
 	 * 
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index 8c9bd474711246f5964d9fdc6cf08116a54cae20..fd8dadc02f788ee33fa1f65f723df61015206b07 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -26,7 +26,6 @@
 package org.olat.basesecurity;
 
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -34,7 +33,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.UUID;
 
 import javax.persistence.EntityNotFoundException;
 import javax.persistence.LockModeType;
@@ -48,12 +46,10 @@ import org.olat.admin.sysinfo.SysinfoController;
 import org.olat.admin.user.UserAdminController;
 import org.olat.admin.user.UserChangePasswordController;
 import org.olat.admin.user.UserCreateController;
-import org.olat.admin.user.delete.service.UserDeletionManager;
 import org.olat.basesecurity.events.NewIdentityCreatedEvent;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.commons.persistence.DBQuery;
-import org.olat.core.commons.persistence.PersistenceHelper;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
 import org.olat.core.id.ModifiedInfo;
@@ -70,8 +66,8 @@ import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.SyncerCallback;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.login.LoginModule;
+import org.olat.portfolio.manager.InvitationDAO;
 import org.olat.resource.OLATResource;
-import org.olat.resource.OLATResourceImpl;
 import org.olat.resource.OLATResourceManager;
 import org.olat.user.ChangePasswordController;
 import org.olat.user.PersonalSettingsController;
@@ -89,6 +85,7 @@ import org.olat.user.UserManager;
 public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 	private DB dbInstance;
 	private OLATResourceManager orm;
+	private InvitationDAO invitationDao;
 	private String dbVendor = "";
 	private static BaseSecurityManager INSTANCE;
 	private static String GUEST_USERNAME_PREFIX = "guest_";
@@ -124,6 +121,14 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 	public void setDbInstance(DB dbInstance) {
 		this.dbInstance = dbInstance;
 	}
+	
+	/**
+	 * [used by Spring]
+	 * @param invitationDao
+	 */
+	public void setInvitationDao(InvitationDAO invitationDao) {
+		this.invitationDao = invitationDao;
+	}
 
 	/**
 	 * @see org.olat.basesecurity.Manager#init()
@@ -290,31 +295,6 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 				.getResultList();
 		return policies;
 	}
-	
-	@Override
-	public List<Policy> getPoliciesOfSecurityGroup(List<SecurityGroup> secGroups, OLATResource... resources) {
-		if(secGroups == null || secGroups.isEmpty()) return Collections.emptyList();
-		
-		StringBuilder sb = new StringBuilder();
-		sb.append("select poi from ").append(PolicyImpl.class.getName()).append(" as poi")
-		  .append(" inner join fetch poi.securityGroup as secGroup")
-		  .append(" inner join fetch poi.olatResource as resource")
-		  .append(" where secGroup.key in (:secGroupKeys)");
-		if(resources != null && resources.length > 0) {
-			sb.append(" and resource.key in (:resourceKeys)");
-		}
-
-		List<Long> secGroupKeys = PersistenceHelper.toKeys(secGroups);
-		TypedQuery<Policy> queryPolicies = DBFactory.getInstance().getCurrentEntityManager()
-				.createQuery(sb.toString(), Policy.class)
-				.setParameter("secGroupKeys", secGroupKeys);
-		if(resources != null && resources.length > 0) {
-			List<Long> resourceKeys = PersistenceHelper.toKeys(resources);
-			queryPolicies.setParameter("resourceKeys", resourceKeys);
-		}	
-		List<Policy> policies =	queryPolicies.getResultList();
-		return policies;
-	}
 
 	/**
 	 * @see org.olat.basesecurity.BaseSecurity#getPoliciesOfResource(org.olat.core.id.OLATResourceable)
@@ -336,40 +316,6 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		}
 		return query.getResultList();
 	}
-	
-	@Override
-	public List<Identity> getIdentitiesWithPermissionWithOlatResourceableType(
-			String permission, String olatResourceableTypeName) {
-		// if the olatResourceable is not persisted as OLATResource, then the answer
-		// is false, therefore we can use the query assuming there is an OLATResource
-		StringBuilder sb = new StringBuilder();
-		sb.append("select distinct im from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi,")
-		  .append(IdentityImpl.class.getName()).append(" as im,")
-		  .append(PolicyImpl.class.getName()).append(" as poi,")
-		  .append(OLATResourceImpl.class.getName()).append(" as ori ")
-		  .append("where im=sgmsi.identity and sgmsi.securityGroup=poi.securityGroup ")
-		  .append(" and poi.permission=:permission and poi.olatResource=ori and ori.resName=:resName");
-
-		return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Identity.class)
-				.setParameter("permission", permission)
-				.setParameter("resName", olatResourceableTypeName)
-				.getResultList();
-	}
-	
-	@Override
-	public List<String> getIdentityPermissionOnresourceable(Identity identity, OLATResourceable olatResourceable) {
-		Long oresid = olatResourceable.getResourceableId();
-		if (oresid == null) {
-			oresid = new Long(0);
-		}
-		List<String> permissions = dbInstance.getCurrentEntityManager()
-				.createNamedQuery("getIdentityPermissionsOnResourceableCheckType", String.class)
-			.setParameter("identitykey", identity.getKey())
-			.setParameter("resid", oresid)
-			.setParameter("resname", olatResourceable.getResourceableTypeName())
-			.getResultList();
-		return permissions;
-	}
 
 	@Override
 	public boolean isIdentityPermittedOnResourceable(Identity identity, String permission, OLATResourceable olatResourceable) {
@@ -426,7 +372,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		boolean poolManager = admin || rolesStr.contains(Constants.GROUP_POOL_MANAGER);
 		
 		if(!rolesStr.contains(Constants.GROUP_OLATUSERS)) {
-			isInvitee = isIdentityInvited(identity);
+			isInvitee = invitationDao.isInvitee(identity);
 			isGuestOnly = isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_GUESTONLY);
 		}
 		
@@ -529,13 +475,6 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 				.setParameter("identityKey", identity.getKey())
 				.getResultList();
 	}
-	
-	@Override
-	public void updatePolicy(Policy policy, Date from, Date to) {
-		((PolicyImpl)policy).setFrom(from);
-		((PolicyImpl)policy).setTo(to);
-		DBFactory.getInstance().updateObject(policy);
-	}
 
 	/**
 	 * @see org.olat.basesecurity.Manager#isIdentityInSecurityGroup(org.olat.core.id.Identity, org.olat.basesecurity.SecurityGroup)
@@ -665,23 +604,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 	 */
 	@Override
 	public Policy createAndPersistPolicy(SecurityGroup secGroup, String permission, OLATResourceable olatResourceable) {
-		return createAndPersistPolicy(secGroup, permission, null, null, olatResourceable);
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#createAndPersistPolicy(org.olat.basesecurity.SecurityGroup, java.lang.String, java.util.Date, java.util.Date, org.olat.core.id.OLATResourceable)
-	 */
-	@Override
-	public Policy createAndPersistPolicy(SecurityGroup secGroup, String permission, Date from, Date to, OLATResourceable olatResourceable) {
 		OLATResource olatResource = orm.findOrPersistResourceable(olatResourceable);
-		return createAndPersistPolicyWithResource(secGroup, permission, from, to, olatResource);
-	}
-
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#createAndPersistPolicyWithResource(org.olat.basesecurity.SecurityGroup, java.lang.String, org.olat.resource.OLATResource)
-	 */
-	@Override
-	public Policy createAndPersistPolicyWithResource(SecurityGroup secGroup, String permission, OLATResource olatResource) {
 		return createAndPersistPolicyWithResource(secGroup, permission, null, null, olatResource);
 	}
 
@@ -733,41 +656,6 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 			return null;
 		}
 		return policies.get(0);
-	}	
-	
-	private void deletePolicy(Policy policy) {
-		DBFactory.getInstance().deleteObject(policy);
-	}
-
-	@Override
-	public boolean deletePolicies(Collection<SecurityGroup> secGroups, Collection<OLATResource> resources) {	
-		if(secGroups == null || secGroups.isEmpty() || resources == null || resources.isEmpty()) return false;
-		
-		StringBuilder sb = new StringBuilder();
-		sb.append("delete from ").append(PolicyImpl.class.getName()).append(" as poi ")
-		  .append(" where poi.olatResource.key in (:resourceKey) and poi.securityGroup.key in (:secGroupKeys)");
-
-		List<Long> secGroupKeys = PersistenceHelper.toKeys(secGroups);
-		List<Long> resourceKeys = PersistenceHelper.toKeys(resources);
-		int rows = DBFactory.getInstance().getCurrentEntityManager()
-				.createQuery(sb.toString())
-				.setParameter("resourceKey", resourceKeys)
-				.setParameter("secGroupKeys", secGroupKeys)
-				.executeUpdate();
-		return rows > 0;
-	}
-
-	/**
-	 * @see org.olat.basesecurity.Manager#deletePolicy(org.olat.basesecurity.SecurityGroup, java.lang.String, org.olat.core.id.OLATResourceable
-	 */
-	@Override
-	public void deletePolicy(SecurityGroup secGroup, String permission, OLATResource resource) {		 
-		if (resource == null) throw new AssertException("cannot delete policy of a null olatresourceable!");
-		Policy p = findPolicy(secGroup, permission, resource);
-		// fj: introduced strict testing here on purpose
-		if (p != null) {
-			deletePolicy(p);
-		}
 	}
 
 	@Override
@@ -785,169 +673,6 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		}
 	}
 
-	/**
-	 * 
-	 * @see org.olat.basesecurity.BaseSecurity#createAndPersistInvitation()
-	 */
-	@Override
-	public Invitation createAndPersistInvitation() {
-		SecurityGroup secGroup = new SecurityGroupImpl();
-		DBFactory.getInstance().saveObject(secGroup);
-		
-		InvitationImpl invitation = new InvitationImpl();
-		invitation.setToken(UUID.randomUUID().toString());
-		invitation.setSecurityGroup(secGroup);
-		DBFactory.getInstance().saveObject(invitation);
-		return invitation;
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#updateInvitation(org.olat.basesecurity.Invitation)
-	 */
-	@Override
-	public void updateInvitation(Invitation invitation) {
-		DBFactory.getInstance().updateObject(invitation);
-	}
-
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#hasInvitationPolicies(java.lang.String, java.util.Date)
-	 */
-	@Override
-	public boolean hasInvitationPolicies(String token, Date atDate) {
-		StringBuilder sb = new StringBuilder();
-	  sb.append("select count(policy) from ").append(PolicyImpl.class.getName()).append(" as policy, ")
-	  	.append(InvitationImpl.class.getName()).append(" as invitation ")
-	  	.append(" inner join policy.securityGroup secGroup ")
-      .append(" where invitation.securityGroup=secGroup ")
-	  	.append(" and invitation.token=:token");
-	  if(atDate != null) {
-      sb.append(" and (policy.from is null or policy.from<=:date)")
-				.append(" and (policy.to is null or policy.to>=:date)");
-	  }
-
-	  DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
-	  query.setString("token", token);
-	  if(atDate != null) {
-	  	query.setDate("date", atDate);
-	  }
-	  
-	  Number counter = (Number)query.uniqueResult();
-    return counter.intValue() > 0;
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#findInvitation(org.olat.basesecurity.SecurityGroup)
-	 */
-	@Override
-	public Invitation findInvitation(SecurityGroup secGroup) {
-		StringBuilder sb = new StringBuilder();
-	  sb.append("select invitation from ").append(InvitationImpl.class.getName()).append(" as invitation ")
-	  	.append(" where invitation.securityGroup=:secGroup ");
-
-	  List<Invitation> invitations = dbInstance.getCurrentEntityManager()
-			  .createQuery(sb.toString(), Invitation.class)
-			  .setParameter("secGroup", secGroup)
-			  .getResultList();
-	  if(invitations.isEmpty()) return null;
-	  return invitations.get(0);
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#findInvitation(java.lang.String)
-	 */
-	@Override
-	public Invitation findInvitation(String token) {
-		StringBuilder sb = new StringBuilder();
-	  sb.append("select invitation from ").append(InvitationImpl.class.getName()).append(" as invitation ")
-	  	.append(" where invitation.token=:token");
-
-	  DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
-	  query.setString("token", token);
-	  
-	  List<Invitation> invitations = query.list();
-	  if(invitations.isEmpty()) return null;
-    return invitations.get(0);
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#isIdentityInvited(org.olat.core.id.Identity)
-	 */
-	@Override
-	public boolean isIdentityInvited(Identity identity) {
-		StringBuilder sb = new StringBuilder();
-	  sb.append("select count(invitation) from ").append(InvitationImpl.class.getName()).append(" as invitation ")
-	  	.append("inner join invitation.securityGroup secGroup ")
-	  	.append("where secGroup in (")
-	  	.append(" select membership.securityGroup from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as membership")
-	  	.append("  where membership.identity=:identity")
-	  	.append(")");
-	  
-	  DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
-	  query.setEntity("identity", identity);
-
-	  Number invitations = (Number)query.uniqueResult();
-    return invitations.intValue() > 0;
-	}
-	
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#deleteInvitation(org.olat.basesecurity.Invitation)
-	 */
-	@Override
-	public void deleteInvitation(Invitation invitation) {
-		//fxdiff: FXOLAT-251: nothing persisted, nothing to delete
-		if(invitation == null || invitation.getKey() == null) return;
-		DBFactory.getInstance().deleteObject(invitation);
-	}
-
-	/**
-	 * @see org.olat.basesecurity.BaseSecurity#cleanUpInvitations()
-	 */
-	@Override
-	public void cleanUpInvitations() {
-		Calendar cal = Calendar.getInstance();
-		cal.setTime(new Date());
-		Date currentTime = cal.getTime();
-		cal.add(Calendar.HOUR, -6);
-		Date dateLimit = cal.getTime();
-
-		StringBuilder sb = new StringBuilder();
-	  sb.append("select invitation from ").append(InvitationImpl.class.getName()).append(" as invitation ")
-	  	.append(" inner join invitation.securityGroup secGroup ")
-      .append(" where invitation.creationDate<:dateLimit")//someone can create an invitation but not add it to a policy within millisecond
-	  	.append(" and secGroup not in (")
-      //select all valid policies from this security group
-      .append("  select policy.securityGroup from ").append(PolicyImpl.class.getName()).append(" as policy ")
-      .append("   where (policy.from is null or policy.from<=:currentDate)")
-			.append("   and (policy.to is null or policy.to>=:currentDate)")
-	  	.append("  )");
-
-	  DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
-	  query.setDate("currentDate", currentTime);
-	  query.setDate("dateLimit", dateLimit);
-	  List<Invitation> oldInvitations = query.list();
-	  if(oldInvitations.isEmpty()) {
-	  	return;
-	  }
-	  
-	  SecurityGroup olatUserSecGroup = findSecurityGroupByName(Constants.GROUP_OLATUSERS);
-	  for(Invitation invitation:oldInvitations) {
-	  	List<Identity> identities = getIdentitiesOfSecurityGroup(invitation.getSecurityGroup());
-	  	//normally only one identity
-	  	for(Identity identity:identities) {
-	  		if(identity.getStatus().compareTo(Identity.STATUS_VISIBLE_LIMIT) >= 0) {
-	  			//already deleted
-	  		} else if(isIdentityInSecurityGroup(identity, olatUserSecGroup)) {
-	  			//out of scope
-	  		} else {
-	  			//delete user
-	  			UserDeletionManager.getInstance().deleteIdentity(identity);
-	  		}
-	  	}
-	  	DBFactory.getInstance().deleteObject(invitation);
-	  	DBFactory.getInstance().intermediateCommit();
-	  }
-	}
-
 	/**
 	 * @param username the username
 	 * @param user the presisted User
diff --git a/src/main/java/org/olat/basesecurity/GroupRoles.java b/src/main/java/org/olat/basesecurity/GroupRoles.java
index 4c5f291a87244268208cd634d52be09843f76c27..0ad5ce8836b4d9e6be7f395cccfeec28c2e2c297 100644
--- a/src/main/java/org/olat/basesecurity/GroupRoles.java
+++ b/src/main/java/org/olat/basesecurity/GroupRoles.java
@@ -34,6 +34,7 @@ public enum GroupRoles {
 	owner,
 	coach,
 	participant,
+	invitee,
 	waiting;
 	
 	
diff --git a/src/main/java/org/olat/basesecurity/IdentityNames.java b/src/main/java/org/olat/basesecurity/IdentityNames.java
index 86d22f62a848628aff01104e6e4e0efeaf988ccc..97bdbbe6b921cb4503a2a98c20ed62e468ec682f 100644
--- a/src/main/java/org/olat/basesecurity/IdentityNames.java
+++ b/src/main/java/org/olat/basesecurity/IdentityNames.java
@@ -27,8 +27,6 @@ package org.olat.basesecurity;
  */
 public interface IdentityNames extends IdentityRef {
 	
-	public Long getKey();
-	
 	public String getName();
 	
 	public String getFirstName();
diff --git a/src/main/java/org/olat/basesecurity/Invitation.java b/src/main/java/org/olat/basesecurity/Invitation.java
index 3235aba94e29b9a353180f3b8649f92a7590bf1e..84069f8307e4df18d792d1acd1dd29ba54154a3f 100644
--- a/src/main/java/org/olat/basesecurity/Invitation.java
+++ b/src/main/java/org/olat/basesecurity/Invitation.java
@@ -46,5 +46,5 @@ public interface Invitation {
 	
 	public void setMail(String mail);
 	
-	public SecurityGroup getSecurityGroup();
+	public Group getBaseGroup();
 }
diff --git a/src/main/java/org/olat/basesecurity/InvitationImpl.hbm.xml b/src/main/java/org/olat/basesecurity/InvitationImpl.hbm.xml
deleted file mode 100644
index 90aa24dfcc49963536be3335b177dec7fb0ec4f7..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/basesecurity/InvitationImpl.hbm.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
-
-<hibernate-mapping default-lazy="false">
-
- <class name="org.olat.basesecurity.InvitationImpl" table="o_bs_invitation">
-     <!-- the default columns -->
-	<id name="key" column="id" type="long" unsaved-value="null">
-		<generator class="hilo"/>
-	</id>
-	<version name="version" access="field" column="version" type="int"/>
-	<property  name="creationDate" column="creationdate" type="timestamp" />
-	
-	<property  name="token" column="token" type="string" length="64"/>
-	<property  name="firstName" column="first_name" type="string" length="64"/>
-	<property  name="lastName" column="last_name" type="string" length="64"/>
-	<property  name="mail" column="mail" type="string" length="128"/>
-	 
-  <many-to-one name="securityGroup" class="org.olat.basesecurity.SecurityGroupImpl" fetch="join" cascade="none"
-  	column="fk_secgroup" not-null="true"	unique-key="policy_unique"/>
-	</class>
-</hibernate-mapping>
-
diff --git a/src/main/java/org/olat/basesecurity/InvitationImpl.java b/src/main/java/org/olat/basesecurity/InvitationImpl.java
deleted file mode 100644
index 250b0996dd3834abcaa5321b7ad5b4e9dc30c7f6..0000000000000000000000000000000000000000
--- a/src/main/java/org/olat/basesecurity/InvitationImpl.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * <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.basesecurity;
-
-import org.olat.core.commons.persistence.PersistentObject;
-
-/**
- * 
- * Description:<br>
- * Implementation of Invitation
- * 
- * <P>
- * Initial Date:  10 nov. 2010 <br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- */
-public class InvitationImpl extends PersistentObject implements Invitation {
-
-	private String token;
-	private String firstName;
-	private String lastName;
-	private String mail;
-	private SecurityGroup securityGroup;
-
-	public String getToken() {
-		return token;
-	}
-
-	public void setToken(String token) {
-		this.token = token;
-	}
-	
-	public String getFirstName() {
-		return firstName;
-	}
-
-	public void setFirstName(String firstName) {
-		this.firstName = firstName;
-	}
-
-	public String getLastName() {
-		return lastName;
-	}
-
-	public void setLastName(String lastName) {
-		this.lastName = lastName;
-	}
-
-	public String getMail() {
-		return mail;
-	}
-
-	public void setMail(String mail) {
-		this.mail = mail;
-	}
-
-	public SecurityGroup getSecurityGroup() {
-		return securityGroup;
-	}
-
-	public void setSecurityGroup(SecurityGroup securityGroup) {
-		this.securityGroup = securityGroup;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if(this == obj) {
-			return true;
-		} else if (obj instanceof InvitationImpl) {
-			InvitationImpl invitation = (InvitationImpl)obj;
-			return getKey() != null && getKey().equals(invitation.getKey());
-		}
-		return false;
-	}
-}
diff --git a/src/main/java/org/olat/basesecurity/_spring/baseSecurityContext.xml b/src/main/java/org/olat/basesecurity/_spring/baseSecurityContext.xml
index 0cc171d1c18ab6c76dadb9d211d1c32452958c3e..0a43cb6cd73176ca9f9eebf46e145804cb11a1df 100644
--- a/src/main/java/org/olat/basesecurity/_spring/baseSecurityContext.xml
+++ b/src/main/java/org/olat/basesecurity/_spring/baseSecurityContext.xml
@@ -15,6 +15,7 @@
 	depends-on="database, i18nModule, triggerI18nModuleInit">
 	<property name="resourceManager" ref="resourceManager"/>
 	<property name="dbInstance" ref="database"/>
+	<property name="invitationDao" ref="invitationDao" />
 	<property name="dbVendor" value="${db.vendor}" />
 </bean>
 
@@ -130,35 +131,4 @@
                 </property>
 </bean>
 
-<bean id="invitationCleanupTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
-	<property name="jobDetail" ref="invitationCleanupJob.${cluster.singleton.services}" />
-  <!-- adjust cron style syntax for your notification needs 
-   	"0 10 0 * *"  e.g. 10 minutes after midnight
-   	
-   	A "Cron-Expression" is a string comprised of 6 or 7 fields separated by white space. The 6 mandatory and 1 optional fields are as follows:
-	Field Name 	  	Allowed Values 	  	Allowed Special Characters
-	Seconds 	  	0-59 	  			, - * /
-	Minutes 	  	0-59 	  			, - * /
-	Hours 	  		0-23 	  			, - * /
-	Day-of-month 	1-31 	  			, - * ? / L W C
-	Month 	  		1-12 or JAN-DEC 	, - * /
-	Day-of-Week 	1-7 or SUN-SAT 	  	, - * ? / L C #
-	Year (Optional)	empty, 1970-2099 	, - * /
-
-	As of OLAT 6.3 it's best to let the cronjob run every two hours since users can now choose how often 
-	they will get notified. The shortest interval is set to two hours. 	    	
-   -->
-	<property name="cronExpression" value="0 2 */12 * * ?" />
-	<property name="startDelay" value="150000" />
-</bean>
-
-<bean id="invitationCleanupJob.enabled" class="org.springframework.scheduling.quartz.JobDetailBean" lazy-init="true">
-	<property name="jobClass" value="org.olat.basesecurity.InvitationCleanupJob" />
-</bean>
-	
-<!-- dummy bean -->
-<bean id="invitationCleanupJob.disabled" class="org.springframework.scheduling.quartz.JobDetailBean"  lazy-init="true">
-	<property name="jobClass" value="org.olat.core.commons.services.scheduler.DummyJob" />
-</bean>
-
 </beans>
\ No newline at end of file
diff --git a/src/main/java/org/olat/basesecurity/manager/GroupDAO.java b/src/main/java/org/olat/basesecurity/manager/GroupDAO.java
index dbc26a096492201eab9015041d8cba5aa31b99f4..c624071c0c66e4f5fc447b6d51699d47b96416b5 100644
--- a/src/main/java/org/olat/basesecurity/manager/GroupDAO.java
+++ b/src/main/java/org/olat/basesecurity/manager/GroupDAO.java
@@ -59,6 +59,15 @@ public class GroupDAO {
 		return group;
 	}
 	
+	public Group createGroup(String name) {
+		GroupImpl group = new GroupImpl();
+		group.setCreationDate(new Date());
+		group.setName(name);
+		dbInstance.getCurrentEntityManager().persist(group);
+		return group;
+	}
+	
+	
 	public Group removeGroup(Group group) {
 		EntityManager em = dbInstance.getCurrentEntityManager();
 		GroupImpl reloadedGroup = em.getReference(GroupImpl.class, group.getKey());
diff --git a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml
index 359476e50c242c3ae23779ae7b9c5ff6be5e66e2..38f4953bc1061ab3da22aea1fc7da298124bdccc 100644
--- a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml
+++ b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml
@@ -34,7 +34,6 @@
 		<mapping-file>org/olat/resource/accesscontrol/provider/paypal/model/PaypalTransaction.hbm.xml</mapping-file>
 		<mapping-file>org/olat/basesecurity/AuthenticationImpl.hbm.xml</mapping-file>
 		<mapping-file>org/olat/basesecurity/PolicyImpl.hbm.xml</mapping-file>
-		<mapping-file>org/olat/basesecurity/InvitationImpl.hbm.xml</mapping-file>
 		<mapping-file>org/olat/basesecurity/IdentityImpl.hbm.xml</mapping-file>
 		<mapping-file>org/olat/basesecurity/NamedGroupImpl.hbm.xml</mapping-file>
 		<mapping-file>org/olat/basesecurity/SecurityGroupImpl.hbm.xml</mapping-file>
@@ -86,6 +85,8 @@
 		<mapping-file>org/olat/upgrade/model/RepositoryEntryUpgrade.hbm.xml</mapping-file>
 		<mapping-file>org/olat/upgrade/model/EPMapUpgrade.hbm.xml</mapping-file>
 		<class>org.olat.upgrade.model.RepositoryEntryUpgradeToGroupRelation</class>
+		<class>org.olat.upgrade.model.EPMapUpgradeToGroupRelation</class>
+		<class>org.olat.upgrade.model.InvitationUpgrade</class>
 		<!-- End upgraders mapping -->
 		
 		<class>org.olat.basesecurity.model.GroupImpl</class>
@@ -140,6 +141,8 @@
 		<class>org.olat.modules.qpool.model.QItemType</class>
 		<class>org.olat.modules.qpool.model.QLicense</class>
 		<class>org.olat.ims.lti.model.LTIOutcomeImpl</class>
+		<class>org.olat.portfolio.model.InvitationImpl</class>
+		<class>org.olat.portfolio.model.structel.EPStructureElementToGroupRelation</class>
 		<properties>
 			<property name="hibernate.generate_statistics" value="true"/>
 			<property name="hibernate.archive.autodetection" value=""/>
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
index d1024f2742882211c3a5e4e8287d253f67ba4a62..d442d120a8eac135582f43799657167d324a4610 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java
@@ -399,6 +399,20 @@ public class BusinessGroupDAO {
 		return query.getResultList();
 	}
 	
+	public BusinessGroup findBusinessGroup(Group baseGroup) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select bgs from ").append(BusinessGroupImpl.class.getName()).append(" as bgs ")
+		  .append(" inner join fetch bgs.resource as bgResource")
+		  .append(" inner join fetch bgs.baseGroup as baseGroup")
+		  .append(" where baseGroup=:group");
+
+		List<BusinessGroup> groups = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), BusinessGroup.class)
+				.setParameter("group", baseGroup)
+				.getResultList();
+		return groups.isEmpty() ? null : groups.get(0);
+	}
+	
 	public List<BusinessGroup> findBusinessGroupsWithWaitingListAttendedBy(Identity identity, RepositoryEntryRef repoEntry) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select bgs from ").append(BusinessGroupImpl.class.getName()).append(" as bgs ")
diff --git a/src/main/java/org/olat/gui/control/OlatFooterController.java b/src/main/java/org/olat/gui/control/OlatFooterController.java
index 40766d2ff6bdf9ebb80016511e7c164a4882258e..852643390892033bb382da7eba3fd4e64389e4cd 100644
--- a/src/main/java/org/olat/gui/control/OlatFooterController.java
+++ b/src/main/java/org/olat/gui/control/OlatFooterController.java
@@ -89,7 +89,7 @@ public class OlatFooterController extends BasicController {
 		if (!isGuest && ureq.getUserSession().isAuthenticated()) {
 			olatFootervc.contextPut("loggedIn", Boolean.TRUE);
 			if(isInvitee) {
-				olatFootervc.contextPut("username", translate("invitee"));
+				olatFootervc.contextPut("username", translate("logged.in.invitee"));
 			} else {
 				String fullName = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(ureq.getIdentity());
 				olatFootervc.contextPut("username", StringHelper.escapeHtml(fullName));
diff --git a/src/main/java/org/olat/gui/control/OlatTopNavController.java b/src/main/java/org/olat/gui/control/OlatTopNavController.java
index 6208c97dee62cbefaf51fe5deb4dc0bb6e9fc926..b1e5c9c2c4bbf8a60773931cf308e2a9a2ce6649 100644
--- a/src/main/java/org/olat/gui/control/OlatTopNavController.java
+++ b/src/main/java/org/olat/gui/control/OlatTopNavController.java
@@ -111,6 +111,8 @@ public class OlatTopNavController extends BasicController implements GenericEven
 		
 		boolean isGuest = ureq.getUserSession().getRoles().isGuestOnly();
 		boolean isInvitee = ureq.getUserSession().getRoles().isInvitee();
+		topNavVC.contextPut("isGuest", new Boolean(isGuest));
+		topNavVC.contextPut("isInvitee", new Boolean(isInvitee));
 		
 		// instant messaging area, only when enabled and user is not a guest user
 		if (CoreSpringFactory.getImpl(InstantMessagingModule.class).isEnabled() && !isGuest && !isInvitee) {
@@ -129,7 +131,6 @@ public class OlatTopNavController extends BasicController implements GenericEven
 		
 		// login link
 		if (ureq.getIdentity() == null) {
-			topNavVC.contextPut("isGuest", Boolean.TRUE);
 			loginLink = LinkFactory.createLink("topnav.login", topNavVC, this);
 			loginLink.setIconLeftCSS("o_icon o_icon_login o_icon-lg");
 			loginLink.setTooltip("topnav.login.alt");
diff --git a/src/main/java/org/olat/gui/control/_content/topnav.html b/src/main/java/org/olat/gui/control/_content/topnav.html
index f3df184bcc52d5a93719863fc91e1cc79d81f6f3..7ba647f149cfbc7c128b2584042d58be4f8a7056 100644
--- a/src/main/java/org/olat/gui/control/_content/topnav.html
+++ b/src/main/java/org/olat/gui/control/_content/topnav.html
@@ -3,7 +3,7 @@
 	#foreach($personalTool in $toolSet)
 		 <li class="o_navbar_tool">$r.render($personalTool)</li>
 	#end
-	#if (!$isGuest)
+	#if (!$isGuest && !$isInvitee)
 		<li id="o_navbar_my_menu" class="dropdown o_portrait">
 			<a id="o_sel_navbar_my_menu_caret" href="#" class="dropdown-toggle" data-toggle="dropdown">
 				$r.render("portrait")
@@ -12,7 +12,7 @@
 			</a>	
 			$r.render("myMenu")
 		</li>
-	#else
+	#elseif(!$isInvitee)
 		<li id="o_navbar_login">
 			$r.render("topnav.login")
 		</li>
diff --git a/src/main/java/org/olat/portfolio/EPMapOnInvitationExtension.java b/src/main/java/org/olat/portfolio/EPMapOnInvitationExtension.java
index 75c921bb70ad09a16af63f7db167aab1e727985a..ba697361c5cc1b32e5540f8c4484e363d68813b2 100644
--- a/src/main/java/org/olat/portfolio/EPMapOnInvitationExtension.java
+++ b/src/main/java/org/olat/portfolio/EPMapOnInvitationExtension.java
@@ -51,9 +51,11 @@ public class EPMapOnInvitationExtension {
 	
 	private static class MapOnInvitationContextEntryControllerCreator extends DefaultContextEntryControllerCreator {
 
+		private PortfolioStructureMap map;
+		
 		@Override
 		public ContextEntryControllerCreator clone() {
-			return this;
+			return new MapOnInvitationContextEntryControllerCreator();
 		}
 
 		@Override
@@ -74,7 +76,10 @@ public class EPMapOnInvitationExtension {
 		@Override
 		public String getTabName(ContextEntry ce, UserRequest ureq) {
 			PortfolioStructureMap map = getMapFromContext(ce);
-			return map.getTitle();
+			if(map != null) {
+				return map.getTitle();
+			}
+			return null;
 		}
 
 		@Override
@@ -83,9 +88,11 @@ public class EPMapOnInvitationExtension {
 				return false;
 			}
 			
-			final EPFrontendManager ePFMgr = (EPFrontendManager) CoreSpringFactory.getBean("epFrontendManager");
+			final EPFrontendManager ePFMgr = CoreSpringFactory.getImpl(EPFrontendManager.class);
 			PortfolioStructureMap map = getMapFromContext(ce);
-			if (map == null) return false;
+			if (map == null) {
+				return false;
+			}
 			boolean visible = ePFMgr.isMapVisible(ureq.getIdentity(), map.getOlatResource());
 			return visible;
 		}
@@ -95,11 +102,12 @@ public class EPMapOnInvitationExtension {
 		 * @return the loaded map or null if not found
 		 */
 		private PortfolioStructureMap getMapFromContext(final ContextEntry ce) {
-			final Long mapKey = ce.getOLATResourceable().getResourceableId();
-			final EPFrontendManager ePFMgr = (EPFrontendManager) CoreSpringFactory.getBean("epFrontendManager");
-			final PortfolioStructureMap map = (PortfolioStructureMap) ePFMgr.loadPortfolioStructureByKey(mapKey);
+			if(map == null) {
+				Long mapKey = ce.getOLATResourceable().getResourceableId();
+				EPFrontendManager ePFMgr = CoreSpringFactory.getImpl(EPFrontendManager.class);
+				map = (PortfolioStructureMap)ePFMgr.loadPortfolioStructureByKey(mapKey);
+			}
 			return map;
 		}
-		
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/portfolio/_spring/portfolioContext.xml b/src/main/java/org/olat/portfolio/_spring/portfolioContext.xml
index 344bcacab37c220ea4f1fc752ade20fa56553669..458ef955a35e7dc93edbb70c4d1dd2dfc71926fc 100644
--- a/src/main/java/org/olat/portfolio/_spring/portfolioContext.xml
+++ b/src/main/java/org/olat/portfolio/_spring/portfolioContext.xml
@@ -144,6 +144,36 @@
 		<property name="jobClass" value="org.olat.core.commons.services.scheduler.DummyJob" />
 	</bean>
 	
+	<bean id="invitationCleanupTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
+		<property name="jobDetail" ref="invitationCleanupJob.${cluster.singleton.services}" />
+		 <!-- adjust cron style syntax for your notification needs 
+		  	"0 10 0 * *"  e.g. 10 minutes after midnight
+		  	
+		  	A "Cron-Expression" is a string comprised of 6 or 7 fields separated by white space. The 6 mandatory and 1 optional fields are as follows:
+			Field Name 	  	Allowed Values 	  	Allowed Special Characters
+			Seconds 	  	0-59 	  			, - * /
+			Minutes 	  	0-59 	  			, - * /
+			Hours 	  		0-23 	  			, - * /
+			Day-of-month 	1-31 	  			, - * ? / L W C
+			Month 	  		1-12 or JAN-DEC 	, - * /
+			Day-of-Week 	1-7 or SUN-SAT 	  	, - * ? / L C #
+			Year (Optional)	empty, 1970-2099 	, - * /
+		
+			As of OLAT 6.3 it's best to let the cronjob run every two hours since users can now choose how often 
+			they will get notified. The shortest interval is set to two hours. 	    	
+		-->
+		<property name="cronExpression" value="0 2 */12 * * ?" />
+		<property name="startDelay" value="150000" />
+	</bean>
+
+	<bean id="invitationCleanupJob.enabled" class="org.springframework.scheduling.quartz.JobDetailBean" lazy-init="true">
+		<property name="jobClass" value="org.olat.portfolio.manager.InvitationCleanupJob" />
+	</bean>
+	<!-- dummy bean -->
+	<bean id="invitationCleanupJob.disabled" class="org.springframework.scheduling.quartz.JobDetailBean"  lazy-init="true">
+		<property name="jobClass" value="org.olat.core.commons.services.scheduler.DummyJob" />
+	</bean>
+	
 	<!--  hook to the personal menu -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension" name="personal.tool.ep" init-method="initExtensionPoints" >
 		<property name="order" value="301" />
diff --git a/src/main/java/org/olat/portfolio/manager/EPFrontendManager.java b/src/main/java/org/olat/portfolio/manager/EPFrontendManager.java
index 8c28a576e9b722e2e23409ea3c565a63218952f7..1b409e973c0b64f18954554993efe87fe4d0eedb 100755
--- a/src/main/java/org/olat/portfolio/manager/EPFrontendManager.java
+++ b/src/main/java/org/olat/portfolio/manager/EPFrontendManager.java
@@ -28,9 +28,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.Constants;
-import org.olat.basesecurity.GroupRoles;
-import org.olat.basesecurity.Policy;
+import org.olat.basesecurity.IdentityRef;
 import org.olat.basesecurity.manager.GroupDAO;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.tagging.manager.TaggingManager;
@@ -713,7 +711,8 @@ public class EPFrontendManager extends BasicManager {
 	 * @param type Limit maps to this or these types
 	 * @return
 	 */
-	public List<PortfolioStructure> getStructureElementsFromOthersWithoutPublic(Identity ident, Identity choosenOwner, ElementType... types){
+	public List<PortfolioStructure> getStructureElementsFromOthersWithoutPublic(IdentityRef ident, IdentityRef choosenOwner,
+			ElementType... types){
 		return structureManager.getStructureElementsFromOthersWithoutPublic(ident, choosenOwner, types);
 	}
 	
@@ -1054,23 +1053,16 @@ public class EPFrontendManager extends BasicManager {
 	 * @param ores
 	 * @return
 	 */
-	public boolean isMapVisible(Identity identity, OLATResourceable ores) {
+	public boolean isMapVisible(IdentityRef identity, OLATResourceable ores) {
 		return structureManager.isMapVisible(identity, ores);
 	}
 	
 	public boolean isMapShared(PortfolioStructureMap map) {
-		OLATResource resource = map.getOlatResource();
-		return isMapShared(resource);
+		return isMapShared(map.getOlatResource());
 	}
 		
 	public boolean isMapShared(OLATResource resource) {
-		List<Policy> policies = securityManager.getPoliciesOfResource(resource, null);
-		for(Policy policy:policies) {
-			if(policy.getPermission().contains(Constants.PERMISSION_READ)) {
-				return true;
-			}
-		}
-		return false;
+		return policyManager.isMapShared(resource);
 	}
 	
 	/**
@@ -1086,8 +1078,8 @@ public class EPFrontendManager extends BasicManager {
 	 * @param map
 	 * @param policyWrappers
 	 */
-	public void updateMapPolicies(PortfolioStructureMap map, List<EPMapPolicy> policyWrappers) {
-		policyManager.updateMapPolicies(map, policyWrappers);
+	public PortfolioStructureMap updateMapPolicies(PortfolioStructureMap map, List<EPMapPolicy> policyWrappers) {
+		return policyManager.updateMapPolicies(map, policyWrappers);
 	}
 	
 	/**
@@ -1110,7 +1102,7 @@ public class EPFrontendManager extends BasicManager {
 		AssessmentManager am = course.getCourseEnvironment().getAssessmentManager();
 		CourseNode courseNode = course.getRunStructure().getNode(resource.getSubPath());
 		
-		List<Identity> owners = groupDao.getMembers(submittedMap.getGroup(), GroupRoles.coach.name());
+		List<Identity> owners = policyManager.getOwners(submittedMap);
 		for(Identity owner:owners) {
 			if (courseNode != null) { // courseNode might have been deleted meanwhile
 				IdentityEnvironment ienv = new IdentityEnvironment(); 
@@ -1204,10 +1196,10 @@ public class EPFrontendManager extends BasicManager {
 	 * @return
 	 */
 	public String getAllOwnersAsString(PortfolioStructureMap map){
-		if(map.getGroup() == null) {
+		if(map.getGroups() == null) {
 			return null;
 		}
-		List<Identity> ownerIdents = groupDao.getMembers(map.getGroup(), GroupRoles.coach.name());
+		List<Identity> ownerIdents = policyManager.getOwners(map);
 		List<String> identNames = new ArrayList<String>();
 		for (Identity identity : ownerIdents) {
 			String fullName = userManager.getUserDisplayName(identity);
@@ -1225,10 +1217,10 @@ public class EPFrontendManager extends BasicManager {
 	 * @return
 	 */
 	public String getFirstOwnerAsString(PortfolioStructureMap map){
-		if(map.getGroup() == null) {
+		if(map.getGroups() == null) {
 			return "n/a";
 		}
-		List<Identity> ownerIdents = groupDao.getMembers(map.getGroup(), GroupRoles.coach.name());
+		List<Identity> ownerIdents = policyManager.getOwners(map);
 		if(ownerIdents.size() > 0){
 			Identity id = ownerIdents.get(0);
 			return userManager.getUserDisplayName(id);
@@ -1237,10 +1229,10 @@ public class EPFrontendManager extends BasicManager {
 	}
 	
 	public String getFirstOwnerAsString(EPMapShort map){
-		if(map.getGroup() == null) {
+		if(map.getGroups() == null) {
 			return "n/a";
 		}
-		List<Identity> ownerIdents = groupDao.getMembers(map.getGroup(), GroupRoles.coach.name());
+		List<Identity> ownerIdents = policyManager.getOwners(map);
 		if(ownerIdents.size() > 0){
 			Identity id = ownerIdents.get(0);
 			return userManager.getUserDisplayName(id);
@@ -1255,10 +1247,10 @@ public class EPFrontendManager extends BasicManager {
 	 * @return
 	 */
 	public Identity getFirstOwnerIdentity(PortfolioStructureMap map){
-		if(map.getGroup() == null) {
+		if(map.getGroups() == null) {
 			return null;
 		}
-		List<Identity> ownerIdents = groupDao.getMembers(map.getGroup(), GroupRoles.coach.name());
+		List<Identity> ownerIdents = policyManager.getOwners(map);
 		if (ownerIdents.size() > 0) {
 			Identity id = ownerIdents.get(0);
 			return id;
diff --git a/src/main/java/org/olat/portfolio/manager/EPMapPolicy.java b/src/main/java/org/olat/portfolio/manager/EPMapPolicy.java
index f3765e5bc7d998bb46cd3681e41a763174f5309b..0ea90843f3b757303280a06e36c06f6b80534b97 100644
--- a/src/main/java/org/olat/portfolio/manager/EPMapPolicy.java
+++ b/src/main/java/org/olat/portfolio/manager/EPMapPolicy.java
@@ -27,10 +27,10 @@ import java.util.List;
 import java.util.Map;
 
 import org.olat.basesecurity.Invitation;
-import org.olat.basesecurity.Policy;
 import org.olat.core.id.Identity;
 import org.olat.core.id.UserConstants;
 import org.olat.group.BusinessGroup;
+import org.olat.portfolio.model.structel.EPStructureElementToGroupRelation;
 
 /**
  * 
@@ -49,9 +49,9 @@ public class EPMapPolicy {
 	private Type type = Type.user;
 	
 	private Invitation invitation;
-	private List<Policy> policies;
-	private List<Identity> identities = new ArrayList<Identity>();
-	private List<BusinessGroup> groups = new ArrayList<BusinessGroup>();
+	private List<Identity> identities = new ArrayList<>();
+	private List<BusinessGroup> groups = new ArrayList<>();
+	private final List<EPStructureElementToGroupRelation> relations = new ArrayList<>();
 	
 	public Invitation getInvitation() {
 		return invitation;
@@ -61,22 +61,12 @@ public class EPMapPolicy {
 		this.invitation = invitation;
 	}
 
-	public List<Policy> getPolicies() {
-		if(policies == null) {
-			policies = new ArrayList<Policy>();
-		}
-		return policies;
+	public List<EPStructureElementToGroupRelation> getRelations() {
+		return relations;
 	}
 	
-	public void setPolicies(List<Policy> policies) {
-		this.policies = policies;
-	}
-	
-	public void addPolicy(Policy policy) {
-		if(policies == null) {
-			policies = new ArrayList<Policy>();
-		}
-		policies.add(policy);
+	public void addRelation(EPStructureElementToGroupRelation relation) {
+		relations.add(relation);
 	}
 
 	public Date getTo() {
@@ -150,11 +140,8 @@ public class EPMapPolicy {
 	}
 	
 	public void addGroup(BusinessGroup group) {
-		if(groups == null) {
-			groups = new ArrayList<BusinessGroup>();
-		}
 		for(BusinessGroup g:groups) {
-			if(g.equalsByPersistableKey(group)) {
+			if(g.equals(group)) {
 				return;
 			}
 		}
diff --git a/src/main/java/org/olat/portfolio/manager/EPPolicyManager.java b/src/main/java/org/olat/portfolio/manager/EPPolicyManager.java
index b5bfe3ec304c8592e0bf055767a3f1d9224c6b7f..0f323bc75f6d6b998ed344b450ba84e99bc37d70 100644
--- a/src/main/java/org/olat/portfolio/manager/EPPolicyManager.java
+++ b/src/main/java/org/olat/portfolio/manager/EPPolicyManager.java
@@ -20,19 +20,26 @@
 package org.olat.portfolio.manager;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.Set;
 
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.Constants;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.Invitation;
-import org.olat.basesecurity.Policy;
-import org.olat.basesecurity.SecurityGroup;
+import org.olat.basesecurity.manager.GroupDAO;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.core.manager.BasicManager;
 import org.olat.group.BusinessGroup;
-import org.olat.group.BusinessGroupService;
+import org.olat.group.manager.BusinessGroupDAO;
+import org.olat.portfolio.model.structel.EPMapShort;
+import org.olat.portfolio.model.structel.EPStructureElement;
+import org.olat.portfolio.model.structel.EPStructureElementToGroupRelation;
 import org.olat.portfolio.model.structel.PortfolioStructureMap;
+import org.olat.portfolio.model.structel.PortfolioStructureMapRef;
 import org.olat.resource.OLATResource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -48,72 +55,105 @@ import org.springframework.stereotype.Service;
 @Service("epPolicyManager")
 public class EPPolicyManager extends BasicManager {
 
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private GroupDAO groupDao;
+	@Autowired
+	private InvitationDAO invitationDao;
 	@Autowired
 	private BaseSecurity securityManager;
 	@Autowired
-	private BusinessGroupService businessGroupService;
+	private BusinessGroupDAO businessGroupDao;
+	
+	public List<Identity> getOwners(PortfolioStructureMapRef map) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select ident from ").append(EPMapShort.class.getName()).append(" as map")
+		  .append(" inner join map.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup ")
+		  .append(" inner join baseGroup.members as members on members.role='").append(GroupRoles.owner.name()).append("'")
+		  .append(" inner join members.identity as ident")
+		  .append(" where map.key=:mapKey");	
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Identity.class)
+				.setParameter("mapKey", map.getKey()).getResultList();
+	}
+	
+	public boolean isMapShared(OLATResource resource) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(relGroup) from ").append(EPMapShort.class.getName()).append(" as map")
+		  .append(" inner join map.groups as relGroup on relGroup.defaultGroup=false")
+		  .append(" where map.olatResource=:resource");	
+		
+		Number count = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Number.class)
+				.setParameter("resource", resource)
+				.getSingleResult();
+		return count == null ? false : count.intValue() > 0;
+	}
 	
 	/**
 	 * Return a list of wrapper containing the read policies of the map
 	 * @param map
 	 */
-	public List<EPMapPolicy> getMapPolicies(PortfolioStructureMap map) {
-		OLATResource resource = map.getOlatResource();
-		List<EPMapPolicy> policyWrappers = new ArrayList<EPMapPolicy>();
-		List<Policy> policies = securityManager.getPoliciesOfResource(resource, null);
-		for(Policy policy:policies) {
-			if(!policy.getPermission().contains(Constants.PERMISSION_READ)) {
+	public List<EPMapPolicy> getMapPolicies(PortfolioStructureMapRef mapRef) {
+		EPMapShort map = dbInstance.getCurrentEntityManager().find(EPMapShort.class, mapRef.getKey());
+		
+		List<EPMapPolicy> policies = new ArrayList<EPMapPolicy>();
+		Set<EPStructureElementToGroupRelation> relations = map.getGroups();
+		for(EPStructureElementToGroupRelation relation:relations) {
+			if(relation.isDefaultGroup()) {
 				continue;
 			}
 			
-			EPMapPolicy wrapper = getWrapperWithSamePolicy(policy, policyWrappers);
-			if(wrapper == null) {
-				wrapper = new EPMapPolicy();
-				wrapper.setTo(policy.getTo());
-				wrapper.setFrom(policy.getFrom());
-				policyWrappers.add(wrapper);
+			EPMapPolicy policy = getEquivalentWrapper(relation, policies);
+			if(policy == null) {
+				policy = new EPMapPolicy();
+				policy.setTo(relation.getValidTo());
+				policy.setFrom(relation.getValidFrom());
+				policies.add(policy);
 			}
 
-			String permission = policy.getPermission();
-			SecurityGroup secGroup = policy.getSecurityGroup();
-			if(permission.startsWith(EPMapPolicy.Type.user.name())) {
-				List<Identity> identities = securityManager.getIdentitiesOfSecurityGroup(secGroup);
-				wrapper.addPolicy(policy);
-				wrapper.setType(EPMapPolicy.Type.user);
-				wrapper.addIdentities(identities);
-			} else if (permission.startsWith(EPMapPolicy.Type.group.name())) {
-				wrapper.addPolicy(policy);
-				BusinessGroup group = null;//TODO group businessGroupService.findBusinessGroup(policy.getSecurityGroup());
-				wrapper.addGroup(group);
-				wrapper.setType(EPMapPolicy.Type.group);
-			} else if (permission.startsWith(EPMapPolicy.Type.invitation.name())) {
-				wrapper.addPolicy(policy);
-				Invitation invitation = securityManager.findInvitation(policy.getSecurityGroup());
-				wrapper.setInvitation(invitation);
-				wrapper.setType(EPMapPolicy.Type.invitation);
-			} else if (permission.startsWith(EPMapPolicy.Type.allusers.name())) {
-				wrapper.addPolicy(policy);
-				wrapper.setType(EPMapPolicy.Type.allusers);
+			String role = relation.getRole();
+			if(role.startsWith(EPMapPolicy.Type.user.name())) {
+				List<Identity> identities = groupDao.getMembers(relation.getGroup(), GroupRoles.participant.name());
+
+				policy.addRelation(relation);
+				policy.setType(EPMapPolicy.Type.user);
+				policy.addIdentities(identities);
+			} else if (role.startsWith(EPMapPolicy.Type.group.name())) {
+				policy.addRelation(relation);
+				BusinessGroup group = businessGroupDao.findBusinessGroup(relation.getGroup());
+				policy.addGroup(group);
+				policy.setType(EPMapPolicy.Type.group);
+			} else if (role.startsWith(EPMapPolicy.Type.invitation.name())) {
+				policy.addRelation(relation);
+				Invitation invitation = invitationDao.findInvitation(relation.getGroup());
+				policy.setInvitation(invitation);
+				policy.setType(EPMapPolicy.Type.invitation);
+			} else if (role.startsWith(EPMapPolicy.Type.allusers.name())) {
+				policy.addRelation(relation);
+				policy.setType(EPMapPolicy.Type.allusers);
 			}
 		}
 		
-		return policyWrappers;
+		return policies;
 	}
 	
-	private EPMapPolicy getWrapperWithSamePolicy(Policy policy, List<EPMapPolicy> policyWrappers) {
-		Date to = policy.getTo();
-		Date from = policy.getFrom();
-		String permission = policy.getPermission();
+	private EPMapPolicy getEquivalentWrapper(EPStructureElementToGroupRelation relation, List<EPMapPolicy> policies) {
+		Date to = relation.getValidTo();
+		Date from = relation.getValidFrom();
+		String role = relation.getRole();
 		
 		a_a:
-		for(EPMapPolicy wrapper:policyWrappers) {
-			for(Policy p:wrapper.getPolicies()) {
-				if(!permission.equals(p.getPermission())) {
+		for(EPMapPolicy policy:policies) {
+			for(EPStructureElementToGroupRelation p:policy.getRelations()) {
+				if(!role.equals(p.getRole())) {
 					continue a_a;
 				}
-				if(from == null && p.getFrom() == null || (from != null && p.getFrom() != null && from.equals(p.getFrom()))) {	
-					if(to == null && p.getTo() == null || (to != null && p.getTo() != null && to.equals(p.getTo()))) {
-						return wrapper;
+				if(from == null && p.getValidFrom() == null || (from != null && p.getValidFrom() != null && from.equals(p.getValidFrom()))) {	
+					if(to == null && p.getValidTo() == null || (to != null && p.getValidTo() != null && to.equals(p.getValidTo()))) {
+						return policy;
 					}
 				}
 			}
@@ -124,114 +164,109 @@ public class EPPolicyManager extends BasicManager {
 	/**
 	 * Update the map policies of a map. The missing policies are deleted!
 	 * @param map
-	 * @param policyWrappers
+	 * @param policies
 	 */
-	public void updateMapPolicies(PortfolioStructureMap map, List<EPMapPolicy> policyWrappers) {
-		List<Policy> currentPolicies = securityManager.getPoliciesOfResource(map.getOlatResource(), null);
-		List<Policy> savedPolicies = new ArrayList<Policy>();
-		for(EPMapPolicy wrapper:policyWrappers) {
-			savedPolicies.addAll(applyPolicy(wrapper, map, currentPolicies));
+	public PortfolioStructureMap updateMapPolicies(PortfolioStructureMap map, List<EPMapPolicy> policies) {
+		map = dbInstance.getCurrentEntityManager().merge(map);
+
+		List<EPStructureElementToGroupRelation> savedPolicies = new ArrayList<EPStructureElementToGroupRelation>();
+		for(EPMapPolicy wrapper:policies) {
+			savedPolicies.addAll(applyPolicy(wrapper, map));
 		}
 		
-		for(Policy currentPolicy:currentPolicies) {
-			boolean inUse = false;
-			for(Policy savedPolicy:savedPolicies) {
-				if(currentPolicy.equalsByPersistableKey(savedPolicy)) {
-					inUse = true;
-					break;
-				}
+		Collection<EPStructureElementToGroupRelation> currentRelations = new ArrayList<>(map.getGroups());
+		for(EPStructureElementToGroupRelation currentRelation:currentRelations) {
+			if(currentRelation.isDefaultGroup()) {
+				continue;
 			}
 			
-			if(!inUse && currentPolicy.getPermission().contains(Constants.PERMISSION_READ)) {
-				deletePolicy(currentPolicy);
+			boolean inUse = savedPolicies.contains(currentRelation);
+			if(!inUse) {
+				map.getGroups().remove(currentRelation);
 			}
 		}
+		return dbInstance.getCurrentEntityManager().merge(map);
 	}
 	
-	private void deletePolicy(Policy policy) {
-		if(policy.getPermission().contains(Constants.PERMISSION_READ)) {
-			String permission = policy.getPermission();
-			securityManager.deletePolicy(policy.getSecurityGroup(), permission, policy.getOlatResource());
-			if("invitation_read".equals(permission)) {
-				Invitation invitation = securityManager.findInvitation(policy.getSecurityGroup());
-				securityManager.deleteInvitation(invitation);
-			}
-		}
-	}
-	
-	private List<Policy> applyPolicy(EPMapPolicy wrapper, PortfolioStructureMap map, List<Policy> currentPolicies) {
-		List<Policy> policies = wrapper.getPolicies();
-		List<Policy> savedPolicies = new ArrayList<Policy>();
-		switch(wrapper.getType()) {
+	private List<EPStructureElementToGroupRelation> applyPolicy(EPMapPolicy policy, PortfolioStructureMap map) {
+		List<EPStructureElementToGroupRelation> savedPolicies = new ArrayList<EPStructureElementToGroupRelation>();
+		switch(policy.getType()) {
 			case user:
-				Policy policy = (policies == null || policies.isEmpty()) ? null : policies.get(0);
-				if(policy == null) {
-					SecurityGroup secGroup = securityManager.createAndPersistSecurityGroup();
-					policy = securityManager.createAndPersistPolicy(secGroup, wrapper.getType() + "_" + Constants.PERMISSION_READ, wrapper.getFrom(), wrapper.getTo(), map.getOlatResource());
-				} else {
-					Policy currentPolicy = reusePolicyInSession(policy, currentPolicies);
-					securityManager.updatePolicy(currentPolicy, wrapper.getFrom(), wrapper.getTo());
-				}
-				SecurityGroup secGroup = policy.getSecurityGroup();
-				List<Object[]> allIdents = securityManager.getIdentitiesAndDateOfSecurityGroup(secGroup);
-				for (Object[] objects : allIdents) {
-					Identity identity = (Identity) objects[0];
-					securityManager.removeIdentityFromSecurityGroup(identity, secGroup);
-				}
-				for(Identity identity:wrapper.getIdentities()) {
-					if(!securityManager.isIdentityInSecurityGroup(identity, secGroup)) {
-						securityManager.addIdentityToSecurityGroup(identity, secGroup);
-					}
-				}
-				savedPolicies.add(policy);
+				savedPolicies.add(applyPolicyToUsers(policy, map));
 				break;
 			case group:
-				for(BusinessGroup group:wrapper.getGroups()) {
-					//TODO group savedPolicies.add(applyPolicyTo(group.getPartipiciantGroup(), wrapper, map));
-					//TODO group savedPolicies.add(applyPolicyTo(group.getOwnerGroup(), wrapper, map));
+				for(BusinessGroup group:policy.getGroups()) {
+					savedPolicies.add(applyPolicyToGroup(group.getBaseGroup(), policy, map));
 				}
 				break;
 			case invitation:
-				Invitation invitation = wrapper.getInvitation();
-				Policy invitationPolicy = applyPolicyTo(invitation, wrapper, map);
+				Invitation invitation = policy.getInvitation();
+				EPStructureElementToGroupRelation invitationPolicy = applyPolicyToInvitation(invitation, policy, map);
 				savedPolicies.add(invitationPolicy);
 				break;
 			case allusers:
-				Policy allUsersPolicy = applyPolicyToAllUsers(wrapper, map);
+				EPStructureElementToGroupRelation allUsersPolicy = applyPolicyToAllUsers(policy, map);
 				savedPolicies.add(allUsersPolicy);
 				break;
 		}
 		return savedPolicies;
 	}
 	
-	private Policy applyPolicyToAllUsers(EPMapPolicy wrapper, PortfolioStructureMap map) {
-		SecurityGroup allUsers = securityManager.findSecurityGroupByName(Constants.GROUP_OLATUSERS);
-		List<Policy> currentPolicies = securityManager.getPoliciesOfResource(map.getOlatResource(), allUsers);
-		if(!currentPolicies.isEmpty()) {
-			Policy currentPolicy = currentPolicies.get(0);
-			securityManager.updatePolicy(currentPolicy, wrapper.getFrom(), wrapper.getTo());
-			return currentPolicy;
+	private EPStructureElementToGroupRelation applyPolicyToAllUsers(EPMapPolicy wrapper, PortfolioStructureMap map) {
+		List<EPStructureElementToGroupRelation> currentRelations = wrapper.getRelations();
+		if(!currentRelations.isEmpty()) {
+			EPStructureElementToGroupRelation currentRelation = currentRelations.get(0);
+			updatePolicy(currentRelation, wrapper.getFrom(), wrapper.getTo());
+			return currentRelation;
 		}
-		
-		Policy policy = securityManager.createAndPersistPolicy(allUsers, wrapper.getType() + "_" + Constants.PERMISSION_READ, wrapper.getFrom(), wrapper.getTo(), map.getOlatResource());
-		return policy;
+		return createBaseRelation(map, null, EPMapPolicy.Type.allusers.name(), wrapper.getFrom(), wrapper.getTo());
 	}
 	
-	private Policy applyPolicyTo(Invitation invitation, EPMapPolicy wrapper, PortfolioStructureMap map) {
-		List<Policy> currentPolicies = securityManager.getPoliciesOfSecurityGroup(invitation.getSecurityGroup());
-		for(Policy currentPolicy:currentPolicies) {
-			if(currentPolicy.getOlatResource().equalsByPersistableKey(map.getOlatResource())) {
-				currentPolicy = reusePolicyInSession(currentPolicy, currentPolicies);
-				securityManager.updatePolicy(currentPolicy, wrapper.getFrom(), wrapper.getTo());
-				securityManager.updateInvitation(invitation);
-				return currentPolicy;
+	private EPStructureElementToGroupRelation applyPolicyToUsers(EPMapPolicy policy, PortfolioStructureMap map) {
+		List<EPStructureElementToGroupRelation> currentRelations = policy.getRelations();
+		EPStructureElementToGroupRelation relation = (currentRelations == null || currentRelations.isEmpty()) ? null : currentRelations.get(0);
+		if(relation == null) {
+			Group secGroup = groupDao.createGroup();
+			relation = createBaseRelation(map, secGroup, EPMapPolicy.Type.user.name(), policy.getFrom(), policy.getTo());
+			for(Identity identity:policy.getIdentities()) {
+				groupDao.addMembership(secGroup, identity, GroupRoles.participant.name());
+			}
+		} else {
+			EPStructureElementToGroupRelation currentPolicy = reusePolicyInSession(relation, map);
+			updatePolicy(currentPolicy, policy.getFrom(), policy.getTo());
+			
+			Group secGroup = relation.getGroup();
+			List<Identity> currentMembers = groupDao.getMembers(secGroup, GroupRoles.participant.name());
+			List<Identity> newMembers = new ArrayList<>(policy.getIdentities());
+			for (Identity newMember:policy.getIdentities()) {
+				if(currentMembers.contains(newMember)) {
+					newMembers.remove(newMember);
+					currentMembers.remove(newMember);
+				}
+			}
+			
+			for(Identity currentMember:currentMembers) {
+				groupDao.removeMembership(secGroup, currentMember);
+			}
+			for(Identity newMember:newMembers) {
+				groupDao.addMembership(secGroup, newMember, GroupRoles.participant.name());
+			}
+		}
+		return relation;
+	}
+
+	private EPStructureElementToGroupRelation applyPolicyToInvitation(Invitation invitation, EPMapPolicy policy, PortfolioStructureMap map) {
+		invitation = dbInstance.getCurrentEntityManager().merge(invitation);
+		Group secGroup = invitation.getBaseGroup();
+		Collection<EPStructureElementToGroupRelation> currentRelations = map.getGroups();
+		for(EPStructureElementToGroupRelation currentRelation:currentRelations) {
+			if(secGroup.equals(currentRelation.getGroup())) {
+				updatePolicy(currentRelation, policy.getFrom(), policy.getTo());
+				return currentRelation;
 			}
 		}
 		
-		SecurityGroup secGroup = invitation.getSecurityGroup();
-		Policy policy = securityManager.createAndPersistPolicy(secGroup, wrapper.getType() + "_" + Constants.PERMISSION_READ, wrapper.getFrom(), wrapper.getTo(), map.getOlatResource());
-		securityManager.updateInvitation(invitation);
-		return policy;
+		return createBaseRelation(map, secGroup, EPMapPolicy.Type.invitation.name(), policy.getFrom(), policy.getTo());
 	}
 	
 	/**
@@ -241,27 +276,43 @@ public class EPPolicyManager extends BasicManager {
 	 * @param currentPolicies
 	 * @return
 	 */
-	private Policy reusePolicyInSession(Policy policy, List<Policy> currentPolicies) {
-		for(Policy currentPolicy:currentPolicies) {
-			if(policy.equalsByPersistableKey(currentPolicy)) {
-				return currentPolicy;
+	private EPStructureElementToGroupRelation reusePolicyInSession(EPStructureElementToGroupRelation relation, PortfolioStructureMap map) {
+		Collection<EPStructureElementToGroupRelation> currentRelations = map.getGroups();
+		for(EPStructureElementToGroupRelation currentRelation:currentRelations) {
+			if(relation.equalsByPersistableKey(currentRelation)) {
+				return currentRelation;
 			}
 		}
-		return policy;
+		return relation;
 	}
 	
-	private Policy applyPolicyTo(SecurityGroup secGroup, EPMapPolicy wrapper, PortfolioStructureMap map) {
-		List<Policy> currentPolicies = securityManager.getPoliciesOfSecurityGroup(secGroup);
-		for(Policy currentPolicy:currentPolicies) {
-			if(currentPolicy.getOlatResource().equalsByPersistableKey(map.getOlatResource())) {
-				currentPolicy = reusePolicyInSession(currentPolicy, currentPolicies);
-				securityManager.updatePolicy(currentPolicy, wrapper.getFrom(), wrapper.getTo());
-				return currentPolicy;
+	private EPStructureElementToGroupRelation applyPolicyToGroup(Group group, EPMapPolicy policy, PortfolioStructureMap map) {
+		Collection<EPStructureElementToGroupRelation> currentRelations = map.getGroups();
+		for(EPStructureElementToGroupRelation currentRelation:currentRelations) {
+			if(currentRelation.getGroup().equals(group)) {
+				updatePolicy(currentRelation, policy.getFrom(), policy.getTo());
+				return currentRelation;
 			}
 		}
-		
-		Policy policy = securityManager.createAndPersistPolicy(secGroup, wrapper.getType() + "_read", wrapper.getFrom(), wrapper.getTo(), map.getOlatResource());
-		return policy;
+		return createBaseRelation(map, group, EPMapPolicy.Type.group.name(), policy.getFrom(), policy.getTo());
+	}
+	
+	private void updatePolicy(EPStructureElementToGroupRelation relation, Date from, Date to) {
+		relation.setValidFrom(from);
+		relation.setValidTo(to);
 	}
 	
-}
+	private EPStructureElementToGroupRelation createBaseRelation(PortfolioStructureMap map, Group group, String role, Date from, Date to) {
+		//create security group
+		EPStructureElementToGroupRelation relation = new EPStructureElementToGroupRelation();
+		relation.setDefaultGroup(false);
+		relation.setCreationDate(new Date());
+		relation.setGroup(group);
+		relation.setStructureElement((EPStructureElement)map);
+		relation.setRole(role);
+		relation.setValidFrom(from);
+		relation.setValidTo(to);
+		map.getGroups().add(relation);
+		return relation;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/portfolio/manager/EPStructureManager.java b/src/main/java/org/olat/portfolio/manager/EPStructureManager.java
index fc17aa6c5017fa65240e670636a5285b354e594a..7fad716dd5018310ae1843e49907953cc1bb7f1f 100755
--- a/src/main/java/org/olat/portfolio/manager/EPStructureManager.java
+++ b/src/main/java/org/olat/portfolio/manager/EPStructureManager.java
@@ -24,17 +24,17 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import javax.persistence.TypedQuery;
 
 import org.hibernate.ObjectNotFoundException;
-import org.olat.basesecurity.Constants;
 import org.olat.basesecurity.Group;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.IdentityRef;
-import org.olat.basesecurity.NamedGroupImpl;
 import org.olat.basesecurity.PolicyImpl;
 import org.olat.basesecurity.SecurityGroupImpl;
 import org.olat.basesecurity.SecurityGroupMembershipImpl;
@@ -60,6 +60,7 @@ import org.olat.portfolio.model.structel.EPDefaultMap;
 import org.olat.portfolio.model.structel.EPMapShort;
 import org.olat.portfolio.model.structel.EPPage;
 import org.olat.portfolio.model.structel.EPStructureElement;
+import org.olat.portfolio.model.structel.EPStructureElementToGroupRelation;
 import org.olat.portfolio.model.structel.EPStructureToArtefactLink;
 import org.olat.portfolio.model.structel.EPStructureToStructureLink;
 import org.olat.portfolio.model.structel.EPStructuredMap;
@@ -182,7 +183,8 @@ public class EPStructureManager extends BasicManager {
 	protected List<PortfolioStructure> getStructureElementsForUser(IdentityRef ident, ElementType... types){
 		StringBuilder sb = new StringBuilder();
 		sb.append("select stEl from ").append(EPStructureElement.class.getName()).append(" as stEl")
-		  .append(" inner join fetch stEl.group as baseGroup")
+		  .append(" inner join stEl.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" where exists (select membership from bgroupmember as membership " )
 		  .append("    where baseGroup=membership.group and membership.identity.key=:identityKey and membership.role='").append(GroupRoles.owner.name()).append("'")
 		  .append(" )");
@@ -212,10 +214,12 @@ public class EPStructureManager extends BasicManager {
 	protected boolean isMapOwner(IdentityRef identity, OLATResourceable ores) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(stEl) from ").append(EPStructureElement.class.getName()).append(" stEl ")
+		  .append(" inner join stEl.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" where stEl.olatResource.resId=:resourceableId")
 		  .append(" and stEl.olatResource.resName=:resourceableTypeName")
 		  .append(" and exists (select membership from bgroupmember as membership " )
-		  .append("   where stEl.group=membership.group and membership.identity.key=:identityKey and membership.role='").append(GroupRoles.owner.name()).append("'")
+		  .append("   where baseGroup=membership.group and membership.identity.key=:identityKey and membership.role='").append(GroupRoles.owner.name()).append("'")
 		  .append(" )");
 		
 		Number count =	dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class)
@@ -232,34 +236,28 @@ public class EPStructureManager extends BasicManager {
 	 * @param ores
 	 * @return
 	 */
-	protected boolean isMapVisible(Identity identity, OLATResourceable ores) {
+	protected boolean isMapVisible(IdentityRef identity, OLATResourceable ores) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select count(stEl) from ").append(EPStructureElement.class.getName()).append(" stEl ")
-			.append(" inner join stEl.olatResource as oRes ")
-			.append(" inner join stEl.group as baseGroup ")
+		sb.append("select count(stEl) from ").append(EPStructureElement.class.getName()).append(" stEl")
+			.append(" inner join stEl.olatResource as oRes")
+			.append(" inner join stEl.groups as relGroup")
 			.append(" where oRes.resId=:resourceableId and oRes.resName=:resourceableTypeName")
-			.append(" and ( oRes in ( " )
-			.append("  select policy.olatResource from")
-			.append("  ").append(PolicyImpl.class.getName()).append(" as policy, ")
-			.append("  ").append(SecurityGroupImpl.class.getName()).append(" as sgi,") 
-			.append("  ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
-			.append("  where sgi = policy.securityGroup")//implicit inner join
-			.append("  and (sgmsi.securityGroup = sgi and sgmsi.identity =:identity) ")//member of the security group
-			.append("  and (policy.from is null or policy.from<=:date)")
-			.append("  and (policy.to is null or policy.to>=:date)")
-			.append(" )")
-			.append(" or exists (select membership from bgroupmember as membership " )
-			.append("   where membership.group=baseGroup and membership.identity.key=:identity")
-			.append(" ))");
+			.append(" and (relGroup.validFrom is null or relGroup.validFrom<=:date)")
+			.append(" and (relGroup.validTo is null or relGroup.validTo>=:date)")
+			.append(" and (relGroup.role='").append(EPMapPolicy.Type.allusers.name()).append("'")
+			.append("   or exists (select membership from bgroupmember as membership " )
+			.append("     where membership.group=relGroup.group and membership.identity.key=:identityKey")
+			.append("   )")
+			.append(" )");
 
-		DBQuery query =	dbInstance.createQuery(sb.toString());
-		query.setEntity("identity", identity);
-		query.setLong("resourceableId", ores.getResourceableId());
-		query.setString("resourceableTypeName", ores.getResourceableTypeName());
-		query.setDate("date", new Date());
-	
-		Number count = (Number)query.uniqueResult();
-		return count.intValue() == 1;
+		Number count = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Number.class)
+				.setParameter("identityKey", identity.getKey())
+				.setParameter("resourceableId", ores.getResourceableId())
+				.setParameter("resourceableTypeName", ores.getResourceableTypeName())
+				.setParameter("date", new Date())
+				.getSingleResult();
+		return count == null ? false : count.intValue() > 0;
 	}
 	
 	/**
@@ -278,9 +276,10 @@ public class EPStructureManager extends BasicManager {
 			  .append(" inner join stEl.olatResource as oRes ");
 		} else {
 			sb.append("select stEl from ").append(EPStructureElement.class.getName()).append(" stEl ")
-			  .append(" inner join fetch stEl.olatResource as oRes ")
-			  .append(" inner join fetch stEl.group as baseGroup ");
+			  .append(" inner join fetch stEl.olatResource as oRes ");
 		}
+		sb.append(" inner join stEl.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup");
 
 		sb.append(" where oRes in ( ").append("  select policy.olatResource from").append("  ").append(PolicyImpl.class.getName()).append(" as policy, ").append("  ")
 				.append(SecurityGroupImpl.class.getName()).append(" as sgi,").append("  ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
@@ -288,8 +287,8 @@ public class EPStructureManager extends BasicManager {
 				.append("  and (sgmsi.securityGroup = sgi and sgmsi.identity =:ident) ")// member of the security group
 				.append("  and (policy.from is null or policy.from<=:date)").append("  and (policy.to is null or policy.to>=:date)").append(" )");
 		// remove owner
-		sb.append(" and stEl.group not in ( ").append("select sgi2 from bgroup as sgi2, bgroupmember as sgmsi2 ")
-		  .append("   where sgmsi2.group=sgi2 and sgmsi2.identity=:ident")
+		sb.append(" and not exists ( ").append("select sgi2 from bgroup as sgi2, bgroupmember as sgmsi2 ")
+		  .append("   where baseGroup=sgi2 and sgmsi2.group=sgi2 and sgmsi2.identity=:ident")
 		  .append(" )");
 
 		if (choosenOwner != null) {
@@ -342,35 +341,20 @@ public class EPStructureManager extends BasicManager {
 		return query.getResultList();
 	}
 	
-	protected List<PortfolioStructure> getStructureElementsFromOthersWithoutPublic(Identity ident, Identity choosenOwner, ElementType... types){
+	protected List<PortfolioStructure> getStructureElementsFromOthersWithoutPublic(IdentityRef ident, IdentityRef choosenOwner, ElementType... types){
 		StringBuilder sb = new StringBuilder();
-		sb.append("select stEl from ").append(EPStructureElement.class.getName()).append(" stEl ");
-		sb.append(" inner join stEl.olatResource as oRes ");
-		sb.append(" where oRes in ( " )
-			.append("  select policy.olatResource from")
-			.append("  ").append(PolicyImpl.class.getName()).append(" as policy, ")
-			.append("  ").append(SecurityGroupImpl.class.getName()).append(" as sgi,")
-			.append("  ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
-			.append("  where sgi = policy.securityGroup ") //implicit inner join
-			.append("  and sgi = policy.securityGroup ") 
-			.append("  and (sgmsi.securityGroup = sgi and sgmsi.identity =:ident) ") //member of the security group
-			.append("  and (policy.from is null or policy.from<=:date)")
-			.append("  and (policy.to is null or policy.to>=:date)")
-			.append("  and sgi not in (")
-			.append("    select ngroup.securityGroup from ")
-			.append("    ").append(NamedGroupImpl.class.getName()).append(" as ngroup ")
-			.append("    where ngroup.groupName =:usersGroup")
-			.append("   )")			
-			.append(" )");
-		
-		//remove owner
-		sb.append(" and stEl.group not in (select sgi2 from bgroup as sgi2, bgroupmember as sgmsi2 ")
-		  .append("   where sgmsi2.group=sgi2 and sgmsi2.identity=:ident")
-		  .append(" )");
+		sb.append("select stEl from ").append(EPStructureElement.class.getName()).append(" stEl ")
+		  .append(" inner join fetch stEl.olatResource as oRes ")
+		  .append(" inner join stEl.groups as relGroup on relGroup.defaultGroup=false")
+		  .append(" inner join relGroup.group as baseGroup")
+		  .append(" inner join baseGroup.members as members")
+		  .append(" where members.identity.key=:identityKey")
+		  .append(" and (relGroup.validFrom is null or relGroup.validFrom<=:date)")
+		  .append(" and (relGroup.validTo is null or relGroup.validTo>=:date)");
 		
 		if(choosenOwner != null) {
-			sb.append(" and stEl.group in (select sgi from bgroup  as sgi, bgroupmember as sgmsi ")
-			  .append("   where sgmsi.group=sgi and sgmsi.identity=:owner")
+			sb.append(" and exists (select sgi from bgroupmember as sgmsi ")
+			  .append("   where sgmsi.group=baseGroup and sgmsi.identity.key=:ownerKey")
 			  .append(" )");
 		}
 		if(types != null && types.length > 0) {
@@ -384,20 +368,17 @@ public class EPStructureManager extends BasicManager {
 			sb.append(")");
 		}
 		
-		DBQuery query =	dbInstance.createQuery(sb.toString());
-		query.setEntity("ident", ident);
-		query.setString("usersGroup", Constants.GROUP_OLATUSERS);
-		query.setDate("date", new Date());
+		TypedQuery<PortfolioStructure> query =dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), PortfolioStructure.class)
+				.setParameter("identityKey", ident.getKey())
+				.setParameter("date", new Date());
 		if(choosenOwner != null) {
-			query.setEntity("owner", choosenOwner);
+			query.setParameter("ownerKey", choosenOwner.getKey());
 		}
 		
-		@SuppressWarnings("unchecked")
-		List<PortfolioStructure> pStructs = query.list();
-		return pStructs;
+		return query.getResultList();
 	}
 	
-	
 	private Class<?> getImplementation(ElementType type) {
 		switch(type) {
 			case DEFAULT_MAP: return EPDefaultMap.class;
@@ -1035,16 +1016,23 @@ public class EPStructureManager extends BasicManager {
 		struct = (EPStructureElement) dbInstance.loadObject((EPStructureElement)struct);
 		dbInstance.deleteObject(struct);		
 		if (struct instanceof EPAbstractMap){
-			EPAbstractMap esmap = (EPAbstractMap)struct;
-			Group group = esmap.getGroup();
-			if (group != null) {
-				groupDao.removeGroup(group);
-			}
+			removeBaseGroup((EPAbstractMap)struct);
 		}
 		
 		resourceManager.deleteOLATResourceable(struct);
 	}
 	
+	private void removeBaseGroup(EPAbstractMap map) {
+		Set<EPStructureElementToGroupRelation> relations = map.getGroups();
+		if (relations != null) {
+			for(EPStructureElementToGroupRelation relation:relations) {
+				if(relation.isDefaultGroup()) {
+					groupDao.removeGroup(relation.getGroup());
+				}
+				dbInstance.getCurrentEntityManager().remove(relation);
+			}
+		}
+	}
 	
 	/**
 	 * Move a structure element up in the list
@@ -1381,17 +1369,19 @@ public class EPStructureManager extends BasicManager {
 
 		StringBuilder sb = new StringBuilder();
 		sb.append("select map from ").append(EPStructuredMap.class.getName()).append(" map")
-		  .append(" inner join fetch map.group as baseGroup")
+		  .append(" left join fetch map.targetResource as targetResource")
+		  .append(" inner join map.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" where map.structuredMapSource=:template");
 		if (targetOres != null) {
-			sb.append(" and map.targetResource.resourceableId=:resourceId")
-			  .append(" and map.targetResource.resourceableTypeName=:resourceType");
+			sb.append(" and targetResource.resourceableId=:resourceId")
+			  .append(" and targetResource.resourceableTypeName=:resourceType");
 		}
 		if (targetSubPath != null) {
-			sb.append(" and map.targetResource.subPath=:subPath");
+			sb.append(" and targetResource.subPath=:subPath");
 		}
 		if (targetBusinessPath != null) {
-			sb.append(" and map.targetResource.businessPath=:businessPath");
+			sb.append(" and targetResource.businessPath=:businessPath");
 		}
 		sb.append(" and exists (select membership from bgroupmember as membership " )
 		  .append("    where baseGroup=membership.group and membership.identity.key=:identityKey and membership.role='").append(GroupRoles.owner.name()).append("'")
@@ -1430,7 +1420,8 @@ public class EPStructureManager extends BasicManager {
 
 		StringBuilder sb = new StringBuilder();
 		sb.append("select map from ").append(EPStructuredMap.class.getName()).append(" map")
-		  .append(" inner join fetch map.group as baseGroup")
+		  .append(" inner join map.groups as relGroup on relGroup.defaultGroup=true")
+		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" inner join fetch map.targetResource as targetResource")
 		  .append(" where targetResource.resourceableId=:resourceId and targetResource.resourceableTypeName=:resourceType");
 
@@ -1539,7 +1530,6 @@ public class EPStructureManager extends BasicManager {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select element from ").append(EPStructureElement.class.getName()).append(" element")
 		  .append(" left join fetch element.olatResource as oRes")
-		  .append(" left join fetch element.group as baseGroup")
 		  .append(" where element.key=:key");;
 		
 		List<PortfolioStructure> resources = dbInstance.getCurrentEntityManager()
@@ -1641,8 +1631,11 @@ public class EPStructureManager extends BasicManager {
 		fillStructureElement(el, title, description);
 		
 		//create security group
-		Group ownerGroup = createBaseGroup(identity);
-		el.setGroup(ownerGroup);
+		EPStructureElementToGroupRelation ownerGroup = createBaseGroup(el, identity);
+		Set<EPStructureElementToGroupRelation> relations = new HashSet<>();
+		relations.add(ownerGroup);
+		el.setGroups(relations);
+		
 		return el;
 	}
 	
@@ -1652,8 +1645,10 @@ public class EPStructureManager extends BasicManager {
 		fillStructureElement(el, title, description);
 
 		//create security group
-		Group ownerGroup = createBaseGroup(identity);
-		el.setGroup(ownerGroup);
+		EPStructureElementToGroupRelation ownerGroup = createBaseGroup(el, identity);
+		Set<EPStructureElementToGroupRelation> relations = new HashSet<>();
+		relations.add(ownerGroup);
+		el.setGroups(relations);
 		
 		return el;
 	}
@@ -1699,7 +1694,11 @@ public class EPStructureManager extends BasicManager {
 		dbInstance.commit();
 		
 		Group ownerGroup = repositoryService.getDefaultGroup(re);
-		el.setGroup(ownerGroup);
+		
+		EPStructureElementToGroupRelation relation = createBaseRelation(el, ownerGroup);
+		Set<EPStructureElementToGroupRelation> relations = new HashSet<>();
+		relations.add(relation);
+		el.setGroups(relations);
 		return el;
 	}
 	
@@ -1719,8 +1718,12 @@ public class EPStructureManager extends BasicManager {
 		el.setStyle(((EPStructureElement)root).getStyle()); 
 		importEPStructureElementRecursively((EPStructureElement)root, el);
 		
-		//create an empty security group
-		el.setGroup(groupDao.createGroup());
+		//create an empty group
+		Group ownerGroup = groupDao.createGroup();
+		EPStructureElementToGroupRelation relation = createBaseRelation(el, ownerGroup);
+		Set<EPStructureElementToGroupRelation> relations = new HashSet<>();
+		relations.add(relation);
+		el.setGroups(relations);
 		
 		return el;
 	}
@@ -1770,7 +1773,11 @@ public class EPStructureManager extends BasicManager {
 			group = groupDao.createGroup();
 			groupDao.addMembership(group, identity, GroupRoles.owner.name());
 		}
-		el.setGroup(group);
+		
+		EPStructureElementToGroupRelation relation = createBaseRelation(el, group);
+		Set<EPStructureElementToGroupRelation> relations = new HashSet<>();
+		relations.add(relation);
+		el.setGroups(relations);
 		dbInstance.saveObject(el);
 		return el;
 	}
@@ -1812,11 +1819,26 @@ public class EPStructureManager extends BasicManager {
 		return addedEntry;
 	}
 	
-	private Group createBaseGroup(Identity author) {
+	private EPStructureElementToGroupRelation createBaseGroup(EPStructureElement element, Identity author) {
 		//create security group
 		Group ownerGroup = groupDao.createGroup();
+		EPStructureElementToGroupRelation relation = new EPStructureElementToGroupRelation();
+		relation.setDefaultGroup(true);
+		relation.setCreationDate(new Date());
+		relation.setGroup(ownerGroup);
+		relation.setStructureElement(element);
 		groupDao.addMembership(ownerGroup, author, GroupRoles.owner.name());
-		return ownerGroup;
+		return relation;
+	}
+	
+	private EPStructureElementToGroupRelation createBaseRelation(EPStructureElement element, Group ownerGroup) {
+		//create security group
+		EPStructureElementToGroupRelation relation = new EPStructureElementToGroupRelation();
+		relation.setDefaultGroup(true);
+		relation.setCreationDate(new Date());
+		relation.setGroup(ownerGroup);
+		relation.setStructureElement(element);
+		return relation;
 	}
 
 	/**
diff --git a/src/main/java/org/olat/portfolio/manager/EPXStreamHandler.java b/src/main/java/org/olat/portfolio/manager/EPXStreamHandler.java
index d82a06176a12c0f796b62cd8a1399c22478c0d0b..2e9b149319566c91bcc3b6db615b23c52b1bc0d0 100644
--- a/src/main/java/org/olat/portfolio/manager/EPXStreamHandler.java
+++ b/src/main/java/org/olat/portfolio/manager/EPXStreamHandler.java
@@ -116,7 +116,7 @@ public class EPXStreamHandler {
 
 			PortfolioStructure struct = (PortfolioStructure) myStream.fromXML(buffer.toString());
 			// OLAT-6344: reset ownerGroup from earlier exports. A new group is created by import in ePFMgr.importPortfolioMapTemplate() later on anyway.
-			((EPAbstractMap) struct).setGroup(null); 
+			((EPAbstractMap) struct).setGroups(null); 
 			return struct;
 		} catch (Exception e) {
 			log.error("Cannot export this map: " + fMapXml, e);
diff --git a/src/main/java/org/olat/basesecurity/InvitationCleanupJob.java b/src/main/java/org/olat/portfolio/manager/InvitationCleanupJob.java
similarity index 90%
rename from src/main/java/org/olat/basesecurity/InvitationCleanupJob.java
rename to src/main/java/org/olat/portfolio/manager/InvitationCleanupJob.java
index fe204a518ab5450d3f8857b63647f34d7e0b455a..bbcc1f0242eab397dc6b9b8cd250aab96f768e4a 100644
--- a/src/main/java/org/olat/basesecurity/InvitationCleanupJob.java
+++ b/src/main/java/org/olat/portfolio/manager/InvitationCleanupJob.java
@@ -17,7 +17,7 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.basesecurity;
+package org.olat.portfolio.manager;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.scheduler.JobWithDB;
@@ -39,8 +39,8 @@ public class InvitationCleanupJob extends JobWithDB {
 	public void executeWithDB(JobExecutionContext context) {
 		try {
 			log.info("Starting invitation clean up job");
-			BaseSecurity securityManager = (BaseSecurity)CoreSpringFactory.getBean("baseSecurityManager");
-			securityManager.cleanUpInvitations();
+			InvitationDAO invitationDao = CoreSpringFactory.getImpl(InvitationDAO.class);
+			invitationDao.cleanUpInvitations();
 		} catch (Exception e) {
 			// ups, something went completely wrong! We log this but continue next time
 			log.error("Error while cleaning up invitation", e);
diff --git a/src/main/java/org/olat/portfolio/manager/InvitationDAO.java b/src/main/java/org/olat/portfolio/manager/InvitationDAO.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ef57eb5e481691e0bcacb74f1eae8dffbb3586d
--- /dev/null
+++ b/src/main/java/org/olat/portfolio/manager/InvitationDAO.java
@@ -0,0 +1,241 @@
+/**
+ * <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.portfolio.manager;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+
+import javax.persistence.TypedQuery;
+
+import org.olat.admin.user.delete.service.UserDeletionManager;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.Constants;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.IdentityRef;
+import org.olat.basesecurity.Invitation;
+import org.olat.basesecurity.PolicyImpl;
+import org.olat.basesecurity.SecurityGroup;
+import org.olat.basesecurity.manager.GroupDAO;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.core.id.User;
+import org.olat.portfolio.model.InvitationImpl;
+import org.olat.user.UserManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * This is only for e-Portfolio. For wider useage, need a refactor of the datamodel
+ * and of process and workflow. Don't be afraid of reference ot e-Portfolio datamodel
+ * here.
+ * 
+ * 
+ * Initial date: 25.06.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service(value="invitationDao")
+public class InvitationDAO {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private GroupDAO groupDao;
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private BaseSecurity securityManager;
+	
+	/**
+	 * Create and persist an invitation with its security group and security token.
+	 * @return
+	 */
+	public Invitation createAndPersistInvitation() {
+		Group group = groupDao.createGroup();
+		
+		InvitationImpl invitation = new InvitationImpl();
+		invitation.setToken(UUID.randomUUID().toString());
+		invitation.setBaseGroup(group);
+		dbInstance.getCurrentEntityManager().persist(invitation);
+		return invitation;
+	}
+	
+	public Invitation update(Invitation invitation) {
+		return dbInstance.getCurrentEntityManager().merge(invitation);
+	}
+	
+	public Identity createIdentityFrom(Invitation invitation, Locale locale) {
+		String tempUsername = UUID.randomUUID().toString();
+		User user = userManager.createAndPersistUser(invitation.getFirstName(), invitation.getLastName(), invitation.getMail());
+		user.getPreferences().setLanguage(locale.toString());
+		Identity invitee = securityManager.createAndPersistIdentity(tempUsername, user, null, null, null);
+		groupDao.addMembership(invitation.getBaseGroup(), invitee, GroupRoles.invitee.name());
+		return invitee;
+	}
+	
+	/**
+	 * Is the invitation linked to any valid policies
+	 * @param token
+	 * @param atDate
+	 * @return
+	 */
+	public boolean hasInvitations(String token, Date atDate) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(relation) from structuretogroup as relation ")
+		  .append(" inner join relation.group as baseGroup")
+	      .append(" where exists (select invitation.key from binvitation as invitation where ")
+	      .append("  invitation.baseGroup=baseGroup and invitation.token=:token")
+	      .append(" )");
+		  
+		if(atDate != null) {
+			sb.append(" and (relation.validFrom is null or relation.validFrom<=:date)")
+			  .append(" and (relation.validTo is null or relation.validTo>=:date)");
+		}
+
+		TypedQuery<Number> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Number.class)
+				.setParameter("token", token);
+		if(atDate != null) {
+		  	query.setParameter("date", atDate);
+		}
+		  
+		Number counter = query.getSingleResult();
+	    return counter == null ? false : counter.intValue() > 0;
+	}
+	
+	/**
+	 * Find an invitation by its security group
+	 * @param secGroup
+	 * @return The invitation or null if not found
+	 */
+	public Invitation findInvitation(Group group) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select invitation from binvitation as invitation ")
+		  .append(" where invitation.baseGroup=:group");
+
+		List<Invitation> invitations = dbInstance.getCurrentEntityManager()
+				  .createQuery(sb.toString(), Invitation.class)
+				  .setParameter("group", group)
+				  .getResultList();
+		if(invitations.isEmpty()) return null;
+		return invitations.get(0);
+	}
+	
+	/**
+	 * Find an invitation by its security token
+	 * @param token
+	 * @return The invitation or null if not found
+	 */
+	public Invitation findInvitation(String token) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select invitation from binvitation as invitation ")
+		  .append(" where invitation.token=:token");
+
+		List<Invitation> invitations = dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), Invitation.class)
+			.setParameter("token", token)
+			.getResultList();
+	    return invitations.isEmpty() ? null : invitations.get(0);
+	}
+	
+	/**
+	 * Check if the identity has an invitation, valid or not
+	 * @param identity
+	 * @return
+	 */
+	public boolean isInvitee(IdentityRef identity) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select count(invitation) from binvitation as invitation")
+		  .append(" inner join invitation.baseGroup as baseGroup ")
+		  .append(" inner join baseGroup.members as members")
+		  .append(" inner join members.identity as ident")
+		  .append(" where ident.key=:identityKey");
+		  
+		Number invitations = dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), Number.class)
+			.setParameter("identityKey", identity.getKey())
+			.getSingleResult();
+	    return invitations == null ? false : invitations.intValue() > 0;
+	}
+	
+	/**
+	 * Delete an invitation
+	 * @param invitation
+	 */
+	public void deleteInvitation(Invitation invitation) {
+		//fxdiff: FXOLAT-251: nothing persisted, nothing to delete
+		if(invitation == null || invitation.getKey() == null) return;
+		dbInstance.getCurrentEntityManager().remove(invitation);
+	}
+	
+	/**
+	 * Clean up old invitation and set to deleted temporary users
+	 */
+	public void cleanUpInvitations() {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(new Date());
+		Date currentTime = cal.getTime();
+		cal.add(Calendar.HOUR, -6);
+		Date dateLimit = cal.getTime();
+
+		StringBuilder sb = new StringBuilder();
+		sb.append("select invitation from ").append(InvitationImpl.class.getName()).append(" as invitation ")
+		  .append(" inner join invitation.securityGroup secGroup ")
+		  .append(" where invitation.creationDate<:dateLimit")//someone can create an invitation but not add it to a policy within millisecond
+		  .append(" and secGroup not in (")
+      //select all valid policies from this security group
+		  .append("  select policy.securityGroup from ").append(PolicyImpl.class.getName()).append(" as policy ")
+		  .append("   where (policy.from is null or policy.from<=:currentDate)")
+		  .append("   and (policy.to is null or policy.to>=:currentDate)")
+		  .append("  )");
+
+		List<Invitation> oldInvitations = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Invitation.class)
+				.setParameter("currentDate", currentTime)
+				.setParameter("dateLimit", dateLimit)
+				.getResultList();
+		
+		if(oldInvitations.isEmpty()) {
+			return;
+		}
+	  
+		SecurityGroup olatUserSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_OLATUSERS);
+		for(Invitation invitation:oldInvitations) {
+			List<Identity> identities = groupDao.getMembers(invitation.getBaseGroup(), GroupRoles.invitee.name());
+			//normally only one identity
+			for(Identity identity:identities) {
+				if(identity.getStatus().compareTo(Identity.STATUS_VISIBLE_LIMIT) >= 0) {
+	  			//already deleted
+				} else if(securityManager.isIdentityInSecurityGroup(identity, olatUserSecGroup)) {
+	  			//out of scope
+				} else {
+	  			//delete user
+					UserDeletionManager.getInstance().deleteIdentity(identity);
+				}
+			}
+			dbInstance.getCurrentEntityManager().remove(invitation);
+			dbInstance.commit();
+		}
+	}
+}
diff --git a/src/main/java/org/olat/portfolio/model/InvitationImpl.java b/src/main/java/org/olat/portfolio/model/InvitationImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..30578fb2b1cf47aebcc356d0111fc4cf73b968d8
--- /dev/null
+++ b/src/main/java/org/olat/portfolio/model/InvitationImpl.java
@@ -0,0 +1,154 @@
+/**
+ * <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.portfolio.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.Invitation;
+import org.olat.basesecurity.model.GroupImpl;
+import org.olat.core.id.CreateInfo;
+import org.olat.core.id.Persistable;
+
+/**
+ * 
+ * Description:<br>
+ * Implementation of Invitation
+ * 
+ * <P>
+ * Initial Date:  10 nov. 2010 <br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+@Entity(name="binvitation")
+@Table(name="o_bs_invitation")
+public class InvitationImpl implements CreateInfo, Persistable, Invitation {
+
+	private static final long serialVersionUID = -9122616013810215550L;
+	
+	@Id
+	@GeneratedValue(generator = "system-uuid")
+	@GenericGenerator(name = "system-uuid", strategy = "hilo")
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+
+	@Column(name="token", nullable=true, unique=true, insertable=true, updatable=true)
+	private String token;
+	@Column(name="first_name", nullable=true, unique=true, insertable=true, updatable=true)
+	private String firstName;
+	@Column(name="last_name", nullable=true, unique=true, insertable=true, updatable=true)
+	private String lastName;
+	@Column(name="mail", nullable=true, unique=true, insertable=true, updatable=true)
+	private String mail;
+
+	@ManyToOne(targetEntity=GroupImpl.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_group_id", nullable=false, insertable=true, updatable=false)
+	private Group baseGroup;
+	
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	@Override
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public String getToken() {
+		return token;
+	}
+
+	public void setToken(String token) {
+		this.token = token;
+	}
+	
+	public String getFirstName() {
+		return firstName;
+	}
+
+	public void setFirstName(String firstName) {
+		this.firstName = firstName;
+	}
+
+	public String getLastName() {
+		return lastName;
+	}
+
+	public void setLastName(String lastName) {
+		this.lastName = lastName;
+	}
+
+	public String getMail() {
+		return mail;
+	}
+
+	public void setMail(String mail) {
+		this.mail = mail;
+	}
+
+	public Group getBaseGroup() {
+		return baseGroup;
+	}
+
+	public void setBaseGroup(Group baseGroup) {
+		this.baseGroup = baseGroup;
+	}
+
+	@Override
+	public int hashCode() {
+		return key == null ? -98260 : key.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		} else if (obj instanceof InvitationImpl) {
+			InvitationImpl invitation = (InvitationImpl)obj;
+			return getKey() != null && getKey().equals(invitation.getKey());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+}
diff --git a/src/main/java/org/olat/portfolio/model/structel/EPAbstractMap.java b/src/main/java/org/olat/portfolio/model/structel/EPAbstractMap.java
index f54b776327311afcee7a36a4d4efb505aa2298f2..4931adffb860cd037373725742201c1a65444e12 100755
--- a/src/main/java/org/olat/portfolio/model/structel/EPAbstractMap.java
+++ b/src/main/java/org/olat/portfolio/model/structel/EPAbstractMap.java
@@ -19,7 +19,7 @@
  */
 package org.olat.portfolio.model.structel;
 
-import org.olat.basesecurity.Group;
+import java.util.Set;
 
 /**
  * 
@@ -34,14 +34,15 @@ public abstract class EPAbstractMap extends EPStructureElement implements Portfo
 
 	private static final long serialVersionUID = 3295737167134638317L;
 	
-	private Group group;
+	private Set<EPStructureElementToGroupRelation> groups;
 
-	public Group getGroup() {
-		return group;
+	@Override
+	public Set<EPStructureElementToGroupRelation> getGroups() {
+		return groups;
 	}
 
-	public void setGroup(Group group) {
-		this.group = group;
+	public void setGroups(Set<EPStructureElementToGroupRelation> groups) {
+		this.groups = groups;
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/portfolio/model/structel/EPMapShort.java b/src/main/java/org/olat/portfolio/model/structel/EPMapShort.java
index 1851578092e4666b6993f2e298e28266b483895d..046d043eae52abecac71e127fb4e801024f649e3 100644
--- a/src/main/java/org/olat/portfolio/model/structel/EPMapShort.java
+++ b/src/main/java/org/olat/portfolio/model/structel/EPMapShort.java
@@ -19,7 +19,8 @@
  */
 package org.olat.portfolio.model.structel;
 
-import org.olat.basesecurity.Group;
+import java.util.Set;
+
 import org.olat.core.commons.persistence.PersistentObject;
 import org.olat.resource.OLATResource;
 
@@ -29,19 +30,15 @@ import org.olat.resource.OLATResource;
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
-public class EPMapShort extends PersistentObject {
+public class EPMapShort extends PersistentObject implements PortfolioStructureMapRef {
 
 	private static final long serialVersionUID = 3093838342982364478L;
 	
 	private String title;
 	private Long sourceMapKey;
 	private OLATResource olatResource;
-	private Group group;
+	private Set<EPStructureElementToGroupRelation> groups;
 	
-	public EPMapShort() {
-		//
-	}
-
 	
 	public Long getSourceMapKey() {
 		return sourceMapKey;
@@ -67,12 +64,12 @@ public class EPMapShort extends PersistentObject {
 		this.olatResource = olatResource;
 	}
 
-	public Group getGroup() {
-		return group;
+	public Set<EPStructureElementToGroupRelation> getGroups() {
+		return groups;
 	}
 
-	public void setGroup(Group group) {
-		this.group = group;
+	public void setGroups(Set<EPStructureElementToGroupRelation> groups) {
+		this.groups = groups;
 	}
 	
 	@Override
diff --git a/src/main/java/org/olat/portfolio/model/structel/EPStructureElementToGroupRelation.java b/src/main/java/org/olat/portfolio/model/structel/EPStructureElementToGroupRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..9dd0f3632b56da8fc6c166bf618ceb0ab26613bc
--- /dev/null
+++ b/src/main/java/org/olat/portfolio/model/structel/EPStructureElementToGroupRelation.java
@@ -0,0 +1,179 @@
+/**
+ * <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.portfolio.model.structel;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.model.GroupImpl;
+import org.olat.core.id.Persistable;
+
+/**
+ * 
+ * Initial date: 24.06.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="structuretogroup")
+@Table(name="o_ep_struct_to_group")
+public class EPStructureElementToGroupRelation implements Persistable {
+
+	private static final long serialVersionUID = 2215547264646107606L;
+
+	@Id
+	@GeneratedValue(generator = "system-uuid")
+	@GenericGenerator(name = "system-uuid", strategy = "hilo")
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+
+	@Column(name="r_defgroup", nullable=false, insertable=true, updatable=false)
+	private boolean defaultGroup = false;
+	
+	@Column(name="r_role", nullable=true, insertable=true, updatable=true)
+	private String role;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="r_valid_from", nullable=true, insertable=true, updatable=true)
+	private Date validFrom;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="r_valid_to", nullable=true, insertable=true, updatable=true)
+	private Date validTo;
+	
+	@ManyToOne(targetEntity=GroupImpl.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_group_id", nullable=true, insertable=true, updatable=false)
+	private Group group;
+
+	@ManyToOne(targetEntity=EPStructureElement.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_struct_id", nullable=true, insertable=true, updatable=true)//updatable need for deletion
+	private EPStructureElement structureElement;
+	
+	public Long getKey() {
+		return key;
+	}
+
+	public void setKey(Long key) {
+		this.key = key;
+	}
+
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public boolean isDefaultGroup() {
+		return defaultGroup;
+	}
+
+	public void setDefaultGroup(boolean defaultGroup) {
+		this.defaultGroup = defaultGroup;
+	}
+
+	public String getRole() {
+		return role;
+	}
+
+	public void setRole(String role) {
+		this.role = role;
+	}
+
+	public Date getValidFrom() {
+		return validFrom;
+	}
+
+	public void setValidFrom(Date validFrom) {
+		this.validFrom = validFrom;
+	}
+
+	public Date getValidTo() {
+		return validTo;
+	}
+
+	public void setValidTo(Date validTo) {
+		this.validTo = validTo;
+	}
+
+	public Group getGroup() {
+		return group;
+	}
+
+	public void setGroup(Group group) {
+		this.group = group;
+	}
+
+	public EPStructureElement getStructureElement() {
+		return structureElement;
+	}
+
+	public void setStructureElement(EPStructureElement entry) {
+		this.structureElement = entry;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("reToGroup[resource=")
+			.append(structureElement.getKey()).append(":")
+			.append("group=").append(group.getKey())
+			.append("]");
+		return sb.toString();
+	}
+	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 29061 : getKey().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof EPStructureElementToGroupRelation) {
+			EPStructureElementToGroupRelation rel = (EPStructureElementToGroupRelation)obj;
+			return getKey() != null && getKey().equals(rel.getKey());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMap.java b/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMap.java
index cc3f1c65d6a2e85fa014a96eea0f3326e9491d23..787a1e201434e9c596d031f73e9154895f40b708 100755
--- a/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMap.java
+++ b/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMap.java
@@ -19,7 +19,7 @@
  */
 package org.olat.portfolio.model.structel;
 
-import org.olat.basesecurity.Group;
+import java.util.Set;
 
 /** 
  * Description:<br>
@@ -28,10 +28,10 @@ import org.olat.basesecurity.Group;
  * Initial Date:  08.06.2010 <br>
  * @author rhaag
  */
-public interface PortfolioStructureMap extends PortfolioStructure {
+public interface PortfolioStructureMap extends PortfolioStructure, PortfolioStructureMapRef {
 	//marker interface
 	
-	public Group getGroup();
+	public Set<EPStructureElementToGroupRelation> getGroups();
 	
 	public String getStatus();
 }
diff --git a/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMapRef.java b/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMapRef.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2f0ec30372d4ee831b13b7eb48a9e30d0b772d1
--- /dev/null
+++ b/src/main/java/org/olat/portfolio/model/structel/PortfolioStructureMapRef.java
@@ -0,0 +1,32 @@
+/**
+ * <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.portfolio.model.structel;
+
+/**
+ * 
+ * Initial date: 24.06.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface PortfolioStructureMapRef {
+	
+	public Long getKey();
+
+}
diff --git a/src/main/java/org/olat/portfolio/model/structel/StructureElement.hbm.xml b/src/main/java/org/olat/portfolio/model/structel/StructureElement.hbm.xml
index 59f3d7f2a2ee856c365df71e721fc1d5d921a421..fdb67cbe7ded6cb83c45e6630f0a9253bde9170e 100644
--- a/src/main/java/org/olat/portfolio/model/structel/StructureElement.hbm.xml
+++ b/src/main/java/org/olat/portfolio/model/structel/StructureElement.hbm.xml
@@ -64,12 +64,11 @@
 		</subclass>
 		
 		<subclass name="org.olat.portfolio.model.structel.EPDefaultMap" discriminator-value="default-map">  
-			<many-to-one name="group"
-                   column="fk_group_id"
-                   class="org.olat.basesecurity.model.GroupImpl" 
-                   outer-join="true"   
-                   unique="true" 
-                   cascade="none"/>
+     
+			<set name="groups" cascade="all,delete-orphan">
+            	<key column="fk_struct_id"/>
+            	<one-to-many class="org.olat.portfolio.model.structel.EPStructureElementToGroupRelation"/>
+        	</set>  
 		
 		</subclass>
 		
@@ -87,12 +86,10 @@
 				<property  name="businessPath" column="target_businesspath" length="2048" type="string" />
 			</component>
                    
-			<many-to-one name="group"
-                   column="fk_group_id"
-                   class="org.olat.basesecurity.model.GroupImpl" 
-                   outer-join="true"   
-                   unique="true" 
-                   cascade="none"/>
+			<set name="groups" cascade="all,delete-orphan">
+            	<key column="fk_struct_id"/>
+            	<one-to-many class="org.olat.portfolio.model.structel.EPStructureElementToGroupRelation"/>
+        	</set> 
 		
 			<many-to-one name="structuredMapSource"
                    column="fk_map_source_id"
@@ -105,12 +102,11 @@
 		
 		<subclass name="org.olat.portfolio.model.structel.EPStructuredMapTemplate" discriminator-value="template-map">
      
-			<many-to-one name="group"
-                   column="fk_group_id"
-                   class="org.olat.basesecurity.model.GroupImpl" 
-                   outer-join="true"   
-                   unique="true" 
-                   cascade="none"/>
+			<set name="groups" cascade="all,delete-orphan">
+            	<key column="fk_struct_id"/>
+            	<one-to-many class="org.olat.portfolio.model.structel.EPStructureElementToGroupRelation"/>
+        	</set> 
+        	
 		</subclass>
 	</class>
 
@@ -154,12 +150,10 @@
 		<property name="sourceMapKey" column="fk_map_source_id" type="long"/>
 		<property name="title" column="title" type="string"/>
 
-		<many-to-one name="group"
-                   column="fk_group_id"
-                   class="org.olat.basesecurity.model.GroupImpl" 
-                   outer-join="true"   
-                   unique="true" 
-                   cascade="none"/>
+		<set name="groups">
+            <key column="fk_struct_id"/>
+            <one-to-many class="org.olat.portfolio.model.structel.EPStructureElementToGroupRelation"/>
+        </set> 
 
 		<many-to-one name="olatResource"
                  column="fk_olatresource"
diff --git a/src/main/java/org/olat/portfolio/ui/EPMapRunController.java b/src/main/java/org/olat/portfolio/ui/EPMapRunController.java
index 436bee5c19e28792ae7300162b5607d97ae4cced..941f3594adf90ffe2bf9d3c9e1edefacfc9d428d 100755
--- a/src/main/java/org/olat/portfolio/ui/EPMapRunController.java
+++ b/src/main/java/org/olat/portfolio/ui/EPMapRunController.java
@@ -236,6 +236,7 @@ public class EPMapRunController extends BasicController implements Activateable2
 			}
 			createMapBox.deactivate();
 			popDownCreateMapBox();
+			toogleHeader(false);
 		} else if (source == searchTemplateCtrl) {
 			if(event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) {
 				RepositoryEntry repoEntry = searchTemplateCtrl.getSelectedEntry();
@@ -279,7 +280,7 @@ public class EPMapRunController extends BasicController implements Activateable2
 		String title = translate("create.map");
 		createMapCtrl = new EPCreateMapController(ureq, getWindowControl());
 		listenTo(createMapCtrl);
-		createMapBox = new CloseableModalController(getWindowControl(), title, createMapCtrl.getInitialComponent());
+		createMapBox = new CloseableModalController(getWindowControl(), title, createMapCtrl.getInitialComponent(), true, title);
 		createMapBox.setCustomWindowCSS("o_sel_add_map_window");
 		listenTo(createMapBox);
 		createMapBox.activate();
@@ -292,7 +293,7 @@ public class EPMapRunController extends BasicController implements Activateable2
 		searchTemplateCtrl = new ReferencableEntriesSearchController(getWindowControl(), ureq,
 				new String[]{EPTemplateMapResource.TYPE_NAME}, commandLabel, false, false, false, false, false);			
 		listenTo(searchTemplateCtrl);
-		createMapBox = new CloseableModalController(getWindowControl(), title, searchTemplateCtrl.getInitialComponent());
+		createMapBox = new CloseableModalController(getWindowControl(), title, searchTemplateCtrl.getInitialComponent(), true, title);
 		createMapBox.setCustomWindowCSS("o_sel_add_map_template_window");
 		listenTo(createMapBox);
 		createMapBox.activate();
diff --git a/src/main/java/org/olat/portfolio/ui/structel/EPCreateMapController.java b/src/main/java/org/olat/portfolio/ui/structel/EPCreateMapController.java
index eea6b0087299f70a3bc73df84414c71ce9df6679..c705184f440c4c43878f353f9002f435691589e4 100644
--- a/src/main/java/org/olat/portfolio/ui/structel/EPCreateMapController.java
+++ b/src/main/java/org/olat/portfolio/ui/structel/EPCreateMapController.java
@@ -71,7 +71,6 @@ public class EPCreateMapController extends FormBasicController {
 				formLayout, getWindowControl());
 		descEl.setNotLongerThanCheck(2047, "map.description.too.long");
 
-		uifactory.addSpacerElement("spacer", formLayout, true);
 		uifactory.addFormSubmitButton("save.and.open.map", formLayout);
 	}
 	
diff --git a/src/main/java/org/olat/portfolio/ui/structel/EPShareListController.java b/src/main/java/org/olat/portfolio/ui/structel/EPShareListController.java
index 4e65c0b536f66ef498c941290ece9aa08882f34a..cc2c5c29ba540a988cd2ecd23552eaf01fabe324 100644
--- a/src/main/java/org/olat/portfolio/ui/structel/EPShareListController.java
+++ b/src/main/java/org/olat/portfolio/ui/structel/EPShareListController.java
@@ -27,14 +27,12 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.olat.admin.user.UserSearchController;
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.Constants;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.Invitation;
 import org.olat.basesecurity.SecurityGroup;
 import org.olat.basesecurity.events.MultiIdentityChosenEvent;
 import org.olat.basesecurity.events.SingleIdentityChosenEvent;
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -73,10 +71,12 @@ import org.olat.group.ui.main.SelectBusinessGroupController;
 import org.olat.portfolio.manager.EPFrontendManager;
 import org.olat.portfolio.manager.EPMapPolicy;
 import org.olat.portfolio.manager.EPMapPolicy.Type;
+import org.olat.portfolio.manager.InvitationDAO;
 import org.olat.portfolio.model.structel.EPStructuredMap;
 import org.olat.portfolio.model.structel.PortfolioStructure;
 import org.olat.portfolio.model.structel.PortfolioStructureMap;
 import org.olat.user.UserManager;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
@@ -90,12 +90,22 @@ import org.olat.user.UserManager;
 public class EPShareListController extends FormBasicController {
 	
 	private final List<EPSharePolicyWrapper> policyWrappers = new ArrayList<EPSharePolicyWrapper>();
-	private final PortfolioStructureMap map;
-	private final EPFrontendManager ePFMgr;
-	private final BaseSecurity securityManager;
-	private final BusinessGroupService businessGroupService;
-	private final UserManager userManager;
-	private final MailManager mailManager;
+	
+	private PortfolioStructureMap map;
+	
+	@Autowired
+	private EPFrontendManager ePFMgr;
+	@Autowired
+	private InvitationDAO invitationDao;
+	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
+	private BusinessGroupService businessGroupService;
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private MailManager mailManager;
+	
 	private final String[] targetKeys = EPMapPolicy.Type.names();
 	private final String[] targetValues = new String[targetKeys.length];
 	private final AtomicInteger cmpSuffixGenerator = new AtomicInteger(1);
@@ -106,15 +116,12 @@ public class EPShareListController extends FormBasicController {
 	
 	private FormLink addPolicyButton;
 	
+	
+	
 	public EPShareListController(UserRequest ureq, WindowControl wControl, PortfolioStructureMap map) {
 		super(ureq, wControl, "shareList");
 		
 		this.map = map;
-		ePFMgr = CoreSpringFactory.getImpl(EPFrontendManager.class);
-		securityManager = BaseSecurityManager.getInstance();
-		userManager = UserManager.getInstance();
-		mailManager = CoreSpringFactory.getImpl(MailManager.class);
-		businessGroupService = CoreSpringFactory.getImpl(BusinessGroupService.class);
 		for(int i=targetKeys.length; i-->0; ) {
 			targetValues[i] = translate("map.share.to." + targetKeys[i]);
 		}
@@ -509,7 +516,7 @@ public class EPShareListController extends FormBasicController {
 					case invitation:
 						Invitation invitation = policyWrapper.getInvitation();
 						if(invitation == null) {
-							invitation = securityManager.createAndPersistInvitation();
+							invitation = invitationDao.createAndPersistInvitation();
 							policyWrapper.setInvitation(invitation);
 						}
 						
diff --git a/src/main/java/org/olat/portfolio/ui/structel/EPSharePolicyWrapper.java b/src/main/java/org/olat/portfolio/ui/structel/EPSharePolicyWrapper.java
index ea1a85977e92705cf2422c413d1737c34c30b905..764ddff23ecf2ce05567343d3759af06ab7d27f0 100644
--- a/src/main/java/org/olat/portfolio/ui/structel/EPSharePolicyWrapper.java
+++ b/src/main/java/org/olat/portfolio/ui/structel/EPSharePolicyWrapper.java
@@ -23,7 +23,6 @@ import java.util.Date;
 import java.util.List;
 
 import org.olat.basesecurity.Invitation;
-import org.olat.basesecurity.Policy;
 import org.olat.core.gui.components.form.flexible.elements.DateChooser;
 import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
 import org.olat.core.gui.components.form.flexible.elements.TextElement;
@@ -32,6 +31,7 @@ import org.olat.core.id.Identity;
 import org.olat.core.util.StringHelper;
 import org.olat.group.BusinessGroup;
 import org.olat.portfolio.manager.EPMapPolicy;
+import org.olat.portfolio.model.structel.EPStructureElementToGroupRelation;
 
 /**
  * 
@@ -88,16 +88,12 @@ public class EPSharePolicyWrapper {
 		mapPolicy.setInvitation(invitation);
 	}
 	
-	public List<Policy> getPolicies() {
-		return mapPolicy.getPolicies();
+	public List<EPStructureElementToGroupRelation> getRelations() {
+		return mapPolicy.getRelations();
 	}
 	
-	public void setPolicies(List<Policy> policies) {
-		mapPolicy.setPolicies(policies);
-	}
-	
-	public void addPolicy(Policy policy) {
-		mapPolicy.addPolicy(policy);
+	public void addRelation(EPStructureElementToGroupRelation relation) {
+		mapPolicy.addRelation(relation);
 	}
 
 	public Date getTo() {
@@ -123,7 +119,7 @@ public class EPSharePolicyWrapper {
 	public void setType(EPMapPolicy.Type type) {
 		if(!type.equals(mapPolicy.getType())) {
 			mapPolicy.setType(type);
-			mapPolicy.getPolicies().clear();
+			mapPolicy.getRelations().clear();
 		}
 	}
 
diff --git a/src/main/java/org/olat/repository/handlers/PortfolioHandler.java b/src/main/java/org/olat/repository/handlers/PortfolioHandler.java
index 3a2e4cea3474661b7973a723798a001dabf51034..aef07492e8e257d300c38d26ab018287462faabe 100644
--- a/src/main/java/org/olat/repository/handlers/PortfolioHandler.java
+++ b/src/main/java/org/olat/repository/handlers/PortfolioHandler.java
@@ -192,7 +192,7 @@ public class PortfolioHandler implements RepositoryHandler {
 		PortfolioStructure map = ePFMgr.loadPortfolioStructure(res);
 		if(map != null) {
 			//owner group has its constraints shared beetwen the repository entry and the template
-			((EPAbstractMap)map).setGroup(null);
+			((EPAbstractMap)map).setGroups(null);
 		}
 		if(map instanceof EPStructuredMapTemplate) {
 			EPStructuredMapTemplate exercise = (EPStructuredMapTemplate)map;
diff --git a/src/main/java/org/olat/search/service/document/PortfolioMapDocument.java b/src/main/java/org/olat/search/service/document/PortfolioMapDocument.java
index 14f29cd3c62482dee559ee20cab42ec9ab60b518..dc07193ca85ec18e9c5565be8bccee6c118879e4 100644
--- a/src/main/java/org/olat/search/service/document/PortfolioMapDocument.java
+++ b/src/main/java/org/olat/search/service/document/PortfolioMapDocument.java
@@ -23,8 +23,6 @@ package org.olat.search.service.document;
 import java.util.List;
 
 import org.apache.lucene.document.Document;
-import org.olat.basesecurity.GroupRoles;
-import org.olat.basesecurity.manager.GroupDAO;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
@@ -39,6 +37,7 @@ import org.olat.core.util.resource.OresHelper;
 import org.olat.portfolio.EPArtefactHandler;
 import org.olat.portfolio.PortfolioModule;
 import org.olat.portfolio.manager.EPFrontendManager;
+import org.olat.portfolio.manager.EPPolicyManager;
 import org.olat.portfolio.model.artefacts.AbstractArtefact;
 import org.olat.portfolio.model.structel.EPAbstractMap;
 import org.olat.portfolio.model.structel.PortfolioStructure;
@@ -59,14 +58,14 @@ public class PortfolioMapDocument extends OlatDocument {
 	private static final long serialVersionUID = -7960651550499734346L;
 	private static final OLog log = Tracing.createLoggerFor(PortfolioMapDocument.class);
 	
-	private static GroupDAO groupDao;
 	private static EPFrontendManager ePFMgr; 
+	private static EPPolicyManager policyManager;
 	private static PortfolioModule portfolioModule;
 
 	public PortfolioMapDocument() {
 		super();
-		groupDao = CoreSpringFactory.getImpl(GroupDAO.class);
 		ePFMgr = CoreSpringFactory.getImpl(EPFrontendManager.class);
+		policyManager = CoreSpringFactory.getImpl(EPPolicyManager.class);
 		portfolioModule = CoreSpringFactory.getImpl(PortfolioModule.class);
 	}
 
@@ -74,8 +73,8 @@ public class PortfolioMapDocument extends OlatDocument {
 		PortfolioMapDocument document = new PortfolioMapDocument();
 		if(map instanceof EPAbstractMap) {
 			EPAbstractMap abstractMap = (EPAbstractMap)map;
-  		if(abstractMap.getGroup() != null) {
-  			List<Identity> identities = groupDao.getMembers(abstractMap.getGroup(), GroupRoles.owner.name());
+  		if(abstractMap.getGroups() != null) {
+  			List<Identity> identities = policyManager.getOwners(abstractMap);
   			StringBuilder authors = new StringBuilder();
   			for(Identity identity:identities) {
   				if(authors.length() > 0) {
diff --git a/src/main/java/org/olat/search/service/indexer/PortfolioMapIndexer.java b/src/main/java/org/olat/search/service/indexer/PortfolioMapIndexer.java
index bba6056d9a7e7e0bb0b71b3e317addad319569e5..fde584c1ea674a97b44e45380e21dade8a43f409 100644
--- a/src/main/java/org/olat/search/service/indexer/PortfolioMapIndexer.java
+++ b/src/main/java/org/olat/search/service/indexer/PortfolioMapIndexer.java
@@ -55,6 +55,6 @@ public class PortfolioMapIndexer extends AbstractPortfolioMapIndexer {
 
 	@Override
 	protected boolean accept(PortfolioStructureMap map) {
-		return map instanceof EPDefaultMap && ((EPDefaultMap)map).getGroup() != null;
+		return map instanceof EPDefaultMap && ((EPDefaultMap)map).getGroups() != null;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_10_0_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_10_0_0.java
index 5e2df8a51b056a83b1e7cb2022674073b5331585..22630162e45c5d3942ff39856a121d417fb75e72 100644
--- a/src/main/java/org/olat/upgrade/OLATUpgrade_10_0_0.java
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_10_0_0.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.Constants;
 import org.olat.basesecurity.Group;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.Policy;
@@ -33,15 +34,19 @@ import org.olat.basesecurity.SecurityGroup;
 import org.olat.basesecurity.manager.GroupDAO;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
+import org.olat.group.BusinessGroupService;
 import org.olat.group.manager.BusinessGroupRelationDAO;
 import org.olat.group.right.BGRightManager;
 import org.olat.group.right.BGRightsRole;
+import org.olat.portfolio.manager.EPMapPolicy;
 import org.olat.repository.RepositoryManager;
 import org.olat.repository.manager.RepositoryEntryRelationDAO;
 import org.olat.resource.OLATResource;
 import org.olat.upgrade.model.BGResourceRelation;
 import org.olat.upgrade.model.BusinessGroupUpgrade;
 import org.olat.upgrade.model.EPMapUpgrade;
+import org.olat.upgrade.model.EPMapUpgradeToGroupRelation;
+import org.olat.upgrade.model.InvitationUpgrade;
 import org.olat.upgrade.model.RepositoryEntryUpgrade;
 import org.olat.upgrade.model.RepositoryEntryUpgradeToGroupRelation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -58,7 +63,8 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 	private static final String TASK_BUSINESS_GROUPS = "Upgrade business groups";
 	private static final String TASK_REPOENTRIES = "Upgrade repository entries";
 	private static final String TASK_REPOENTRY_TO_BUSINESSGROUP = "Upgrade relation business groups to repository entries";
-	private static final String TASK_UPGRADE_MAP = "Upgrade maps";
+	private static final String TASK_INVITATION = "Upgrade invitations";
+	private static final String TASK_UPGRADE_MAP = "Upgrade e-portfolio maps";
 	private static final String VERSION = "OLAT_10.0.0";
 
 	@Autowired
@@ -75,6 +81,8 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 	private BusinessGroupRelationDAO businessGroupRelationDao;
 	@Autowired
 	private RepositoryEntryRelationDAO repositoryEntryToGroupDAO;
+	@Autowired
+	private BusinessGroupService businessGroupService;
 	
 	public OLATUpgrade_10_0_0() {
 		super();
@@ -104,6 +112,7 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 		allOk &= upgradeBusinessGroups(upgradeManager, uhd);
 		allOk &= upgradeRepositoryEntries(upgradeManager, uhd);
 		allOk &= upgradeRelationsRepoToBusinessGroups(upgradeManager, uhd);
+		allOk &= upgradeInvitation(upgradeManager, uhd);
 		allOk &= upgradeEPMap(upgradeManager, uhd);
 		
 		uhd.setInstallationComplete(allOk);
@@ -311,6 +320,44 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 				.getResultList();
 	}
 	
+	private boolean upgradeInvitation(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
+		if (!uhd.getBooleanDataValue(TASK_INVITATION)) {
+			int counter = 0;
+			List<InvitationUpgrade> invitations;
+			do {
+				invitations = findInvitations(counter, BATCH_SIZE);
+				for(InvitationUpgrade invitation:invitations) {
+					if(invitation.getBaseGroup() == null) {
+						processInvitation(invitation);
+					}
+				}
+				counter += invitations.size();
+				log.audit("Invitations processed: " + invitations.size() + ", total processed (" + counter + ")");
+				dbInstance.commitAndCloseSession();
+			} while(invitations.size() == BATCH_SIZE);
+			uhd.setBooleanDataValue(TASK_INVITATION, true);
+			upgradeManager.setUpgradesHistory(uhd, VERSION);
+		}
+		return true;
+	}
+	
+	private List<InvitationUpgrade> findInvitations(int firstResult, int maxResult) {
+		String sb = "select invitation from invitationupgrade as invitation order by invitation.key";
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb, InvitationUpgrade.class)
+				.setFirstResult(firstResult)
+				.setMaxResults(maxResult)
+				.getResultList();
+	}
+	
+	private void processInvitation(InvitationUpgrade invitation) {
+		if(invitation.getBaseGroup() == null) {
+			Group invitationGroup = groupDao.createGroup();
+			invitation.setBaseGroup(invitationGroup);
+			dbInstance.getCurrentEntityManager().merge(invitation);
+		}
+	}
+	
 	private boolean upgradeEPMap(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
 		if (!uhd.getBooleanDataValue(TASK_UPGRADE_MAP)) {
 			int counter = 0;
@@ -331,26 +378,113 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 	}
 	
 	private void processMap(EPMapUpgrade map) {
-		if(map.getGroup() != null) return;
+		if(map.getGroups() != null && map.getGroups().size() > 0) {
+			return;
+		}
 		
 		SecurityGroup ownerGroup = map.getOwnerGroup();
 		if(ownerGroup != null) {
+			//create default group
 			RepositoryEntryUpgrade re = findMapRepoEntry(ownerGroup);
+			Set<EPMapUpgradeToGroupRelation> relations = map.getGroups();
 			if(re != null) {
 				Group reGroup = repositoryEntryToGroupDAO.getDefaultGroup(re);
 				if(reGroup != null) {
-					map.setGroup(reGroup);
+					relations.add(createDefaultGroup(map, reGroup));
 				}
 			}
-			if(map.getGroup() == null) {
+			if(relations.isEmpty()) {
 				Group group = groupDao.createGroup();
-				map.setGroup(group);
+				relations.add(createDefaultGroup(map, group));
 				processSecurityGroup(group, GroupRoles.owner.name(), ownerGroup);
 			}
+			
+			//create policy -> relation
+
+			List<Policy> policies = securityManager.getPoliciesOfResource(map.getOlatResource(), null);
+			for(Policy policy:policies) {
+				if(policy.getPermission().contains(Constants.PERMISSION_READ)) {
+					EPMapUpgradeToGroupRelation politeRelation = processMapPolicy(policy, map);
+					if(politeRelation != null) {
+						relations.add(politeRelation);
+					}
+				}
+			}
 			dbInstance.getCurrentEntityManager().merge(map);
 		}
 	}
 	
+	private EPMapUpgradeToGroupRelation processMapPolicy(Policy policy, EPMapUpgrade element) {
+		String permission = policy.getPermission();
+		SecurityGroup secGroup = policy.getSecurityGroup();
+		Group group;
+		String role;
+		if(permission.startsWith(EPMapPolicy.Type.user.name())) {
+			group = groupDao.createGroup();
+			processSecurityGroup(group, GroupRoles.participant.name(), secGroup);
+			role = EPMapPolicy.Type.user.name();
+		} else if (permission.startsWith(EPMapPolicy.Type.group.name())) {
+			group = findGroupOfBusinessGroup(secGroup);
+			role = EPMapPolicy.Type.group.name();
+		} else if (permission.startsWith(EPMapPolicy.Type.invitation.name())) {
+			InvitationUpgrade invitation = findInvitation(policy.getSecurityGroup());
+			group = invitation.getBaseGroup();
+			role = EPMapPolicy.Type.invitation.name();
+		} else if (permission.startsWith(EPMapPolicy.Type.allusers.name())) {
+			group = groupDao.createGroup(EPMapPolicy.Type.allusers.name());
+			role = EPMapPolicy.Type.allusers.name();
+		} else {
+			return null;
+		}
+		
+		if(group == null) {
+			log.error("Group not resolve for policy of map: " + element.getKey() + " and policy: " + policy.getKey());
+			return null;
+		}
+		
+		EPMapUpgradeToGroupRelation relation = new EPMapUpgradeToGroupRelation();
+		relation.setDefaultGroup(false);
+		relation.setCreationDate(new Date());
+		relation.setEntry(element);
+		relation.setValidTo(policy.getTo());
+		relation.setValidFrom(policy.getFrom());
+		relation.setGroup(group);
+		relation.setRole(role);
+		return relation;
+	}
+	
+	private InvitationUpgrade findInvitation(SecurityGroup secGroup) {
+		String sb = "select invitation from invitationupgrade as invitation where invitation.securityGroup=:secGroup";
+		List<InvitationUpgrade> invitations = dbInstance.getCurrentEntityManager()
+				.createQuery(sb, InvitationUpgrade.class)
+				.setParameter("secGroup", secGroup)
+				.getResultList();
+		return invitations.isEmpty() ? null : invitations.get(0);
+	}
+	
+	private EPMapUpgradeToGroupRelation createDefaultGroup(EPMapUpgrade element, Group group) {
+		EPMapUpgradeToGroupRelation relation = new EPMapUpgradeToGroupRelation();
+		relation.setDefaultGroup(true);
+		relation.setCreationDate(new Date());
+		relation.setGroup(group);
+		relation.setEntry(element);
+		return relation;
+	}
+	
+	private Group findGroupOfBusinessGroup(SecurityGroup secGroup) {
+		StringBuilder sb = new StringBuilder(); 
+		sb.append("select bgi.baseGroup from ").append(BusinessGroupUpgrade.class.getName()).append(" as bgi ")
+		  .append(" where (bgi.partipiciantGroup=:secGroup or bgi.ownerGroup=:secGroup or bgi.waitingGroup=:secGroup)");
+
+		List<Group> res = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Group.class)
+				.setParameter("secGroup", secGroup)
+				.getResultList();
+
+		if(res.isEmpty()) return null;
+		return res.get(0);
+	}
+	
 	private RepositoryEntryUpgrade findMapRepoEntry(SecurityGroup ownerGroup) {
 		StringBuilder sb = new StringBuilder();	
 		sb.append("select v from ").append(RepositoryEntryUpgrade.class.getName()).append(" as v")
@@ -368,9 +502,7 @@ public class OLATUpgrade_10_0_0 extends OLATUpgrade {
 	private List<EPMapUpgrade> findMaps(int firstResult, int maxResults) {
 		StringBuilder sb = new StringBuilder();	
 		sb.append("select map from ").append(EPMapUpgrade.class.getName()).append(" map")
-		  .append(" left join fetch map.group as baseGroup")
-		  .append(" left join fetch map.ownerGroup as ownerGroup")
-		  .append(" where map.group is null and map.ownerGroup is not null")
+		  .append(" where map.ownerGroup is not null")
 		  .append(" order by map.key");
 		return dbInstance.getCurrentEntityManager().createQuery(sb.toString(), EPMapUpgrade.class)
 				.setFirstResult(firstResult)
diff --git a/src/main/java/org/olat/upgrade/model/EPMapUpgrade.hbm.xml b/src/main/java/org/olat/upgrade/model/EPMapUpgrade.hbm.xml
index 748d7caa4ddfc22e6ddbe55be863c5e4b82e7d79..f1503d828d8460b9ee521c4c163a59fb756a0916 100644
--- a/src/main/java/org/olat/upgrade/model/EPMapUpgrade.hbm.xml
+++ b/src/main/java/org/olat/upgrade/model/EPMapUpgrade.hbm.xml
@@ -13,13 +13,19 @@
                   outer-join="true"   
                   unique="true" not-found="ignore"
                   cascade="none"/>
+        
+		<many-to-one name="olatResource"
+                 column="fk_olatresource"
+                 class="org.olat.resource.OLATResourceImpl" 
+                 outer-join="true"   
+                 unique="true" 
+                 cascade="none"/>
+
+		<set name="groups" cascade="all,delete-orphan">
+            <key column="fk_struct_id"/>
+            <one-to-many class="org.olat.upgrade.model.EPMapUpgradeToGroupRelation"/>
+        </set> 
                   
-		<many-to-one name="group"
-                  column="fk_group_id"
-                  class="org.olat.basesecurity.model.GroupImpl" 
-                  outer-join="true"   
-                  unique="true" not-found="ignore"
-                  cascade="none"/>
 	</class>
   
 </hibernate-mapping>
\ No newline at end of file
diff --git a/src/main/java/org/olat/upgrade/model/EPMapUpgrade.java b/src/main/java/org/olat/upgrade/model/EPMapUpgrade.java
index 903b9384728d36b6567a8a93cd7222772805c9c3..46959123911f32bb992baa68e571a73f784e37f5 100644
--- a/src/main/java/org/olat/upgrade/model/EPMapUpgrade.java
+++ b/src/main/java/org/olat/upgrade/model/EPMapUpgrade.java
@@ -19,9 +19,11 @@
  */
 package org.olat.upgrade.model;
 
-import org.olat.basesecurity.Group;
+import java.util.Set;
+
 import org.olat.basesecurity.SecurityGroup;
 import org.olat.core.commons.persistence.PersistentObject;
+import org.olat.resource.OLATResource;
 
 /**
  * Needed to upgrade the maps
@@ -33,16 +35,25 @@ import org.olat.core.commons.persistence.PersistentObject;
 public class EPMapUpgrade extends PersistentObject {
 
 	private static final long serialVersionUID = 9041327840189041360L;
-	
-	private Group group;
+
 	private SecurityGroup ownerGroup;
+	private OLATResource olatResource;
+	private Set<EPMapUpgradeToGroupRelation> groups;
 	 
-	public Group getGroup() {
-		return group;
+	public OLATResource getOlatResource() {
+		return olatResource;
+	}
+
+	public void setOlatResource(OLATResource olatResource) {
+		this.olatResource = olatResource;
+	}
+
+	public Set<EPMapUpgradeToGroupRelation> getGroups() {
+		return groups;
 	}
 
-	public void setGroup(Group group) {
-		this.group = group;
+	public void setGroups(Set<EPMapUpgradeToGroupRelation> groups) {
+		this.groups = groups;
 	}
 
 	public SecurityGroup getOwnerGroup() {
diff --git a/src/main/java/org/olat/upgrade/model/EPMapUpgradeToGroupRelation.java b/src/main/java/org/olat/upgrade/model/EPMapUpgradeToGroupRelation.java
new file mode 100644
index 0000000000000000000000000000000000000000..20ae7ca3c56b41ddf58ce6ad3a4084486da73bf4
--- /dev/null
+++ b/src/main/java/org/olat/upgrade/model/EPMapUpgradeToGroupRelation.java
@@ -0,0 +1,180 @@
+/**
+ * <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.upgrade.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.model.GroupImpl;
+import org.olat.core.id.Persistable;
+import org.olat.portfolio.model.structel.EPStructureElement;
+
+/**
+ * 
+ * Initial date: 20.02.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="structureuptogroup")
+@Table(name="o_ep_struct_to_group")
+public class EPMapUpgradeToGroupRelation implements Persistable {
+
+	private static final long serialVersionUID = 2215547264646107606L;
+
+	@Id
+	@GeneratedValue(generator = "system-uuid")
+	@GenericGenerator(name = "system-uuid", strategy = "hilo")
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+
+	@Column(name="r_defgroup", nullable=false, insertable=true, updatable=false)
+	private boolean defaultGroup = false;
+	
+	@Column(name="r_role", nullable=true, insertable=true, updatable=true)
+	private String role;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="r_valid_from", nullable=true, insertable=true, updatable=true)
+	private Date validFrom;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="r_valid_to", nullable=true, insertable=true, updatable=true)
+	private Date validTo;
+	
+	@ManyToOne(targetEntity=GroupImpl.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_group_id", nullable=true, insertable=true, updatable=true)
+	private Group group;
+
+	@ManyToOne(targetEntity=EPStructureElement.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_struct_id", nullable=false, insertable=true, updatable=true)
+	private EPMapUpgrade entry;
+	
+	public Long getKey() {
+		return key;
+	}
+
+	public void setKey(Long key) {
+		this.key = key;
+	}
+
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public boolean isDefaultGroup() {
+		return defaultGroup;
+	}
+
+	public void setDefaultGroup(boolean defaultGroup) {
+		this.defaultGroup = defaultGroup;
+	}
+
+	public String getRole() {
+		return role;
+	}
+
+	public void setRole(String role) {
+		this.role = role;
+	}
+
+	public Date getValidFrom() {
+		return validFrom;
+	}
+
+	public void setValidFrom(Date validFrom) {
+		this.validFrom = validFrom;
+	}
+
+	public Date getValidTo() {
+		return validTo;
+	}
+
+	public void setValidTo(Date validTo) {
+		this.validTo = validTo;
+	}
+
+	public Group getGroup() {
+		return group;
+	}
+
+	public void setGroup(Group group) {
+		this.group = group;
+	}
+
+	public EPMapUpgrade getEntry() {
+		return entry;
+	}
+
+	public void setEntry(EPMapUpgrade entry) {
+		this.entry = entry;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("mapToGroup[resource=")
+			.append(entry.getKey()).append(":")
+			.append("group=").append(group.getKey())
+			.append("]");
+		return sb.toString();
+	}
+	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 29061 : getKey().hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof EPMapUpgradeToGroupRelation) {
+			EPMapUpgradeToGroupRelation rel = (EPMapUpgradeToGroupRelation)obj;
+			return getKey() != null && getKey().equals(rel.getKey());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/upgrade/model/InvitationUpgrade.java b/src/main/java/org/olat/upgrade/model/InvitationUpgrade.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd493a09dbda979a6c57026f832db383c35cd514
--- /dev/null
+++ b/src/main/java/org/olat/upgrade/model/InvitationUpgrade.java
@@ -0,0 +1,109 @@
+/**
+ * <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.upgrade.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.olat.basesecurity.Group;
+import org.olat.basesecurity.SecurityGroup;
+import org.olat.basesecurity.SecurityGroupImpl;
+import org.olat.basesecurity.model.GroupImpl;
+import org.olat.core.id.Persistable;
+
+/**
+ * 
+ * Description:<br>
+ * Implementation of Invitation
+ * 
+ * <P>
+ * Initial Date:  10 nov. 2010 <br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+@Entity(name="invitationupgrade")
+@Table(name="o_bs_invitation")
+public class InvitationUpgrade implements Persistable {
+
+	private static final long serialVersionUID = -9122616013810215550L;
+	
+	@Id
+	@GeneratedValue(generator = "system-uuid")
+	@GenericGenerator(name = "system-uuid", strategy = "hilo")
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+
+	@ManyToOne(targetEntity=GroupImpl.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_group_id", nullable=false, insertable=true, updatable=true)
+	private Group baseGroup;
+	
+	@ManyToOne(targetEntity=SecurityGroupImpl.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_secgroup", nullable=true, insertable=true, updatable=true)
+	private SecurityGroup securityGroup;
+	
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	public Group getBaseGroup() {
+		return baseGroup;
+	}
+
+	public void setBaseGroup(Group baseGroup) {
+		this.baseGroup = baseGroup;
+	}
+
+	public SecurityGroup getSecurityGroup() {
+		return securityGroup;
+	}
+
+	public void setSecurityGroup(SecurityGroup securityGroup) {
+		this.securityGroup = securityGroup;
+	}
+
+	@Override
+	public int hashCode() {
+		return key == null ? -98260 : key.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		} else if (obj instanceof InvitationUpgrade) {
+			InvitationUpgrade invitation = (InvitationUpgrade)obj;
+			return getKey() != null && getKey().equals(invitation.getKey());
+		}
+		return false;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+}
diff --git a/src/main/java/org/olat/user/UserInfoMainController.java b/src/main/java/org/olat/user/UserInfoMainController.java
index ee23f4979ade861e785afddaee51f15c9d285985..a4da52472002acf3968f52f95ade437f1a6ab5b9 100644
--- a/src/main/java/org/olat/user/UserInfoMainController.java
+++ b/src/main/java/org/olat/user/UserInfoMainController.java
@@ -31,7 +31,6 @@ package org.olat.user;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.commons.calendar.CalendarManager;
 import org.olat.commons.calendar.CalendarManagerFactory;
 import org.olat.commons.calendar.model.KalendarConfig;
@@ -70,6 +69,8 @@ import org.olat.core.util.vfs.callbacks.VFSSecurityCallback;
 import org.olat.modules.co.ContactFormController;
 import org.olat.portfolio.EPUIFactory;
 import org.olat.portfolio.PortfolioModule;
+import org.olat.portfolio.manager.InvitationDAO;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * Initial Date: July 26, 2005
@@ -109,6 +110,9 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 	
 	private GenericTreeNode folderNode;
 	private GenericTreeNode contactNode;
+	
+	@Autowired
+	private InvitationDAO invitationDao;
 
 	/**
 	 * @param ureq
@@ -209,7 +213,7 @@ public class UserInfoMainController extends MainLayoutBasicController implements
 		// following user info elements are only shown for undeleted and real
 		// users (not invited
 		// eportfolio users)
-		boolean isInvitee = BaseSecurityManager.getInstance().isIdentityInvited(chosenIdentity);
+		boolean isInvitee = invitationDao.isInvitee(chosenIdentity);
 		boolean isDeleted = chosenIdentity.getStatus().equals(Identity.STATUS_DELETED);
 		
 		if ( !isDeleted && ! isInvitee) {
diff --git a/src/main/resources/database/mysql/alter_9_4_0_to_10_0_0.sql b/src/main/resources/database/mysql/alter_9_4_0_to_10_0_0.sql
index 5fa27179833ed44110c272a3f2de5f40fecd1d93..eaacd7e0595d371e030db2f3ab2654a73f180f12 100644
--- a/src/main/resources/database/mysql/alter_9_4_0_to_10_0_0.sql
+++ b/src/main/resources/database/mysql/alter_9_4_0_to_10_0_0.sql
@@ -5,9 +5,6 @@ alter table o_repositoryentry modify softkey varchar(36) not null unique;
 alter table o_repositoryentry modify launchcounter bigint null default 0;
 alter table o_repositoryentry modify downloadcounter bigint null default 0;
 
-alter table o_ep_struct_el add column fk_group_id bigint;
-
-
 -- repository entry statistics table
 create table o_repositoryentry_stats (
    id bigint not null,
@@ -89,6 +86,29 @@ alter table o_re_to_group add constraint re_to_group_re_ctx foreign key (fk_entr
 -- alter table o_gp_business add constraint gp_to_group_business_ctx foreign key (fk_group_id) references o_bs_group (id);
 
 
+-- portfolio
+alter table o_bs_invitation modify fk_secgroup bigint null default null;
+alter table o_bs_invitation modify version bigint null default null;
+
+alter table o_bs_invitation add column fk_group_id bigint;
+alter table o_bs_invitation add constraint inv_to_group_group_ctx foreign key (fk_group_id) references o_bs_group (id);
+
+create table o_ep_struct_to_group (
+   id bigint not null,
+   creationdate datetime not null,
+   r_defgroup boolean not null,
+   r_role varchar(64),
+   r_valid_from datetime,
+   r_valid_to datetime,
+   fk_group_id bigint,
+   fk_struct_id bigint,
+   primary key (id)
+);
+alter table o_ep_struct_to_group ENGINE = InnoDB;
+alter table o_ep_struct_to_group add constraint struct_to_group_group_ctx foreign key (fk_group_id) references o_bs_group (id);
+alter table o_ep_struct_to_group add constraint struct_to_group_re_ctx foreign key (fk_struct_id) references o_ep_struct_el (structure_id);
+
+
 -- managed groups
 create or replace view o_gp_business_v  as (
    select
@@ -313,6 +333,9 @@ alter table o_repositoryentry drop foreign key repo_parti_sec_group_ctx;
 
 alter table o_repositorymetadata drop foreign key FKDB97A6493F14E3EE;
 
+alter table o_bs_policy drop foreign key FK9A1C5109F9C3F1D;
+
+
 alter table o_bookmark drop foreign key FK68C4E30663219E27;
 
 alter table o_gp_business_to_resource drop foreign key idx_bgp_to_rsrc_rsrc;
@@ -324,4 +347,6 @@ alter table o_gp_bgcontextresource_rel drop foreign key FK9903BEACDF6BCD14;
 
 alter table o_gp_bgarea drop foreign key FK9EFAF698DF6BCD14;
 
-alter table o_ep_struct_el drop foreign key FKF26C8375236F29X;
\ No newline at end of file
+alter table o_ep_struct_el drop foreign key FKF26C8375236F29X;
+
+alter table o_bs_invitation drop foreign key FKF26C8375236F27X;
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index df380e4277a184b07b73a80f7eeed0cb5e858ac7..c9efaf968bbc48f1c94539ec7c6c2228c733df39 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -712,13 +712,12 @@ create table if not exists o_tag (
 
 create table if not exists o_bs_invitation (
    id bigint not null,
-   version mediumint unsigned not null,
    creationdate datetime,
    token varchar(64) not null,
    first_name varchar(64),
    last_name varchar(64),
    mail varchar(128),
-   fk_secgroup bigint,
+   fk_group_id bigint,
    primary key (id)
 );
 
@@ -799,6 +798,17 @@ create table if not exists o_ep_struct_artefact_link (
   fk_artefact_id bigint not null,
   primary key (link_id)
 );
+create table o_ep_struct_to_group (
+   id bigint not null,
+   creationdate datetime not null,
+   r_defgroup boolean not null,
+   r_role varchar(64),
+   r_valid_from datetime,
+   r_valid_to datetime,
+   fk_group_id bigint,
+   fk_struct_id bigint,
+   primary key (id)
+);
 
 -- mail system
 
@@ -1815,6 +1825,7 @@ alter table o_ep_collect_restriction ENGINE = InnoDB;
 alter table o_ep_struct_el ENGINE = InnoDB;
 alter table o_ep_struct_struct_link ENGINE = InnoDB;
 alter table o_ep_struct_artefact_link ENGINE = InnoDB;
+alter table o_ep_struct_to_group ENGINE = InnoDB;
 alter table o_co_db_entry ENGINE = InnoDB;
 alter table o_mail ENGINE = InnoDB;
 alter table o_mail_to_recipient ENGINE = InnoDB;
@@ -1924,14 +1935,13 @@ create index identstatus_idx on o_bs_identity (status);
 create index idx_ident_creationdate_idx on o_bs_identity (creationdate);
 create index idx_id_lastlogin_idx on o_bs_identity (lastlogin);
 
-alter table o_bs_policy add constraint FK9A1C5109F9C3F1D foreign key (oresource_id) references o_olatresource (resource_id);
 alter table o_bs_policy add constraint FK9A1C5101E2E76DB foreign key (group_id) references o_bs_secgroup (id);
 create index idx_policy_grp_rsrc_idx on o_bs_policy (oresource_id, group_id);
 
 alter table o_bs_membership add constraint FK7B6288B45259603C foreign key (identity_id) references o_bs_identity (id);
 alter table o_bs_membership add constraint FK7B6288B4B85B522C foreign key (secgroup_id) references o_bs_secgroup (id);
 
-alter table o_bs_invitation add constraint FKF26C8375236F27X foreign key (fk_secgroup) references o_bs_secgroup (id);
+alter table o_bs_invitation add constraint inv_to_group_group_ctx foreign key (fk_group_id) references o_bs_group (id);
 
 -- user
 create index usr_notification_interval_idx on o_user (notification_interval);
@@ -2091,6 +2101,9 @@ alter table o_ep_struct_artefact_link add constraint FKF26C8375236F24X foreign k
 alter table o_ep_struct_artefact_link add constraint FKF26C8375236F25X foreign key (fk_artefact_id) references o_ep_artefact (artefact_id);
 alter table o_ep_struct_artefact_link add constraint FKF26C8375236F26Y foreign key (fk_auth_id) references o_bs_identity (id);
 
+alter table o_ep_struct_to_group add constraint struct_to_group_group_ctx foreign key (fk_group_id) references o_bs_group (id);
+alter table o_ep_struct_to_group add constraint struct_to_group_re_ctx foreign key (fk_struct_id) references o_ep_struct_el (structure_id);
+
 -- tag
 alter table o_tag add constraint FK6491FCA5A4FA5DC foreign key (fk_author_id) references o_bs_identity (id);
 
diff --git a/src/main/resources/database/postgresql/drop_after_10_0_0.sql b/src/main/resources/database/postgresql/drop_after_10_0_0.sql
index e92bb71f6fad1673c11caf82c0cff019f6a25fe8..7fcedd4df1ad17afceeb14d6dff9c19b26fbcce4 100644
--- a/src/main/resources/database/postgresql/drop_after_10_0_0.sql
+++ b/src/main/resources/database/postgresql/drop_after_10_0_0.sql
@@ -14,6 +14,11 @@ alter table o_gp_business drop column businessgrouptype;
 
 alter table o_area drop groupcontext_fk;
 
+alter table o_bs_invitation drop column fk_secgroup;
+alter table o_bs_invitation drop column version;
+
+alter table o_ep_struct_el drop column fk_ownergroup
+
 -- drop tables
 drop table o_gp_bgcontext;
 drop table o_gp_business_to_resource;
diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
index 054a84d83f14b575ba9390bbd2890369188c37bc..ec33ae018d922ef9c4b6ba8b47fb2fa953118ca1 100644
--- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
+++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
@@ -574,49 +574,6 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		Assert.assertTrue(policies.contains(policy_1));
 		Assert.assertTrue(policies.contains(policy_2));		
 	}
-	
-	/**
-	 * Test this method
-	 * @see getPoliciesOfSecurityGroup(List<SecurityGroup> secGroups, resource1)
-	 */
-	@Test
-	public void testGetPoliciesOfSecurityGroups() {
-		//create 2 security groups and 2 resources
-		SecurityGroup secGroup_1 = securityManager.createAndPersistSecurityGroup();
-		SecurityGroup secGroup_2 = securityManager.createAndPersistSecurityGroup();
-		OLATResource resource_1 = JunitTestHelper.createRandomResource();
-		OLATResource resource_2 = JunitTestHelper.createRandomResource();
-		Policy policy_1 = securityManager.createAndPersistPolicy(secGroup_1, "test.right1_1", resource_1);
-		Policy policy_2 = securityManager.createAndPersistPolicy(secGroup_1, "test.right1_2", resource_2);
-		Policy policy_3 = securityManager.createAndPersistPolicy(secGroup_2, "test.right2_1", resource_1);
-		Policy policy_4 = securityManager.createAndPersistPolicy(secGroup_2, "test.right2_2", resource_2);
-		dbInstance.commitAndCloseSession();
-	
-		//test group_1 and resource_1
-		List<Policy> policies1_1 =  securityManager.getPoliciesOfSecurityGroup(Collections.singletonList(secGroup_1), resource_1);
-		Assert.assertNotNull(policies1_1);
-		Assert.assertEquals(1, policies1_1.size());
-		Assert.assertEquals(policy_1, policies1_1.get(0));
-
-		//test group_1 + resource_1 and 2
-		List<Policy> policies1 =  securityManager.getPoliciesOfSecurityGroup(Collections.singletonList(secGroup_1), resource_1, resource_2);
-		Assert.assertNotNull(policies1);
-		Assert.assertEquals(2, policies1.size());
-		Assert.assertTrue(policies1.contains(policy_1));
-		Assert.assertTrue(policies1.contains(policy_2));
-		
-		//test group 2
-		List<Policy> policies2 =  securityManager.getPoliciesOfSecurityGroup(Collections.singletonList(secGroup_2));
-		Assert.assertNotNull(policies2);
-		Assert.assertEquals(2, policies2.size());
-		Assert.assertTrue(policies2.contains(policy_3));
-		Assert.assertTrue(policies2.contains(policy_4));
-		
-		//test dummy
-		List<Policy> policiesEmpty =  securityManager.getPoliciesOfSecurityGroup(Collections.<SecurityGroup>emptyList());
-		Assert.assertNotNull(policiesEmpty);
-		Assert.assertTrue(policiesEmpty.isEmpty());
-	}
 		
 	/**
 	 * Test the method
@@ -704,101 +661,6 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		assertTrue("Does not found policy", foundPolicy);
 	}
 	
-	/**
-	 * @see public void deletePolicy(SecurityGroup secGroup, String permission, OLATResource resource);
-	 */
-	@Test
-	public void testDeletePolicy() {
-		//create 3 rights
-		SecurityGroup secGroup1 = securityManager.createAndPersistSecurityGroup();
-		SecurityGroup secGroup2 = securityManager.createAndPersistSecurityGroup();
-		OLATResource resource1 = JunitTestHelper.createRandomResource();
-		OLATResource resource2 = JunitTestHelper.createRandomResource();
-		Policy policy_1_1 = securityManager.createAndPersistPolicy(secGroup1, "test.r1-1_1", resource1);
-		Policy policy_1_1b = securityManager.createAndPersistPolicy(secGroup1, "test.r2-1_1", resource1);
-		Policy policy_1_2 = securityManager.createAndPersistPolicy(secGroup1, "test.r3_1-2", resource2);
-		Policy policy_2_2 = securityManager.createAndPersistPolicy(secGroup2, "test.r3_2-2", resource2);
-		dbInstance.commitAndCloseSession();
-		
-		//delete policy 1_1b
-		securityManager.deletePolicy(secGroup1, "test.r2-1_1", resource1);
-		
-		//test the method
-		List<Policy> policies_1_1 = securityManager.getPoliciesOfResource(resource1, secGroup1);
-		Assert.assertNotNull(policies_1_1);
-		Assert.assertEquals(1, policies_1_1.size());
-		Assert.assertTrue(policies_1_1.contains(policy_1_1));
-		Assert.assertFalse(policies_1_1.contains(policy_1_1b));
-		
-		//too much deleted in resource 1?
-		List<Policy> policies_x_1 = securityManager.getPoliciesOfResource(resource1, null);
-		Assert.assertNotNull(policies_x_1);
-		Assert.assertEquals(1, policies_x_1.size());
-		Assert.assertTrue(policies_x_1.contains(policy_1_1));
-		Assert.assertFalse(policies_x_1.contains(policy_1_1b));
-		
-		//too much deleted in resource 2?
-		List<Policy> policies_x_2 = securityManager.getPoliciesOfResource(resource2, null);
-		Assert.assertNotNull(policies_x_2);
-		Assert.assertEquals(2, policies_x_2.size());
-		Assert.assertTrue(policies_x_2.contains(policy_1_2));
-		Assert.assertTrue(policies_x_2.contains(policy_2_2));
-	}
-	
-	/**
-	 * @see public boolean deletePolicies(Collection<SecurityGroup> secGroups, Collection<OLATResource> resources)
-	 */
-	@Test
-	public void testDeletePolicies() {
-		//prepare enough rights
-		SecurityGroup secGroup1 = securityManager.createAndPersistSecurityGroup();
-		SecurityGroup secGroup2 = securityManager.createAndPersistSecurityGroup();
-		SecurityGroup secGroup3 = securityManager.createAndPersistSecurityGroup();
-		OLATResource resource1 = JunitTestHelper.createRandomResource();
-		OLATResource resource2 = JunitTestHelper.createRandomResource();
-		OLATResource resource3 = JunitTestHelper.createRandomResource();
-		Policy policy_1_1 = securityManager.createAndPersistPolicy(secGroup1, "test.r1-1_1", resource1);
-		Policy policy_1_b = securityManager.createAndPersistPolicy(secGroup1, "test.r2-1_1", resource1);
-		Policy policy_1_2 = securityManager.createAndPersistPolicy(secGroup1, "test.r3_1-2", resource2);
-		Policy policy_1_3 = securityManager.createAndPersistPolicy(secGroup1, "test.r4_1-3", resource3);
-		Policy policy_2_2 = securityManager.createAndPersistPolicy(secGroup2, "test.r5_2-2", resource2);
-		Policy policy_3_1 = securityManager.createAndPersistPolicy(secGroup3, "test.r6_3-1", resource1);
-		Policy policy_3_2 = securityManager.createAndPersistPolicy(secGroup3, "test.r7_3-2", resource2);
-		Policy policy_3_3 = securityManager.createAndPersistPolicy(secGroup3, "test.r8_3-3", resource3);
-		dbInstance.commitAndCloseSession();
-		
-		//delete group 1 and 2, delete resource 1 and 3
-		List<SecurityGroup> secGroups = new ArrayList<SecurityGroup>(2);
-		secGroups.add(secGroup1);
-		secGroups.add(secGroup2);
-		List<OLATResource> resources = new ArrayList<OLATResource>(2);
-		resources.add(resource1);
-		resources.add(resource3);
-		boolean deleted = securityManager.deletePolicies(secGroups, resources);
-		Assert.assertTrue(deleted);
-		
-		//check resource 1 -> only policy_3_1 survives
-		List<Policy> policies_1 = securityManager.getPoliciesOfResource(resource1, null);
-		Assert.assertNotNull(policies_1);
-		Assert.assertEquals(1, policies_1.size());
-		Assert.assertTrue(policies_1.contains(policy_3_1));
-		Assert.assertFalse(policies_1.contains(policy_1_1));
-		Assert.assertFalse(policies_1.contains(policy_1_b));
-		//check resource 2 -> all its policies survive
-		List<Policy> policies_2 = securityManager.getPoliciesOfResource(resource2, null);
-		Assert.assertNotNull(policies_2);
-		Assert.assertEquals(3, policies_2.size());
-		Assert.assertTrue(policies_2.contains(policy_1_2));
-		Assert.assertTrue(policies_2.contains(policy_2_2));
-		Assert.assertTrue(policies_2.contains(policy_3_2));
-		//check resource 3
-		List<Policy> policies_3 = securityManager.getPoliciesOfResource(resource3, null);
-		Assert.assertNotNull(policies_3);
-		Assert.assertEquals(1, policies_3.size());
-		Assert.assertTrue(policies_3.contains(policy_3_3));
-		Assert.assertFalse(policies_3.contains(policy_1_3));
-	}
-	
 	@Test
 	public void isIdentityPermittedOnResourceable_checkType() {
 		//create an identity, a security group, a resource and give the identity some
@@ -863,28 +725,6 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		//check that null doesn't return an exception but false
 		boolean hasIpor = securityManager.isIdentityPermittedOnResourceable(null, "test.ipornc-null", resource, false);
 		Assert.assertFalse(hasIpor);
-
-	}
-	
-	@Test
-	public void getIdentityPermissionsOnResourceable() {
-		//create an identity, a security group, a resource and give the identity some
-		//permissions on the resource
-		SecurityGroup secGroup = securityManager.createAndPersistSecurityGroup();
-		OLATResource resource = JunitTestHelper.createRandomResource();
-		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("test-gpor-1-" + UUID.randomUUID().toString());
-		securityManager.addIdentityToSecurityGroup(id, secGroup);
-		securityManager.createAndPersistPolicy(secGroup, "test.gpor-1_1", resource);
-		securityManager.createAndPersistPolicy(secGroup, "test.gpor-1_2", resource);
-		dbInstance.commitAndCloseSession();
-		
-		//check
-		List<String> permissions = securityManager.getIdentityPermissionOnresourceable(id, resource);
-		Assert.assertNotNull(permissions);
-		Assert.assertTrue(permissions.size() >= 2);
-		Assert.assertTrue(permissions.contains("test.gpor-1_1"));
-		Assert.assertTrue(permissions.contains("test.gpor-1_2"));
-		Assert.assertFalse(permissions.contains("test.gpor-1_3"));
 	}
 
 	/**
diff --git a/src/test/java/org/olat/portfolio/EPFrontendManagerTest.java b/src/test/java/org/olat/portfolio/EPFrontendManagerTest.java
index 799d0f7a918213d1319247c3be6edeea21c0bf07..7ca021872078365a1acdcf9ddcb1d8171e1eff72 100644
--- a/src/test/java/org/olat/portfolio/EPFrontendManagerTest.java
+++ b/src/test/java/org/olat/portfolio/EPFrontendManagerTest.java
@@ -39,6 +39,7 @@ import org.olat.portfolio.manager.EPFrontendManager;
 import org.olat.portfolio.manager.EPMapPolicy;
 import org.olat.portfolio.manager.EPMapPolicy.Type;
 import org.olat.portfolio.manager.EPStructureManager;
+import org.olat.portfolio.manager.InvitationDAO;
 import org.olat.portfolio.model.artefacts.AbstractArtefact;
 import org.olat.portfolio.model.structel.EPDefaultMap;
 import org.olat.portfolio.model.structel.EPPage;
@@ -89,6 +90,8 @@ public class EPFrontendManagerTest extends OlatTestCase {
 	
 	@Autowired
 	private BaseSecurity securityManager;
+	@Autowired
+	private InvitationDAO invitationDao;
 	
 	@Autowired
 	private RepositoryManager repositoryManager;
@@ -593,7 +596,7 @@ public class EPFrontendManagerTest extends OlatTestCase {
 		policies.add(userPolicy);
 		
 		//invitation
-		Invitation invitation = securityManager.createAndPersistInvitation();
+		Invitation invitation = invitationDao.createAndPersistInvitation();
 		invitation.setFirstName("John");
 		invitation.setLastName("Doe");
 		invitation.setMail("john@doe.ch");
@@ -628,7 +631,7 @@ public class EPFrontendManagerTest extends OlatTestCase {
 		//save a list of policies
 		List<EPMapPolicy> policies = new ArrayList<EPMapPolicy>();
 		//invitation
-		Invitation invitation = securityManager.createAndPersistInvitation();
+		Invitation invitation = invitationDao.createAndPersistInvitation();
 		invitation.setFirstName("John");
 		invitation.setLastName("Doe");
 		invitation.setMail("john2@doe.ch");
@@ -636,7 +639,7 @@ public class EPFrontendManagerTest extends OlatTestCase {
 		invitationPolicy.setType(Type.invitation);
 		invitationPolicy.setInvitation(invitation);
 		policies.add(invitationPolicy);
-		epFrontendManager.updateMapPolicies(map, policies);
+		map = epFrontendManager.updateMapPolicies(map, policies);
 		dbInstance.commitAndCloseSession();
 		
 		//remove the policy
diff --git a/src/test/java/org/olat/portfolio/EPPerformanceTest.java b/src/test/java/org/olat/portfolio/EPPerformanceTest.java
index 8b4ec39e2f37602682afcaa8d9de2c070c7242f3..87d7b23e92bc16d8b6fd39bf396be8cecb81c418 100644
--- a/src/test/java/org/olat/portfolio/EPPerformanceTest.java
+++ b/src/test/java/org/olat/portfolio/EPPerformanceTest.java
@@ -177,21 +177,9 @@ public class EPPerformanceTest extends OlatTestCase {
 		return epFrontendManager.updateArtefact(artefact);
 	}
 	
-	
-	
-	@Test
-	public void testMaps300(){
-		internalTestCreateManyMaps(300);
-	}
-	
-	@Test
-	public void testMaps1000(){
-		internalTestCreateManyMaps(1000);
-	}
-	
 	@Test
-	public void testMaps3000(){
-		internalTestCreateManyMaps(3000);
+	public void testMaps500(){
+		internalTestCreateManyMaps(500);
 	}
 	
 	private void deleteMaps(){
diff --git a/src/test/java/org/olat/portfolio/FunctionalArtefactTest.java b/src/test/java/org/olat/portfolio/FunctionalArtefactTest.java
index c24e402b80560452f2ccd86e6b4f043e49379455..afaae7609712a93662df11ab68ba656472ef5762 100644
--- a/src/test/java/org/olat/portfolio/FunctionalArtefactTest.java
+++ b/src/test/java/org/olat/portfolio/FunctionalArtefactTest.java
@@ -196,11 +196,11 @@ public class FunctionalArtefactTest {
 			String pageName, String pageDescription,
 			String structureName, String structureDescription,
 			Class<?> artefactClass, String artefactName, String artefactDescription, String[] artefactTags, String[] artefactContent){
-		Binder binder = findBinderByName(this.map, binderName);
+		Binder binder = findBinderByName(map, binderName);
 		
 		if(binder == null){
 			binder = new Binder(binderName, binderDescription);
-			this.map.add(binder);
+			map.add(binder);
 		}
 		
 		Binder.Page page = findPageByName(binder.page, pageName);
diff --git a/src/test/java/org/olat/portfolio/manager/EPPolicyManagerTest.java b/src/test/java/org/olat/portfolio/manager/EPPolicyManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdd7d9f9d16c239977ae29d3b4e685b204b911f4
--- /dev/null
+++ b/src/test/java/org/olat/portfolio/manager/EPPolicyManagerTest.java
@@ -0,0 +1,120 @@
+/**
+ * <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.portfolio.manager;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.basesecurity.Invitation;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.portfolio.model.structel.PortfolioStructure;
+import org.olat.portfolio.model.structel.PortfolioStructureMap;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 24.06.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class EPPolicyManagerTest extends OlatTestCase {
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private InvitationDAO invitationDao;
+	@Autowired
+	private EPPolicyManager policyManager;
+	@Autowired
+	private EPFrontendManager epFrontendManager;
+	
+	@Test
+	public void getOwners() {
+		//create a map
+		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("Policy-User-1-");
+		PortfolioStructureMap originalMap = epFrontendManager.createAndPersistPortfolioDefaultMap(user, "Title", "Description");
+		PortfolioStructure page1 = epFrontendManager.createAndPersistPortfolioPage(originalMap, "Page title", "Page description");
+		assertNotNull(page1);
+		dbInstance.commitAndCloseSession();
+
+		List<Identity> owners = policyManager.getOwners(originalMap);
+		Assert.assertNotNull(owners);
+		Assert.assertEquals(1, owners.size());
+		Assert.assertEquals(user, owners.get(0));
+	}
+	
+	@Test
+	public void isMapShared_HQL() {
+		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("Policy-User-2-");
+		PortfolioStructureMap map = epFrontendManager.createAndPersistPortfolioDefaultMap(user, "Title", "Description");
+		dbInstance.commitAndCloseSession();
+		
+		boolean shared = policyManager.isMapShared(map.getOlatResource());
+		Assert.assertFalse(shared);
+	}
+	
+	@Test
+	public void createPolicy_invitation() {
+		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("Policy-User-2-");
+		PortfolioStructureMap map = epFrontendManager.createAndPersistPortfolioDefaultMap(user, "Title", "Description");
+		Invitation invitation = invitationDao.createAndPersistInvitation();
+		dbInstance.commit();
+		
+		invitation.setFirstName("John");
+		invitation.setLastName("Smith Portfolio");
+		EPMapPolicy policy = new EPMapPolicy();
+		policy.setType(EPMapPolicy.Type.invitation);
+		policy.setInvitation(invitation);
+		
+		policyManager.updateMapPolicies(map, Collections.singletonList(policy));
+		dbInstance.commitAndCloseSession();
+		
+		//check that the policy is saved
+		List<EPMapPolicy> policies = policyManager.getMapPolicies(map);
+		Assert.assertNotNull(policies);
+		Assert.assertEquals(1, policies.size());
+		EPMapPolicy invitationPolicy = policies.get(0);
+		Assert.assertEquals(EPMapPolicy.Type.invitation, invitationPolicy.getType());
+		
+		//convert invitation to identity
+		Identity invitee = invitationDao.createIdentityFrom(invitation, Locale.ENGLISH);
+		dbInstance.commitAndCloseSession();
+
+		//check is shared
+		boolean shared = policyManager.isMapShared(map.getOlatResource());
+		Assert.assertTrue(shared);
+		
+		boolean visible = epFrontendManager.isMapVisible(invitee, map.getOlatResource());
+		Assert.assertTrue(visible);
+	}
+	
+	
+	
+	
+
+}
diff --git a/src/test/java/org/olat/portfolio/EPStructureManagerTest.java b/src/test/java/org/olat/portfolio/manager/EPStructureManagerTest.java
similarity index 97%
rename from src/test/java/org/olat/portfolio/EPStructureManagerTest.java
rename to src/test/java/org/olat/portfolio/manager/EPStructureManagerTest.java
index e67c7a9629388d3d2d0040240f2e156171d3ac5a..a9b29f4f890e1cded03feabe6af28f7d5f32f894 100644
--- a/src/test/java/org/olat/portfolio/EPStructureManagerTest.java
+++ b/src/test/java/org/olat/portfolio/manager/EPStructureManagerTest.java
@@ -18,7 +18,7 @@
  * <p>
  */
 
-package org.olat.portfolio;
+package org.olat.portfolio.manager;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -103,24 +103,26 @@ public class EPStructureManagerTest extends OlatTestCase {
 	
 	@Test
 	public void testGetStructureElementsForUser(){
-		PortfolioStructure el = epFrontendManager.createAndPersistPortfolioDefaultMap(ident1, "users-test-map", "a-map-to-test-get-afterwards");
+		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("EP-1-");
+		
+		PortfolioStructure el = epFrontendManager.createAndPersistPortfolioDefaultMap(user, "users-test-map", "a-map-to-test-get-afterwards");
 		assertNotNull(el);
 		dbInstance.commitAndCloseSession();
 		
-		List<SecurityGroup> secGroups = securityManager.getSecurityGroupsForIdentity(ident1);
+		List<SecurityGroup> secGroups = securityManager.getSecurityGroupsForIdentity(user);
 		assertNotNull(secGroups);
 		assertTrue(secGroups.size() >= 1);
 		
-		List<PortfolioStructure> elRes = epFrontendManager.getStructureElementsForUser(ident1);
+		List<PortfolioStructure> elRes = epFrontendManager.getStructureElementsForUser(user);
 		assertNotNull(elRes);
 		assertTrue(elRes.size() == 1);
 		assertEquals( ((EPStructureElement)elRes.get(0)).getTitle(), "users-test-map");
 
 		// get another map
-		PortfolioStructure el2 = epFrontendManager.createAndPersistPortfolioDefaultMap(ident1, "users-test-map-2", "2-a-map-to-test-get-afterwards");
+		PortfolioStructure el2 = epFrontendManager.createAndPersistPortfolioDefaultMap(user, "users-test-map-2", "2-a-map-to-test-get-afterwards");
 		assertNotNull(el2);
 		dbInstance.commitAndCloseSession();
-		List<PortfolioStructure> elRes2 = epFrontendManager.getStructureElementsForUser(ident1);
+		List<PortfolioStructure> elRes2 = epFrontendManager.getStructureElementsForUser(user);
 		assertNotNull(elRes2);
 		assertTrue(elRes2.size() == 2);
 	}
@@ -423,7 +425,7 @@ public class EPStructureManagerTest extends OlatTestCase {
 	}
 	
 	@Test
-	public void testLoadPortfolioStructuredMaps(){
+	public void testLoadPortfolioStructuredMaps() {
 		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("EP-tmp-");
 		//a template
 		PortfolioStructureMap template = epStructureManager.createPortfolioMapTemplate(user, "paged-parent-structure-el", "parent-structure-element");
@@ -449,6 +451,20 @@ public class EPStructureManagerTest extends OlatTestCase {
 		Assert.assertEquals(map, myCloneAlt.get(0));
 	}
 	
+	@Test
+	public void testCountStructureElementsFromOthers() {
+		Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("EP-tmp-");
+		
+		PortfolioStructureMap map = epStructureManager.createPortfolioDefaultMap("map-el", "map-element");
+		epStructureManager.savePortfolioStructure(map);
+		dbInstance.commitAndCloseSession();
+		
+		//clone the template
+		int count = epStructureManager.countStructureElementsFromOthers(user, null);
+		Assert.assertEquals(0, count);
+	}
+	
+	
 	
 	@Test
 	public void testMoveUp() {
diff --git a/src/test/java/org/olat/portfolio/manager/InvitationDAOTest.java b/src/test/java/org/olat/portfolio/manager/InvitationDAOTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..50fd1383186b7c35f27de4c05b16416a53675cb1
--- /dev/null
+++ b/src/test/java/org/olat/portfolio/manager/InvitationDAOTest.java
@@ -0,0 +1,105 @@
+/**
+ * <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.portfolio.manager;
+
+import java.util.Date;
+import java.util.UUID;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.basesecurity.Invitation;
+import org.olat.core.commons.persistence.DB;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 25.06.2014<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class InvitationDAOTest extends OlatTestCase {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private InvitationDAO invitationDao;
+	@Autowired
+	private EPPolicyManager policyManager;
+	
+	
+	@Test
+	public void createAndPersistInvitation() {
+		Invitation invitation = invitationDao.createAndPersistInvitation();
+		Assert.assertNotNull(invitation);
+		dbInstance.commit();
+		
+		Assert.assertNotNull(invitation);
+		Assert.assertNotNull(invitation.getKey());
+		Assert.assertNotNull(invitation.getBaseGroup());
+		Assert.assertNotNull(invitation.getToken());
+	}
+	
+	@Test
+	public void findInvitation_token() {
+		Invitation invitation = invitationDao.createAndPersistInvitation();
+		Assert.assertNotNull(invitation);
+		dbInstance.commitAndCloseSession();
+		
+		Invitation reloadedInvitation = invitationDao.findInvitation(invitation.getToken());
+		Assert.assertNotNull(reloadedInvitation);
+		Assert.assertNotNull(reloadedInvitation.getKey());
+		Assert.assertNotNull(reloadedInvitation.getBaseGroup());
+		Assert.assertEquals(invitation, reloadedInvitation);
+		Assert.assertEquals(invitation.getToken(), reloadedInvitation.getToken());
+	}
+	
+	@Test
+	public void hasInvitationPolicies_testHQL() {
+		String token = UUID.randomUUID().toString();
+		Date atDate = new Date();
+		boolean hasInvitation = invitationDao.hasInvitations(token, atDate);
+		Assert.assertFalse(hasInvitation);
+	}
+	
+	@Test
+	public void createAndUpdateInvitation() {
+		Invitation invitation = invitationDao.createAndPersistInvitation();
+		dbInstance.commit();
+		
+		invitation.setFirstName("Kanu");
+		invitation.setLastName("Unchou");
+		invitation.setMail("kanu.unchou@frentix.com");
+		Invitation updatedInvitation = invitationDao.update(invitation);
+		dbInstance.commit();
+		
+		Assert.assertEquals("Kanu", updatedInvitation.getFirstName());
+		Assert.assertEquals("Unchou", updatedInvitation.getLastName());
+		Assert.assertEquals("kanu.unchou@frentix.com", updatedInvitation.getMail());
+		
+		Invitation reloadedInvitation = invitationDao.findInvitation(invitation.getToken());
+		Assert.assertEquals("Kanu", reloadedInvitation.getFirstName());
+		Assert.assertEquals("Unchou", reloadedInvitation.getLastName());
+		Assert.assertEquals("kanu.unchou@frentix.com", reloadedInvitation.getMail());
+	}
+	
+	
+
+}
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index 67680537d45e4ced47986b5ee846b28e20ea65a2..8ea0619c5ec5fff2ada072f53390434196b7d4c5 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -169,7 +169,9 @@ import org.junit.runners.Suite;
 	org.olat.portfolio.PortfolioModuleTest.class,
 	org.olat.portfolio.EPArtefactManagerTest.class,
 	org.olat.portfolio.EPFrontendManagerTest.class,
-	org.olat.portfolio.EPStructureManagerTest.class,
+	org.olat.portfolio.manager.EPStructureManagerTest.class,
+	org.olat.portfolio.manager.EPPolicyManagerTest.class,
+	org.olat.portfolio.manager.InvitationDAOTest.class,
 	org.olat.portfolio.EPStructureToArtefactTest.class,
 	org.olat.portfolio.EPImportTest.class,
 	org.olat.modules.openmeetings.OpenMeetingsTest.class,