From 353b18cc1e45b3973e3c711bf2ebedec896bdca3 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 27 Sep 2012 10:46:07 +0200
Subject: [PATCH] OMA-87: enhance the REST API for notifications, add more
 informations about forum and folder notifications

---
 .../den/DENCourseNodeNotificationHandler.java |   2 +-
 .../calendar/CalendarNotificationHandler.java |   2 +-
 .../FolderNotificationsHandler.java           |   8 +-
 .../manager/MailNotificationsHandler.java     |   2 +-
 .../items/SubscriptionListItem.java           |  12 +-
 .../AssessmentNotificationsHandler.java       |   5 +-
 .../ta/AbstractTaskNotificationHandler.java   |   7 +-
 .../dialog/FileUploadNotificationHandler.java |   5 +-
 .../modules/fo/ForumNotificationsHandler.java |   6 +-
 ...PageChangeOrCreateNotificationHandler.java |   7 +-
 .../restapi/NotificationsWebService.java      |  52 ++-
 .../restapi/vo/SubscriptionInfoVO.java        |   6 -
 .../restapi/vo/SubscriptionListItemVO.java    |  58 +++
 .../manager/EPNotificationManager.java        |  24 +-
 .../NewUsersNotificationHandler.java          |   2 +-
 .../basesecurity/BaseSecurityManagerTest.java |  20 ++
 .../restapi/CoursesResourcesFoldersTest.java  |   5 +-
 .../org/olat/restapi/GroupFoldersTest.java    |   2 +-
 .../org/olat/restapi/NotificationsTest.java   | 332 +++++++++++++++---
 19 files changed, 463 insertions(+), 94 deletions(-)

diff --git a/src/main/java/de/bps/course/nodes/den/DENCourseNodeNotificationHandler.java b/src/main/java/de/bps/course/nodes/den/DENCourseNodeNotificationHandler.java
index 754cd9f69fb..439b91adb9e 100644
--- a/src/main/java/de/bps/course/nodes/den/DENCourseNodeNotificationHandler.java
+++ b/src/main/java/de/bps/course/nodes/den/DENCourseNodeNotificationHandler.java
@@ -84,7 +84,7 @@ public class DENCourseNodeNotificationHandler implements NotificationsHandler {
 						String desc = trans.translate("notifications.entry", new String[] { denNode.getLongTitle(), changer });
 
 						Date modDate = new Date();
-						subListItem = new SubscriptionListItem(desc, null, modDate, cssClass);
+						subListItem = new SubscriptionListItem(desc, null, null, modDate, cssClass);
 						si.addSubscriptionListItem(subListItem);
 					}
 				}
diff --git a/src/main/java/org/olat/commons/calendar/CalendarNotificationHandler.java b/src/main/java/org/olat/commons/calendar/CalendarNotificationHandler.java
index 79eb43b3d25..63645faa771 100644
--- a/src/main/java/org/olat/commons/calendar/CalendarNotificationHandler.java
+++ b/src/main/java/org/olat/commons/calendar/CalendarNotificationHandler.java
@@ -147,7 +147,7 @@ public class CalendarNotificationHandler extends LogDelegator implements Notific
 							String desc = translator.translate("cal.notifications.entry", new String[] { subject, dateStr, location, author });
 							String businessPath = bPath + "[path=" + kalendarEvent.getID() + ":0]";
 							String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
-							SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, modDate, CSS_CLASS_CALENDAR_ICON);
+							SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, CSS_CLASS_CALENDAR_ICON);
 							si.addSubscriptionListItem(subListItem);
 						}
 					}
diff --git a/src/main/java/org/olat/core/commons/modules/bc/notifications/FolderNotificationsHandler.java b/src/main/java/org/olat/core/commons/modules/bc/notifications/FolderNotificationsHandler.java
index 79cfc421c6d..8ab53554f1e 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/notifications/FolderNotificationsHandler.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/notifications/FolderNotificationsHandler.java
@@ -82,7 +82,7 @@ public class FolderNotificationsHandler implements NotificationsHandler {
 		Publisher p = subscriber.getPublisher();
 		Date latestNews = p.getLatestNewsDate();
 
-		String businessPath = p.getBusinessPath() + "[path=";
+		String genericBusinessPath = p.getBusinessPath() + "[path=";
 		
 		SubscriptionInfo si;
 		// there could be news for me, investigate deeper
@@ -110,10 +110,12 @@ public class FolderNotificationsHandler implements NotificationsHandler {
 
 					String desc = translator.translate("notifications.entry", new String[] { title, NotificationHelper.getFormatedName(ident) });
 					String urlToSend = null;
+					String businessPath = null;
 					if(p.getBusinessPath() != null) {
-						urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath + fi.getRelPath() + "]");
+						businessPath = genericBusinessPath + fi.getRelPath() + "]";
+						urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 					}
-					subListItem = new SubscriptionListItem(desc, urlToSend, modDate, iconCssClass);
+					subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, iconCssClass);
 					si.addSubscriptionListItem(subListItem);
 				}
 			} else {
diff --git a/src/main/java/org/olat/core/util/mail/manager/MailNotificationsHandler.java b/src/main/java/org/olat/core/util/mail/manager/MailNotificationsHandler.java
index 598d5071646..abc170e7d58 100644
--- a/src/main/java/org/olat/core/util/mail/manager/MailNotificationsHandler.java
+++ b/src/main/java/org/olat/core/util/mail/manager/MailNotificationsHandler.java
@@ -98,7 +98,7 @@ public class MailNotificationsHandler extends LogDelegator implements Notificati
 						String subject = mail.getSubject();
 						String businessPath = "[Inbox:" + mail.getKey() + "]";
 						String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
-						SubscriptionListItem subListItem = new SubscriptionListItem(subject, urlToSend, mail.getCreationDate(), "o_co_icon");
+						SubscriptionListItem subListItem = new SubscriptionListItem(subject, urlToSend, businessPath, mail.getCreationDate(), "o_co_icon");
 						si.addSubscriptionListItem(subListItem);
 					}
 				}
diff --git a/src/main/java/org/olat/core/util/notifications/items/SubscriptionListItem.java b/src/main/java/org/olat/core/util/notifications/items/SubscriptionListItem.java
index 3a4cb7f35a2..dccfc1d0e3b 100644
--- a/src/main/java/org/olat/core/util/notifications/items/SubscriptionListItem.java
+++ b/src/main/java/org/olat/core/util/notifications/items/SubscriptionListItem.java
@@ -40,20 +40,22 @@ import org.olat.core.util.notifications.SubscriptionInfo;
 public class SubscriptionListItem {
 
 	private final String link;
+	private final String businessPath; 
 	private final Date date;
 	private final String description;
 	private final String descriptionTooltip;
 	private final String iconCssClass;
 	private Object userObject;
 	
-	public SubscriptionListItem(String desc, String url, Date dateInfo, String iconCssClass) {
-		this(desc, null, url, dateInfo, iconCssClass);
+	public SubscriptionListItem(String desc, String url, String businessPath, Date dateInfo, String iconCssClass) {
+		this(desc, null, url, businessPath, dateInfo, iconCssClass);
 	}
 	
-	public SubscriptionListItem(String desc, String tooltip, String url, Date dateInfo, String iconCssClass) {	
+	public SubscriptionListItem(String desc, String tooltip, String url, String businessPath, Date dateInfo, String iconCssClass) {	
 		this.description = desc;
 		this.descriptionTooltip = tooltip;
 		this.link = url;
+		this.businessPath = businessPath;
 		this.date = dateInfo;
 		this.iconCssClass = iconCssClass;
 	}
@@ -62,6 +64,10 @@ public class SubscriptionListItem {
 		return link;
 	}
 
+	public String getBusinessPath() {
+		return businessPath;
+	}
+
 	public Date getDate() {
 		return date;
 	}
diff --git a/src/main/java/org/olat/course/assessment/AssessmentNotificationsHandler.java b/src/main/java/org/olat/course/assessment/AssessmentNotificationsHandler.java
index a50e7db22b1..57983f5cdf4 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentNotificationsHandler.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentNotificationsHandler.java
@@ -380,12 +380,13 @@ public class AssessmentNotificationsHandler implements NotificationsHandler {
 								}
 
 								String urlToSend = null;
+								String businessPath = null;
 								if(p.getBusinessPath() != null) {
-									String businessPath = p.getBusinessPath() + "[assessmentTool:0][Identity:" + assessedIdentity.getKey() + "][CourseNode:" + test.getIdent() + "]";
+									businessPath = p.getBusinessPath() + "[assessmentTool:0][Identity:" + assessedIdentity.getKey() + "][CourseNode:" + test.getIdent() + "]";
 									urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 								}
 								
-								SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, modDate, CSS_CLASS_USER_ICON);
+								SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, CSS_CLASS_USER_ICON);
 								if(si == null) {
 									String title = translator.translate("notifications.header", new String[]{course.getCourseTitle()});
 									String css = CourseNodeFactory.getInstance().getCourseNodeConfigurationEvenForDisabledBB(test.getType()).getIconCSSClass();
diff --git a/src/main/java/org/olat/course/nodes/ta/AbstractTaskNotificationHandler.java b/src/main/java/org/olat/course/nodes/ta/AbstractTaskNotificationHandler.java
index 8a46581080c..af93c899fc2 100644
--- a/src/main/java/org/olat/course/nodes/ta/AbstractTaskNotificationHandler.java
+++ b/src/main/java/org/olat/course/nodes/ta/AbstractTaskNotificationHandler.java
@@ -99,14 +99,15 @@ public abstract class AbstractTaskNotificationHandler extends LogDelegator {
 							String fullUserName = getUserNameFromFilePath(filePath);
 							
 							Date modDate = fi.getLastModified();
-							String desc = translator.translate(getNotificationEntryKey(), new String[] { filePath, fullUserName }); 
-							String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(p.getBusinessPath());
+							String desc = translator.translate(getNotificationEntryKey(), new String[] { filePath, fullUserName });
+							String businessPath = p.getBusinessPath();
+							String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 							
 							String iconCssClass =  null;
 							if (metaInfo != null) {
 								iconCssClass = metaInfo.getIconCssClass();
 							}
-							subListItem = new SubscriptionListItem(desc, urlToSend, modDate, iconCssClass);
+							subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, iconCssClass);
 							si.addSubscriptionListItem(subListItem);						
 						}
 			} else {
diff --git a/src/main/java/org/olat/modules/dialog/FileUploadNotificationHandler.java b/src/main/java/org/olat/modules/dialog/FileUploadNotificationHandler.java
index 66c1625ebd0..4d0e05b133b 100644
--- a/src/main/java/org/olat/modules/dialog/FileUploadNotificationHandler.java
+++ b/src/main/java/org/olat/modules/dialog/FileUploadNotificationHandler.java
@@ -99,10 +99,11 @@ public class FileUploadNotificationHandler implements NotificationsHandler {
 						Date modDate = element.getDate();
 
 						String desc = translator.translate("notifications.entry", new String[] { filename, NotificationHelper.getFormatedName(ident) });
-						String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(p.getBusinessPath());
+						String businessPath = p.getBusinessPath();
+						String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 						String cssClass = CSSHelper.createFiletypeIconCssClassFor(filename);
 
-						subListItem = new SubscriptionListItem(desc, urlToSend, modDate, cssClass);
+						subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, cssClass);
 						si.addSubscriptionListItem(subListItem);
 					}
 				}
diff --git a/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java b/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
index 6bf3658b6c1..7402720f270 100644
--- a/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
+++ b/src/main/java/org/olat/modules/fo/ForumNotificationsHandler.java
@@ -104,11 +104,13 @@ public class ForumNotificationsHandler extends LogDelegator implements Notificat
 					final String descKey = "notifications.entry" + (mInfo.getCreationDate().equals(mInfo.getLastModified()) ? "" : ".modified");
 					final String desc = translator.translate(descKey, new String[] { title, name });
 					String urlToSend = null;
+					String businessPath = null;
 					if(p.getBusinessPath() != null) {
-						urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessControlString + mInfo.getKey().toString() + "]");
+						businessPath = businessControlString + mInfo.getKey().toString() + "]";
+						urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 					}
 					
-					SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, modDate, ForumHelper.CSS_ICON_CLASS_MESSAGE);
+					SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, ForumHelper.CSS_ICON_CLASS_MESSAGE);
 					si.addSubscriptionListItem(subListItem);
 				}
 			} else {
diff --git a/src/main/java/org/olat/modules/wiki/WikiPageChangeOrCreateNotificationHandler.java b/src/main/java/org/olat/modules/wiki/WikiPageChangeOrCreateNotificationHandler.java
index 2c977266dce..3d0314287fe 100644
--- a/src/main/java/org/olat/modules/wiki/WikiPageChangeOrCreateNotificationHandler.java
+++ b/src/main/java/org/olat/modules/wiki/WikiPageChangeOrCreateNotificationHandler.java
@@ -142,17 +142,18 @@ public class WikiPageChangeOrCreateNotificationHandler extends LogDelegator impl
 								}
 
 								//build Businesscontrol-Path						
-								String bControlString = businessControlString + element.getPageName() + "]";						
+								String businessPath = null;						
 								String urlToSend = null;
 								if(p.getBusinessPath() != null) {
-									urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bControlString);
+									businessPath = businessControlString + element.getPageName() + "]";		
+									urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 								}
 								
 								// string[] gets filled into translation key by adding {0...n} to
 								// the string
 								Identity ident = BaseSecurityManager.getInstance().loadIdentityByKey(Long.valueOf(element.getModifyAuthor()));
 								String desc = translator.translate("notifications.entry", new String[] { element.getPageName(), NotificationHelper.getFormatedName(ident)});							
-								subListItem = new SubscriptionListItem(desc, urlToSend, modDate, CSS_CLASS_WIKI_PAGE_CHANGED_ICON);
+								subListItem = new SubscriptionListItem(desc, urlToSend, businessPath,  modDate, CSS_CLASS_WIKI_PAGE_CHANGED_ICON);
 								si.addSubscriptionListItem(subListItem);
 							} else {
 								//there are no more new pages so we stop here
diff --git a/src/main/java/org/olat/notifications/restapi/NotificationsWebService.java b/src/main/java/org/olat/notifications/restapi/NotificationsWebService.java
index 42d9b6a3a9e..97af1211481 100644
--- a/src/main/java/org/olat/notifications/restapi/NotificationsWebService.java
+++ b/src/main/java/org/olat/notifications/restapi/NotificationsWebService.java
@@ -40,12 +40,17 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.olat.core.id.Identity;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.id.context.ContextEntry;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.notifications.NotificationHelper;
 import org.olat.core.util.notifications.NotificationsManager;
+import org.olat.core.util.notifications.Publisher;
 import org.olat.core.util.notifications.Subscriber;
 import org.olat.core.util.notifications.SubscriptionInfo;
+import org.olat.core.util.notifications.items.SubscriptionListItem;
 import org.olat.notifications.restapi.vo.SubscriptionInfoVO;
+import org.olat.notifications.restapi.vo.SubscriptionListItemVO;
 import org.olat.restapi.security.RestSecurityHelper;
 
 /**
@@ -96,7 +101,8 @@ public class NotificationsWebService {
 		for(Map.Entry<Subscriber, SubscriptionInfo> entry: subsInfoMap.entrySet()) {
 			SubscriptionInfo info = entry.getValue();
 			if(info.hasNews()) {
-				voes.add(new SubscriptionInfoVO(info));
+				Subscriber subscriber = entry.getKey();
+				voes.add(createSubscriptionInfoVO(subscriber.getPublisher(), info));
 			}
 		}
 		SubscriptionInfoVO[] voesArr = new SubscriptionInfoVO[voes.size()];
@@ -104,6 +110,50 @@ public class NotificationsWebService {
 		return Response.ok(voesArr).build();
 	}
 	
+	private SubscriptionInfoVO createSubscriptionInfoVO(Publisher publisher, SubscriptionInfo info) {
+		SubscriptionInfoVO infoVO  = new SubscriptionInfoVO(info);
+		if(info.getSubscriptionListItems() != null && !info.getSubscriptionListItems().isEmpty()) {
+			List<SubscriptionListItemVO> itemVOes = new ArrayList<SubscriptionListItemVO>(info.getSubscriptionListItems().size());
+			
+			String publisherType = publisher.getType();
+			String resourceType = publisher.getResName();
+			for(SubscriptionListItem item:info.getSubscriptionListItems()) {
+				SubscriptionListItemVO itemVO = new SubscriptionListItemVO(item); 
+				//resource specific
+				if("BusinessGroup".equals(resourceType)) {
+					itemVO.setGroupKey(publisher.getResId());
+				} else if("CourseModule".equals(resourceType)) {
+					itemVO.setCourseKey(publisher.getResId());
+					itemVO.setCourseNodeId(publisher.getSubidentifier());
+				}
+				
+				//publisher specififc
+				if("Forum".equals(publisherType)) {
+					//extract the message id
+					List<ContextEntry> ces = BusinessControlFactory.getInstance().createCEListFromString(item.getBusinessPath());
+					if(ces.size() > 0) {
+						ContextEntry lastCe = ces.get(ces.size() - 1);
+						if("Message".equals(lastCe.getOLATResourceable().getResourceableTypeName())) {
+							itemVO.setMessageKey(lastCe.getOLATResourceable().getResourceableId());
+						}
+					}	
+				} else if("FolderModule".equals(publisherType)) {
+					List<ContextEntry> ces = BusinessControlFactory.getInstance().createCEListFromString(item.getBusinessPath());
+					if(ces.size() > 0) {
+						ContextEntry lastCe = ces.get(ces.size() - 1);
+						if(lastCe.getOLATResourceable().getResourceableTypeName().startsWith("path=")) {
+							String path = BusinessControlFactory.getInstance().getPath(lastCe);
+							itemVO.setPath(path);
+						}
+					}	
+				}
+				itemVOes.add(itemVO);
+			}
+			infoVO.setItems(itemVOes);
+		}
+		return infoVO;
+	}
+	
 	private Date parseDate(String date, Locale locale) {
 		if(StringHelper.containsNonWhitespace(date)) {
 			if(date.indexOf('T') > 0) {
diff --git a/src/main/java/org/olat/notifications/restapi/vo/SubscriptionInfoVO.java b/src/main/java/org/olat/notifications/restapi/vo/SubscriptionInfoVO.java
index 62508023ed3..5b5ec323999 100644
--- a/src/main/java/org/olat/notifications/restapi/vo/SubscriptionInfoVO.java
+++ b/src/main/java/org/olat/notifications/restapi/vo/SubscriptionInfoVO.java
@@ -30,7 +30,6 @@ import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.olat.core.util.notifications.SubscriptionInfo;
-import org.olat.core.util.notifications.items.SubscriptionListItem;
 
 
 /**
@@ -61,11 +60,6 @@ public class SubscriptionInfoVO {
 		key = info.getKey();
 		type = info.getType();
 		title = info.getTitle(SubscriptionInfo.MIME_PLAIN);
-		if(info.getSubscriptionListItems() != null) {
-			for(SubscriptionListItem item:info.getSubscriptionListItems()) {
-				items.add(new SubscriptionListItemVO(item));
-			}
-		}
 	}
 
 	public Long getKey() {
diff --git a/src/main/java/org/olat/notifications/restapi/vo/SubscriptionListItemVO.java b/src/main/java/org/olat/notifications/restapi/vo/SubscriptionListItemVO.java
index 0597bea2e5a..5e3498d69a0 100644
--- a/src/main/java/org/olat/notifications/restapi/vo/SubscriptionListItemVO.java
+++ b/src/main/java/org/olat/notifications/restapi/vo/SubscriptionListItemVO.java
@@ -43,6 +43,16 @@ public class SubscriptionListItemVO {
 	private String description;
 	private String iconCssClass;
 	
+	private String type;
+	private Long groupKey;
+	private Long courseKey;
+	private String courseNodeId;
+	
+	//to the message of a forum
+	private Long messageKey;
+	//to the file path
+	private String path;
+	
 	public SubscriptionListItemVO() {
 		//make JAXB happy
 	}
@@ -85,4 +95,52 @@ public class SubscriptionListItemVO {
 	public void setIconCssClass(String iconCssClass) {
 		this.iconCssClass = iconCssClass;
 	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public Long getGroupKey() {
+		return groupKey;
+	}
+
+	public void setGroupKey(Long groupKey) {
+		this.groupKey = groupKey;
+	}
+
+	public Long getCourseKey() {
+		return courseKey;
+	}
+
+	public void setCourseKey(Long courseKey) {
+		this.courseKey = courseKey;
+	}
+
+	public String getCourseNodeId() {
+		return courseNodeId;
+	}
+
+	public void setCourseNodeId(String courseNodeId) {
+		this.courseNodeId = courseNodeId;
+	}
+
+	public Long getMessageKey() {
+		return messageKey;
+	}
+
+	public void setMessageKey(Long messageKey) {
+		this.messageKey = messageKey;
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/portfolio/manager/EPNotificationManager.java b/src/main/java/org/olat/portfolio/manager/EPNotificationManager.java
index 1b6478a0531..284c1049bef 100644
--- a/src/main/java/org/olat/portfolio/manager/EPNotificationManager.java
+++ b/src/main/java/org/olat/portfolio/manager/EPNotificationManager.java
@@ -68,15 +68,15 @@ public class EPNotificationManager {
 			if ("page".equals(notification.getType())) {
 				String bPath = rootBusinessPath + "[EPPage:" + notification.getPageKey() + "]";
 				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
-				item = new SubscriptionListItem(translator.translate("li.newpage", title), linkUrl, notification.getCreationDate(), "b_ep_page_icon");
+				item = new SubscriptionListItem(translator.translate("li.newpage", title), linkUrl, bPath, notification.getCreationDate(), "b_ep_page_icon");
 				item.setUserObject(notification.getPageKey());
 			} else {
-				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(rootBusinessPath);
+				String bPath = rootBusinessPath;
 				if (notification.getPageKey() != null) {
-					String bPath = rootBusinessPath + "[EPPage:" + notification.getPageKey() + "]";
-					linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
+					bPath = rootBusinessPath + "[EPPage:" + notification.getPageKey() + "]";
 				}
-				item = new SubscriptionListItem(translator.translate("li.newstruct", title), linkUrl, notification.getCreationDate(), "b_ep_struct_icon");
+				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
+				item = new SubscriptionListItem(translator.translate("li.newstruct", title), linkUrl, bPath, notification.getCreationDate(), "b_ep_struct_icon");
 				item.setUserObject(notification.getPageKey());
 			}
 			if(item != null) {
@@ -100,7 +100,7 @@ public class EPNotificationManager {
 
 			String bPath = rootBusinessPath + "[EPPage:" + pageKey + "]";
 			String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
-			SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.newartefact", title), linkUrl, link.getCreationDate(), "b_eportfolio_link");
+			SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.newartefact", title), linkUrl, bPath, link.getCreationDate(), "b_eportfolio_link");
 			item.setUserObject(pageKey);
 			items.add(item);
 		}
@@ -117,20 +117,20 @@ public class EPNotificationManager {
 				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(rootBusinessPath);
 				if (rating.getLastModified() != null) {
 					// there is a modified date, also add this as a listitem
-					items.add(new SubscriptionListItem(translator.translate("li.changerating", title), linkUrl, rating.getLastModified(), "b_star_icon"));
+					items.add(new SubscriptionListItem(translator.translate("li.changerating", title), linkUrl, rootBusinessPath, rating.getLastModified(), "b_star_icon"));
 				}
-				items.add(new SubscriptionListItem(translator.translate("li.newrating", title), linkUrl, rating.getCreationDate(), "b_star_icon"));
+				items.add(new SubscriptionListItem(translator.translate("li.newrating", title), linkUrl, rootBusinessPath, rating.getCreationDate(), "b_star_icon"));
 			} else {
 				String bPath = rootBusinessPath + "[EPPage:" + rating.getPageKey() + "]";
 				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
 				String[] title = new String[] { rating.getTitle(), userManager.getUserDisplayName(rating.getAuthor()) };
 				if (rating.getLastModified() != null) {
 					// there is a modified date, also add this as a listitem
-					SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.changerating", title ), linkUrl, rating.getLastModified(), "b_star_icon");
+					SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.changerating", title ), linkUrl, bPath, rating.getLastModified(), "b_star_icon");
 					item.setUserObject(rating.getPageKey());
 					items.add(item);
 				}
-				SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.newrating", title), linkUrl, rating.getCreationDate(), "b_star_icon");
+				SubscriptionListItem item = new SubscriptionListItem(translator.translate("li.newrating", title), linkUrl, bPath, rating.getCreationDate(), "b_star_icon");
 				item.setUserObject(rating.getPageKey());
 				items.add(item);
 			}
@@ -147,12 +147,12 @@ public class EPNotificationManager {
 			if(comment.getPageKey() == null) {
 				String[] title = new String[] { comment.getMapTitle(), userManager.getUserDisplayName(comment.getAuthor()) };
 				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(rootBusinessPath);
-				item = new SubscriptionListItem(translator.translate("li.newcomment", title), linkUrl, comment.getCreationDate(), "b_info_icon");
+				item = new SubscriptionListItem(translator.translate("li.newcomment", title), linkUrl, rootBusinessPath, comment.getCreationDate(), "b_info_icon");
 			} else {
 				String bPath = rootBusinessPath + "[EPPage:" + comment.getPageKey() + "]";
 				String linkUrl = BusinessControlFactory.getInstance().getURLFromBusinessPathString(bPath);
 				String[] title = new String[] { comment.getTitle(), userManager.getUserDisplayName(comment.getAuthor()) };
-				item = new SubscriptionListItem(translator.translate("li.newcomment", title), linkUrl, comment.getCreationDate(), "b_info_icon");
+				item = new SubscriptionListItem(translator.translate("li.newcomment", title), linkUrl, bPath, comment.getCreationDate(), "b_info_icon");
 				item.setUserObject(comment.getPageKey());
 			}
 			items.add(item);
diff --git a/src/main/java/org/olat/user/notification/NewUsersNotificationHandler.java b/src/main/java/org/olat/user/notification/NewUsersNotificationHandler.java
index 5b885fee89c..4e1d2b7edc6 100644
--- a/src/main/java/org/olat/user/notification/NewUsersNotificationHandler.java
+++ b/src/main/java/org/olat/user/notification/NewUsersNotificationHandler.java
@@ -75,7 +75,7 @@ public class NewUsersNotificationHandler implements NotificationsHandler {
 						String businessPath = "[Identity:" + newUser.getKey() + "]";
 						String urlToSend = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath);
 						Date modDate = newUser.getCreationDate();
-						subListItem = new SubscriptionListItem(desc, urlToSend, modDate, CSSHelper.CSS_CLASS_USER);
+						subListItem = new SubscriptionListItem(desc, urlToSend, businessPath, modDate, CSSHelper.CSS_CLASS_USER);
 						si.addSubscriptionListItem(subListItem);
 					}
 				}
diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
index 3fb823275af..5a28bc4db2a 100644
--- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
+++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
@@ -88,6 +88,26 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		Assert.assertEquals(id, foundId);
 	}
 	
+	@Test
+	public void testFindIdentityByNames() {
+		//create a user it
+		String name1 = "find-me-1-" + UUID.randomUUID().toString();
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser(name1);
+		String name2 = "find-me-2-" + UUID.randomUUID().toString();
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser(name2);
+		dbInstance.commitAndCloseSession();
+		
+		//find it
+		List<String> names = new ArrayList<String>(2);
+		names.add(name1);
+		names.add(name2);
+		List<Identity> foundIds = securityManager.findIdentitiesByName(names);
+		Assert.assertNotNull(foundIds);
+		Assert.assertEquals(2, foundIds.size());
+		Assert.assertTrue(foundIds.contains(id1));
+		Assert.assertTrue(foundIds.contains(id2));
+	}
+	
 	@Test
 	public void testGetSecurityGroupsForIdentity() {
 		// create
diff --git a/src/test/java/org/olat/restapi/CoursesResourcesFoldersTest.java b/src/test/java/org/olat/restapi/CoursesResourcesFoldersTest.java
index 44dd1506f4c..56a9041d32e 100644
--- a/src/test/java/org/olat/restapi/CoursesResourcesFoldersTest.java
+++ b/src/test/java/org/olat/restapi/CoursesResourcesFoldersTest.java
@@ -41,6 +41,7 @@ import java.util.List;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriBuilder;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.http.Header;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -98,11 +99,11 @@ public class CoursesResourcesFoldersTest extends OlatJerseyTestCase {
 		}
 	}
 	
-	private void copyFileInResourceFolder(VFSContainer container, String filename, String prefix) {
+	private void copyFileInResourceFolder(VFSContainer container, String filename, String prefix) throws IOException {
 		InputStream pageStream = CoursesElementsTest.class.getResourceAsStream(filename);
 		VFSLeaf item = container.createChildLeaf(prefix + filename);
 		OutputStream outStream = item.getOutputStream(false);
-		FileUtils.copy(pageStream, outStream);
+		IOUtils.copy(pageStream, outStream);
 		FileUtils.closeSafely(pageStream);
 		FileUtils.closeSafely(outStream);
 	}
diff --git a/src/test/java/org/olat/restapi/GroupFoldersTest.java b/src/test/java/org/olat/restapi/GroupFoldersTest.java
index b835021e3fc..94dea7eff46 100644
--- a/src/test/java/org/olat/restapi/GroupFoldersTest.java
+++ b/src/test/java/org/olat/restapi/GroupFoldersTest.java
@@ -289,7 +289,7 @@ public class GroupFoldersTest extends OlatJerseyTestCase {
 		if(file == null) {
 			file = newFolder1.createChildLeaf("portrait.jpg");
 			OutputStream out = file.getOutputStream(true);
-			InputStream in = UserMgmtTest.class.getResourceAsStream("portrait.jpg");
+			InputStream in = GroupFoldersTest.class.getResourceAsStream("portrait.jpg");
 			FileUtils.copy(in, out, file.getSize());
 			FileUtils.closeSafely(in);
 			FileUtils.closeSafely(out);
diff --git a/src/test/java/org/olat/restapi/NotificationsTest.java b/src/test/java/org/olat/restapi/NotificationsTest.java
index ff933109787..f20ab7ee75c 100644
--- a/src/test/java/org/olat/restapi/NotificationsTest.java
+++ b/src/test/java/org/olat/restapi/NotificationsTest.java
@@ -33,37 +33,59 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 import java.util.UUID;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriBuilder;
 
+import junit.framework.Assert;
+
+import org.apache.commons.io.IOUtils;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.type.TypeReference;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.olat.core.commons.persistence.DBFactory;
+import org.olat.collaboration.CollaborationTools;
+import org.olat.collaboration.CollaborationToolsFactory;
+import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.core.util.notifications.NotificationsManager;
 import org.olat.core.util.notifications.PublisherData;
 import org.olat.core.util.notifications.SubscriptionContext;
 import org.olat.core.util.resource.OresHelper;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.course.CourseFactory;
+import org.olat.course.ICourse;
+import org.olat.course.nodes.BCCourseNode;
+import org.olat.course.nodes.CourseNodeConfiguration;
+import org.olat.course.nodes.CourseNodeFactory;
+import org.olat.course.nodes.FOCourseNode;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupService;
 import org.olat.modules.fo.Forum;
 import org.olat.modules.fo.ForumManager;
 import org.olat.modules.fo.Message;
 import org.olat.notifications.restapi.vo.SubscriptionInfoVO;
+import org.olat.notifications.restapi.vo.SubscriptionListItemVO;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.olat.restapi.repository.course.CoursesWebService;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatJerseyTestCase;
 import org.olat.user.notification.UsersSubscriptionManager;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
@@ -79,64 +101,59 @@ public class NotificationsTest extends OlatJerseyTestCase {
 	private static Identity userAndForumSubscriberId;
 	
 	private static Forum forum;
-	private RestConnection conn;
+	private static boolean setup = false;
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private BusinessGroupService businessGroupService;
+	@Autowired
+	private NotificationsManager notificationManager;
+	@Autowired
+	private RepositoryManager repositoryManager;
 	
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		conn = new RestConnection();
-		
-		userSubscriberId = JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-1");
-		userAndForumSubscriberId = JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-2");
-		JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-3");
-		
-		SubscriptionContext subContext = UsersSubscriptionManager.getInstance().getNewUsersSubscriptionContext();
-		PublisherData publisherData = UsersSubscriptionManager.getInstance().getNewUsersPublisherData();
-		NotificationsManager notifManager = NotificationsManager.getInstance();
-		if(!notifManager.isSubscribed(userSubscriberId, subContext)) {
-			notifManager.subscribe(userSubscriberId, subContext, publisherData);
-		}
-		if(!notifManager.isSubscribed(userAndForumSubscriberId, subContext)) {
-			notifManager.subscribe(userAndForumSubscriberId, subContext, publisherData);
-		}
-		
-		//create a forum
-		ForumManager fm = ForumManager.getInstance();
-		forum = ForumManager.getInstance().addAForum();
-		Message m1 = fm.createMessage();
-		m1.setTitle("Thread-1");
-		m1.setBody("Body of Thread-1");
-		fm.addTopMessage(userSubscriberId, forum, m1);
-		
-		//subscribe
-		SubscriptionContext forumSubContext = new SubscriptionContext("NotificationRestCourse", forum.getKey(), "2387");
-		PublisherData forumPdata = new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), "");
-		if(!notifManager.isSubscribed(userAndForumSubscriberId, forumSubContext)) {
-			notifManager.subscribe(userAndForumSubscriberId, forumSubContext, forumPdata);
-		}
-		notifManager.markPublisherNews(forumSubContext, userSubscriberId);
-		
-		//generate one notification
-		String randomLogin = UUID.randomUUID().toString().replace("-", "");
-		JunitTestHelper.createAndPersistIdentityAsUser(randomLogin);
-		
-		DBFactory.getInstance().commitAndCloseSession();
-	}
-	
-  @After
-	public void tearDown() throws Exception {
-		try {
-			if(conn != null) {
-				conn.shutdown();
+		if(!setup) {
+			userSubscriberId = JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-1");
+			userAndForumSubscriberId = JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-2");
+			JunitTestHelper.createAndPersistIdentityAsUser("rest-notifications-test-3");
+			
+			SubscriptionContext subContext = UsersSubscriptionManager.getInstance().getNewUsersSubscriptionContext();
+			PublisherData publisherData = UsersSubscriptionManager.getInstance().getNewUsersPublisherData();
+			if(!notificationManager.isSubscribed(userSubscriberId, subContext)) {
+				notificationManager.subscribe(userSubscriberId, subContext, publisherData);
 			}
-		} catch (Exception e) {
-      e.printStackTrace();
-      throw e;
+			if(!notificationManager.isSubscribed(userAndForumSubscriberId, subContext)) {
+				notificationManager.subscribe(userAndForumSubscriberId, subContext, publisherData);
+			}
+			
+			//create a forum
+			forum = ForumManager.getInstance().addAForum();
+			Message m1 = createMessage(userSubscriberId, forum);
+			Assert.assertNotNull(m1);
+			
+			//subscribe
+			SubscriptionContext forumSubContext = new SubscriptionContext("NotificationRestCourse", forum.getKey(), "2387");
+			PublisherData forumPdata = new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), "");
+			if(!notificationManager.isSubscribed(userAndForumSubscriberId, forumSubContext)) {
+				notificationManager.subscribe(userAndForumSubscriberId, forumSubContext, forumPdata);
+			}
+			notificationManager.markPublisherNews(forumSubContext, userSubscriberId);
+
+			//generate one notification
+			String randomLogin = UUID.randomUUID().toString().replace("-", "");
+			JunitTestHelper.createAndPersistIdentityAsUser(randomLogin);
+			setup = true;
 		}
+		
+		dbInstance.commitAndCloseSession();
 	}
 	
 	@Test
 	public void testGetNotifications() throws IOException, URISyntaxException {
+		RestConnection conn = new RestConnection();
 		assertTrue(conn.login("rest-notifications-test-1", "A6B7C8"));
 		
 		URI request = UriBuilder.fromUri(getContextURI()).path("notifications").build();
@@ -156,10 +173,13 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		assertNotNull(infoVO.getTitle());
 		assertNotNull(infoVO.getItems());
 		assertFalse(infoVO.getItems().isEmpty());
+
+		conn.shutdown();
 	}
 	
 	@Test
 	public void testGetUserNotifications() throws IOException, URISyntaxException {
+		RestConnection conn = new RestConnection();
 		assertTrue(conn.login("rest-notifications-test-1", "A6B7C8"));
 		
 		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications").queryParam("type", "User");
@@ -179,10 +199,13 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		assertNotNull(infoVO.getTitle());
 		assertNotNull(infoVO.getItems());
 		assertFalse(infoVO.getItems().isEmpty());
+
+		conn.shutdown();
 	}
 	
 	@Test
 	public void testGetUserForumNotifications() throws URISyntaxException, IOException {
+		RestConnection conn = new RestConnection();
 		assertTrue(conn.login(userAndForumSubscriberId.getName(), "A6B7C8"));
 		
 		Calendar cal = Calendar.getInstance();
@@ -197,10 +220,13 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		List<SubscriptionInfoVO> infos = parseUserArray(response);
 		assertNotNull(infos);
 		assertTrue(2 <= infos.size());
+
+		conn.shutdown();
 	}
 	
 	@Test
 	public void testGetUserForumNotificationsByType() throws IOException, URISyntaxException {
+		RestConnection conn = new RestConnection();
 		assertTrue(conn.login(userAndForumSubscriberId.getName(), "A6B7C8"));
 		
 		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications").queryParam("type", "Forum");
@@ -220,10 +246,13 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		assertNotNull(infoVO.getTitle());
 		assertNotNull(infoVO.getItems());
 		assertFalse(infoVO.getItems().isEmpty());
+
+		conn.shutdown();
 	}
 	
 	@Test
 	public void testGetNoNotifications() throws IOException, URISyntaxException {
+		RestConnection conn = new RestConnection();
 		assertTrue(conn.login("rest-notifications-test-3", "A6B7C8"));
 		
 		URI request = UriBuilder.fromUri(getContextURI()).path("/notifications").build();
@@ -235,6 +264,209 @@ public class NotificationsTest extends OlatJerseyTestCase {
 		
 		assertNotNull(infos);
 		assertTrue(infos.isEmpty());
+
+		conn.shutdown();
+	}
+	
+	@Test
+	public void testGetBusinessGroupForumNotifications() throws IOException, URISyntaxException {
+		//create a business group with forum notifications
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("rest-not-4-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(id, "Notifications 1", "REST forum notifications for group", null, null, false, false, null);
+		CollaborationTools tools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(group);
+		tools.setToolEnabled(CollaborationTools.TOOL_FORUM, true);
+		Forum forum = tools.getForum();
+		dbInstance.commitAndCloseSession();
+		
+		//publish
+		String businessPath = "[BusinessGroup:" + group.getKey() + "][toolforum:0]";
+		SubscriptionContext forumSubContext = new SubscriptionContext("BusinessGroup", group.getKey(), "toolforum");
+		PublisherData forumPdata =
+				new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), businessPath);
+		notificationManager.subscribe(id, forumSubContext, forumPdata);
+		Message message = createMessage(id, forum);
+		notificationManager.markPublisherNews(forumSubContext, null);
+		dbInstance.commitAndCloseSession();
+		
+		//get the notification
+		RestConnection conn = new RestConnection();
+		assertTrue(conn.login(id.getName(), "A6B7C8"));
+		
+		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications");
+		HttpGet method = conn.createGet(request.build(), MediaType.APPLICATION_JSON, true);
+		HttpResponse response = conn.execute(method);
+		assertEquals(200, response.getStatusLine().getStatusCode());
+		List<SubscriptionInfoVO> infos = parseUserArray(response.getEntity().getContent());
+		Assert.assertNotNull(infos);
+		Assert.assertEquals(1, infos.size());
+		SubscriptionInfoVO infoVO = infos.get(0);
+		Assert.assertNotNull(infoVO.getItems());
+		Assert.assertEquals(1, infoVO.getItems().size());
+		SubscriptionListItemVO itemVO = infoVO.getItems().get(0);
+		Assert.assertNotNull(itemVO);
+		Assert.assertEquals(group.getKey(), itemVO.getGroupKey());
+		Assert.assertEquals(message.getKey(), itemVO.getMessageKey());
+	}
+	
+	@Test
+	public void testGetBusinessGroupFolderNotifications() throws IOException, URISyntaxException {
+		//create a business group with folder notifications
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("rest-not-5-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(id, "Notifications 2", "REST folder notifications for group", null, null, false, false, null);
+		CollaborationTools tools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(group);
+		tools.setToolEnabled(CollaborationTools.TOOL_FOLDER, true);
+		String relPath = tools.getFolderRelPath();
+		dbInstance.commitAndCloseSession();
+		
+		//publish
+		String businessPath = "[BusinessGroup:" + group.getKey() + "][toolfolder:0]";
+		SubscriptionContext folderSubContext = new SubscriptionContext("BusinessGroup", group.getKey(), "toolfolder");
+		PublisherData folderPdata = new PublisherData("FolderModule", relPath, businessPath);
+		notificationManager.subscribe(id, folderSubContext, folderPdata);
+		//add a file
+		OlatRootFolderImpl folder = tools.getSecuredFolder(group, folderSubContext, id, true);
+		String filename = addFile(folder);
+		
+		//mark as published
+		notificationManager.markPublisherNews(folderSubContext, null);
+		dbInstance.commitAndCloseSession();
+		
+		//get the notification
+		RestConnection conn = new RestConnection();
+		assertTrue(conn.login(id.getName(), "A6B7C8"));
+		
+		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications");
+		HttpGet method = conn.createGet(request.build(), MediaType.APPLICATION_JSON, true);
+		HttpResponse response = conn.execute(method);
+		assertEquals(200, response.getStatusLine().getStatusCode());
+		List<SubscriptionInfoVO> infos = parseUserArray(response.getEntity().getContent());
+		Assert.assertNotNull(infos);
+		Assert.assertEquals(1, infos.size());
+		SubscriptionInfoVO infoVO = infos.get(0);
+		Assert.assertNotNull(infoVO.getItems());
+		Assert.assertEquals(1, infoVO.getItems().size());
+		SubscriptionListItemVO itemVO = infoVO.getItems().get(0);
+		Assert.assertNotNull(itemVO);
+		Assert.assertEquals(group.getKey(), itemVO.getGroupKey());
+		Assert.assertEquals("/" + filename, itemVO.getPath());
+	}
+	
+	@Test
+	public void testGetCourseForumNotifications() throws IOException, URISyntaxException {
+		//create a course with a forum
+		Identity id = JunitTestHelper.createAndPersistIdentityAsAuthor("rest-not-6-" + UUID.randomUUID().toString());
+		ICourse course = CoursesWebService.createEmptyCourse(id, "Course forum not", "Course forum with notification", null);
+		dbInstance.intermediateCommit();
+		//create the forum
+		CourseNodeConfiguration newNodeConfig = CourseNodeFactory.getInstance().getCourseNodeConfiguration("fo");
+		FOCourseNode forumNode = (FOCourseNode)newNodeConfig.getInstance();
+		forumNode.setShortTitle("Forum");
+		forumNode.setLearningObjectives("forum objectives");
+		forumNode.setNoAccessExplanation("You don't have access");
+		Forum forum = forumNode.loadOrCreateForum(course.getCourseEnvironment());
+		course.getEditorTreeModel().addCourseNode(forumNode, course.getRunStructure().getRootNode());
+		CourseFactory.publishCourse(course, id, Locale.ENGLISH);
+		dbInstance.intermediateCommit();
+		
+		//add message and publisher
+		RepositoryEntry re = repositoryManager.lookupRepositoryEntry(course.getCourseEnvironment().getCourseGroupManager().getCourseResource(), true);
+		String businessPath = "[RepositoryEntry:" + re.getKey() + "][CourseNode:" + forumNode.getIdent() + "]";
+		SubscriptionContext forumSubContext = new SubscriptionContext("CourseModule", course.getResourceableId(), forumNode.getIdent());
+		PublisherData forumPdata =
+				new PublisherData(OresHelper.calculateTypeName(Forum.class), forum.getKey().toString(), businessPath);
+		notificationManager.subscribe(id, forumSubContext, forumPdata);
+		Message message = createMessage(id, forum);
+		notificationManager.markPublisherNews(forumSubContext, null);
+		dbInstance.commitAndCloseSession();
+		
+		//get the notification
+		RestConnection conn = new RestConnection();
+		assertTrue(conn.login(id.getName(), "A6B7C8"));
+		
+		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications");
+		HttpGet method = conn.createGet(request.build(), MediaType.APPLICATION_JSON, true);
+		HttpResponse response = conn.execute(method);
+		assertEquals(200, response.getStatusLine().getStatusCode());
+		List<SubscriptionInfoVO> infos = parseUserArray(response.getEntity().getContent());
+		Assert.assertNotNull(infos);
+		Assert.assertEquals(1, infos.size());
+		SubscriptionInfoVO infoVO = infos.get(0);
+		Assert.assertNotNull(infoVO.getItems());
+		Assert.assertEquals(1, infoVO.getItems().size());
+		SubscriptionListItemVO itemVO = infoVO.getItems().get(0);
+		Assert.assertNotNull(itemVO);
+		Assert.assertEquals(course.getResourceableId(), itemVO.getCourseKey());
+		Assert.assertEquals(forumNode.getIdent(), itemVO.getCourseNodeId());
+		Assert.assertEquals(message.getKey(), itemVO.getMessageKey());
+	}
+	
+	@Test
+	public void testGetCourseFolderNotifications() throws IOException, URISyntaxException {
+		//create a course with a forum
+		Identity id = JunitTestHelper.createAndPersistIdentityAsAuthor("rest-not-7-" + UUID.randomUUID().toString());
+		ICourse course = CoursesWebService.createEmptyCourse(id, "Course folder not", "Course with folder and notification", null);
+		dbInstance.intermediateCommit();
+		//create the folder
+		CourseNodeConfiguration newNodeConfig = CourseNodeFactory.getInstance().getCourseNodeConfiguration("bc");
+		BCCourseNode folderNode = (BCCourseNode)newNodeConfig.getInstance();
+		folderNode.setShortTitle("Folder");
+		folderNode.setLearningObjectives("folder objectives");
+		folderNode.setNoAccessExplanation("You don't have access");
+		String relPath = BCCourseNode.getFoldernodePathRelToFolderBase(course.getCourseEnvironment(), folderNode);
+		VFSContainer folder = BCCourseNode.getNodeFolderContainer(folderNode, course.getCourseEnvironment());
+		course.getEditorTreeModel().addCourseNode(folderNode, course.getRunStructure().getRootNode());
+		CourseFactory.publishCourse(course, id, Locale.ENGLISH);
+		dbInstance.intermediateCommit();
+		
+		//add message and publisher
+		RepositoryEntry re = repositoryManager.lookupRepositoryEntry(course.getCourseEnvironment().getCourseGroupManager().getCourseResource(), true);
+		String businessPath = "[RepositoryEntry:" + re.getKey() + "][CourseNode:" + folderNode.getIdent() + "]";
+		SubscriptionContext folderSubContext = new SubscriptionContext("CourseModule", course.getResourceableId(), folderNode.getIdent());
+		PublisherData folderPdata = new PublisherData("FolderModule", relPath, businessPath);
+		notificationManager.subscribe(id, folderSubContext, folderPdata);
+		String filename = addFile(folder);
+		notificationManager.markPublisherNews(folderSubContext, null);
+		dbInstance.commitAndCloseSession();
+
+		//get the notification
+		RestConnection conn = new RestConnection();
+		assertTrue(conn.login(id.getName(), "A6B7C8"));
+		
+		UriBuilder request = UriBuilder.fromUri(getContextURI()).path("notifications");
+		HttpGet method = conn.createGet(request.build(), MediaType.APPLICATION_JSON, true);
+		HttpResponse response = conn.execute(method);
+		assertEquals(200, response.getStatusLine().getStatusCode());
+		List<SubscriptionInfoVO> infos = parseUserArray(response.getEntity().getContent());
+		Assert.assertNotNull(infos);
+		Assert.assertEquals(1, infos.size());
+		SubscriptionInfoVO infoVO = infos.get(0);
+		Assert.assertNotNull(infoVO.getItems());
+		Assert.assertEquals(1, infoVO.getItems().size());
+		SubscriptionListItemVO itemVO = infoVO.getItems().get(0);
+		Assert.assertNotNull(itemVO);
+		Assert.assertEquals(course.getResourceableId(), itemVO.getCourseKey());
+		Assert.assertEquals(folderNode.getIdent(), itemVO.getCourseNodeId());
+		Assert.assertEquals("/" + filename, itemVO.getPath());
+	}
+	
+	private String addFile(VFSContainer folder) throws IOException {
+		String name = UUID.randomUUID().toString();
+		VFSLeaf file = folder.createChildLeaf(name + ".jpg");
+		OutputStream out = file.getOutputStream(true);
+		InputStream in = UserMgmtTest.class.getResourceAsStream("portrait.jpg");
+		IOUtils.copy(in, out);
+		IOUtils.closeQuietly(in);
+		IOUtils.closeQuietly(out);
+		return file.getName();
+	}
+	
+	private Message createMessage(Identity id, Forum forum) {
+		ForumManager fm = ForumManager.getInstance();
+		Message m1 = fm.createMessage();
+		m1.setTitle("Thread-1");
+		m1.setBody("Body of Thread-1");
+		fm.addTopMessage(id, forum, m1);
+		return m1;
 	}
 	
 	protected List<SubscriptionInfoVO> parseUserArray(HttpResponse response) throws IOException, URISyntaxException {
-- 
GitLab