From c458fb7a5d1f96c9e1b76b341cccd37b310fec0f Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Tue, 11 Nov 2014 11:24:07 +0100
Subject: [PATCH] OO-1245: make the courses accessible via WebDAV to students

---
 .../calendar/CalendarWebDAVProvider.java      |   6 +-
 .../filechooser/FileChooserController.java    |   2 +-
 .../modules/bc/BriefcaseWebDAVProvider.java   |  11 +-
 .../commons/services/webdav/WebDAVModule.java | 110 +++++++++--------
 .../services/webdav/WebDAVProvider.java       |   4 +-
 .../services/webdav/_spring/webdavContext.xml |  64 ++--------
 .../webdav/manager/WebDAVAuthManager.java     |  30 +----
 .../webdav/manager/WebDAVManagerImpl.java     |  55 +++------
 .../manager/WebDAVProviderNamedContainer.java |  12 +-
 .../webdav/servlets/WebDAVDispatcherImpl.java |  31 +----
 .../webdav/ui/WebDAVAdminController.java      |  22 +++-
 .../ui/_i18n/LocalStrings_de.properties       |   2 +
 .../ui/_i18n/LocalStrings_en.properties       |   2 +
 .../java/org/olat/course/CourseFactory.java   |   2 +-
 .../course/CoursefolderWebDAVMergeSource.java |  68 ++++++++---
 .../CoursefolderWebDAVNamedContainer.java     |   7 +-
 .../course/CoursefolderWebDAVProvider.java    |   9 +-
 src/main/java/org/olat/course/ICourse.java    |   3 +
 .../olat/course/MergedCourseContainer.java    | 115 +++++++++++++++++-
 .../org/olat/course/PersistingCourseImpl.java |  27 +++-
 .../course/run/CourseRuntimeController.java   |   2 +-
 .../group/GroupfoldersWebDAVProvider.java     |   8 +-
 .../PersonalFolderControllerCreator.java      |   4 +-
 .../SharedFolderWebDAVProvider.java           |   7 +-
 .../olat/repository/RepositoryManager.java    |  64 +++++++++-
 .../olat/repository/RepositoryService.java    |   1 -
 .../RepositorySearchController.java           |   2 +-
 .../manager/RepositoryEntryDAO.java           |   3 +-
 .../user/restapi/UserCoursesWebService.java   |   4 +-
 .../services/webdav/WebDAVCommandsTest.java   |  87 ++++++++++++-
 .../commons/services/webdav/webdav_course.zip | Bin 0 -> 24513 bytes
 .../repository/RepositoryManagerTest.java     |  39 +++++-
 .../manager/RepositoryEntryDAOTest.java       |   7 ++
 .../java/org/olat/test/JunitTestHelper.java   |  26 ++++
 .../olat/test/file_resources/Basic_course.zip | Bin 0 -> 14021 bytes
 35 files changed, 573 insertions(+), 263 deletions(-)
 create mode 100644 src/test/java/org/olat/core/commons/services/webdav/webdav_course.zip
 create mode 100644 src/test/java/org/olat/test/file_resources/Basic_course.zip

diff --git a/src/main/java/org/olat/commons/calendar/CalendarWebDAVProvider.java b/src/main/java/org/olat/commons/calendar/CalendarWebDAVProvider.java
index 63a6f7f6d6b..28474840361 100644
--- a/src/main/java/org/olat/commons/calendar/CalendarWebDAVProvider.java
+++ b/src/main/java/org/olat/commons/calendar/CalendarWebDAVProvider.java
@@ -28,7 +28,7 @@ package org.olat.commons.calendar;
 import java.io.File;
 
 import org.olat.core.commons.services.webdav.WebDAVProvider;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.LocalFileImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VirtualContainer;
@@ -38,12 +38,12 @@ public class CalendarWebDAVProvider implements WebDAVProvider {
 
 	private static final String MOUNT_POINT = "calendars";
 	
-	public VFSContainer getContainer(Identity identity) {
+	public VFSContainer getContainer(IdentityEnvironment identityEnv) {
 		VirtualContainer calendars = new VirtualContainer("calendars");
 		calendars.setLocalSecurityCallback(new ReadOnlyCallback());
 		// get private calendar
 		CalendarManager calendarManager = CalendarManagerFactory.getInstance().getCalendarManager();
-		File fPersonalCalendar = calendarManager.getCalendarICalFile(CalendarManager.TYPE_USER, identity.getName());
+		File fPersonalCalendar = calendarManager.getCalendarICalFile(CalendarManager.TYPE_USER, identityEnv.getIdentity().getName());
 		calendars.addItem(new LocalFileImpl(fPersonalCalendar));
 		return calendars;
 	}
diff --git a/src/main/java/org/olat/commons/file/filechooser/FileChooserController.java b/src/main/java/org/olat/commons/file/filechooser/FileChooserController.java
index c4a5cb6bc4f..7046f845c91 100644
--- a/src/main/java/org/olat/commons/file/filechooser/FileChooserController.java
+++ b/src/main/java/org/olat/commons/file/filechooser/FileChooserController.java
@@ -179,7 +179,7 @@ public class FileChooserController extends BasicController {
 					}
 					selectedContainer = null;
 					if (selectedFolder == 0) { // personal folder
-						selectedContainer = PersonalFolderManager.getInstance().getContainer(ureq.getIdentity());
+						selectedContainer = PersonalFolderManager.getInstance().getContainer(ureq.getUserSession().getIdentityEnvironment());
 					} else { // process other folders
 						selectedContainer = containerRefs.get(selectedFolder - 1);
 					}
diff --git a/src/main/java/org/olat/core/commons/modules/bc/BriefcaseWebDAVProvider.java b/src/main/java/org/olat/core/commons/modules/bc/BriefcaseWebDAVProvider.java
index 8111a26ee74..47dbbb2c85f 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/BriefcaseWebDAVProvider.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/BriefcaseWebDAVProvider.java
@@ -28,6 +28,7 @@ package org.olat.core.commons.modules.bc;
 
 import org.olat.core.commons.services.webdav.WebDAVProvider;
 import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.VFSContainer;
 /**
  * 
@@ -39,13 +40,19 @@ public class BriefcaseWebDAVProvider implements WebDAVProvider {
 	public String getMountPoint() {
 		return MOUNTPOINT;
 	}
+	
+	public VFSContainer getContainer(Identity identity) {
+		// merge /public and /private
+		return new BriefcaseWebDAVMergeSource(identity);
+	}
 
 	/**
 	 * @see org.olat.core.commons.services.webdav.WebDAVProvider#getContainer(org.olat.core.id.Identity)
 	 */
-	public VFSContainer getContainer(Identity identity) {
+	@Override
+	public VFSContainer getContainer(IdentityEnvironment identityEnv) {
 		// merge /public and /private
-		return new BriefcaseWebDAVMergeSource(identity);
+		return getContainer(identityEnv.getIdentity());
 	}
 	
 	protected String getRootPathFor(Identity identity) {
diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java
index ec0b089f5cd..1400accfc9b 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/WebDAVModule.java
@@ -26,34 +26,47 @@
 
 package org.olat.core.commons.services.webdav;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.olat.core.configuration.AbstractOLATModule;
+import org.olat.core.configuration.AbstractSpringModule;
 import org.olat.core.configuration.ConfigOnOff;
-import org.olat.core.configuration.PersistedProperties;
-import org.olat.core.logging.AssertException;
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
 
-public class WebDAVModule extends AbstractOLATModule implements ConfigOnOff {
-	
-	private static final OLog log = Tracing.createLoggerFor(WebDAVModule.class);
+@Service("webdavModule")
+public class WebDAVModule extends AbstractSpringModule implements ConfigOnOff {
 
 	private static final String WEBDAV_ENABLED = "webdav.enabled";
 	private static final String WEBDAV_LINKS_ENABLED = "webdav.links.enabled";
 	private static final String DIGEST_AUTH_ENABLED = "auth.digest.enabled";
 	private static final String TERMS_FOLDERS_ENABLED = "webdav.termsfolders.enabled";
+	private static final String LEARNERS_BOOKMARKS_COURSE = "webdav.learners.bookmarks.courses";
+	private static final String LEARNERS_PARTICIPATING_COURSES = "webdav.learners.participating.courses";
 	
-	private Map<String, WebDAVProvider> webdavProviders;
+	@Autowired
+	private List<WebDAVProvider> webdavProviders;
 
+	@Value("${webdav.enabled:true}")
 	private boolean enabled;
+	@Value("${webdav.links.enabled:true}")
 	private boolean linkEnabled;
+	@Value("${auth.digest.enabled:true}")
 	private boolean digestAuthenticationEnabled;
+	@Value("${webdav.termsfolders.enabled:true}")
 	private boolean termsFoldersEnabled;
+
+	private boolean enableLearnersBookmarksCourse;
+	private boolean enableLearnersParticipatingCourses;
+	
+	@Autowired
+	public WebDAVModule(CoordinatorManager coordinatorManager) {
+		super(coordinatorManager);
+	}
 	
 	@Override
 	public void init() {
@@ -77,33 +90,25 @@ public class WebDAVModule extends AbstractOLATModule implements ConfigOnOff {
 		if(StringHelper.containsNonWhitespace(termsFoldersEnabledObj)) {
 			termsFoldersEnabled = "true".equals(termsFoldersEnabledObj);
 		}
-
+		
+		String learnersBookmarksCourseObj = getStringPropertyValue(LEARNERS_BOOKMARKS_COURSE, true);
+		enableLearnersBookmarksCourse = "true".equals(learnersBookmarksCourseObj);
+		String learnersParticipatingCoursesObj = getStringPropertyValue(LEARNERS_PARTICIPATING_COURSES, true);
+		enableLearnersParticipatingCourses = "true".equals(learnersParticipatingCoursesObj);
 	}
 	
-	@Override
-	protected void initDefaultProperties() {
-		enabled = getBooleanConfigParameter(WEBDAV_ENABLED, true);
-		linkEnabled = getBooleanConfigParameter(WEBDAV_LINKS_ENABLED, true);
-		digestAuthenticationEnabled = getBooleanConfigParameter(DIGEST_AUTH_ENABLED, true);
-		termsFoldersEnabled = getBooleanConfigParameter(TERMS_FOLDERS_ENABLED, true);
-	}
-
 	@Override
 	protected void initFromChangedProperties() {
 		init();
 	}
-
-	@Override
-	public void setPersistedProperties(PersistedProperties persistedProperties) {
-		this.moduleConfigProperties = persistedProperties;
-	}
-
+	
 	@Override
 	public boolean isEnabled() {
 		return enabled;
 	}
 	
 	public void setEnabled(boolean enabled) {
+		this.enabled = enabled;
 		String enabledStr = enabled ? "true" : "false";
 		setStringProperty(WEBDAV_ENABLED, enabledStr, true);
 	}
@@ -113,6 +118,7 @@ public class WebDAVModule extends AbstractOLATModule implements ConfigOnOff {
 	}
 
 	public void setLinkEnabled(boolean linkEnabled) {
+		this.linkEnabled = linkEnabled;
 		String enabledStr = linkEnabled ? "true" : "false";
 		setStringProperty(WEBDAV_LINKS_ENABLED, enabledStr, true);
 	}
@@ -122,52 +128,50 @@ public class WebDAVModule extends AbstractOLATModule implements ConfigOnOff {
 	}
 	
 	public void setDigestAuthenticationEnabled(boolean digestAuthenticationEnabled) {
+		this.digestAuthenticationEnabled = digestAuthenticationEnabled;
 		String enabledStr = digestAuthenticationEnabled ? "true" : "false";
 		setStringProperty(DIGEST_AUTH_ENABLED, enabledStr, true);
 	}
 	
+	public boolean isTermsFoldersEnabled() {
+		return termsFoldersEnabled;
+	}
 
 	public void setTermsFoldersEnabled(boolean termsFoldersEnabled) {
+		this.termsFoldersEnabled = termsFoldersEnabled;
 		String enabledStr = termsFoldersEnabled ? "true" : "false";
 		setStringProperty(TERMS_FOLDERS_ENABLED, enabledStr, true);
 	}
 
-	public boolean isTermsFoldersEnabled() {
-		return termsFoldersEnabled;
+	public boolean isEnableLearnersBookmarksCourse() {
+		return enableLearnersBookmarksCourse;
 	}
 
+	public void setEnableLearnersBookmarksCourse(boolean enabled) {
+		this.enableLearnersBookmarksCourse = enabled;
+		setStringProperty(LEARNERS_BOOKMARKS_COURSE, enabled ? "true" : "false", true);
+	}
+
+	public boolean isEnableLearnersParticipatingCourses() {
+		return enableLearnersParticipatingCourses;
+	}
+
+	public void setEnableLearnersParticipatingCourses(boolean enabled) {
+		this.enableLearnersParticipatingCourses = enabled;
+		setStringProperty(LEARNERS_PARTICIPATING_COURSES, enabled ? "true" : "false", true);
+	}
 
 	/**
 	 * Return an unmodifiable map
 	 * @return
 	 */
 	public Map<String, WebDAVProvider> getWebDAVProviders() {
-		return Collections.unmodifiableMap(webdavProviders); 
-	}
-	
-	/**
-	 * Set the list of webdav providers.
-	 * @param webdavProviders
-	 */
-	public void setWebdavProviderList(List<WebDAVProvider> webdavProviders) {
-		if (webdavProviders == null) return;//nothing to do
-		
-		for (WebDAVProvider provider : webdavProviders) {
-			addWebdavProvider(provider);
-		}
-	}
-	
-	/**
-	 * Add a new webdav provider.
-	 * @param provider
-	 */
-	public void addWebdavProvider(WebDAVProvider provider) {
-		if (webdavProviders == null) {
-			webdavProviders = new HashMap<String, WebDAVProvider>();
+		Map<String,WebDAVProvider> providerMap = new HashMap<>();
+		if(webdavProviders != null) {
+			for(WebDAVProvider webdavProvider:webdavProviders) {
+				providerMap.put(webdavProvider.getMountPoint(), webdavProvider);
+			}
 		}
-		if (webdavProviders.containsKey(provider.getMountPoint()))
-			throw new AssertException("May not add two providers with the same mount point.");
-		webdavProviders.put(provider.getMountPoint(), provider);
-		log.info("Adding webdav mountpoint '" + provider.getMountPoint() + "'.");
+		return providerMap; 
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProvider.java b/src/main/java/org/olat/core/commons/services/webdav/WebDAVProvider.java
index 010e902f966..e0901275e19 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/WebDAVProvider.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/WebDAVProvider.java
@@ -26,7 +26,7 @@
 
 package org.olat.core.commons.services.webdav;
 
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.VFSContainer;
 
 public interface WebDAVProvider {
@@ -41,6 +41,6 @@ public interface WebDAVProvider {
 	 * @param identity
 	 * @return
 	 */
-	public VFSContainer getContainer(Identity identity);
+	public VFSContainer getContainer(IdentityEnvironment identityEnv);
 	
 }
diff --git a/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml b/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml
index d10b1624773..17cae7aa32e 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml
+++ b/src/main/java/org/olat/core/commons/services/webdav/_spring/webdavContext.xml
@@ -1,64 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:context="http://www.springframework.org/schema/context" 
 	xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
-  http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-	<!-- WebDAV module -->
-	<bean id="webdavModule" class="org.olat.core.commons.services.webdav.WebDAVModule" depends-on="org.olat.core.util.WebappHelper">
-		<property name="persistedProperties">
-	  		<bean class="org.olat.core.configuration.PersistedProperties" scope="prototype" init-method="init" destroy-method="destroy" 
-	  			depends-on="coordinatorManager,org.olat.core.util.WebappHelper">
-	    		<constructor-arg index="0" ref="coordinatorManager"/>
-	    		<constructor-arg index="1" ref="webdavModule" />
-	  		</bean>
-		</property>
-		<property name="webdavProviderList">
-			<list>
-				<ref bean="webdav_briefcase"/>
-				<ref bean="webdav_coursefolders"/>
-				<ref bean="webdav_sharedfolders"/>
-				<ref bean="webdav_groupfolders"/>
-			</list>
-		</property>
-	</bean>
+  http://www.springframework.org/schema/beans/spring-beans.xsd
+  http://www.springframework.org/schema/context 
+  http://www.springframework.org/schema/context/spring-context.xsd">
 	
-	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
-		<property name="targetObject" ref="webdavModule" />
-		<property name="targetMethod" value="init" />
-		<property name="arguments">
-			<value>
-				webdav.enabled=${webdav.enabled}
-				webdav.links.enabled=${webdav.links.enabled}
-				auth.digest.enabled=${auth.digest.enabled}
-				webdav.termsfolders.enabled=${webdav.termsfolders.enabled}
-			</value>
-		</property>
-	</bean>
-
-	<bean id="webDAVManager" class="org.olat.core.commons.services.webdav.manager.WebDAVManagerImpl" init-method="init">
-		<constructor-arg ref="coordinatorManager"/>
-		<property name="sessionManager" ref="userSessionManager" />
-		<property name="webDAVAuthManager" ref="webDAVAuthenticationSpi" />
-		<property name="webDAVModule" ref="webdavModule" />
-	</bean>
-	
-	<bean id="webDAVDispatcher" class="org.olat.core.commons.services.webdav.servlets.WebDAVDispatcherImpl">
-		<property name="lockManager" ref="vfsLockManager" />
-		<property name="webDAVManager" ref="webDAVManager" />
-		<property name="webDAVModule" ref="webdavModule" />
-	</bean>
-	
-	<bean id="webDAVAuthenticationSpi" class="org.olat.core.commons.services.webdav.manager.WebDAVAuthManager" >
-		<property name="securityManager" ref="baseSecurityManager" />
-		<property name="olatAuthenticationSpi" ref="olatAuthenticationSpi" />
-		<property name="webDAVModule" ref="webdavModule" />
-	</bean>
+	<context:component-scan base-package="org.olat.core.commons.services.webdav" />
 	
-	<bean id="webdav_briefcase" class="org.olat.core.commons.modules.bc.BriefcaseWebDAVProvider" scope="prototype" />
-	<bean id="webdav_coursefolders" class="org.olat.course.CoursefolderWebDAVProvider" scope="prototype" />
-	<bean id="webdav_sharedfolders" class="org.olat.modules.sharedfolder.SharedFolderWebDAVProvider" scope="prototype" >
+	<bean id="webdav_briefcase" class="org.olat.core.commons.modules.bc.BriefcaseWebDAVProvider" />
+	<bean id="webdav_coursefolders" class="org.olat.course.CoursefolderWebDAVProvider" />
+	<bean id="webdav_sharedfolders" class="org.olat.modules.sharedfolder.SharedFolderWebDAVProvider" >
 				<!-- 
 					Optional configuration: specify shared folder that should be visible to normal users. 
 					By default, shared folders are only mounted for shared folder owners (read/write). By
@@ -84,7 +38,7 @@
 					</property>
 				-->
 	</bean> 
-	<bean id="webdav_groupfolders" class="org.olat.group.GroupfoldersWebDAVProvider" scope="prototype">
+	<bean id="webdav_groupfolders" class="org.olat.group.GroupfoldersWebDAVProvider">
 		<property name="collaborationManager" ref="collaborationManager" />
 	</bean>
 	
diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java
index b7d781086df..6a7fd75d551 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVAuthManager.java
@@ -32,6 +32,8 @@ import org.olat.core.util.Encoder.Algorithm;
 import org.olat.login.LoginModule;
 import org.olat.login.auth.AuthenticationSPI;
 import org.olat.login.auth.OLATAuthManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 
 /**
@@ -43,6 +45,7 @@ import org.olat.login.auth.OLATAuthManager;
  * Initial Date:  13 apr. 2010 <br>
  * @author srosse, stephane.rosse@frentix.com
  */
+@Service("webDAVAuthenticationSpi")
 public class WebDAVAuthManager implements AuthenticationSPI {
 	
 	public static final String PROVIDER_WEBDAV = "WEBDAV";
@@ -50,34 +53,13 @@ public class WebDAVAuthManager implements AuthenticationSPI {
 	
 	private static final OLog log = Tracing.createLoggerFor(WebDAVAuthManager.class);
 
+	@Autowired
 	private WebDAVModule webDAVModule;
+	@Autowired
 	private BaseSecurity securityManager;
+	@Autowired
 	private OLATAuthManager olatAuthenticationSpi;
 	
-	/**
-	 * [used by Spring]
-	 * @param webDAVModule
-	 */
-	public void setWebDAVModule(WebDAVModule webDAVModule) {
-		this.webDAVModule = webDAVModule;
-	}
-
-	/**
-	 * [used by Spring]
-	 * @param securityManager
-	 */
-	public void setSecurityManager(BaseSecurity securityManager) {
-		this.securityManager = securityManager;
-	}
-
-	/**
-	 * [used by Spring]
-	 * @param olatAuthenticationSpi
-	 */
-	public void setOlatAuthenticationSpi(OLATAuthManager olatAuthenticationSpi) {
-		this.olatAuthenticationSpi = olatAuthenticationSpi;
-	}
-	
 	public Identity digestAuthentication(String httpMethod, DigestAuthentication digestAuth) {
 		String username = digestAuth.getUsername();
 		
diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java
index 91cdde54f83..c9d042f2246 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVManagerImpl.java
@@ -43,6 +43,7 @@ import org.olat.core.commons.services.webdav.WebDAVProvider;
 import org.olat.core.commons.services.webdav.servlets.WebResourceRoot;
 import org.olat.core.helpers.Settings;
 import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.Roles;
 import org.olat.core.id.User;
 import org.olat.core.id.UserConstants;
@@ -56,6 +57,9 @@ import org.olat.core.util.vfs.MergeSource;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VirtualContainer;
 import org.olat.core.util.vfs.callbacks.ReadOnlyCallback;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 
 /**
  * Initial Date:  16.04.2003
@@ -66,50 +70,29 @@ import org.olat.core.util.vfs.callbacks.ReadOnlyCallback;
  * Comment:  
  * 
  */
-public class WebDAVManagerImpl implements WebDAVManager {
+@Service("webDAVManager")
+public class WebDAVManagerImpl implements WebDAVManager, InitializingBean {
 	private static boolean enabled = true;
 	
 	public static final String BASIC_AUTH_REALM = "OLAT WebDAV Access";
 	private CoordinatorManager coordinatorManager;
 
 	private CacheWrapper<CacheKey,UserSession> timedSessionCache;
-	
+
+	@Autowired
 	private UserSessionManager sessionManager;
+	@Autowired
 	private WebDAVAuthManager webDAVAuthManager;
+	@Autowired
 	private WebDAVModule webdavModule;
 
-	/**
-	 * [spring]
-	 */
-	private WebDAVManagerImpl(CoordinatorManager coordinatorManager) {
+	@Autowired
+	public WebDAVManagerImpl(CoordinatorManager coordinatorManager) {
 		this.coordinatorManager = coordinatorManager;
 	}
 
-	/**
-	 * [used by Spring]
-	 * @param sessionManager
-	 */
-	public void setSessionManager(UserSessionManager sessionManager) {
-		this.sessionManager = sessionManager;
-	}
-	
-	/**
-	 * [used by Spring]
-	 * @param webDAVAuthManager
-	 */
-	public void setWebDAVAuthManager(WebDAVAuthManager webDAVAuthManager) {
-		this.webDAVAuthManager = webDAVAuthManager;
-	}
-	
-	/**
-	 * [used by Spring]
-	 * @param webdavModule
-	 */
-	public void setWebDAVModule(WebDAVModule webdavModule) {
-		this.webdavModule = webdavModule;
-	}
-	
-	public void init() {
+	@Override
+	public void afterPropertiesSet() throws Exception {
 		timedSessionCache = coordinatorManager.getCoordinator().getCacher().getCache(WebDAVManager.class.getSimpleName(), "webdav");
 	}
 	
@@ -126,15 +109,15 @@ public class WebDAVManagerImpl implements WebDAVManager {
 			return fdc;
 		}
 		
-		Identity identity = usess.getIdentity();
-		VFSContainer webdavContainer = getMountableRoot(identity);
+		IdentityEnvironment identityEnv = usess.getIdentityEnvironment();
+		VFSContainer webdavContainer = getMountableRoot(identityEnv);
 		
 		//create the / folder
 		VirtualContainer rootContainer = new VirtualContainer("");
 		rootContainer.addItem(webdavContainer);
 		rootContainer.setLocalSecurityCallback(new ReadOnlyCallback());
 
-		fdc = new VFSResourceRoot(identity, rootContainer);
+		fdc = new VFSResourceRoot(identityEnv.getIdentity(), rootContainer);
 		usess.putEntry("_DIRCTX", fdc);
 		return fdc;
 	}
@@ -143,11 +126,11 @@ public class WebDAVManagerImpl implements WebDAVManager {
 	 * Returns a mountable root containing all entries which will be exposed to the webdav mount.
 	 * @return
 	 */
-	private VFSContainer getMountableRoot(Identity identity) {
+	private VFSContainer getMountableRoot(IdentityEnvironment identityEnv) {
 		MergeSource vfsRoot = new MergeSource(null, "webdav");
 		for (Map.Entry<String, WebDAVProvider> entry : webdavModule.getWebDAVProviders().entrySet()) {
 			WebDAVProvider provider = entry.getValue();
-			vfsRoot.addContainer(new WebDAVProviderNamedContainer(identity, provider));
+			vfsRoot.addContainer(new WebDAVProviderNamedContainer(identityEnv, provider));
 		}
 		return vfsRoot;
 	}
diff --git a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java
index d1d9589fdaf..f1dfd83c336 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/manager/WebDAVProviderNamedContainer.java
@@ -20,7 +20,7 @@
 package org.olat.core.commons.services.webdav.manager;
 
 import org.olat.core.commons.services.webdav.WebDAVProvider;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.NamedContainerImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.filters.VFSItemFilter;
@@ -31,25 +31,25 @@ import org.olat.core.util.vfs.filters.VFSItemFilter;
  */
 public class WebDAVProviderNamedContainer extends NamedContainerImpl {
 	
-	private Identity identity;
+	private IdentityEnvironment identityEnv;
 	private final WebDAVProvider provider;
 	private VFSContainer parentContainer;
 	
-	public WebDAVProviderNamedContainer(Identity identity, WebDAVProvider provider) {
+	public WebDAVProviderNamedContainer(IdentityEnvironment identityEnv, WebDAVProvider provider) {
 		super(provider.getMountPoint(), null);
 		this.provider = provider;
-		this.identity = identity;
+		this.identityEnv = identityEnv;
 	}
 
 	@Override
 	public VFSContainer getDelegate() {
 		if(super.getDelegate() == null) {
-			setDelegate(provider.getContainer(identity));
+			setDelegate(provider.getContainer(identityEnv));
 			if(parentContainer != null) {
 				super.setParentContainer(parentContainer);
 				parentContainer = null;
 			}
-			identity = null;
+			identityEnv = null;
 		}
 		return super.getDelegate();
 	}
diff --git a/src/main/java/org/olat/core/commons/services/webdav/servlets/WebDAVDispatcherImpl.java b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebDAVDispatcherImpl.java
index 7d6d535ae1d..137a9b6761a 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/servlets/WebDAVDispatcherImpl.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/servlets/WebDAVDispatcherImpl.java
@@ -59,6 +59,8 @@ import org.olat.core.util.vfs.QuotaExceededException;
 import org.olat.core.util.vfs.VFSItem;
 import org.olat.core.util.vfs.lock.LockInfo;
 import org.olat.core.util.vfs.lock.VFSLockManagerImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -130,7 +132,7 @@ import org.xml.sax.SAXException;
  * @author Remy Maucherat
  * @version $Id$
  */
-
+@Service("webDAVDispatcher")
 public class WebDAVDispatcherImpl
     extends DefaultDispatcher implements WebDAVDispatcher, Dispatcher {
 
@@ -225,37 +227,16 @@ public class WebDAVDispatcherImpl
      */
     private boolean allowSpecialPaths = false;
     
+    @Autowired
     private VFSLockManagerImpl lockManager;
+    @Autowired
     private WebDAVManager webDAVManager;
+    @Autowired
     private WebDAVModule webDAVModule;
 
     public WebDAVDispatcherImpl() {
     	//
     }
-    
-    /**
-     * [used by Spring]
-     * @param lockManager
-     */
-    public void setLockManager(VFSLockManagerImpl lockManager) {
-		this.lockManager = lockManager;
-	}
-
-    /**
-     * [used by Spring]
-     * @param webDAVManager
-     */
-	public void setWebDAVManager(WebDAVManager webDAVManager) {
-		this.webDAVManager = webDAVManager;
-	}
-	
-	/**
-	 * [used by Spring]
-	 * @param webDAVModule
-	 */
-	public void setWebDAVModule(WebDAVModule webDAVModule) {
-		this.webDAVModule = webDAVModule;
-	}
 
 	@Override
 	protected WebResourceRoot getResources(HttpServletRequest req) {
diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java b/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java
index 7093868a9c4..0b9a4a83f38 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java
+++ b/src/main/java/org/olat/core/commons/services/webdav/ui/WebDAVAdminController.java
@@ -37,7 +37,9 @@ import org.olat.core.gui.control.WindowControl;
  */
 public class WebDAVAdminController extends FormBasicController {
 	
-	private MultipleSelectionElement enableModuleEl, enableLinkEl, enableDigestEl, enableTermsFoldersEl;
+	private MultipleSelectionElement enableModuleEl, enableLinkEl, enableDigestEl, enableTermsFoldersEl,
+			learnersAsParticipantEl, learnersBookmarkEl;
+	
 	private final WebDAVModule webDAVModule;
 	
 	public WebDAVAdminController(UserRequest ureq, WindowControl wControl) {
@@ -76,8 +78,18 @@ public class WebDAVAdminController extends FormBasicController {
 		enableTermsFoldersEl.select("xx", webDAVModule.isTermsFoldersEnabled());
 		enableTermsFoldersEl.addActionListener(FormEvent.ONCHANGE);
 		enableTermsFoldersEl.setEnabled(enabled);
+
+		uifactory.addSpacerElement("spacer2", formLayout, false);
 		
+		learnersAsParticipantEl = uifactory.addCheckboxesHorizontal("learnersParticipants", "webdav.for.learners.participants", formLayout, new String[]{"xx"}, values);
+		learnersAsParticipantEl.select("xx", webDAVModule.isEnableLearnersParticipatingCourses());
+		learnersAsParticipantEl.addActionListener(FormEvent.ONCHANGE);
+		learnersAsParticipantEl.setEnabled(enabled);
 		
+		learnersBookmarkEl = uifactory.addCheckboxesHorizontal("learnerBookmarks", "webdav.for.learners.bookmarks", formLayout, new String[]{"xx"}, values);
+		learnersBookmarkEl.select("xx", webDAVModule.isEnableLearnersBookmarksCourse());
+		learnersBookmarkEl.addActionListener(FormEvent.ONCHANGE);
+		learnersBookmarkEl.setEnabled(enabled);
 	}
 
 	@Override
@@ -93,6 +105,8 @@ public class WebDAVAdminController extends FormBasicController {
 			enableLinkEl.setEnabled(enabled);
 			enableDigestEl.setEnabled(enabled);
 			enableTermsFoldersEl.setEnabled(enabled);
+			learnersAsParticipantEl.setEnabled(enabled);
+			learnersBookmarkEl.setEnabled(enabled);
 		} else if(source == enableLinkEl) {
 			boolean enabled = enableLinkEl.isAtLeastSelected(1);
 			webDAVModule.setLinkEnabled(enabled);
@@ -102,6 +116,12 @@ public class WebDAVAdminController extends FormBasicController {
 		} else if(source == enableTermsFoldersEl) {
 			boolean enabled = enableTermsFoldersEl.isAtLeastSelected(1);
 			webDAVModule.setTermsFoldersEnabled(enabled);
+		} else if(source == learnersAsParticipantEl) {
+			boolean enabled = learnersAsParticipantEl.isAtLeastSelected(1);
+			webDAVModule.setEnableLearnersParticipatingCourses(enabled);
+		} else if(source == learnersBookmarkEl) {
+			boolean enabled = learnersBookmarkEl.isAtLeastSelected(1);
+			webDAVModule.setEnableLearnersBookmarksCourse(enabled);
 		}
 		super.formInnerEvent(ureq, source, event);
 	}
diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties
index 90ed3399016..d5261b43932 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties
@@ -18,3 +18,5 @@ webdav.link=WebDAV Links anzeigen
 webdav.module=WebDAV Zugang
 webdav.on=ein
 webdav.termsfolders=Kurse nach Semesterdaten gruppieren
+webdav.for.learners.participants=Zugriff für Studenten Kursen
+webdav.for.learners.bookmarks=Zugriff für Studenten Favoriten
diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties
index e7f3ad82c50..e7d2f00badd 100644
--- a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties
@@ -18,4 +18,6 @@ webdav.link=Show WebDAV links
 webdav.module=WebDAV access
 webdav.on=enabled
 webdav.termsfolders=Group courses by semester terms
+webdav.for.learners.participants=Enable access for courses where user is participant
+webdav.for.learners.bookmarks=Enable for courses that users marked as favorite
 
diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java
index d10df68b53c..d7883276dff 100644
--- a/src/main/java/org/olat/course/CourseFactory.java
+++ b/src/main/java/org/olat/course/CourseFactory.java
@@ -429,7 +429,7 @@ public class CourseFactory extends BasicManager {
 			targetCourse.saveEditorTreeModel();
 
 			// copy course folder
-			File fSourceCourseFolder = sourceCourse.getIsolatedCourseFolder().getBasefile();
+			File fSourceCourseFolder = sourceCourse.getIsolatedCourseBaseFolder();
 			if (fSourceCourseFolder.exists()) FileUtils.copyDirToDir(fSourceCourseFolder, fTargetCourseBasePath, false, "copy course folder");
 			
 			// copy folder nodes directories
diff --git a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
index 49907ab320f..363f6eb095f 100644
--- a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
+++ b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java
@@ -21,14 +21,16 @@ package org.olat.course;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.services.webdav.WebDAVModule;
 import org.olat.core.commons.services.webdav.manager.WebDAVMergeSource;
 import org.olat.core.commons.services.webdav.servlets.RequestUtil;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.NamedContainerImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VirtualContainer;
@@ -42,21 +44,27 @@ import org.olat.repository.model.RepositoryEntryLifecycle;
  * 
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
-class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {	
-	public CoursefolderWebDAVMergeSource(Identity identity) {
-		super(identity);
+class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {
+	
+	private final IdentityEnvironment identityEnv;
+	
+	private final WebDAVModule webDAVModule;
+	private final RepositoryManager repositoryManager;
+	
+	public CoursefolderWebDAVMergeSource(IdentityEnvironment identityEnv) {
+		super(identityEnv.getIdentity());
+		this.identityEnv = identityEnv;
+		webDAVModule = CoreSpringFactory.getImpl(WebDAVModule.class);
+		repositoryManager = CoreSpringFactory.getImpl(RepositoryManager.class);
 	}
 	
 	@Override
 	protected List<VFSContainer> loadMergedContainers() {
-		RepositoryManager rm = RepositoryManager.getInstance();
-		List<RepositoryEntry> courseEntries = rm.queryByEditor(getIdentity(), CourseModule.getCourseTypeName());
 		List<VFSContainer> containers = new ArrayList<>();
 		
 		Map<String, VFSContainer> terms = null;
 		VirtualContainer noTermContainer = null;
 		
-		WebDAVModule webDAVModule = CoreSpringFactory.getImpl(WebDAVModule.class);
 		boolean useTerms = webDAVModule.isTermsFoldersEnabled();
 		if (useTerms) {
 			// prepare no-terms folder for all resources without semester term info or private date
@@ -64,10 +72,45 @@ class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {
 			noTermContainer = new VirtualContainer("other");
 		}
 		
+		Set<RepositoryEntry> duplicates = new HashSet<>();
+		List<RepositoryEntry> editorEntries = repositoryManager.queryByEditor(getIdentity(), "CourseModule");
+		appendCourses(editorEntries, true, containers, useTerms,  terms, noTermContainer, duplicates);
+		
+		//add courses as participant
+		if(webDAVModule.isEnableLearnersParticipatingCourses()) {
+			List<RepositoryEntry> participantEntries = repositoryManager.getLearningResourcesAsStudent(getIdentity(), "CourseModule", 0, -1);
+			appendCourses(participantEntries, false, containers, useTerms,  terms, noTermContainer, duplicates);
+		}
+		
+		//add bookmarked courses
+		if(webDAVModule.isEnableLearnersBookmarksCourse()) {
+			List<RepositoryEntry> bookmarkedEntries = repositoryManager.getLearningResourcesAsBookmark(getIdentity(), identityEnv.getRoles(), "CourseModule", 0, -1);
+			appendCourses(bookmarkedEntries, false, containers, useTerms,  terms, noTermContainer, duplicates);
+		}
+
+		if (useTerms) {
+			// add no-terms folder if any have been found
+			if (noTermContainer.getItems().size() > 0) {
+				addContainerToList(noTermContainer, containers);
+			}
+		}
+
+		return containers;
+	}
+	
+	private void appendCourses(List<RepositoryEntry> courseEntries, boolean editor, List<VFSContainer> containers,
+			boolean useTerms, Map<String, VFSContainer> terms, VirtualContainer noTermContainer,
+			Set<RepositoryEntry> duplicates) {	
+		
 		// Add all found repo entries to merge source
 		for (RepositoryEntry re:courseEntries) {
+			if(duplicates.contains(re)) {
+				continue;
+			}
+			duplicates.add(re);
+			
 			String courseTitle = RequestUtil.normalizeFilename(re.getDisplayname());
-			NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(courseTitle, re.getOlatResource());
+			NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(courseTitle, re.getOlatResource(), editor ? null : identityEnv);
 			
 			if (useTerms) {
 				RepositoryEntryLifecycle lc = re.getLifecycle();
@@ -92,14 +135,5 @@ class CoursefolderWebDAVMergeSource extends WebDAVMergeSource {
 				addContainerToList(cfContainer, containers);				
 			}
 		}
-
-		if (useTerms) {
-			// add no-terms folder if any have been found
-			if (noTermContainer.getItems().size() > 0) {
-				addContainerToList(noTermContainer, containers);
-			}
-		}
-
-		return containers;
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/CoursefolderWebDAVNamedContainer.java b/src/main/java/org/olat/course/CoursefolderWebDAVNamedContainer.java
index b7895f1f04e..9b33e6266ed 100644
--- a/src/main/java/org/olat/course/CoursefolderWebDAVNamedContainer.java
+++ b/src/main/java/org/olat/course/CoursefolderWebDAVNamedContainer.java
@@ -19,6 +19,7 @@
  */
 package org.olat.course;
 
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
@@ -37,10 +38,12 @@ class CoursefolderWebDAVNamedContainer extends NamedContainerImpl {
 	
 	private OLATResourceable res;
 	private VFSContainer parentContainer;
+	private IdentityEnvironment identityEnv;
 	
-	public CoursefolderWebDAVNamedContainer(String courseTitle, OLATResourceable res) {
+	public CoursefolderWebDAVNamedContainer(String courseTitle, OLATResourceable res, IdentityEnvironment identityEnv) {
 		super(courseTitle, null);
 		this.res = OresHelper.clone(res);
+		this.identityEnv = identityEnv;
 	}
 
 	@Override
@@ -53,7 +56,7 @@ class CoursefolderWebDAVNamedContainer extends NamedContainerImpl {
 		if(super.getDelegate() == null) {
 			try {
 				ICourse course = CourseFactory.loadCourse(res.getResourceableId());
-				VFSContainer courseFolder = course.getCourseFolderContainer();
+				VFSContainer courseFolder = course.getCourseFolderContainer(identityEnv);
 				setDelegate(courseFolder);
 				if(parentContainer != null) {
 					super.setParentContainer(parentContainer);
diff --git a/src/main/java/org/olat/course/CoursefolderWebDAVProvider.java b/src/main/java/org/olat/course/CoursefolderWebDAVProvider.java
index 9a1f3f062e7..52eccf277dc 100644
--- a/src/main/java/org/olat/course/CoursefolderWebDAVProvider.java
+++ b/src/main/java/org/olat/course/CoursefolderWebDAVProvider.java
@@ -26,22 +26,23 @@
 package org.olat.course;
 
 import org.olat.core.commons.services.webdav.WebDAVProvider;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.VFSContainer;
 /**
  * 
  * Description:<br>
- * TODO: guido Class Description for CoursefolderWebDAVProvider
  */
 public class CoursefolderWebDAVProvider implements WebDAVProvider {
 
 	private static final String MOUNTPOINT = "coursefolders";
 
+	@Override
 	public String getMountPoint() {
 		return MOUNTPOINT;
 	}
 
-	public VFSContainer getContainer(Identity identity) {
-		return new CoursefolderWebDAVMergeSource(identity);
+	@Override
+	public VFSContainer getContainer(IdentityEnvironment identityEnv) {
+		return new CoursefolderWebDAVMergeSource(identityEnv);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/ICourse.java b/src/main/java/org/olat/course/ICourse.java
index bb0e53b718d..5f08b9540fe 100644
--- a/src/main/java/org/olat/course/ICourse.java
+++ b/src/main/java/org/olat/course/ICourse.java
@@ -28,6 +28,7 @@ package org.olat.course;
 import java.io.File;
 
 import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.course.config.CourseConfig;
@@ -88,6 +89,8 @@ public interface ICourse extends OLATResourceable {
 	 */
 	public VFSContainer getCourseFolderContainer();
 	
+	public VFSContainer getCourseFolderContainer(IdentityEnvironment identityEnv);
+	
 	public OlatRootFolderImpl getCourseExportDataDir();
 
 	/**
diff --git a/src/main/java/org/olat/course/MergedCourseContainer.java b/src/main/java/org/olat/course/MergedCourseContainer.java
index ca868725a24..b06c5f0b40e 100644
--- a/src/main/java/org/olat/course/MergedCourseContainer.java
+++ b/src/main/java/org/olat/course/MergedCourseContainer.java
@@ -22,6 +22,9 @@ package org.olat.course;
 import org.olat.core.commons.modules.bc.vfs.OlatNamedContainerImpl;
 import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
 import org.olat.core.commons.services.webdav.servlets.RequestUtil;
+import org.olat.core.gui.components.tree.GenericTreeModel;
+import org.olat.core.gui.components.tree.TreeNode;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
@@ -34,9 +37,14 @@ import org.olat.core.util.vfs.filters.VFSItemFilter;
 import org.olat.course.config.CourseConfig;
 import org.olat.course.nodes.BCCourseNode;
 import org.olat.course.nodes.CourseNode;
+import org.olat.course.run.userview.NodeEvaluation;
+import org.olat.course.run.userview.TreeEvaluation;
+import org.olat.course.run.userview.UserCourseEnvironment;
+import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.modules.sharedfolder.SharedFolderManager;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
+import org.olat.repository.model.RepositoryEntrySecurity;
 
 /**
  * 
@@ -47,10 +55,16 @@ public class MergedCourseContainer extends MergeSource {
 	private static final OLog log = Tracing.createLoggerFor(MergedCourseContainer.class);
 	
 	private final Long courseId;
+	private final IdentityEnvironment identityEnv;
 	
 	public MergedCourseContainer(Long courseId, String name) {
+		this(courseId, name, null);
+	}
+	
+	public MergedCourseContainer(Long courseId, String name, IdentityEnvironment identityEnv) {
 		super(null, name);
 		this.courseId = courseId;
+		this.identityEnv = identityEnv;
 	}
 	
 	@Override
@@ -59,8 +73,16 @@ public class MergedCourseContainer extends MergeSource {
 		ICourse course = CourseFactory.loadCourse(courseId);
 		if(course instanceof PersistingCourseImpl) {
 			PersistingCourseImpl persistingCourse = (PersistingCourseImpl)course;
-			addContainersChildren(persistingCourse.getIsolatedCourseFolder(), true);
-			
+			if(identityEnv == null || identityEnv.getRoles().isOLATAdmin()) {
+				addContainersChildren(persistingCourse.getIsolatedCourseFolder(), true);
+			} else {
+				RepositoryEntry re = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+				RepositoryEntrySecurity reSecurity = RepositoryManager.getInstance()
+						.isAllowed(identityEnv.getIdentity(), identityEnv.getRoles(), re);
+				if(reSecurity.isEntryAdmin()) {
+					addContainersChildren(persistingCourse.getIsolatedCourseFolder(), true);
+				}
+			}
 			// grab any shared folder that is configured
 			OlatRootFolderImpl sharedFolder = null;
 			String sfSoftkey = persistingCourse.getCourseConfig().getSharedFolderSoftkey();
@@ -69,7 +91,7 @@ public class MergedCourseContainer extends MergeSource {
 				RepositoryEntry re = rm.lookupRepositoryEntryBySoftkey(sfSoftkey, false);
 				if (re != null) {
 					sharedFolder = SharedFolderManager.getInstance().getSharedFolder(re.getOlatResource());
-					if (sharedFolder != null){
+					if (sharedFolder != null) {
 						sharedFolder.setLocalSecurityCallback(new ReadOnlyCallback());
 						//add local course folder's children as read/write source and any sharedfolder as subfolder
 						addContainer(new NamedContainerImpl("_sharedfolder", sharedFolder));
@@ -79,13 +101,98 @@ public class MergedCourseContainer extends MergeSource {
 			
 			// add all course building blocks of type BC to a virtual folder
 			MergeSource nodesContainer = new MergeSource(null, "_courseelementdata");
-			addFolderBuildingBlocks(persistingCourse, nodesContainer, persistingCourse.getRunStructure().getRootNode());
+			if(identityEnv == null) {
+				CourseNode rootNode = course.getRunStructure().getRootNode();
+				addFolderBuildingBlocks(persistingCourse, nodesContainer, rootNode);
+			} else {
+				TreeEvaluation treeEval = new TreeEvaluation();
+				GenericTreeModel treeModel = new GenericTreeModel();
+				UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment());
+				CourseNode rootCn = userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode();
+				NodeEvaluation rootNodeEval = rootCn.eval(userCourseEnv.getConditionInterpreter(), treeEval);
+				TreeNode treeRoot = rootNodeEval.getTreeNode();
+				treeModel.setRootNode(treeRoot);
+				addFolderBuildingBlocks(persistingCourse, nodesContainer, treeRoot);
+			}
+			
 			if (nodesContainer.getItems().size() > 0) {
 				addContainer(nodesContainer);
 			}
 		}
 	}
 	
+	private void addFolderBuildingBlocks(PersistingCourseImpl course, MergeSource nodesContainer, TreeNode courseNode) {
+		for (int i = 0; i < courseNode.getChildCount(); i++) {
+			TreeNode child = (TreeNode)courseNode.getChildAt(i);
+			
+			NodeEvaluation nodeEval;
+			if(child.getUserObject() instanceof NodeEvaluation) {
+				nodeEval = (NodeEvaluation)child.getUserObject();
+			} else {
+				continue;
+			}
+			
+			if(nodeEval != null && nodeEval.getCourseNode() != null) {
+				CourseNode courseNodeChild = nodeEval.getCourseNode();
+				String folderName = RequestUtil.normalizeFilename(courseNodeChild.getShortTitle());
+				MergeSource courseNodeContainer;
+				if (courseNodeChild instanceof BCCourseNode) {
+					final BCCourseNode bcNode = (BCCourseNode) courseNodeChild;
+					// add folder not to merge source. Use name and node id to have unique name
+					String path = BCCourseNode.getFoldernodePathRelToFolderBase(course.getCourseEnvironment(), bcNode);
+					OlatRootFolderImpl rootFolder = new OlatRootFolderImpl(path, null);
+					
+					boolean canDownload = nodeEval.isCapabilityAccessible("download");
+					if(canDownload) {
+						if(nodeEval.isCapabilityAccessible("upload")) {
+							//inherit the security callback from the course as for author
+						} else {
+							rootFolder.setLocalSecurityCallback(new ReadOnlyCallback());
+						}
+						
+						// add node ident if multiple files have same name
+						if (nodesContainer.getItems(new VFSItemFilter() {
+							@Override
+							public boolean accept(VFSItem vfsItem) {
+								return (bcNode.getShortTitle().equals(RequestUtil.normalizeFilename(bcNode.getShortTitle())));
+							}
+						}).size() > 0) {
+							folderName = folderName + " (" + bcNode.getIdent() + ")";
+						}
+						
+						// Create a container for this node content and wrap it with a merge source which is attached to tree
+						VFSContainer nodeContentContainer = new OlatNamedContainerImpl(folderName, rootFolder);
+						courseNodeContainer = new MergeSource(nodesContainer, folderName);
+						courseNodeContainer.addContainersChildren(nodeContentContainer, true);
+						nodesContainer.addContainer(courseNodeContainer);	
+						// Do recursion for all children
+						addFolderBuildingBlocks(course, courseNodeContainer, child);
+		
+					} else {
+						// For non-folder course nodes, add merge source (no files to show) ...
+						courseNodeContainer = new MergeSource(null, folderName);
+						// , then do recursion for all children ...
+						addFolderBuildingBlocks(course, courseNodeContainer, child);
+						// ... but only add this container if it contains any children with at least one BC course node
+						if (courseNodeContainer.getItems().size() > 0) {
+							nodesContainer.addContainer(courseNodeContainer);
+						}
+					}	
+				} else {
+					// For non-folder course nodes, add merge source (no files to show) ...
+					courseNodeContainer = new MergeSource(null, folderName);
+					// , then do recursion for all children ...
+					addFolderBuildingBlocks(course, courseNodeContainer, child);
+					// ... but only add this container if it contains any children with at least one BC course node
+					if (courseNodeContainer.getItems().size() > 0) {
+						nodesContainer.addContainer(courseNodeContainer);
+					}
+				}
+			}
+		}
+	}
+
+	
 	/**
 	 * internal method to recursively add all course building blocks of type
 	 * BC to a given VFS container. This should only be used for an author view,
diff --git a/src/main/java/org/olat/course/PersistingCourseImpl.java b/src/main/java/org/olat/course/PersistingCourseImpl.java
index 26c9f60dcbf..e8406d6379e 100644
--- a/src/main/java/org/olat/course/PersistingCourseImpl.java
+++ b/src/main/java/org/olat/course/PersistingCourseImpl.java
@@ -31,6 +31,7 @@ import java.io.Serializable;
 import org.olat.admin.quota.QuotaConstants;
 import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
 import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.logging.AssertException;
 import org.olat.core.logging.OLATRuntimeException;
@@ -163,16 +164,26 @@ public class PersistingCourseImpl implements ICourse, OLATResourceable, Serializ
 	/**
 	 * @see org.olat.course.ICourse#getCourseFolderPath()
 	 */
+	@Override
 	public VFSContainer getCourseFolderContainer() {
 		// add local course folder's children as read/write source and any sharedfolder as subfolder
 		MergedCourseContainer courseFolderContainer = new MergedCourseContainer(resourceableId, getCourseTitle());
 		courseFolderContainer.init();
 		return courseFolderContainer;
 	}
+
+	@Override
+	public VFSContainer getCourseFolderContainer(IdentityEnvironment identityEnv) {
+		// add local course folder's children as read/write source and any sharedfolder as subfolder
+		MergedCourseContainer courseFolderContainer = new MergedCourseContainer(resourceableId, getCourseTitle(), identityEnv);
+		courseFolderContainer.init();
+		return courseFolderContainer;
+	}
 	
 	/**
 	 * @see org.olat.course.ICourse#getCourseEnvironment()
 	 */
+	@Override
 	public CourseEnvironment getCourseEnvironment() {
 		return courseEnvironment;
 	}
@@ -207,20 +218,28 @@ public class PersistingCourseImpl implements ICourse, OLATResourceable, Serializ
 		OlatRootFolderImpl isolatedCourseFolder = new OlatRootFolderImpl(courseRootContainer.getRelPath() + File.separator + COURSEFOLDER, null);
 		// generate course folder
 		File fCourseFolder = isolatedCourseFolder.getBasefile();
-		if (!fCourseFolder.exists() && !fCourseFolder.mkdirs()) throw new OLATRuntimeException(this.getClass(),
-				"could not create course's coursefolder path:" + fCourseFolder.getAbsolutePath(), null);
+		if (!fCourseFolder.exists() && !fCourseFolder.mkdirs()) {
+			throw new OLATRuntimeException(this.getClass(),
+					"could not create course's coursefolder path:" + fCourseFolder.getAbsolutePath(), null);
+		}
 		
 		QuotaManager qm = QuotaManager.getInstance();
 		Quota q = qm.getCustomQuota(isolatedCourseFolder.getRelPath());
 		if (q == null){
 			Quota defQuota = qm.getDefaultQuota(QuotaConstants.IDENTIFIER_DEFAULT_COURSE);
-			q = QuotaManager.getInstance().createQuota(isolatedCourseFolder.getRelPath(), defQuota.getQuotaKB(), defQuota.getUlLimitKB());
+			q = qm.createQuota(isolatedCourseFolder.getRelPath(), defQuota.getQuotaKB(), defQuota.getUlLimitKB());
 		}
 		FullAccessWithQuotaCallback secCallback = new FullAccessWithQuotaCallback(q);
 		isolatedCourseFolder.setLocalSecurityCallback(secCallback);
 		return isolatedCourseFolder;
 	}
 	
+	protected File getIsolatedCourseBaseFolder() {
+		// create local course folder
+		OlatRootFolderImpl isolatedCourseFolder = new OlatRootFolderImpl(courseRootContainer.getRelPath() + File.separator + COURSEFOLDER, null);
+		return isolatedCourseFolder.getBasefile();
+	}
+	
 	/**
 	 * Save the run structure to disk, persist to the xml file
 	 */
@@ -283,7 +302,7 @@ public class PersistingCourseImpl implements ICourse, OLATResourceable, Serializ
 		// fxdiff: export layout-folder
 		FileUtils.copyDirToDir(new OlatRootFolderImpl(courseRootContainer.getRelPath() + File.separator + "layout", null).getBasefile(), exportDirectory, "course export layout folder");
 		// export course folder
-		FileUtils.copyDirToDir(getIsolatedCourseFolder().getBasefile(), exportDirectory, "course export folder");
+		FileUtils.copyDirToDir(getIsolatedCourseBaseFolder(), exportDirectory, "course export folder");
 		// export any node data
 		log.info("exportToFilesystem: exporting course "+this+": exporting all nodes...");
 		Visitor visitor = new NodeExportVisitor(fExportedDataDir, this);
diff --git a/src/main/java/org/olat/course/run/CourseRuntimeController.java b/src/main/java/org/olat/course/run/CourseRuntimeController.java
index cca25898df9..feadc681e0d 100644
--- a/src/main/java/org/olat/course/run/CourseRuntimeController.java
+++ b/src/main/java/org/olat/course/run/CourseRuntimeController.java
@@ -85,8 +85,8 @@ import org.olat.course.assessment.CoachingGroupAccessAssessmentCallback;
 import org.olat.course.assessment.EfficiencyStatementManager;
 import org.olat.course.assessment.FullAccessAssessmentCallback;
 import org.olat.course.assessment.UserEfficiencyStatement;
-import org.olat.course.certificate.ui.CertificatesOptionsController;
 import org.olat.course.certificate.ui.CertificateAndEfficiencyStatementController;
+import org.olat.course.certificate.ui.CertificatesOptionsController;
 import org.olat.course.config.CourseConfig;
 import org.olat.course.config.CourseConfigEvent;
 import org.olat.course.config.ui.CourseOptionsController;
diff --git a/src/main/java/org/olat/group/GroupfoldersWebDAVProvider.java b/src/main/java/org/olat/group/GroupfoldersWebDAVProvider.java
index a5086002366..71f8579d588 100644
--- a/src/main/java/org/olat/group/GroupfoldersWebDAVProvider.java
+++ b/src/main/java/org/olat/group/GroupfoldersWebDAVProvider.java
@@ -27,7 +27,7 @@ package org.olat.group;
 
 import org.olat.collaboration.CollaborationManager;
 import org.olat.core.commons.services.webdav.WebDAVProvider;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.VFSContainer;
 /**
  * 
@@ -47,12 +47,14 @@ public class GroupfoldersWebDAVProvider implements WebDAVProvider {
 		this.collaborationManager = collaborationManager;
 	}
 
+	@Override
 	public String getMountPoint() {
 		return MOUNTPOINT;
 	}
 
-	public VFSContainer getContainer(Identity identity) {
-		return new GroupfoldersWebDAVMergeSource(identity, collaborationManager);
+	@Override
+	public VFSContainer getContainer(IdentityEnvironment identityEnv) {
+		return new GroupfoldersWebDAVMergeSource(identityEnv.getIdentity(), collaborationManager);
 	}
 }
 
diff --git a/src/main/java/org/olat/home/controllerCreators/PersonalFolderControllerCreator.java b/src/main/java/org/olat/home/controllerCreators/PersonalFolderControllerCreator.java
index 7218773897a..0ac8f3b7668 100644
--- a/src/main/java/org/olat/home/controllerCreators/PersonalFolderControllerCreator.java
+++ b/src/main/java/org/olat/home/controllerCreators/PersonalFolderControllerCreator.java
@@ -24,6 +24,7 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.creator.AutoCreator;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.user.PersonalFolderManager;
 
 /**
@@ -54,6 +55,7 @@ public class PersonalFolderControllerCreator extends AutoCreator  {
 	 */
 	@Override
 	public Controller createController(UserRequest ureq, WindowControl lwControl) {
-		return new FolderRunController(PersonalFolderManager.getInstance().getContainer(ureq.getIdentity()), true, true, true, ureq, lwControl);
+		IdentityEnvironment identityEnv = ureq.getUserSession().getIdentityEnvironment();
+		return new FolderRunController(PersonalFolderManager.getInstance().getContainer(identityEnv), true, true, true, ureq, lwControl);
 	}
 }
diff --git a/src/main/java/org/olat/modules/sharedfolder/SharedFolderWebDAVProvider.java b/src/main/java/org/olat/modules/sharedfolder/SharedFolderWebDAVProvider.java
index deb89ffae25..8fa86c8ca16 100644
--- a/src/main/java/org/olat/modules/sharedfolder/SharedFolderWebDAVProvider.java
+++ b/src/main/java/org/olat/modules/sharedfolder/SharedFolderWebDAVProvider.java
@@ -28,7 +28,7 @@ package org.olat.modules.sharedfolder;
 import java.util.List;
 
 import org.olat.core.commons.services.webdav.WebDAVProvider;
-import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.callbacks.ReadOnlyCallback;
 import org.olat.core.util.vfs.callbacks.VFSSecurityCallback;
@@ -86,7 +86,8 @@ public class SharedFolderWebDAVProvider implements WebDAVProvider {
 	/**
 	 * @see org.olat.core.commons.services.webdav.WebDAVProvider#getContainer(org.olat.core.id.Identity)
 	 */
-	public VFSContainer getContainer(Identity identity) {
-		return new SharedFolderWebDAVMergeSource(identity, publiclyReadableFolders);
+	@Override
+	public VFSContainer getContainer(IdentityEnvironment identityEnv) {
+		return new SharedFolderWebDAVMergeSource(identityEnv.getIdentity(), publiclyReadableFolders);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 1c9d4be0a80..814b9f63af2 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -1766,7 +1766,7 @@ public class RepositoryManager extends BasicManager {
 	 * @param identity
 	 * @return list of RepositoryEntries
 	 */
-	public List<RepositoryEntry> getLearningResourcesAsStudent(Identity identity, int firstResult, int maxResults, RepositoryEntryOrder... orderby) {
+	public List<RepositoryEntry> getLearningResourcesAsStudent(Identity identity, String type, int firstResult, int maxResults, RepositoryEntryOrder... orderby) {
 		StringBuilder sb = new StringBuilder(1200);
 		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
 		  .append(" inner join fetch v.olatResource as res ")
@@ -1777,6 +1777,10 @@ public class RepositoryManager extends BasicManager {
 		  .append(" inner join baseGroup.members as membership")
 		  .append(" where (v.access>=3 or (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true))")
 		  .append(" and membership.identity.key=:identityKey and membership.role='").append(GroupRoles.participant.name()).append("'");
+		if(StringHelper.containsNonWhitespace(type)) {
+			sb.append(" and res.resName=:resourceType");
+		}
+		
 		appendOrderBy(sb, "v", orderby);
 
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
@@ -1786,6 +1790,56 @@ public class RepositoryManager extends BasicManager {
 		if(maxResults > 0) {
 			query.setMaxResults(maxResults);
 		}
+		if(StringHelper.containsNonWhitespace(type)) {
+			query.setParameter("resourceType", type);
+		}
+		List<RepositoryEntry> repoEntries = query.getResultList();
+		return repoEntries;
+	}
+	
+	public List<RepositoryEntry> getLearningResourcesAsBookmark(Identity identity, Roles roles, String type, int firstResult, int maxResults, RepositoryEntryOrder... orderby) {
+		if(roles.isGuestOnly()) {
+			return Collections.emptyList();
+		}
+
+		StringBuilder sb = new StringBuilder(1200);
+		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
+		  .append(" inner join fetch v.olatResource as res ")
+		  .append(" inner join fetch v.statistics as statistics")
+		  .append(" left join fetch v.lifecycle as lifecycle")
+		  .append(" where exists (select mark.key from ").append(MarkImpl.class.getName()).append(" as mark ")
+		  .append("   where mark.creator.key=:identityKey and mark.resId=v.key and mark.resName='RepositoryEntry'")
+		  .append(" ) ");
+		if(StringHelper.containsNonWhitespace(type)) {
+			sb.append(" and res.resName=:resourceType");
+		}
+		sb.append(" and (v.access >= ");
+		if (roles.isAuthor()) {
+			sb.append(RepositoryEntry.ACC_OWNERS_AUTHORS);
+		} else {
+			sb.append(RepositoryEntry.ACC_USERS);
+		}
+		sb.append(" or (")
+		  .append("  v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true")
+		  .append("  and exists (select rel from repoentrytogroup as rel, bgroup as baseGroup, bgroupmember as membership")
+		  .append("    where rel.entry=v and rel.group=baseGroup and membership.group=baseGroup and membership.identity.key=:identityKey")
+		  .append("      and membership.role in ('").append(GroupRoles.owner.name()).append("','").append(GroupRoles.coach.name()).append("','").append(GroupRoles.participant.name()).append("')")
+		  .append("  )")
+		  .append(" )")
+		  .append(")");
+
+		appendOrderBy(sb, "v", orderby);
+
+		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), RepositoryEntry.class)
+				.setParameter("identityKey", identity.getKey())
+				.setFirstResult(firstResult);
+		if(maxResults > 0) {
+			query.setMaxResults(maxResults);
+		}
+		if(StringHelper.containsNonWhitespace(type)) {
+			query.setParameter("resourceType", type);
+		}
 		List<RepositoryEntry> repoEntries = query.getResultList();
 		return repoEntries;
 	}
@@ -1795,10 +1849,10 @@ public class RepositoryManager extends BasicManager {
 		sb.append("select v from repoentrylight as v ")
 		  .append(" inner join fetch v.olatResource as res ")
 		  .append(" where exists (select rel from repoentrytogroup as rel, bgroup as baseGroup, bgroupmember as membership  ")
-			     .append("    where rel.entry=v and rel.group=baseGroup and membership.group=baseGroup and membership.identity.key=:identityKey ")
-			     .append("      and membership.role='").append(GroupRoles.participant.name()).append("')")
-			     .append("  )")
-			     .append(" )")
+		  .append("    where rel.entry=v and rel.group=baseGroup and membership.group=baseGroup and membership.identity.key=:identityKey ")
+		  .append("      and membership.role='").append(GroupRoles.participant.name()).append("')")
+		  .append("  )")
+		  .append(" )")
 		  .append(" and (v.access>=3 or (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true))");
 		appendOrderBy(sb, "v", orderby);
 		
diff --git a/src/main/java/org/olat/repository/RepositoryService.java b/src/main/java/org/olat/repository/RepositoryService.java
index 169cdf4c412..98ebfdb60f5 100644
--- a/src/main/java/org/olat/repository/RepositoryService.java
+++ b/src/main/java/org/olat/repository/RepositoryService.java
@@ -141,5 +141,4 @@ public interface RepositoryService {
 	public int countAuthorView(SearchAuthorRepositoryEntryViewParams params);
 	
 	public List<RepositoryEntryAuthorView> searchAuthorView(SearchAuthorRepositoryEntryViewParams params, int firstResult, int maxResults);
-
 }
diff --git a/src/main/java/org/olat/repository/controllers/RepositorySearchController.java b/src/main/java/org/olat/repository/controllers/RepositorySearchController.java
index d3043a5437d..89dae1a9b96 100644
--- a/src/main/java/org/olat/repository/controllers/RepositorySearchController.java
+++ b/src/main/java/org/olat/repository/controllers/RepositorySearchController.java
@@ -432,7 +432,7 @@ public class RepositorySearchController extends BasicController implements Activ
 	private void doSearchMyCoursesStudent(UserRequest ureq, String limitType, boolean updateFilters) {
 		searchType = SearchType.myAsStudent;
 		RepositoryManager rm = RepositoryManager.getInstance();
-		List<RepositoryEntry> entries = rm.getLearningResourcesAsStudent(ureq.getIdentity(), 0, -1);
+		List<RepositoryEntry> entries = rm.getLearningResourcesAsStudent(ureq.getIdentity(), null, 0, -1);
 		filterRepositoryEntries(entries);
 		doSearchMyRepositoryEntries(ureq, entries, limitType, updateFilters);
 	}
diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
index aae66366f94..7073be4b758 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
@@ -120,5 +120,4 @@ public class RepositoryEntryDAO {
 				.setMaxResults(maxResults)
 				.getResultList();
 	}
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/user/restapi/UserCoursesWebService.java b/src/main/java/org/olat/user/restapi/UserCoursesWebService.java
index 273c4feef6b..95ae4397ba7 100644
--- a/src/main/java/org/olat/user/restapi/UserCoursesWebService.java
+++ b/src/main/java/org/olat/user/restapi/UserCoursesWebService.java
@@ -78,7 +78,7 @@ public class UserCoursesWebService {
 		
 		RepositoryManager rm = RepositoryManager.getInstance();
 		if(MediaTypeVariants.isPaged(httpRequest, request)) {
-			List<RepositoryEntry> repoEntries = rm.getLearningResourcesAsStudent(identity, start, limit, RepositoryEntryOrder.nameAsc);
+			List<RepositoryEntry> repoEntries = rm.getLearningResourcesAsStudent(identity, null, start, limit, RepositoryEntryOrder.nameAsc);
 			int totalCount= rm.countLearningResourcesAsStudent(identity);
 
 			CourseVO[] vos = toCourseVo(repoEntries);
@@ -87,7 +87,7 @@ public class UserCoursesWebService {
 			voes.setTotalCount(totalCount);
 			return Response.ok(voes).build();
 		} else {
-			List<RepositoryEntry> repoEntries = rm.getLearningResourcesAsStudent(identity, 0, -1, RepositoryEntryOrder.nameAsc);
+			List<RepositoryEntry> repoEntries = rm.getLearningResourcesAsStudent(identity, null, 0, -1, RepositoryEntryOrder.nameAsc);
 			CourseVO[] vos = toCourseVo(repoEntries);
 			return Response.ok(vos).build();
 		}
diff --git a/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java b/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java
index 5278ff015ef..c52d4e80bce 100644
--- a/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java
+++ b/src/test/java/org/olat/core/commons/services/webdav/WebDAVCommandsTest.java
@@ -42,6 +42,7 @@ import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
 import org.apache.poi.util.IOUtils;
+import org.junit.After;
 import org.junit.Test;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.GroupRoles;
@@ -60,6 +61,7 @@ import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryService;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
 import org.olat.restapi.CoursePublishTest;
 import org.olat.test.JunitTestHelper;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -76,11 +78,21 @@ public class WebDAVCommandsTest extends WebDAVTestCase {
 	@Autowired
 	private DB dbInstance;
 	@Autowired
+	private WebDAVModule webDAVModule;
+	@Autowired
+	private VFSLockManager lockManager;
+	@Autowired
 	private BaseSecurity securityManager;
 	@Autowired
 	private RepositoryService repositoryService;
 	@Autowired
-	private VFSLockManager lockManager;
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+	
+	@After
+	public void resetWebDAVModule() {
+		webDAVModule.setEnableLearnersBookmarksCourse(false);
+		webDAVModule.setEnableLearnersParticipatingCourses(false);
+	}
 	
 	/**
 	 * Check the DAV, Ms-Author and Allow header
@@ -587,6 +599,71 @@ public class WebDAVCommandsTest extends WebDAVTestCase {
 		IOUtils.closeQuietly(conn);
 	}
 	
+	@Test
+	public void coursePermissions_participant()
+	throws IOException, URISyntaxException {
+		webDAVModule.setEnableLearnersBookmarksCourse(true);
+		webDAVModule.setEnableLearnersParticipatingCourses(true);
+		
+		Identity author = JunitTestHelper.createAndPersistIdentityAsAuthor("auth-webdav");
+		Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("participant-webdav");
+		URL courseWithForumsUrl = WebDAVCommandsTest.class.getResource("webdav_course.zip");
+		RepositoryEntry course = deployTestCourse(author, null, courseWithForumsUrl);
+		repositoryEntryRelationDao.addRole(participant, course, GroupRoles.participant.name());
+		dbInstance.commitAndCloseSession();
+		
+		WebDAVConnection conn = new WebDAVConnection();
+		conn.setCredentials(participant.getName(), "A6B7C8");
+
+		URI courseUri = conn.getBaseURI().path("webdav").path("coursefolders").build();
+		String publicXml = conn.propfind(courseUri, 2);
+		//cannot access course storage
+		Assert.assertFalse(publicXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/Course%20storage/</D:href>"));
+		//can access course elements
+		Assert.assertTrue(publicXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/</D:href>"));
+
+		URI courseElementUri = conn.getBaseURI().path("webdav").path("coursefolders")
+				.path("other").path("WebDAV%20course").path("_courseelementdata").build();
+		String publicElementXml = conn.propfind(courseElementUri, 2);
+		Assert.assertTrue(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Folder%20for%20all/</D:href>"));
+		Assert.assertFalse(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Student%20read-only%20%2890600786058954%29/Readonly%20students/</D:href>"));
+		Assert.assertFalse(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Not%20for%20students%20%2890600786058958%29/Not%20for%20students/</D:href>"));
+
+		conn.close();
+	}
+	
+	@Test
+	public void coursePermissions_owner()
+	throws IOException, URISyntaxException {
+		webDAVModule.setEnableLearnersBookmarksCourse(true);
+		webDAVModule.setEnableLearnersParticipatingCourses(true);
+		
+		Identity author = JunitTestHelper.createAndPersistIdentityAsAuthor("auth-webdav");
+		URL courseWithForumsUrl = WebDAVCommandsTest.class.getResource("webdav_course.zip");
+		deployTestCourse(author, null, courseWithForumsUrl);
+		dbInstance.commitAndCloseSession();
+		
+		WebDAVConnection conn = new WebDAVConnection();
+		conn.setCredentials(author.getName(), "A6B7C8");
+
+		URI courseUri = conn.getBaseURI().path("webdav").path("coursefolders").build();
+		String publicXml = conn.propfind(courseUri, 2);
+		//cane access course storage
+		Assert.assertTrue(publicXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/Course%20storage/</D:href>"));
+		//can access course elements
+		Assert.assertTrue(publicXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/</D:href>"));
+
+		URI courseElementUri = conn.getBaseURI().path("webdav").path("coursefolders")
+				.path("other").path("WebDAV%20course").path("_courseelementdata").build();
+		String publicElementXml = conn.propfind(courseElementUri, 2);
+		//can access all 3 course nodes
+		Assert.assertTrue(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Folder%20for%20all/</D:href>"));
+		Assert.assertTrue(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Student%20read-only%20%2890600786058954%29/Readonly%20students/</D:href>"));
+		Assert.assertTrue(publicElementXml.contains("<D:href>/webdav/coursefolders/other/WebDAV%20course/_courseelementdata/Not%20for%20students%20%2890600786058958%29/Not%20for%20students/</D:href>"));
+
+		conn.close();
+	}
+	
 	private VFSItem createFile(VFSContainer container, String filename) throws IOException {
 		VFSLeaf testLeaf = container.createChildLeaf(filename);
 		InputStream in = WebDAVCommandsTest.class.getResourceAsStream("text.txt");
@@ -598,8 +675,14 @@ public class WebDAVCommandsTest extends WebDAVTestCase {
 		return container.resolve(filename);
 	}
 	
-	private RepositoryEntry deployTestCourse(Identity author, Identity coAuthor) throws URISyntaxException {
+	private RepositoryEntry deployTestCourse(Identity author, Identity coAuthor)
+	throws URISyntaxException {
 		URL courseWithForumsUrl = CoursePublishTest.class.getResource("myCourseWS.zip");
+		return deployTestCourse(author, coAuthor, courseWithForumsUrl);
+	}
+	
+	private RepositoryEntry deployTestCourse(Identity author, Identity coAuthor, URL courseWithForumsUrl)
+	throws URISyntaxException {
 		Assert.assertNotNull(courseWithForumsUrl);
 		File courseWithForums = new File(courseWithForumsUrl.toURI());
 		String softKey = UUID.randomUUID().toString().replace("-", "").substring(0, 30);
diff --git a/src/test/java/org/olat/core/commons/services/webdav/webdav_course.zip b/src/test/java/org/olat/core/commons/services/webdav/webdav_course.zip
new file mode 100644
index 0000000000000000000000000000000000000000..b216bb6e6aaff6897cfd89bc5d8f5c01c231db4a
GIT binary patch
literal 24513
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
S@=FNxHTb6n{Cww!c=tcm%xro9

literal 0
HcmV?d00001

diff --git a/src/test/java/org/olat/repository/RepositoryManagerTest.java b/src/test/java/org/olat/repository/RepositoryManagerTest.java
index cc03446ac80..6c830df2c73 100644
--- a/src/test/java/org/olat/repository/RepositoryManagerTest.java
+++ b/src/test/java/org/olat/repository/RepositoryManagerTest.java
@@ -276,7 +276,7 @@ public class RepositoryManagerTest extends OlatTestCase {
 		repositoryEntryRelationDao.addRole(id, re, GroupRoles.participant.name());
 		dbInstance.commitAndCloseSession();
 
-		List<RepositoryEntry> entries = repositoryManager.getLearningResourcesAsStudent(id, 0, -1, RepositoryEntryOrder.nameAsc);
+		List<RepositoryEntry> entries = repositoryManager.getLearningResourcesAsStudent(id, null, 0, -1, RepositoryEntryOrder.nameAsc);
 		Assert.assertNotNull(entries);
 		Assert.assertFalse(entries.isEmpty());
 		Assert.assertTrue(entries.contains(re));
@@ -298,7 +298,7 @@ public class RepositoryManagerTest extends OlatTestCase {
 	    businessGroupRelationDao.addRole(id, group, GroupRoles.participant.name());
 		dbInstance.commitAndCloseSession();
 
-		List<RepositoryEntry> entries = repositoryManager.getLearningResourcesAsStudent(id, 0, -1);
+		List<RepositoryEntry> entries = repositoryManager.getLearningResourcesAsStudent(id, null, 0, -1);
 		Assert.assertNotNull(entries);
 		Assert.assertFalse(entries.isEmpty());
 		Assert.assertTrue(entries.contains(re));
@@ -311,6 +311,41 @@ public class RepositoryManagerTest extends OlatTestCase {
 			}
 		}
 	}
+
+	@Test
+	public void getLearningResourcesAsBookmark() {
+		Identity owner = JunitTestHelper.createAndPersistIdentityAsRndUser("webdav-courses-1");
+		Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("webdav-courses-2");
+		RepositoryEntry course = JunitTestHelper.deployBasicCourse(owner);
+		markManager.setMark(course, participant, null, "[RepositoryEntry:" + course.getKey() + "]");
+		dbInstance.commitAndCloseSession();
+		
+		//participant bookmarks
+		Roles roles = new Roles(false, false, false, false, false, false, false);
+		List<RepositoryEntry> courses = repositoryManager.getLearningResourcesAsBookmark(participant, roles, "CourseModule", 0, -1);
+		Assert.assertNotNull(courses);
+		Assert.assertEquals(1, courses.size());
+	}
+	
+	/**
+	 * Check that the method return only courses within the permissions of the user.
+	 */
+	@Test
+	public void getLearningResourcesAsBookmark_noPermissions() {
+		Identity owner = JunitTestHelper.createAndPersistIdentityAsRndUser("webdav-courses-1");
+		Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("webdav-courses-2");
+		RepositoryEntry course = JunitTestHelper.deployBasicCourse(owner);
+		markManager.setMark(course, participant, null, "[RepositoryEntry:" + course.getKey() + "]");
+		dbInstance.commitAndCloseSession();
+		repositoryManager.setAccess(course, RepositoryEntry.ACC_OWNERS, false);
+		dbInstance.commitAndCloseSession();
+		
+		//participant bookmarks
+		Roles roles = new Roles(false, false, false, false, false, false, false);
+		List<RepositoryEntry> courses = repositoryManager.getLearningResourcesAsBookmark(participant, roles, "CourseModule", 0, -1);
+		Assert.assertNotNull(courses);
+		Assert.assertEquals(0, courses.size());
+	}
 	
 	@Test
 	public void getParticipantRepositoryEntry() {
diff --git a/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java b/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
index 1c8258409ae..5ac7201d22c 100644
--- a/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
+++ b/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
@@ -25,6 +25,7 @@ import junit.framework.Assert;
 
 import org.junit.Test;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.services.mark.MarkManager;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryService;
 import org.olat.test.OlatTestCase;
@@ -41,9 +42,13 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 	@Autowired
 	private DB dbInstance;
 	@Autowired
+	private MarkManager markManager;
+	@Autowired
 	private RepositoryService repositoryService;
 	@Autowired
 	private RepositoryEntryDAO repositoryEntryDao;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
 	
 	@Test
 	public void loadByKey() {
@@ -69,4 +74,6 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 		Assert.assertFalse(allRes.isEmpty());
 		Assert.assertTrue(allRes.size() < 26);
 	}
+	
+
 }
\ No newline at end of file
diff --git a/src/test/java/org/olat/test/JunitTestHelper.java b/src/test/java/org/olat/test/JunitTestHelper.java
index 48c3101a6f2..d242dc949c5 100644
--- a/src/test/java/org/olat/test/JunitTestHelper.java
+++ b/src/test/java/org/olat/test/JunitTestHelper.java
@@ -219,4 +219,30 @@ public class JunitTestHelper {
 		}
 		return re;
 	}
+	
+	/**
+	 * Deploy a course with only a single page.
+	 * @param initialAuthor
+	 * @return
+	 */
+	public static RepositoryEntry deployBasicCourse(Identity initialAuthor) {		
+		String displayname = "Basic course (" + CodeHelper.getForeverUniqueID() + ")";
+		String description = "A course with only a single page";
+
+		RepositoryEntry re = null;
+		try {
+			URL courseUrl = JunitTestHelper.class.getResource("file_resources/Basic_course.zip");
+			File courseFile = new File(courseUrl.toURI());
+			
+			RepositoryHandler courseHandler = RepositoryHandlerFactory.getInstance()
+					.getRepositoryHandler(CourseModule.getCourseTypeName());
+			re = courseHandler.importResource(initialAuthor, null, displayname, description, true, Locale.ENGLISH, courseFile, null);
+			
+			ICourse course = CourseFactory.loadCourse(re.getOlatResource());
+			CourseFactory.publishCourse(course, RepositoryEntry.ACC_USERS, false,  initialAuthor, Locale.ENGLISH);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+		return re;
+	}
 }
diff --git a/src/test/java/org/olat/test/file_resources/Basic_course.zip b/src/test/java/org/olat/test/file_resources/Basic_course.zip
new file mode 100644
index 0000000000000000000000000000000000000000..6e887df4e24022d15eb5f85c75d83544892a963e
GIT binary patch
literal 14021
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

literal 0
HcmV?d00001

-- 
GitLab