diff --git a/src/main/java/org/olat/basesecurity/AuthHelper.java b/src/main/java/org/olat/basesecurity/AuthHelper.java
index 7000f8c763e31b8b44f3a54033b4198801efe199..8d272b355d39472565e0edc574b55db0f5f8dcf4 100644
--- a/src/main/java/org/olat/basesecurity/AuthHelper.java
+++ b/src/main/java/org/olat/basesecurity/AuthHelper.java
@@ -91,8 +91,8 @@ public class AuthHelper {
 	 * <code>LOGOUT_PAGE</code>
 	 */
 	public  static final int LOGIN_OK = 0;
-	private static final int LOGIN_FAILED = 1;
-	private static final int LOGIN_DENIED = 2;
+	public static final int LOGIN_FAILED = 1;
+	public static final int LOGIN_DENIED = 2;
 	public  static final int LOGIN_NOTAVAILABLE = 3;
 
 	private static final int MAX_SESSION_NO_LIMIT = 0;
@@ -228,8 +228,14 @@ public class AuthHelper {
 		if ( locale == null || ! supportedLanguages.contains(locale.toString()) ) {
 			locale = I18nModule.getDefaultLocale();
 		}
-		Identity guestIdent = BaseSecurityManager.getInstance().getAndUpdateAnonymousUserForLanguage(locale);
-		return doLogin(guestIdent, BaseSecurityModule.getDefaultAuthProviderIdentifier(), ureq);
+		BaseSecurity securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);
+		Identity guestIdent = securityManager.getAndUpdateAnonymousUserForLanguage(locale);
+		Roles guestRoles = securityManager.getRoles(guestIdent);
+		if(guestRoles.isGuestOnly()) {
+			return doLogin(guestIdent, BaseSecurityModule.getDefaultAuthProviderIdentifier(), ureq);
+		}
+		log.error("Guest account has user permissions: " + guestIdent);
+		return LOGIN_DENIED;
 	}
 
 	public static int doInvitationLogin(String invitationToken, UserRequest ureq, Locale locale) {
diff --git a/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml b/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml
index bc26b80c70b770c10ba06323ee9de1c32ca13684..3b8a6745ec2598c0d1ad75e496e8fbe4242c4c6f 100644
--- a/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml
+++ b/src/main/java/org/olat/core/dispatcher/_spring/dispatcherContext.xml
@@ -247,9 +247,23 @@
 		class="org.olat.core.gui.components.form.flexible.impl.elements.richText.plugins.olatmovieviewer.TinyDispatcher" />
 
 	<!-- podcast media dispatcher -->
-	<bean id="podcastMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher" />
+	<bean id="podcastMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher">
+		<property name="feedManager" ref="feedManager"/>
+		<property name="coordinatorManager" ref="coordinatorManager"/>
+		<property name="securityManager" ref="baseSecurityManager"/>
+		<property name="repositoryManager" ref="repositoryManager"/>
+		<property name="dbInstance" ref="database"/>
+		<property name="resourceManager" ref="resourceManager"/>
+	</bean>
 
 	<!-- blog media dispatcher -->
-	<bean id="blogMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher" />
+	<bean id="blogMediaBean" class="org.olat.modules.webFeed.dispatching.FeedMediaDispatcher">
+		<property name="feedManager" ref="feedManager"/>
+		<property name="coordinatorManager" ref="coordinatorManager"/>
+		<property name="securityManager" ref="baseSecurityManager"/>
+		<property name="repositoryManager" ref="repositoryManager"/>
+		<property name="dbInstance" ref="database"/>
+		<property name="resourceManager" ref="resourceManager"/>
+	</bean>
 	
 </beans>
diff --git a/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java b/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java
index 74c12867c35369f503bec0f3938423151a5cce35..a84b0b7285fd4936842efefbe05113af5a29ec6b 100644
--- a/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java
+++ b/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java
@@ -1679,7 +1679,7 @@ public class MailManagerImpl implements MailManager, InitializingBean  {
 	@Override
 	public void sendMessage(MimeMessage msg, MailerResult result) {
 		if (msg == null) return;
-			
+
 		String smtpFrom = WebappHelper.getMailConfig("smtpFrom");
 		if(StringHelper.containsNonWhitespace(smtpFrom)) {
 			try {
@@ -1689,6 +1689,17 @@ public class MailManagerImpl implements MailManager, InitializingBean  {
 			} catch (MessagingException e) {
 				log.error("", e);
 			}
+		} else {
+			try {
+				if (msg.getReplyTo().length > 0) {
+					Address a = msg.getReplyTo()[0];
+					SMTPMessage smtpMsg = new SMTPMessage(msg);
+					smtpMsg.setEnvelopeFrom(((InternetAddress)a).getAddress());
+					msg = smtpMsg;
+				}
+			} catch (MessagingException e) {
+				log.error("", e);
+			}
 		}
 
 		try{
diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java
index 17a36fe87982367866b972ce124e4d07b5beb663..9aabc5a0e491ad99bea98fe2508b9ca166d40f30 100644
--- a/src/main/java/org/olat/course/CourseFactory.java
+++ b/src/main/java/org/olat/course/CourseFactory.java
@@ -480,7 +480,7 @@ public class CourseFactory {
 			int count = 0;
 			for (Reference ref: refs) {
 				referenceManager.addReference(targetCourse, ref.getTarget(), ref.getUserdata());
-				if(count % 20 == 0) {
+				if(count++ % 20 == 0) {
 					DBFactory.getInstance().intermediateCommit();
 				}
 			}
diff --git a/src/main/java/org/olat/course/editor/PublishProcess.java b/src/main/java/org/olat/course/editor/PublishProcess.java
index 684a1dbe4fae92332c4d92924ab1984a4a799954..91b495d72042a70f63e2aed1e4f02004a129cb1e 100644
--- a/src/main/java/org/olat/course/editor/PublishProcess.java
+++ b/src/main/java/org/olat/course/editor/PublishProcess.java
@@ -100,7 +100,7 @@ public class PublishProcess {
 	private static final OLog log = Tracing.createLoggerFor(PublishProcess.class);
 	
 	private static final String PACKAGE = Util.getPackageName(PublishProcess.class);
-	private static Translator translator;
+	private final Translator translator;
 	
 	/*
 	 * publishing means 
diff --git a/src/main/java/org/olat/login/LoginAuthprovidersController.java b/src/main/java/org/olat/login/LoginAuthprovidersController.java
index 12bb709eba1bca4146aee439a6d8c28712100427..0b29ffe4ad2165975cd6970591fba63c9b4f3ba4 100644
--- a/src/main/java/org/olat/login/LoginAuthprovidersController.java
+++ b/src/main/java/org/olat/login/LoginAuthprovidersController.java
@@ -67,8 +67,7 @@ import org.olat.login.auth.AuthenticationProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * Description:<br>
- * TODO: patrickb Class Description for LoginAuthprovidersController
+ * A container for all the authentications methods available to the user.
  * 
  * <P>
  * Initial Date:  02.09.2007 <br>
@@ -81,7 +80,7 @@ public class LoginAuthprovidersController extends MainLayoutBasicController impl
 
 	private VelocityContainer content;
 	private Controller authController;
-	private final List<Controller> authControllers = new ArrayList<Controller>();
+	private final List<Controller> authControllers = new ArrayList<>();
 	private Link anoLink;
 	private StackedPanel dmzPanel;
 	
@@ -227,27 +226,23 @@ public class LoginAuthprovidersController extends MainLayoutBasicController impl
 		
 		return contentBorn;
 	}
-	
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#doDispose()
-	 */
+
 	@Override
 	protected void doDispose() {
 		//auto-disposed
 	}
 
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
-	 */
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		 if (source == anoLink) {
 			if (loginModule.isGuestLoginEnabled()) {				
 				int loginStatus = AuthHelper.doAnonymousLogin(ureq, ureq.getLocale());
 				if (loginStatus == AuthHelper.LOGIN_OK) {
-					return;
-				} else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){
+					//
+				} else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) {
 					DispatcherModule.redirectToServiceNotAvailable( ureq.getHttpResp() );
+				} else if(loginStatus == AuthHelper.LOGIN_DENIED) {
+					getWindowControl().setError(translate("error.guest.login", WebappHelper.getMailConfig("mailSupport")));
 				} else {
 					getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailSupport")));
 				}	
@@ -288,8 +283,8 @@ public class LoginAuthprovidersController extends MainLayoutBasicController impl
 		// Add translator and languages info
 		I18nManager i18nMgr = I18nManager.getInstance();
 		Collection<String> enabledKeysSet = i18nModule.getEnabledLanguageKeys();
-		Map<String, String> langNames = new HashMap<String, String>();
-		Map<String, String> langTranslators = new HashMap<String, String>();
+		Map<String, String> langNames = new HashMap<>();
+		Map<String, String> langTranslators = new HashMap<>();
 		String[] enabledKeys = ArrayHelper.toArray(enabledKeysSet);
 		String[] names = new String[enabledKeys.length];
 		for (int i = 0; i < enabledKeys.length; i++) {
@@ -318,7 +313,7 @@ public class LoginAuthprovidersController extends MainLayoutBasicController impl
 			Identity identity = authEvent.getIdentity();
 			int loginStatus = AuthHelper.doLogin(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier(), ureq);
 			if (loginStatus == AuthHelper.LOGIN_OK) {
-				return;
+				// it's ok
 			} else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){
 				DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp());
 			} else {
diff --git a/src/main/java/org/olat/login/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/login/_i18n/LocalStrings_de.properties
index 8039081a567a87ee31a632229dd20eef4ee51640..443f742ffe8428a5cc3f60faf7d8c282f773de79 100644
--- a/src/main/java/org/olat/login/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/login/_i18n/LocalStrings_de.properties
@@ -114,6 +114,7 @@ browsercheck.yourbrowser.usragent=User agent\:
 
 default.shib.intro=Sie werden zur Authentifizierung weitergeleitet. 
 enabled=ein
+error.guest.login=Gastanmeldung ist derzeit nicht verf\u00FCgbar. Wenden Sie Sich an {0}.
 guest.login=Gastlogin auf Login Seite
 guest.login.links=Links f\u00FCr Gast
 guest.search=Volltextsuche f\u00FCr Gast
diff --git a/src/main/java/org/olat/login/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/login/_i18n/LocalStrings_en.properties
index 8fe0703372af67aeb79eee71073055fd4580d3ec..7ebc3a2d35877a531d19409f8591a59cf73e600e 100644
--- a/src/main/java/org/olat/login/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/login/_i18n/LocalStrings_en.properties
@@ -114,6 +114,7 @@ browsercheck.yourbrowser.usragent=User agent\:
 
 default.shib.intro=You will be redirected to authentication.
 enabled=enabled
+error.guest.login=Guest is currently not available. Please contact {0}.
 guest.login=Guest login on login page
 guest.login.links=Links for guests
 guest.search=Full-text search for guest
diff --git a/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties
index e2045cdb6d7a05d134c758c4b447f8b2229ca012..79e681facdee5b66a6c35f24ce48500f8a1478ed 100644
--- a/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/login/_i18n/LocalStrings_fr.properties
@@ -1,4 +1,4 @@
-#Tue Sep 11 19:11:27 CEST 2018
+#Fri Jun 01 10:19:57 CEST 2018
 about.copyright=Copyright et participations
 about.custom=
 about.custom.title=
@@ -79,6 +79,7 @@ default.shib.intro=Vous \u00EAtes redirig\u00E9 vers la connexion
 disable.history=d\u00E9sactiv\u00E9
 enabled=on
 error.wrong.int=Format de nombre incorrect.
+error.guest.login=La connexion en temps qu'invit\u00E9 n'est pas disponible pour l'instant. Veuillez contacter {0}.
 guest.login=Connexion invit\u00E9 sur la page de login
 guest.login.links=Liens pour invit\u00E9s
 guest.search=Recherche plein texte pour les invit\u00E9s
diff --git a/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java b/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java
index 19b712adde8b5b67501733e375ecd695e4cf0ec9..9dc24338fe28863b37e128af9baded94815135e2 100644
--- a/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java
+++ b/src/main/java/org/olat/modules/webFeed/dispatching/FeedMediaDispatcher.java
@@ -20,34 +20,37 @@
 package org.olat.modules.webFeed.dispatching;
 
 import java.io.IOException;
-import java.util.Hashtable;
+import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.olat.basesecurity.Authentication;
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.dispatcher.Dispatcher;
 import org.olat.core.dispatcher.DispatcherModule;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.UserRequestImpl;
+import org.olat.core.gui.control.Event;
 import org.olat.core.gui.media.MediaResource;
 import org.olat.core.gui.media.ServletUtil;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
-import org.olat.core.logging.LogDelegator;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.WebappHelper;
+import org.olat.core.util.cache.CacheWrapper;
+import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSMediaResource;
 import org.olat.course.CourseFactory;
-import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.run.userview.NodeEvaluation;
@@ -62,6 +65,10 @@ import org.olat.modules.webFeed.manager.FeedManager;
 import org.olat.portfolio.manager.EPFrontendManager;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
+import org.olat.repository.RepositoryService;
+import org.olat.repository.controllers.EntryChangedEvent;
+import org.olat.repository.controllers.EntryChangedEvent.Change;
+import org.olat.repository.model.RepositoryEntrySecurity;
 import org.olat.resource.OLATResourceManager;
 
 /**
@@ -75,80 +82,182 @@ import org.olat.resource.OLATResourceManager;
  * 
  * @author gwassmann
  */
-public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
+public class FeedMediaDispatcher implements Dispatcher, GenericEventListener {
+	
+	private static final OLog log = Tracing.createLoggerFor(FeedMediaDispatcher.class);
 
-	private static final String PODCAST_URI_PREFIX = FeedManager.KIND_PODCAST;
-	private static final String BLOG_URI_PREFIX = FeedManager.KIND_BLOG;
 	public static final String TOKEN_PROVIDER = "feed";
+	
+	private DB dbInstance;
+	private FeedManager feedManager;
+	private BaseSecurity securityManager;
+	private RepositoryManager repositoryManager;
+	private OLATResourceManager resourceManager;
+	private CacheWrapper<FeedPathKey,Boolean> validatedUriCache;
 
-	public static Hashtable<String, String> resourceTypes, uriPrefixes;
-	static {
-		// Mapping: uri prefix --> resource type
-		resourceTypes = new Hashtable<String, String>();
-		resourceTypes.put(PODCAST_URI_PREFIX, PodcastFileResource.TYPE_NAME);
-		resourceTypes.put(BLOG_URI_PREFIX, BlogFileResource.TYPE_NAME);
-
-		// Mapping: resource type --> uri prefix
-		uriPrefixes = new Hashtable<String, String>();
-		uriPrefixes.put(PodcastFileResource.TYPE_NAME, PODCAST_URI_PREFIX);
-		uriPrefixes.put(BlogFileResource.TYPE_NAME, BLOG_URI_PREFIX);
+	/**
+	 * [used by Spring]
+	 * @param feedManager
+	 */
+	public void setFeedManager(FeedManager feedManager) {
+		this.feedManager = feedManager;
+	}
+	
+	/**
+	 * [used by Spring]
+	 * @param coordinator
+	 */
+	public void setCoordinatorManager(CoordinatorManager coordinator) {
+		validatedUriCache = coordinator.getCoordinator().getCacher().getCache(Path.class.getSimpleName(), "feed");
+		coordinator.getCoordinator().getEventBus().registerFor(this, null, RepositoryService.REPOSITORY_EVENT_ORES);	
+	}
+	
+	/**
+	 * [used by Spring]
+	 * @param securityManager
+	 */
+	public void setSecurityManager(BaseSecurity securityManager) {
+		this.securityManager = securityManager;
+	}
+	
+	/**
+	 * [used by Spring]
+	 * @param repositoryManager
+	 */
+	public void setRepositoryManager(RepositoryManager repositoryManager) {
+		this.repositoryManager = repositoryManager;
+	}
+	
+	/**
+	 * [used by Spring]
+	 * @param dbInstance
+	 */
+	public void setDbInstance(DB dbInstance) {
+		this.dbInstance = dbInstance;
 	}
-
-	private static final OLog log = Tracing.createLoggerFor(FeedMediaDispatcher.class);
 
 	/**
-	 * @see org.olat.core.dispatcher.Dispatcher#execute(javax.servlet.http.HttpServletRequest,
-	 *      javax.servlet.http.HttpServletResponse, java.lang.String)
+	 * [used by Spring]
+	 * @param resourceManager
 	 */
+	public void setResourceManager(OLATResourceManager resourceManager) {
+		this.resourceManager = resourceManager;
+	}
+
+	@Override
+	public void event(Event event) {
+		if(event instanceof EntryChangedEvent) {
+			EntryChangedEvent ece = (EntryChangedEvent)event;
+			if(ece.getChange() == Change.modifiedAccess || ece.getChange() == Change.modifiedAtPublish) {
+				Long entryKey = ece.getRepositoryEntryKey();
+				discardCache(entryKey);
+			}
+		}
+	}
+	
+	private void discardCache(Long entryKey) {
+		List<FeedPathKey> keys = validatedUriCache.getKeys();
+		if(!keys.isEmpty()) {	
+			RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(entryKey);
+			if(entry != null) {
+				Long resourceId = entry.getOlatResource().getResourceableId();
+				for(FeedPathKey key:keys) {
+					if(resourceId.equals(key.getResourceId())) {
+						try {
+							validatedUriCache.remove(key);
+						} catch (Exception e) {
+							log.info("Cannot remove this key: " + key);
+						}
+					}
+				}
+			}
+		}
+	}
+
 	@Override
 	public void execute(HttpServletRequest request, HttpServletResponse response) {
 		String uriPrefix = DispatcherModule.getLegacyUriPrefix(request);
 		String requestedPath = getPath(request, uriPrefix);
 
+		UserRequest ureq = null;
+		try{
+			//upon creation URL is checked for 
+			ureq = new UserRequestImpl(uriPrefix, request, response);
+		} catch(NumberFormatException nfe) {
+			//
+		}
+
 		Path path = null;
 		try {
 			// Assume the URL was correct.
 			// At first, look up path in cache. Even before extracting any parameters
-
 			path = new Path(requestedPath);
+			path.compile();
+			
 			// See brasatoconfigpart.xml. The uriPrefix is like '/olat/podcast/' or
 			// '/company/blog/'. Get the podcast or blog string.
 			// remove the last slash if it exists
 			int lastIndex = uriPrefix.length() - 1;
-			if (uriPrefix.lastIndexOf("/") == lastIndex) {
+			if (uriPrefix.lastIndexOf('/') == lastIndex) {
 				uriPrefix = uriPrefix.substring(0, lastIndex);
 			}
-			int lastSlashPos = uriPrefix.lastIndexOf("/");
+			int lastSlashPos = uriPrefix.lastIndexOf('/');
 			String feedUriPrefix = uriPrefix.substring(lastSlashPos + 1);
-			// String feedUriPrefix = uriPrefix.replaceAll("olat|/", "");
-			OLATResourceable feed = null;
-
-			if (path.isCachedAndAccessible()) {
-				// Serve request
-				path.compile();
-				feed = OLATResourceManager.getInstance().findResourceable(path.getFeedId(), resourceTypes.get(feedUriPrefix));
+			OLATResourceable feed = resourceManager.findResourceable(path.getFeedId(), getResourceType(feedUriPrefix));
+			if(isAccessible(ureq, path, feed)) {
 				deliverFile(request, response, feed, path);
 			} else {
-				path.compile();
-				feed = OLATResourceManager.getInstance().findResourceable(path.getFeedId(), resourceTypes.get(feedUriPrefix));
-				if (hasAccess(feed, path)) {
-					// Only cache when accessible
-					path.cache(feed, true);
-					deliverFile(request, response, feed, path);
-				} else {
-					// Deny access
-					log.info("Access was denied. Path::" + path);
-					DispatcherModule.sendForbidden(request.getRequestURI(), response);
-				}
+				log.info("Access was denied. Path::" + path);
+				DispatcherModule.sendForbidden(request.getRequestURI(), response);
 			}
 		} catch (InvalidPathException e) {
-			logWarn("The requested path is invalid. path::" + path, e);
+			log.warn("The requested path is invalid. path::" + path, e);
 			DispatcherModule.sendBadRequest(request.getRequestURI(), response);
-		} catch (Throwable t) {
-			logWarn("Nothing was delivered. Path::" + path, t);
+		} catch (Exception e) {
+			log.warn("Nothing was delivered. Path::" + path, e);
 			DispatcherModule.sendNotFound(request.getRequestURI(), response);
 		}
 	}
+	
+	public static final String getURIPrefix(String type) {
+		if(PodcastFileResource.TYPE_NAME.equals(type)) {
+			return FeedManager.KIND_PODCAST;
+		} else if(BlogFileResource.TYPE_NAME.equals(type)) {
+			return FeedManager.KIND_BLOG;
+		}
+		return null;
+	}
+	
+	private static final String getResourceType(String feedUriPrefix) {
+		if(FeedManager.KIND_PODCAST.equals(feedUriPrefix)) {
+			return PodcastFileResource.TYPE_NAME;
+		} else if(FeedManager.KIND_BLOG.equals(feedUriPrefix)) {
+			return BlogFileResource.TYPE_NAME;
+		}
+		return null;
+	}
+	
+	private boolean isAccessible(UserRequest ureq, Path path, OLATResourceable feed) {
+		Boolean accessible = null;
+	
+		Long ressourceId;
+		if(path.getCourseId() == null) {
+			ressourceId = path.getFeedId();
+		} else {
+			ressourceId = path.getCourseId();
+		}
+		if(path.getIdentityKey() == null && ureq != null && ureq.getIdentity() != null) {
+			path.setIdentityKey(ureq.getIdentity().getKey());
+		}
+		
+		FeedPathKey key = new FeedPathKey(path.getIdentityKey(), ressourceId, path.getNodeId());
+		accessible = validatedUriCache.computeIfAbsent(key, k -> {
+			boolean hasAccess = hasAccess(ureq, feed, path);
+			return Boolean.valueOf(hasAccess);
+		});
+
+		return accessible != null && accessible.booleanValue();
+	}
 
 	/**
 	 * Dispatch and deliver the requested file given in the path.
@@ -161,18 +270,20 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 	private void deliverFile(HttpServletRequest request, HttpServletResponse response, OLATResourceable feed, Path path) {
 		// OLAT-5243 related: deliverFile can last arbitrary long, which can cause the open db connection to timeout and cause errors,
 		// hence we need to do an intermediateCommit here
-		DBFactory.getInstance().intermediateCommit();
+		dbInstance.intermediateCommit();
 
 		// Create the resource to be delivered
 		MediaResource resource = null;
-		FeedManager manager = FeedManager.getInstance();
 
 		if (path.isFeedType()) {
 			// Only create feed if modified. Send not modified response else.
-			Identity identity = getIdentity(path.getIdentityKey());
+			Identity identity = null;
+			if(path.getIdentityKey() != null) {
+				identity = securityManager.loadIdentityByKey(path.getIdentityKey());
+			}
 			long sinceModifiedMillis = request.getDateHeader("If-Modified-Since");
 			
-			Feed feedLight = manager.loadFeed(feed);
+			Feed feedLight = feedManager.loadFeed(feed);
 			long lastModifiedMillis = -1;
 			if (feedLight != null) {
 				lastModifiedMillis = feedLight.getLastModified().getTime();
@@ -189,17 +300,17 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 					return;
 				}
 			} else {
-				resource = manager.createFeedFile(feed, identity, path.getCourseId(), path.getNodeId());
+				resource = feedManager.createFeedFile(feed, identity, path.getCourseId(), path.getNodeId());
 			}
 		} else if (path.isItemType()) {
-			resource = manager.createItemMediaFile(feed, path.getItemId(), path.getItemFileName());
+			resource = feedManager.createItemMediaFile(feed, path.getItemId(), path.getItemFileName());
 		} else if (path.isIconType()) {
 			Size thumbnailSize = null;
 			String thumbnail = request.getParameter("thumbnail");
 			if(StringHelper.containsNonWhitespace(thumbnail)) {
 				thumbnailSize = Size.parseString(thumbnail);
 			}
-			VFSLeaf resourceFile = manager.createFeedMediaFile(feed, path.getIconFileName(), thumbnailSize);
+			VFSLeaf resourceFile = feedManager.createFeedMediaFile(feed, path.getIconFileName(), thumbnailSize);
 			if(resourceFile != null) {
 				resource = new VFSMediaResource(resourceFile);
 			}
@@ -208,20 +319,6 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 		ServletUtil.serveResource(request, response, resource);
 	}
 
-	/**
-	 * Get the identity from the key.
-	 * 
-	 * @param idKey
-	 * @return the Identity
-	 */
-	private Identity getIdentity(Long idKey) {
-		Identity identity = null;
-		if (idKey != null) {
-			identity = BaseSecurityManager.getInstance().loadIdentityByKey(idKey);
-		}
-		return identity;
-	}
-
 	/**
 	 * Remove some prefixes from the request path.
 	 * 
@@ -245,21 +342,18 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 	 * @param path
 	 * @return true if the path may be dispatched.
 	 */
-	private boolean hasAccess(OLATResourceable feed, Path path) {
+	private boolean hasAccess(UserRequest ureq, OLATResourceable feed, Path path) {
 		boolean hasAccess = false;
-		Identity identity = getIdentity(path.getIdentityKey());
-
 		if (path.isCourseType()) {
 			// A course node is being requested
-			OLATResourceable oresCourse = OLATResourceManager.getInstance()
-					.findResourceable(path.getCourseId(), CourseModule.getCourseTypeName());
-			ICourse course = CourseFactory.loadCourse(oresCourse);
-			CourseNode node = course.getEditorTreeModel().getCourseNode(path.getNodeId());
-			// Check access
-			hasAccess = hasAccess(identity, path.getToken(), course, node);
+			ICourse course = CourseFactory.loadCourse(path.getCourseId());
+			if(course != null) {
+				CourseNode node = course.getEditorTreeModel().getCourseNode(path.getNodeId());
+				hasAccess = hasAccess(ureq, path, course, node, feed);
+			}
 		} else {
 			// A learning resource is being requested
-			hasAccess = hasAccess(identity, path.getToken(), feed);
+			hasAccess = hasAccess(ureq, path, feed);
 		}
 		return hasAccess;
 	}
@@ -274,25 +368,48 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 	 * @return True if the identity has access to the node in the given course.
 	 *         False otherwise.
 	 */
-	private boolean hasAccess(Identity identity, String token, ICourse course, CourseNode node) {
+	private boolean hasAccess(UserRequest ureq, Path path, ICourse course, CourseNode node, OLATResourceable feed) {
+		RepositoryEntry entry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
+		if (allowsGuestAccess(entry)) {
+			return true;
+		}
+		
+		Roles roles = null;
+		Identity identity = null;
+		RepositoryEntrySecurity reSecurity = null;
+		if(ureq != null && ureq.getIdentity() != null) {
+			identity = ureq.getIdentity();
+			roles = ureq.getUserSession().getRoles();
+			reSecurity = repositoryManager.isAllowed(ureq, entry);
+		} else if(path.getIdentityKey() != null) {
+			identity = securityManager.loadIdentityByKey(path.getIdentityKey());
+			if(validAuthentication(identity, path.getToken())) {
+				roles = securityManager.getRoles(identity);
+				reSecurity = repositoryManager.isAllowed(identity, roles, entry);
+			}
+		}
+		
 		boolean hasAccess = false;
-		final RepositoryManager resMgr = RepositoryManager.getInstance();
-		final RepositoryEntry repoEntry = resMgr.lookupRepositoryEntry(course, false);
-		if (allowsGuestAccess(repoEntry)) {
-			hasAccess = true;
-		} else {
-			IdentityEnvironment ienv = new IdentityEnvironment();
-			ienv.setIdentity(identity);
-			Roles roles = BaseSecurityManager.getInstance().getRoles(identity);
-			ienv.setRoles(roles);
-			UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(ienv, course.getCourseEnvironment());
+		if(identity != null && roles != null && reSecurity != null) {
+			IdentityEnvironment ienv = new IdentityEnvironment(identity, roles);
+			UserCourseEnvironment userCourseEnv = new UserCourseEnvironmentImpl(ienv, course.getCourseEnvironment(), null, null, null, null,
+					reSecurity.isCourseCoach() || reSecurity.isGroupCoach(), reSecurity.isEntryAdmin(), reSecurity.isCourseParticipant() || reSecurity.isGroupParticipant(),
+					false);
 			// Build an evaluation tree
 			TreeEvaluation treeEval = new TreeEvaluation();
 			NodeEvaluation nodeEval = node.eval(userCourseEnv.getConditionInterpreter(), treeEval, new VisibleTreeFilter());
-			if (nodeEval.isVisible() && validAuthentication(identity, token)) {
+			if (nodeEval.isVisible()) {
 				hasAccess = true;
 			}
 		}
+		
+		if(!hasAccess) {
+			//allow if the feed resource itself allow guest access
+			entry = repositoryManager.lookupRepositoryEntry(feed, false);
+			if (allowsGuestAccess(entry)) {
+				return true;
+			}
+		}
 		return hasAccess;
 	}
 
@@ -304,24 +421,39 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 	 * @param feed
 	 * @return true if the identity has access.
 	 */
-	private boolean hasAccess(Identity identity, String token, OLATResourceable feed) {
+	private boolean hasAccess(UserRequest ureq, Path path, OLATResourceable feed) {
+		RepositoryEntry entry = repositoryManager.lookupRepositoryEntry(feed, false);
+		if (allowsGuestAccess(entry)) {
+			return true;
+		}
+		
+		Identity identity = null;
+		RepositoryEntrySecurity reSecurity = null;
+		if(ureq != null && ureq.getIdentity() != null) {
+			identity = ureq.getIdentity();
+			if(entry != null) {
+				Roles roles = ureq.getUserSession().getRoles();
+				reSecurity = repositoryManager.isAllowed(identity, roles, entry);
+			}
+		} else if(path.getIdentityKey() != null) {
+			identity = securityManager.loadIdentityByKey(path.getIdentityKey());
+			if(validAuthentication(identity, path.getToken())) {
+				Roles roles = securityManager.getRoles(identity);
+				reSecurity = repositoryManager.isAllowed(identity, roles, entry);
+			}
+		}
+		
 		boolean hasAccess = false;
-		RepositoryManager resMgr = RepositoryManager.getInstance();
-		RepositoryEntry repoEntry = resMgr.lookupRepositoryEntry(feed, false);
-		if (allowsGuestAccess(repoEntry)) {
-			hasAccess = true;
-		} else if (identity != null) {
-			if (repoEntry != null){
-				final Roles roles = BaseSecurityManager.getInstance().getRoles(identity);
-				final boolean isAllowedToLaunch = resMgr.isAllowedToLaunch(identity, roles, repoEntry);
-				if (isAllowedToLaunch && validAuthentication(identity, token)) {
+		if (identity != null) {
+			if (entry != null){
+				if (reSecurity != null && reSecurity.canLaunch()) {
 					hasAccess = true;
 				}
 			} else {
 				// no repository entry -> could be a feed without a repository-entry (ePortfolio-Blog-feed)
 				EPFrontendManager ePFMgr = (EPFrontendManager) CoreSpringFactory.getBean("epFrontendManager");
 				if (ePFMgr.checkFeedAccess(feed, identity)){
-					return validAuthentication(identity, token);
+					return validAuthentication(identity, path.getToken());
 				}
 			}
 		}
@@ -336,13 +468,10 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 	 * @return True if authentication is valid
 	 */
 	private boolean validAuthentication(Identity identity, String token) {
-		boolean valid = false;
-		BaseSecurity secMgr = BaseSecurityManager.getInstance();
-		Authentication authentication = secMgr.findAuthenticationByAuthusername(identity.getKey().toString(), TOKEN_PROVIDER);
-		if (authentication != null && authentication.getCredential().equals(token)) {
-			valid = true;
-		}
-		return valid;
+		if(identity == null || token == null) return false;
+		
+		Authentication authentication = securityManager.findAuthenticationByAuthusername(identity.getKey().toString(), TOKEN_PROVIDER);
+		return authentication != null && authentication.getCredential().equals(token);
 	}
 
 	/**
@@ -356,15 +485,4 @@ public class FeedMediaDispatcher extends LogDelegator implements Dispatcher {
 		}
 		return guestsAllowed;
 	}
-
-	/**
-	 * Redirect to Path.getFeedBaseUri()
-	 * 
-	 * @param feed
-	 * @param identityKey
-	 * @return The feed base uri for the given user (identity)
-	 */
-	public static String getFeedBaseUri(Feed feed, Identity identity, Long courseId, String nodeId) {
-		return Path.getFeedBaseUri(feed, identity, courseId, nodeId);
-	}
 }
diff --git a/src/main/java/org/olat/modules/webFeed/dispatching/FeedPathKey.java b/src/main/java/org/olat/modules/webFeed/dispatching/FeedPathKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec57b2e0be569fe258265a0d16abe8b9d9874108
--- /dev/null
+++ b/src/main/java/org/olat/modules/webFeed/dispatching/FeedPathKey.java
@@ -0,0 +1,65 @@
+/**
+ * <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.modules.webFeed.dispatching;
+
+/**
+ * 
+ * Initial date: 9 Oct 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class FeedPathKey {
+	
+	private final Long identityKey;
+	private final Long ressourceId;
+	private final String nodeId;
+	
+	public FeedPathKey(Long identityKey, Long ressourceId, String nodeId) {
+		this.identityKey = identityKey;
+		this.ressourceId = ressourceId;
+		this.nodeId = nodeId;
+	}
+	
+	public Long getResourceId() {
+		return ressourceId;
+	}
+	
+	@Override
+	public int hashCode() {
+		return ressourceId.hashCode()
+				+ (identityKey == null ? 1819879 : identityKey.hashCode())
+				+ (nodeId == null ? 52387 : nodeId.hashCode());
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(obj == this) {
+			return true;
+		}
+		if(obj instanceof FeedPathKey) {
+			FeedPathKey key = (FeedPathKey)obj;
+			return ressourceId.equals(key.ressourceId)
+					&& ((identityKey == null && key.identityKey == null) || (identityKey != null && identityKey.equals(key.identityKey)))
+					&& ((nodeId == null && key.nodeId == null) || (nodeId != null && nodeId.equals(key.nodeId)));
+			
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/modules/webFeed/dispatching/Path.java b/src/main/java/org/olat/modules/webFeed/dispatching/Path.java
index 9da1482bbbefc3ad16accf15a1a629b84e18aff6..5a439f2618260c2c7d9cbae02ca59b69926d8f2e 100644
--- a/src/main/java/org/olat/modules/webFeed/dispatching/Path.java
+++ b/src/main/java/org/olat/modules/webFeed/dispatching/Path.java
@@ -24,20 +24,7 @@ import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.lang.RandomStringUtils;
-import org.olat.basesecurity.Authentication;
-import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.BaseSecurityManager;
-import org.olat.core.helpers.Settings;
-import org.olat.core.id.Identity;
-import org.olat.core.id.OLATResourceable;
-import org.olat.core.util.cache.CacheWrapper;
-import org.olat.core.util.coordinate.CoordinatorManager;
-import org.olat.core.util.coordinate.SyncerExecutor;
-import org.olat.modules.webFeed.Feed;
 import org.olat.modules.webFeed.manager.FeedManager;
-import org.olat.repository.RepositoryEntry;
-import org.olat.repository.RepositoryManager;
 
 /**
  * The Path class.
@@ -53,9 +40,6 @@ import org.olat.repository.RepositoryManager;
  * @author gwassmann
  */
 public class Path {
-	// Not private for better performance (apperently)
-	protected static final CacheWrapper<String,Boolean> validatedUriCache = CoordinatorManager.getInstance().getCoordinator().getCacher().getCache(Path.class.getSimpleName(),
-			"feed");
 
 	// Instance variables
 	private int type;
@@ -81,16 +65,13 @@ public class Path {
 	private static final int AUTHENTICATED_COURSE_ICON = FEED_MEDIA + COURSE + AUTHENTICATED;
 	private static final int AUTHENTICATED_COURSE_ITEM = ITEM_MEDIA + COURSE + AUTHENTICATED;
 
-	// Class variables
-	private static final String TOKEN_PROVIDER = FeedMediaDispatcher.TOKEN_PROVIDER;
-
 	// Patterns
 	private static final String NUMBER = "(\\d+)";
 	private static final String WORD = "(\\w+)";
 	private static final String FILE_NAME = "([^/]+\\.\\w{3,4})";
 	private static final String SLASH = "/";
 	private static final String BASE_PATH_DELIMITER = "/_/";
-	private static final String COURSE_NODE_INDICATOR = "coursenode";
+	public static final String COURSE_NODE_INDICATOR = "coursenode";
 	public static final String MEDIA_DIR = "media";	
 
 	private static final String AUTHENTICATION = NUMBER + SLASH + WORD + SLASH;
@@ -118,7 +99,7 @@ public class Path {
 	private static final Pattern authCourseFeedMediaPattern = Pattern.compile(AUTH_COURSE_PATH + FEED_MEDIA_PATH);
 	private static final Pattern authCourseItemMediaPattern = Pattern.compile(AUTH_COURSE_PATH + ITEM_MEDIA_PATH);
 
-	private static List<Pattern> patterns = new ArrayList<Pattern>();
+	private static List<Pattern> patterns = new ArrayList<>();
 	static {
 		patterns.add(feedPattern);
 		patterns.add(feedMediaPattern);
@@ -275,52 +256,6 @@ public class Path {
 		}
 	}
 
-	/**
-	 * @return true if the path is in cache and it is accessible, meaning access
-	 *         has been verified successfully.
-	 */
-	public boolean isCachedAndAccessible() {
-		// return true if path is in the cache and the validation was successful
-		Boolean accessible = validatedUriCache.get(getKey());
-		return accessible != null ? accessible : false;
-	}
-
-	/**
-	 * A feed contains many URLs and links etc. The validation of the URL and
-	 * verification of access should only be done once for performance reasons.
-	 * That's where caching comes in. We'll store the URL as the key and give it a
-	 * boolean value whether access has been successfully granted or not.
-	 */
-	public void cache(OLATResourceable ores, final boolean accessible) {
-		final String key = getKey();
-		CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(ores, new SyncerExecutor() {
-			public void execute() {
-				if (validatedUriCache.get(key) == null) {
-					validatedUriCache.put(key, accessible);
-				} else {
-					validatedUriCache.update(key, accessible);
-				}
-			}
-		});
-	}
-
-	/**
-	 * Get the key for caching.
-	 * 
-	 * @return the key for caching. I.e. the URL without any file or item
-	 *         information.
-	 */
-	private String getKey() {
-		String key = null;
-		// If the type is ITEM_MEDIA (+ something) remove the item id and the filename
-		// from the original path.
-		if (originalPath != null) {
-			int delimiterIndex = originalPath.indexOf(BASE_PATH_DELIMITER);
-			key = originalPath.substring(0, delimiterIndex);
-		}
-		return key;
-	}
-
 	/**
 	 * @param feedId The feedId to set.
 	 */
@@ -475,74 +410,9 @@ public class Path {
 		return type > COURSE;
 	}
 
-	/**
-	 * @see java.lang.Object#toString()
-	 */
+	@Override
 	public String toString() {
 		return originalPath;
 	}
-
-	/**
-	 * Returns a podcast base URI of the type<br>
-	 * http://myolat.org/olat/[podcast|blog]/[IDKEY/TOKEN]/ORESID
-	 * 
-	 * @param feed
-	 * @param identityKey
-	 * @return The feed base uri for the given user (identity)
-	 */
-	public static String getFeedBaseUri(Feed feed, Identity identity, Long courseId, String nodeId) {
-		BaseSecurity manager = BaseSecurityManager.getInstance();
-		boolean isCourseNode = courseId != null && nodeId != null;
-
-		final String slash = "/";
-		StringBuffer uri = new StringBuffer();
-		uri.append(Settings.getServerContextPathURI());
-		uri.append(slash);
-		uri.append(FeedMediaDispatcher.uriPrefixes.get(feed.getResourceableTypeName()));
-		uri.append(slash);
-
-		if (isCourseNode) {
-			uri.append(COURSE_NODE_INDICATOR);
-			uri.append(slash);
-		}
-
-		if (identity != null) {
-			// The identity can be null for guests
-			String idKey = identity.getKey().toString();
-			Authentication authentication = manager.findAuthenticationByAuthusername(idKey, TOKEN_PROVIDER);
-			if (authentication == null) {
-				// Create an authentication
-				String token = RandomStringUtils.randomAlphanumeric(6);
-				authentication = manager.createAndPersistAuthentication(identity, TOKEN_PROVIDER, idKey, token, null);
-			}
-			// If the repository entry allows guest access it is public, thus not
-			// private.
-			boolean isPrivate = true;
-			RepositoryEntry entry = RepositoryManager.getInstance().lookupRepositoryEntry(feed, false);
-			if (entry != null && entry.isGuests()) {
-				isPrivate = false;
-			}
-
-			if (isPrivate) {
-				// identity key
-				uri.append(idKey);
-				uri.append(slash);
-				// token
-				uri.append(authentication.getCredential());
-				uri.append(slash);
-			}
-		}
-
-		if (isCourseNode) {
-			uri.append(courseId);
-			uri.append(slash);
-			uri.append(nodeId);
-			uri.append(slash);
-		}
-		// feed id
-		uri.append(feed.getResourceableId());
-		// Append base uri delimiter. (Used to identify the root path for caching)
-		uri.append("/_");
-		return uri.toString();
-	}
+	
 }
diff --git a/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java b/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java
index d47e4afeb35bf37c939ca0ad43d3c75b2f69e65d..33e9d1bc2748ce9544b762c7b34adddd6e9cc879 100644
--- a/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java
+++ b/src/main/java/org/olat/modules/webFeed/manager/FeedManagerImpl.java
@@ -27,7 +27,9 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
+import org.apache.commons.lang.RandomStringUtils;
 import org.olat.admin.quota.QuotaConstants;
+import org.olat.basesecurity.Authentication;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged;
@@ -39,6 +41,7 @@ import org.olat.core.commons.services.image.Size;
 import org.olat.core.commons.services.notifications.NotificationsManager;
 import org.olat.core.gui.components.form.flexible.elements.FileElement;
 import org.olat.core.gui.media.MediaResource;
+import org.olat.core.helpers.Settings;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.logging.OLog;
@@ -591,7 +594,7 @@ public class FeedManagerImpl extends FeedManager {
 	 * @return A unique key for the item of the feed
 	 */
 	private String itemKey(String string, String string2) {
-		final StringBuffer key = new StringBuffer();
+		final StringBuilder key = new StringBuilder(128);
 		key.append("feed").append(string2);
 		key.append("_item_").append(string);
 		return key.toString();
@@ -606,8 +609,7 @@ public class FeedManagerImpl extends FeedManager {
 	 * @return A unique key for the item of the feed
 	 */
 	protected String itemKey(Item item, OLATResourceable feed) {
-		String key = itemKey(item.getGuid(), feed.getResourceableId().toString());
-		return key;
+		return itemKey(item.getGuid(), feed.getResourceableId().toString());
 	}
 
 	@Override
@@ -669,9 +671,69 @@ public class FeedManagerImpl extends FeedManager {
 		return mediaResource;
 	}
 
+	/**
+	 * Returns a podcast base URI of the type<br>
+	 * http://myolat.org/olat/[podcast|blog]/[IDKEY/TOKEN]/ORESID
+	 * 
+	 * @param feed
+	 * @param identityKey
+	 * @return The feed base uri for the given user (identity)
+	 */
 	@Override
 	public String getFeedBaseUri(Feed feed, Identity identity, Long courseId, String nodeId) {
-		return FeedMediaDispatcher.getFeedBaseUri(feed, identity, courseId, nodeId);
+		boolean isCourseNode = courseId != null && nodeId != null;
+
+		final String slash = "/";
+		StringBuilder uri = new StringBuilder(256);
+		uri.append(Settings.getServerContextPathURI());
+		uri.append(slash);
+		uri.append(FeedMediaDispatcher.getURIPrefix(feed.getResourceableTypeName()));
+		uri.append(slash);
+
+		if (isCourseNode) {
+			uri.append(org.olat.modules.webFeed.dispatching.Path.COURSE_NODE_INDICATOR);
+			uri.append(slash);
+		}
+
+		if (identity != null) {
+			// The identity can be null for guests
+			String idKey = identity.getKey().toString();
+			Authentication authentication = securityManager.findAuthenticationByAuthusername(idKey, FeedMediaDispatcher.TOKEN_PROVIDER);
+			if (authentication == null) {
+				// Create an authentication
+				String token = RandomStringUtils.randomAlphanumeric(6);
+				authentication = securityManager.createAndPersistAuthentication(identity, FeedMediaDispatcher.TOKEN_PROVIDER, idKey, token, null);
+			}
+			// If the repository entry allows guest access it is public, thus not
+			// private.
+			RepositoryEntry entry;
+			if(courseId != null) {//check the course
+				OLATResourceable courseOres = OresHelper.createOLATResourceableInstance("CourseModule", courseId);
+				entry = repositoryManager.lookupRepositoryEntry(courseOres, false);
+			} else {
+				entry = repositoryManager.lookupRepositoryEntry(feed, false);
+			}
+			if (entry == null || entry.isGuests()) {
+				// identity key
+				uri.append(idKey);
+				uri.append(slash);
+				// token
+				uri.append(authentication.getCredential());
+				uri.append(slash);
+			}
+		}
+
+		if (isCourseNode) {
+			uri.append(courseId);
+			uri.append(slash);
+			uri.append(nodeId);
+			uri.append(slash);
+		}
+		// feed id
+		uri.append(feed.getResourceableId());
+		// Append base uri delimiter. (Used to identify the root path for caching)
+		uri.append("/_");
+		return uri.toString();
 	}
 
 	@Override
@@ -813,6 +875,14 @@ public class FeedManagerImpl extends FeedManager {
 
 		if (feed != null) {
 			List<Item> itemsFromXml = feedFileStorage.loadItemsFromXML(ores);
+			
+			// clean up for MySQL
+			if(dbInstance.isMySQL()) {
+				for (Item itemFromXml : itemsFromXml) {
+					mysqlCleanUp(itemFromXml);
+				}
+			}
+			
 			itemsFromXml = fixFeedVersionIssues(feedFromXml, itemsFromXml);
 			for (Item itemFromXml : itemsFromXml) {
 				// Check if the item already exits or create it.
diff --git a/src/main/resources/infinispan-config.xml b/src/main/resources/infinispan-config.xml
index 68aed851e512d73d36f7c0076f8f52c26aa9b590..a6d45b940a00315defbe6914c18695abf0e99188 100644
--- a/src/main/resources/infinispan-config.xml
+++ b/src/main/resources/infinispan-config.xml
@@ -161,20 +161,11 @@
 			<expiration max-idle="3600000" interval="15000" />
 		</local-cache>
 		
-		<local-cache name="FeedManager@feed" simple-cache="true" statistics="true" statistics-available="true">
-			<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" />
-			<transaction mode="NONE" auto-commit="true" />
-			<memory>
-				<object size="1000" strategy="REMOVE" />
-			</memory>
-			<expiration max-idle="900000" interval="15000" />
-		</local-cache>
-		
 		<local-cache name="Path@feed" simple-cache="true" statistics="true" statistics-available="true">
 			<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false" />
 			<transaction mode="NONE" auto-commit="true" />
 			<memory>
-				<object size="1000" strategy="REMOVE" />
+				<object size="5000" strategy="REMOVE" />
 			</memory>
 			<expiration max-idle="900000" interval="15000" />
 		</local-cache>
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 49e637d9992e2bf02c1420b599afc33313d5ddd8..6f7b48510df4667078c731b7b404f7a560be808a 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -124,7 +124,7 @@ smtp.sslCheckCertificate=false
 smtp.starttls=false
 # timeout in milliseconds
 smtp.timeout=8000
-# fix from of the smtp envelope
+# smtp.from will override the mail envelope, leave it empty to set it to the first reply-to address
 smtp.from=
 # system mails will be sent from this address (from local domain with valid reverse dns):
 fromemail=no-reply@your.domain
@@ -1190,8 +1190,13 @@ ldap.learningResourceManagerRoleValue=
 # Build properties
 #####
 application.name=OpenOLAT
+<<<<<<< HEAD
 build.version=13.0b7
 build.identifier=openolat130b7-dev
+=======
+build.version=12.5.7
+build.identifier=openolat1257-dev
+>>>>>>> refs/remotes/origin/OpenOLAT_12.5
 build.repo.revision=local-devel
 
 #####
diff --git a/src/test/java/org/olat/modules/webFeed/manager/FeedManagerImplTest.java b/src/test/java/org/olat/modules/webFeed/manager/FeedManagerImplTest.java
index d43a1349b30bf649e67c1742bc3a1e6856507608..13504230d16d8779325cad18cf0f22d73e9efde0 100644
--- a/src/test/java/org/olat/modules/webFeed/manager/FeedManagerImplTest.java
+++ b/src/test/java/org/olat/modules/webFeed/manager/FeedManagerImplTest.java
@@ -42,6 +42,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.services.notifications.NotificationsManager;
 import org.olat.core.gui.components.form.flexible.elements.FileElement;
 import org.olat.core.id.Identity;
@@ -87,7 +88,9 @@ public class FeedManagerImplTest {
 	private FileElement fileElementDummy;
 	@Mock
 	private Syncer syncerDummy;
-
+	
+	@Mock
+	private DB dbInstanceMock;
 	@Mock
 	private FeedDAO feedDAOMock;
 	@Mock
@@ -118,6 +121,8 @@ public class FeedManagerImplTest {
 		when(coordinaterMock.getSyncer()).thenReturn(syncerDummy);
 		sut = new FeedManagerImpl(resourceManagerMock, fileResourceManagerMock, coordinaterManagerMock);
 		feedDAOMock = mock(FeedDAO.class);
+		
+		ReflectionTestUtils.setField(sut, "dbInstance", dbInstanceMock);
 		ReflectionTestUtils.setField(sut, "feedDAO", feedDAOMock);
 		ReflectionTestUtils.setField(sut, "itemDAO", itemDAOMock);
 		ReflectionTestUtils.setField(sut, "feedFileStorage", feedFileStorageMock);
diff --git a/src/test/java/org/olat/selenium/ImsQTI21EditorTest.java b/src/test/java/org/olat/selenium/ImsQTI21EditorTest.java
index 342e45c0f1ccf90ec6c5d5c560dcbbc6f93367d1..f80e802b58018c5f1e8781d98e92ec76720b0a43 100644
--- a/src/test/java/org/olat/selenium/ImsQTI21EditorTest.java
+++ b/src/test/java/org/olat/selenium/ImsQTI21EditorTest.java
@@ -935,6 +935,7 @@ public class ImsQTI21EditorTest extends Deployments {
 		File backgroundImageFile = new File(backgroundImageUrl.toURI());
 		hotspotEditor
 			.updloadBackground(backgroundImageFile)
+			.moveToHotspotEditor()
 			.resizeCircle()
 			.moveCircle(300, 120)
 			.addRectangle()
@@ -960,6 +961,7 @@ public class ImsQTI21EditorTest extends Deployments {
 			.addHotspot();
 		hotspotEditor
 			.updloadBackground(backgroundImageFile)
+			.moveToHotspotEditor()
 			.resizeCircle()
 			.moveCircle(310, 125)
 			.addRectangle()
@@ -1015,17 +1017,17 @@ public class ImsQTI21EditorTest extends Deployments {
 		ryomouQtiPage
 			.assertOnAssessmentItem()
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Incorrect")
 			.assertCorrectSolution("Correct solution")
 			.hint()
 			.assertFeedback("Hint")
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Correct feedback")
 			.nextAnswer()
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertCorrectSolution("Correct solution")
 			.assertFeedback("Incorrect")
 			.endTest()
@@ -1051,11 +1053,11 @@ public class ImsQTI21EditorTest extends Deployments {
 			.getQTI21Page(participantBrowser)
 			.assertOnAssessmentItem()
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Correct feedback")
 			.nextAnswer()
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.endTest()
 			.assertOnAssessmentResults()
 			.assertOnAssessmentTestScore(5);// 3 points from the first question, 2 from the second
@@ -1106,6 +1108,7 @@ public class ImsQTI21EditorTest extends Deployments {
 		File backgroundImageFile = new File(backgroundImageUrl.toURI());
 		hotspotEditor
 			.updloadBackground(backgroundImageFile)
+			.moveToHotspotEditor()
 			.resizeCircle()
 			.moveCircle(300, 120)
 			.addRectangle()
@@ -1132,6 +1135,7 @@ public class ImsQTI21EditorTest extends Deployments {
 			.addHotspot();
 		hotspotEditor
 			.updloadBackground(backgroundImageFile)
+			.moveToHotspotEditor()
 			.resizeCircle()
 			.moveCircle(310, 125)
 			.addRectangle()
@@ -1188,17 +1192,17 @@ public class ImsQTI21EditorTest extends Deployments {
 		ryomouQtiPage
 			.assertOnAssessmentItem()
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Incorrect")
 			.assertCorrectSolution("Correct solution")
 			.hint()
 			.assertFeedback("Hint")
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Correct feedback")
 			.nextAnswer()
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertCorrectSolution("Correct solution")
 			.assertFeedback("Incorrect")
 			.endTest()
@@ -1225,12 +1229,12 @@ public class ImsQTI21EditorTest extends Deployments {
 			.assertOnAssessmentItem()
 			.answerHotspot("circle")
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.assertFeedback("Correct feedback")
 			.nextAnswer()
 			.answerHotspot("circle")
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.endTest()
 			.assertOnAssessmentResults()
 			.assertOnAssessmentTestScore(6);// 3 points from the first question, 3 from the second
@@ -3280,6 +3284,7 @@ public class ImsQTI21EditorTest extends Deployments {
 		File backgroundImageFile = new File(backgroundImageUrl.toURI());
 		hotspotEditor
 			.updloadBackground(backgroundImageFile)
+			.moveToHotspotEditor()
 			.resizeCircle()
 			.moveCircle(300, 120)
 			.addRectangle()
@@ -3333,7 +3338,7 @@ public class ImsQTI21EditorTest extends Deployments {
 			.answerMultipleChoice("Correct")
 			.saveAnswer()
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.endTest()
 		//check the results
 			.assertOnAssessmentResults()
@@ -3366,7 +3371,7 @@ public class ImsQTI21EditorTest extends Deployments {
 			.answerMultipleChoice("Faux")
 			.saveAnswer()
 			.answerHotspot("rect")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.endTest()
 		//check the results
 			.assertOnAssessmentResults()
@@ -3398,7 +3403,7 @@ public class ImsQTI21EditorTest extends Deployments {
 			.answerMultipleChoice("Correct")
 			.saveAnswer()
 			.answerHotspot("circle")
-			.saveAnswer()
+			.saveGraphicAnswer()
 			.endTest()
 		//check the results
 			.assertOnAssessmentResults()
diff --git a/src/test/java/org/olat/selenium/ImsQTI21InteractionsTest.java b/src/test/java/org/olat/selenium/ImsQTI21InteractionsTest.java
index b5ffd935b492fecc83b98bf9c5d4c14c1134516d..78d8a208a10671916881a9b6763e141192ec6b7d 100644
--- a/src/test/java/org/olat/selenium/ImsQTI21InteractionsTest.java
+++ b/src/test/java/org/olat/selenium/ImsQTI21InteractionsTest.java
@@ -448,7 +448,7 @@ public class ImsQTI21InteractionsTest extends Deployments {
 		qtiPage
 			.clickToolbarBack()
 			.assertOnAssessmentItem()
-			.answerSelectPoint(100, 110)
+			.answerSelectPoint(100, 110, 192, 280)
 			.saveAnswer()
 			.endTest()
 			.closeTest();
diff --git a/src/test/java/org/olat/selenium/page/graphene/Position.java b/src/test/java/org/olat/selenium/page/graphene/Position.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd434f254bad8f5d627c38ff9cf9211f7a83b71c
--- /dev/null
+++ b/src/test/java/org/olat/selenium/page/graphene/Position.java
@@ -0,0 +1,93 @@
+/**
+ * <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.selenium.page.graphene;
+
+import java.util.List;
+
+import org.olat.ims.qti21.model.xml.AssessmentItemFactory;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.firefox.FirefoxDriver;
+
+/**
+ * 
+ * Initial date: 5 Oct 2018<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class Position {
+	
+	private final int x;
+	private final int y;
+	
+	private Position(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+	
+	public int getX() {
+		return x;
+	}
+	
+	public int getY() {
+		return y;
+	}
+	
+	public static Position valueOf(String coords, Dimension dimension, WebDriver browser) {
+		List<Integer> coordList = AssessmentItemFactory.coordsList(coords);
+		
+		int x = 0;
+		int y = 0;
+		if(coordList.size() == 3) {// circle
+			x = coordList.get(0).intValue();
+			y = coordList.get(1).intValue();
+		} else if(coordList.size() == 4) {// rectangle
+			int x1 = coordList.get(0).intValue();
+			int y1 = coordList.get(1).intValue();
+			int x2 = coordList.get(2).intValue();
+			int y2 = coordList.get(3).intValue();
+			x = (x2 + x1) / 2;
+			y = (y2 + y1) / 2;
+		}
+		
+		if(browser instanceof FirefoxDriver) {
+			x = x - Math.round(dimension.getWidth() / 2.0f);
+			y = y - Math.round(dimension.getHeight() / 2.0f);
+		}
+		return new Position(x, y);
+	}
+	
+	public static Position valueOf(int x, int y, Dimension dimension, WebDriver browser) {
+		if(browser instanceof FirefoxDriver) {
+			x = x - Math.round(dimension.getWidth() / 2.0f);
+			y = y - Math.round(dimension.getHeight() / 2.0f);
+		}
+		return new Position(x, y);
+	}
+	
+	public static Position valueOf(int x, int y, int width, int height, WebDriver browser) {
+		if(browser instanceof FirefoxDriver) {
+			x = x - Math.round(width / 2.0f);
+			y = y - Math.round(height / 2.0f);
+		}
+		return new Position(x, y);
+	}
+
+}
diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21HotspotEditorPage.java b/src/test/java/org/olat/selenium/page/qti/QTI21HotspotEditorPage.java
index faed98382a9d26bb16c33d592ae00c5983e68c22..beb294e84680f532531b6be856c8363abab7dec1 100644
--- a/src/test/java/org/olat/selenium/page/qti/QTI21HotspotEditorPage.java
+++ b/src/test/java/org/olat/selenium/page/qti/QTI21HotspotEditorPage.java
@@ -22,10 +22,12 @@ package org.olat.selenium.page.qti;
 import java.io.File;
 
 import org.olat.selenium.page.graphene.OOGraphene;
+import org.olat.selenium.page.graphene.Position;
 import org.openqa.selenium.By;
 import org.openqa.selenium.Dimension;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
 import org.openqa.selenium.interactions.Actions;
 
 import uk.ac.ed.ph.jqtiplus.value.Cardinality;
@@ -77,6 +79,16 @@ public class QTI21HotspotEditorPage extends QTI21AssessmentItemEditorPage {
 		return this;
 	}
 	
+	public QTI21HotspotEditorPage moveToHotspotEditor() {
+		By editorBy = By.id("o_qti_hotspots_edit");
+		OOGraphene.waitElement(editorBy, browser);
+		
+		if(browser instanceof FirefoxDriver) {
+			OOGraphene.scrollTo(editorBy, browser);
+		}
+		return this;
+	}
+	
 	/**
 	 * Resize the default circle
 	 * @return Itself
@@ -85,8 +97,10 @@ public class QTI21HotspotEditorPage extends QTI21AssessmentItemEditorPage {
 		By circleBy = By.cssSelector("div.o_draw_circle");
 		OOGraphene.waitElement(circleBy, browser);
 		WebElement circleEl = browser.findElement(circleBy);
+		Dimension dim = circleEl.getSize();
+		Position pos = Position.valueOf(10, 10, dim, browser);
 		new Actions(browser)
-			.moveToElement(circleEl, 10, 10)
+			.moveToElement(circleEl, pos.getX(), pos.getY())
 			.clickAndHold()
 			.moveByOffset(60, 60)
 			.release()
diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java
index 977bfe014a48929c7c7fe21049a84314ba04e772..9e65fdb3712212bfbb79d8ed6d9fb9da31bda60d 100644
--- a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java
+++ b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java
@@ -25,11 +25,13 @@ import java.util.List;
 import org.junit.Assert;
 import org.olat.selenium.page.NavigationPage;
 import org.olat.selenium.page.graphene.OOGraphene;
+import org.olat.selenium.page.graphene.Position;
 import org.olat.selenium.page.repository.RepositoryAccessPage;
 import org.openqa.selenium.By;
 import org.openqa.selenium.Dimension;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
 import org.openqa.selenium.interactions.Actions;
 import org.openqa.selenium.support.ui.Select;
 
@@ -161,8 +163,21 @@ public class QTI21Page {
 		OOGraphene.waitElement(By.className("hotspotInteraction"), browser);
 		By areaBy = By.xpath("//div[contains(@class,'hotspotInteraction')]//map/area[@shape='" + shape + "']");
 		List<WebElement> elements = browser.findElements(areaBy);
-		Assert.assertEquals("Hotspot of shape " + shape, 1, elements.size()); 
-		elements.get(0).click();
+		Assert.assertEquals("Hotspot of shape " + shape, 1, elements.size());
+		WebElement areaEl = elements.get(0);
+		if(browser instanceof FirefoxDriver) {
+			String coords = areaEl.getAttribute("coords");
+			By hotspotBy = By.xpath("//div[contains(@class,'hotspotInteraction')]/div/div/img");
+			WebElement element = browser.findElement(hotspotBy);
+			Dimension dim = element.getSize();
+			Position pos = Position.valueOf(coords, dim, browser);
+			new Actions(browser)
+				.moveToElement(element, pos.getX(), pos.getY())
+				.click()
+				.perform();
+		} else {
+			elements.get(0).click();
+		}
 		return this;
 	}
 	
@@ -389,11 +404,12 @@ public class QTI21Page {
 	 * @param y The y coordinate
 	 * @return Itself
 	 */
-	public QTI21Page answerSelectPoint(int x, int y) {
+	public QTI21Page answerSelectPoint(int x, int y, int width, int height) {
+		Position pos = Position.valueOf(x, y, width, height, browser);
 		By canvasBy = By.xpath("//div[contains(@class,'selectPointInteraction')]/div/canvas");
 		WebElement canvasEl = browser.findElement(canvasBy);
 		new Actions(browser)
-			.moveToElement(canvasEl, x, y)
+			.moveToElement(canvasEl, pos.getX(), pos.getY())
 			.click()
 			.build()
 			.perform();
@@ -492,6 +508,22 @@ public class QTI21Page {
 		return this;
 	}
 	
+	/**
+	 * For hotspot because Firefox cannot click the save without
+	 * special scrolling.
+	 * @return
+	 */
+	public QTI21Page saveGraphicAnswer() {
+		By saveAnswerBy = By.cssSelector("button.o_sel_assessment_item_submit");
+		browser.findElement(saveAnswerBy).click();
+		if(browser instanceof FirefoxDriver) {
+			OOGraphene.clickAndWait(saveAnswerBy, browser);
+		} else {
+			OOGraphene.waitBusy(browser);
+		}
+		return this;
+	}
+	
 	public QTI21Page saveAnswerMoveAndScrollTop() {
 		By saveAnswerBy = By.cssSelector("button.o_sel_assessment_item_submit");
 		OOGraphene.click(saveAnswerBy, browser);