From 85e593fca18cfb0df4193b6d50351da7869e0f9c Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Wed, 6 Jul 2016 16:22:13 +0200
Subject: [PATCH] OO-2057: search in media center

---
 .../BaseFullWebappWindowControl.java          |   5 +-
 .../core/gui/control/LocalWindowControl.java  |   5 +-
 .../olat/core/gui/control/WindowControl.java  |   3 +-
 .../closablewrapper/CalloutSettings.java      |  62 +++++++++++
 .../CloseableCalloutWindowController.java     |  40 ++++++-
 .../core/gui/control/guistack/GuiStack.java   |   3 +-
 .../control/guistack/GuiStackNiceImpl.java    |  14 ++-
 .../control/guistack/_content/callout.html    |  90 ++++++++++-----
 .../context/StackedBusinessWindowControl.java |   5 +-
 .../tool/AssessedIdentityListController.java  |   3 +-
 .../modules/portfolio/PortfolioService.java   |  11 +-
 .../modules/portfolio/manager/MediaDAO.java   |  26 ++++-
 .../modules/portfolio/manager/PageDAO.java    |  31 ++++++
 .../manager/PortfolioServiceImpl.java         |  14 ++-
 .../modules/portfolio/model/MediaRow.java     |  43 +++++++-
 .../portfolio/ui/BinderListController.java    |   4 +-
 .../portfolio/ui/MediaCenterController.java   |  82 ++++++++++----
 .../modules/portfolio/ui/MediaDataModel.java  | 103 ++++++++++++++++--
 .../portfolio/ui/PageEditController.java      |   4 +-
 .../portfolio/ui/PageListDataModel.java       |   2 +-
 .../portfolio/ui/PageMetadataController.java  |  19 ++++
 .../portfolio/ui/_content/page_meta.html      |   2 +-
 .../ui/_i18n/LocalStrings_de.properties       |  11 +-
 .../ui/_i18n/LocalStrings_en.properties       |  10 +-
 .../ui/editor/PageEditorController.java       |  13 ++-
 .../ui/media/CollectImageMediaController.java |   4 +-
 .../ui/media/CollectTextMediaController.java  |   4 +-
 .../ui/renderer/MediaTypeCellRenderer.java    |  58 ++++++++++
 .../ui/EPArtefactPoolRunController.java       |  14 ++-
 .../portfolio/ui/_content/artefactsmain.html  |   7 +-
 .../view/EPArtefactViewController.java        |   4 +-
 .../ui/structel/EPAddElementsController.java  |   2 +-
 .../restapi/support/ErrorWindowControl.java   |   3 +-
 .../en/EnrollmentManagerConcurrentTest.java   |   3 +-
 34 files changed, 595 insertions(+), 109 deletions(-)
 create mode 100644 src/main/java/org/olat/core/gui/control/generic/closablewrapper/CalloutSettings.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/renderer/MediaTypeCellRenderer.java

diff --git a/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappWindowControl.java b/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappWindowControl.java
index 6d8d9324ead..2064ef73107 100644
--- a/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappWindowControl.java
+++ b/src/main/java/org/olat/core/commons/fullWebApp/BaseFullWebappWindowControl.java
@@ -23,6 +23,7 @@ import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.WindowBackOffice;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.WindowControlInfoImpl;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.info.WindowControlInfo;
 import org.olat.core.id.context.BusinessControl;
 import org.olat.core.id.context.BusinessControlFactory;
@@ -61,8 +62,8 @@ class BaseFullWebappWindowControl implements WindowControl {
 	}
 
 	@Override
-	public void pushAsCallout(Component comp, String targetId) {
-		webappCtrl.getCurrentGuiStack().pushCallout(comp, targetId);
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings) {
+		webappCtrl.getCurrentGuiStack().pushCallout(comp, targetId, settings);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/core/gui/control/LocalWindowControl.java b/src/main/java/org/olat/core/gui/control/LocalWindowControl.java
index b0e65b9b6cf..c5e6c4f239c 100644
--- a/src/main/java/org/olat/core/gui/control/LocalWindowControl.java
+++ b/src/main/java/org/olat/core/gui/control/LocalWindowControl.java
@@ -27,6 +27,7 @@
 package org.olat.core.gui.control;
 
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.info.WindowControlInfo;
 import org.olat.core.id.context.BusinessControl;
 import org.olat.core.logging.OLog;
@@ -71,8 +72,8 @@ public class LocalWindowControl implements WindowControl {
 	}
 
 	@Override
-	public void pushAsCallout(Component comp, String targetId) {
-		origWControl.pushAsCallout(comp, targetId);
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings) {
+		origWControl.pushAsCallout(comp, targetId, settings);
 		localHeight++;
 	}
 
diff --git a/src/main/java/org/olat/core/gui/control/WindowControl.java b/src/main/java/org/olat/core/gui/control/WindowControl.java
index f9002d0e4c3..4ecc320ac5e 100644
--- a/src/main/java/org/olat/core/gui/control/WindowControl.java
+++ b/src/main/java/org/olat/core/gui/control/WindowControl.java
@@ -27,6 +27,7 @@
 package org.olat.core.gui.control;
 
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.info.WindowControlInfo;
 import org.olat.core.id.context.BusinessControl;
 
@@ -61,7 +62,7 @@ public interface WindowControl {
 	 * @param comp
 	 * @param targetId
 	 */
-	public void pushAsCallout(Component comp, String targetId);
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings);
 	
 
 	/**
diff --git a/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CalloutSettings.java b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CalloutSettings.java
new file mode 100644
index 00000000000..e5694600009
--- /dev/null
+++ b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CalloutSettings.java
@@ -0,0 +1,62 @@
+/**
+ * <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.core.gui.control.generic.closablewrapper;
+
+/**
+ * 
+ * Initial date: 06.07.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CalloutSettings {
+	
+	private final boolean arrow;
+	private final CalloutOrientation orientation;
+	
+	public CalloutSettings() {
+		this(true, CalloutOrientation.bottom);
+	}
+	
+	public CalloutSettings(CalloutOrientation orientation) {
+		this(true, orientation);
+	}
+	
+	public CalloutSettings(boolean arrow) {
+		this(arrow, CalloutOrientation.bottom);
+	}
+	
+	public CalloutSettings(boolean arrow, CalloutOrientation orientation) {
+		this.orientation = orientation;
+		this.arrow = arrow;
+	}
+	
+	public boolean isArrow() {
+		return arrow;
+	}
+
+	public CalloutOrientation getOrientation() {
+		return orientation;
+	}
+
+	public enum CalloutOrientation {
+		top,
+		bottom
+	}
+}
diff --git a/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
index 1dfcc787f16..c63d92faab6 100644
--- a/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/closablewrapper/CloseableCalloutWindowController.java
@@ -61,6 +61,7 @@ import org.olat.core.logging.OLATRuntimeException;
 public class CloseableCalloutWindowController extends BasicController {
 	public static final Event CLOSE_WINDOW_EVENT = new Event("CLOSE_WINDOW_EVENT");
 
+	private final CalloutSettings settings;
 	private VelocityContainer calloutVC;
 	private CloseableModalController cmc;
 
@@ -92,8 +93,14 @@ public class CloseableCalloutWindowController extends BasicController {
 	 */
 	public CloseableCalloutWindowController(UserRequest ureq, WindowControl wControl, Component calloutWindowContent,
 			String targetDomID, String title, boolean closable, String cssClasses) {
+		this(ureq, wControl, calloutWindowContent, targetDomID, title, closable, cssClasses, new CalloutSettings());
+	}
+	
+	public CloseableCalloutWindowController(UserRequest ureq, WindowControl wControl, Component calloutWindowContent,
+			String targetDomID, String title, boolean closable, String cssClasses, CalloutSettings settings) {
 		super(ureq, wControl);
-
+		
+		this.settings = settings;
 		boolean ajax = getWindowControl().getWindowBackOffice().getWindowManager().isAjaxEnabled();
 		if (ajax) {
 			final Panel guiMsgPlace = new Panel("guimessage_place");
@@ -158,8 +165,7 @@ public class CloseableCalloutWindowController extends BasicController {
 	 * render the content of this controller your self with the
 	 * getInitialComponent() method.
 	 * 
-	 * @param ureq
-	 *            The user request
+	 * @param ureq The user request
 	 * @param wControl
 	 *            The window control
 	 * @param calloutWindowContent
@@ -176,6 +182,32 @@ public class CloseableCalloutWindowController extends BasicController {
 				+ targetFormLink.getComponent().getDispatchID(), title, closable, cssClasses);
 	}
 	
+	/**
+	 * Constructor for a closable callout window controller. After calling the
+	 * constructor, the callout window will be visible immediately, there is no
+	 * need to activate the window. In contrast to the modal dialogs you have to
+	 * render the content of this controller your self with the
+	 * getInitialComponent() method.
+	 * 
+	 * @param ureq
+	 * 				The user request
+	 * @param wControl
+	 * 				The window control
+	 * @param calloutWindowContent
+	 * 				The component that should be displayed in the callout window
+	 * @param targetFormLink
+	 * @param title
+	 * @param closable
+	 * @param cssClasses
+	 * @param settings
+	 */
+	public CloseableCalloutWindowController(UserRequest ureq,
+			WindowControl wControl, Component calloutWindowContent,
+			FormLink targetFormLink, String title, boolean closable, String cssClasses, CalloutSettings settings) {
+		this(ureq, wControl, calloutWindowContent, "o_fi"
+				+ targetFormLink.getComponent().getDispatchID(), title, closable, cssClasses, settings);
+	}
+	
 	public String getDOMTarget() {
 		if (calloutVC != null) {
 			// Setting the new target makes this callout VC dirty which redraws
@@ -281,7 +313,7 @@ public class CloseableCalloutWindowController extends BasicController {
 			cmc.activate();
 		} else {
 			// push to modal stack
-			getWindowControl().pushAsCallout(calloutVC, getDOMTarget());
+			getWindowControl().pushAsCallout(calloutVC, getDOMTarget(), settings);
 		}
 	}
 
diff --git a/src/main/java/org/olat/core/gui/control/guistack/GuiStack.java b/src/main/java/org/olat/core/gui/control/guistack/GuiStack.java
index 749abb0d2b7..7b4022fe121 100644
--- a/src/main/java/org/olat/core/gui/control/guistack/GuiStack.java
+++ b/src/main/java/org/olat/core/gui/control/guistack/GuiStack.java
@@ -27,6 +27,7 @@ package org.olat.core.gui.control.guistack;
 
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.panel.StackedPanel;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 
 /**
  * Description:<br>
@@ -49,7 +50,7 @@ public interface GuiStack {
 	 * @param content The component to push as callout window
 	 * @param targetId The target element
 	 */
-	public void pushCallout(Component content, String targetId);
+	public void pushCallout(Component content, String targetId, CalloutSettings settings);
 
 	/**
 	 * @see org.olat.core.gui.control.GuiStackHandle#pushContent(org.olat.core.gui.components.Component)
diff --git a/src/main/java/org/olat/core/gui/control/guistack/GuiStackNiceImpl.java b/src/main/java/org/olat/core/gui/control/guistack/GuiStackNiceImpl.java
index db2ad0daed2..f589ca46ef2 100644
--- a/src/main/java/org/olat/core/gui/control/guistack/GuiStackNiceImpl.java
+++ b/src/main/java/org/olat/core/gui/control/guistack/GuiStackNiceImpl.java
@@ -30,10 +30,12 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.panel.LayeredPanel;
 import org.olat.core.gui.components.panel.Panel;
-import org.olat.core.gui.components.panel.StackedPanel;
 import org.olat.core.gui.components.panel.SimpleStackedPanel;
+import org.olat.core.gui.components.panel.StackedPanel;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.WindowBackOffice;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings.CalloutOrientation;
 import org.olat.core.gui.control.util.ZIndexWrapper;
 import org.olat.core.gui.render.ValidationResult;
 import org.olat.core.util.Util;
@@ -126,7 +128,7 @@ public class GuiStackNiceImpl implements GuiStack {
 	
 	
 	@Override
-	public void pushCallout(Component content, String targetId) {
+	public void pushCallout(Component content, String targetId, CalloutSettings settings) {
 		// wrap the component into a modal foreground dialog with alpha-blended-background
 		final Panel guiMsgPlace = new Panel("guimsgplace_for_callout");
 		VelocityContainer inset = new VelocityContainer("inset", VELOCITY_ROOT + "/callout.html", null, null) {
@@ -146,7 +148,13 @@ public class GuiStackNiceImpl implements GuiStack {
 		inset.contextPut("zindexshim", zindex);
 		inset.contextPut("zindexarea", zindex+5);
 		inset.contextPut("zindexextwindows", zindex+50);
-
+		if(settings != null) {
+			inset.contextPut("arrow", settings.isArrow());
+			inset.contextPut("orientation", settings.getOrientation().name());
+		} else {
+			inset.contextPut("arrow", Boolean.TRUE);
+			inset.contextPut("orientation", CalloutOrientation.bottom.name());
+		}
 		modalPanel.pushContent(inset);
 		modalLayers++;
 	}
diff --git a/src/main/java/org/olat/core/gui/control/guistack/_content/callout.html b/src/main/java/org/olat/core/gui/control/guistack/_content/callout.html
index ce9f444491a..35d0601c816 100644
--- a/src/main/java/org/olat/core/gui/control/guistack/_content/callout.html
+++ b/src/main/java/org/olat/core/gui/control/guistack/_content/callout.html
@@ -1,6 +1,7 @@
 <div id="callout_$r.getCId()" class="popover in bottom" style="z-index:$zindexarea;">
-	<div class="arrow"></div>
+	#if($arrow && $orientation == "bottom")<div class="arrow"></div>#end
 	<div class="popover-content">$r.render("cont")</div>
+	#if($arrow && $orientation == "bottom")<div class="arrow"></div>#end
 </div>
 <div id="callout_overlay_$r.getCId()" class="o_callout_overlay" style="z-index:$zindexoverlay;"></div>
 <script type="text/javascript">
@@ -14,35 +15,70 @@ jQuery(function() {
 			callout.css('display','block').css('z-index', o_info.zseed + 105);
 			jQuery('#callout_overlay_$r.getCId()').css('z-index', o_info.zseed + 103);
 			
+			var orientation = '${orientation}';
+			var arrow = $arrow;
 			var docWidth = jQuery(document).width();
-			if(targetOffset.left >= 0 && callout.width() > 50 && targetOffset.left <= (callout.width() / 2)) {
-				## left edge case: show at left border
-				## add 15px for button size
-				## add 10px for a bit space at left side
-				callout.offset({
-					top: (targetOffset.top + targetEl.height() + 15) ,
-					left: '10px'
-				});
-				var percent = (targetOffset.left + targetEl.width()/2 + 15 + 10/2) / callout.width() * 100; 
-				callout.find('.arrow').css({'left' : percent + '%'});
+			if(orientation == 'top') {
+				if(targetOffset.left >= 0 && callout.width() > 50 && targetOffset.left <= (callout.width() / 2)) {
+					## left edge case: show at left border
+					## add 15px for button size
+					## add 10px for a bit space at left side
+					callout.offset({
+						top: (targetOffset.top - targetEl.height() - 25) ,
+						left: '10px'
+					});
+					var percent = (targetOffset.left + targetEl.width()/2 + 15 + 10/2) / callout.width() * 100; 
+					callout.find('.arrow').css({'left' : percent + '%'});
+					
+				} else if(targetOffset.left >= 0 && callout.width() > 50 && ((targetOffset.left + (callout.width() / 2)) >= docWidth)) {
+					## right edge case: show at right border
+					## add 15px for button size
+					## add 10px for a bit space at right side
+					callout.offset({
+						top: (targetOffset.top - targetEl.height() - 25) ,
+						left: (docWidth - callout.width() - 10)
+					});
+					var percent = (1 - ((docWidth - targetOffset.left - targetEl.width()/2 - 15 - 10/2 ) / callout.width() )) * 100; 
+					callout.find('.arrow').css({'left' : percent + '%'});
+	
+				} else {
+					## standard case: show centered below element
+					callout.offset({
+						top: (targetOffset.top - targetEl.height() - 25),
+						left: (targetOffset.left + targetEl.width()) - callout.width()/2 
+					});
+				}
 				
-			} else if(targetOffset.left >= 0 && callout.width() > 50 && ((targetOffset.left + (callout.width() / 2)) >= docWidth)) {
-				## right edge case: show at right border
-				## add 15px for button size
-				## add 10px for a bit space at right side
-				callout.offset({
-					top: (targetOffset.top + targetEl.height() + 15) ,
-					left: (docWidth - callout.width() - 10)
-				});
-				var percent = (1 - ((docWidth - targetOffset.left - targetEl.width()/2 - 15 - 10/2 ) / callout.width() )) * 100; 
-				callout.find('.arrow').css({'left' : percent + '%'});
-
 			} else {
-				## standard case: show centered below element
-				callout.offset({
-					top: (targetOffset.top + targetEl.height() + 25),
-					left: (targetOffset.left + targetEl.width()) - callout.width()/2 
-				});
+				if(targetOffset.left >= 0 && callout.width() > 50 && targetOffset.left <= (callout.width() / 2)) {
+					## left edge case: show at left border
+					## add 15px for button size
+					## add 10px for a bit space at left side
+					callout.offset({
+						top: (targetOffset.top + targetEl.height() +  15) ,
+						left: '10px'
+					});
+					var percent = (targetOffset.left + targetEl.width()/2 + 15 + 10/2) / callout.width() * 100; 
+					callout.find('.arrow').css({'left' : percent + '%'});
+					
+				} else if(targetOffset.left >= 0 && callout.width() > 50 && ((targetOffset.left + (callout.width() / 2)) >= docWidth)) {
+					## right edge case: show at right border
+					## add 15px for button size
+					## add 10px for a bit space at right side
+					callout.offset({
+						top: (targetOffset.top + targetEl.height() + 15) ,
+						left: (docWidth - callout.width() - 10)
+					});
+					var percent = (1 - ((docWidth - targetOffset.left - targetEl.width()/2 - 15 - 10/2 ) / callout.width() )) * 100; 
+					callout.find('.arrow').css({'left' : percent + '%'});
+	
+				} else {
+					## standard case: show centered below element
+					callout.offset({
+						top: (targetOffset.top + targetEl.height() + (arrow ? 25 : 15)),
+						left: (targetOffset.left + targetEl.width()) - callout.width()/2 
+					});
+				}
 			}
 		}
 	});
diff --git a/src/main/java/org/olat/core/id/context/StackedBusinessWindowControl.java b/src/main/java/org/olat/core/id/context/StackedBusinessWindowControl.java
index dc2b538e436..4e2da23ab61 100644
--- a/src/main/java/org/olat/core/id/context/StackedBusinessWindowControl.java
+++ b/src/main/java/org/olat/core/id/context/StackedBusinessWindowControl.java
@@ -31,6 +31,7 @@ package org.olat.core.id.context;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.WindowBackOffice;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.info.WindowControlInfo;
 
 /**
@@ -73,8 +74,8 @@ public class StackedBusinessWindowControl implements WindowControl {
 	}
 
 	@Override
-	public void pushAsCallout(Component comp, String targetId) {
-		origWControl.pushAsCallout(comp, targetId);
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings) {
+		origWControl.pushAsCallout(comp, targetId, settings);
 	}
 
 	public void pushToMainArea(Component comp) {
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListController.java
index adcf81ea638..10786123997 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityListController.java
@@ -212,7 +212,8 @@ public class AssessedIdentityListController extends FormBasicController implemen
 		stackPanel.pushController(fullname, userController);
 	}
 
-	public static class AssessedUserTableModel extends DefaultFlexiTableDataModel<AssessedIdentityRow> implements SortableFlexiTableDataModel<AssessedIdentityRow> {
+	public static class AssessedUserTableModel extends DefaultFlexiTableDataModel<AssessedIdentityRow>
+		implements SortableFlexiTableDataModel<AssessedIdentityRow> {
 
 		private ConcurrentMap<Long, CertificateLight> certificates;
 
diff --git a/src/main/java/org/olat/modules/portfolio/PortfolioService.java b/src/main/java/org/olat/modules/portfolio/PortfolioService.java
index 13cbddf96bf..ab0b731089d 100644
--- a/src/main/java/org/olat/modules/portfolio/PortfolioService.java
+++ b/src/main/java/org/olat/modules/portfolio/PortfolioService.java
@@ -152,6 +152,15 @@ public interface PortfolioService {
 	 */
 	public List<Identity> getMembers(BinderRef binder, String... roles);
 	
+	/**
+	 * The list of owners of the page and only of the specified page.
+	 * 
+	 * @param page
+	 * @param roles At least a role need to be specified
+	 * @return
+	 */
+	public List<Identity> getMembers(Page page, String... roles);
+	
 	public List<AccessRights> getAccessRights(Binder binder);
 	
 	public List<AccessRights> getAccessRights(Binder binder, Identity identity);
@@ -318,7 +327,7 @@ public interface PortfolioService {
 	
 	public Media getMediaByKey(Long key);
 	
-	public List<MediaLight> searchOwnedMedias(IdentityRef author);
+	public List<MediaLight> searchOwnedMedias(IdentityRef author, String searchString);
 	
 	public void updateCategories(Media media, List<String> categories);
 	
diff --git a/src/main/java/org/olat/modules/portfolio/manager/MediaDAO.java b/src/main/java/org/olat/modules/portfolio/manager/MediaDAO.java
index 16ce28a5af7..c531cbba131 100644
--- a/src/main/java/org/olat/modules/portfolio/manager/MediaDAO.java
+++ b/src/main/java/org/olat/modules/portfolio/manager/MediaDAO.java
@@ -19,12 +19,18 @@
  */
 package org.olat.modules.portfolio.manager;
 
+import static org.olat.core.commons.persistence.PersistenceHelper.appendFuzzyLike;
+import static org.olat.core.commons.persistence.PersistenceHelper.makeFuzzyQueryString;
+
 import java.util.Date;
 import java.util.List;
 
+import javax.persistence.TypedQuery;
+
 import org.olat.basesecurity.IdentityRef;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
+import org.olat.core.util.StringHelper;
 import org.olat.modules.portfolio.Media;
 import org.olat.modules.portfolio.MediaLight;
 import org.olat.modules.portfolio.model.MediaImpl;
@@ -79,17 +85,27 @@ public class MediaDAO {
 		return medias == null || medias.isEmpty() ? null : medias.get(0);
 	}
 	
-	public List<MediaLight> loadByAuthor(IdentityRef author) {
+	public List<MediaLight> searchByAuthor(IdentityRef author, String searchString) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select media from pfmedia as media")
 		  .append(" inner join fetch media.author as author")
 		  .append(" where author.key=:authorKey");
+		if(StringHelper.containsNonWhitespace(searchString)) {
+			searchString = makeFuzzyQueryString(searchString);
+			sb.append(" and (");
+			appendFuzzyLike(sb, "media.title", "searchString", dbInstance.getDbVendor());
+			sb.append(" or ");
+			appendFuzzyLike(sb, "media.description", "searchString", dbInstance.getDbVendor());
+			sb.append(")");
+		}
 		
-		List<MediaLight> medias = dbInstance.getCurrentEntityManager()
+		TypedQuery<MediaLight> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), MediaLight.class)
-				.setParameter("authorKey", author.getKey())
-				.getResultList();
-		return medias;
+				.setParameter("authorKey", author.getKey());
+		if(StringHelper.containsNonWhitespace(searchString)) {
+			query.setParameter("searchString", searchString.toLowerCase());
+		}
+		return query.getResultList();
 	}
 
 }
diff --git a/src/main/java/org/olat/modules/portfolio/manager/PageDAO.java b/src/main/java/org/olat/modules/portfolio/manager/PageDAO.java
index 0290f73edcd..1d8f2491a0d 100644
--- a/src/main/java/org/olat/modules/portfolio/manager/PageDAO.java
+++ b/src/main/java/org/olat/modules/portfolio/manager/PageDAO.java
@@ -19,6 +19,8 @@
  */
 package org.olat.modules.portfolio.manager;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -27,6 +29,8 @@ import org.olat.basesecurity.GroupRoles;
 import org.olat.basesecurity.IdentityRef;
 import org.olat.basesecurity.manager.GroupDAO;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.core.util.StringHelper;
 import org.olat.modules.portfolio.BinderRef;
 import org.olat.modules.portfolio.Page;
 import org.olat.modules.portfolio.PageBody;
@@ -157,6 +161,33 @@ public class PageDAO {
 		return pages;
 	}
 	
+	public List<Identity> getMembers(Page page, String... roles)  {
+		if(page == null || roles == null || roles.length == 0 || roles[0] == null) {
+			return Collections.emptyList();
+		}
+
+		StringBuilder sb = new StringBuilder();
+		sb.append("select membership.identity from pfpage as page")
+		  .append(" inner join page.baseGroup as baseGroup")
+		  .append(" inner join baseGroup.members as membership")
+		  .append(" inner join membership.identity as ident")
+		  .append(" inner join fetch ident.user as identUser")
+		  .append(" where page.key=:pageKey and membership.role in (:roles)");
+		
+		List<String> roleList = new ArrayList<>(roles.length);
+		for(String role:roles) {
+			if(StringHelper.containsNonWhitespace(role)) {
+				roleList.add(role);
+			}
+		}
+		
+		return dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), Identity.class)
+			.setParameter("pageKey", page.getKey())
+			.setParameter("roles", roleList)
+			.getResultList();
+	}
+	
 	public Page loadByKey(Long key) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select page from pfpage as page")
diff --git a/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java b/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java
index 1975b025bb4..53b0b496e18 100644
--- a/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java
+++ b/src/main/java/org/olat/modules/portfolio/manager/PortfolioServiceImpl.java
@@ -267,6 +267,11 @@ public class PortfolioServiceImpl implements PortfolioService {
 		return binderDao.getMembers(binder, roles);
 	}
 
+	@Override
+	public List<Identity> getMembers(Page page, String... roles) {
+		return pageDao.getMembers(page, roles);
+	}
+
 	@Override
 	public boolean isBinderVisible(Identity identity, Binder binder) {
 		return true;
@@ -589,8 +594,9 @@ public class PortfolioServiceImpl implements PortfolioService {
 	}
 
 	@Override
-	public PagePart updatePart(PagePart part) {
-		return pageDao.merge(part);
+	@SuppressWarnings("unchecked")
+	public <U extends PagePart> U updatePart(U part) {
+		return (U)pageDao.merge(part);
 	}
 
 	@Override
@@ -617,8 +623,8 @@ public class PortfolioServiceImpl implements PortfolioService {
 	}
 
 	@Override
-	public List<MediaLight> searchOwnedMedias(IdentityRef author) {
-		return mediaDao.loadByAuthor(author);
+	public List<MediaLight> searchOwnedMedias(IdentityRef author, String searchString) {
+		return mediaDao.searchByAuthor(author, searchString);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/portfolio/model/MediaRow.java b/src/main/java/org/olat/modules/portfolio/model/MediaRow.java
index e36cf7314e1..7d71b41caba 100644
--- a/src/main/java/org/olat/modules/portfolio/model/MediaRow.java
+++ b/src/main/java/org/olat/modules/portfolio/model/MediaRow.java
@@ -19,6 +19,8 @@
  */
 package org.olat.modules.portfolio.model;
 
+import java.util.Date;
+
 import org.olat.core.gui.components.form.flexible.elements.FormLink;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.modules.portfolio.MediaLight;
@@ -29,7 +31,7 @@ import org.olat.modules.portfolio.MediaLight;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class MediaRow {
+public class MediaRow implements MediaLight {
 	
 	private MediaLight media;
 
@@ -42,14 +44,51 @@ public class MediaRow {
 		this.openFormLink = openFormLink;
 	}
 	
+	@Override
 	public Long getKey() {
 		return media.getKey();
 	}
 	
+	@Override
+	public Date getCreationDate() {
+		return media.getCreationDate();
+	}
+
+	@Override
 	public String getTitle() {
 		return media.getTitle();
 	}
-	
+
+	@Override
+	public Date getCollectionDate() {
+		return media.getCollectionDate();
+	}
+
+	@Override
+	public String getType() {
+		return media.getType();
+	}
+
+	@Override
+	public String getStoragePath() {
+		return media.getStoragePath();
+	}
+
+	@Override
+	public String getRootFilename() {
+		return media.getRootFilename();
+	}
+
+	@Override
+	public String getDescription() {
+		return media.getDescription();
+	}
+
+	@Override
+	public String getBusinessPath() {
+		return media.getBusinessPath();
+	}
+
 	public FormLink getOpenFormItem() {
 		return openFormLink;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java b/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
index e3348a9df93..73dd0bf9fe9 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
@@ -52,6 +52,7 @@ import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
 import org.olat.core.gui.control.generic.dtabs.Activateable2;
@@ -319,7 +320,8 @@ public class BinderListController extends FormBasicController
 		listenTo(chooseNewBinderTypeCtrl);
 
 		newBinderCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(),
-				chooseNewBinderTypeCtrl.getInitialComponent(), newBinderDropdown.getFormDispatchId(), "", true, "");
+				chooseNewBinderTypeCtrl.getInitialComponent(), newBinderDropdown.getFormDispatchId(),
+				"", true, "", new CalloutSettings(false));
 		listenTo(newBinderCalloutCtrl);
 		newBinderCalloutCtrl.activate();
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java b/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java
index a0cb60c82a3..c54155fefa3 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java
@@ -20,12 +20,13 @@
 package org.olat.modules.portfolio.ui;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.commons.services.image.Size;
 import org.olat.core.dispatcher.mapper.Mapper;
 import org.olat.core.gui.UserRequest;
@@ -33,6 +34,9 @@ import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
 import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableSort;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions;
 import org.olat.core.gui.components.form.flexible.elements.FormLink;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
@@ -42,6 +46,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponentDelegate;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableRendererType;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableSearchEvent;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
@@ -65,14 +70,16 @@ import org.olat.modules.portfolio.MediaHandler;
 import org.olat.modules.portfolio.MediaLight;
 import org.olat.modules.portfolio.PortfolioService;
 import org.olat.modules.portfolio.model.MediaRow;
-import org.olat.modules.portfolio.ui.BindersDataModel.PortfolioCols;
+import org.olat.modules.portfolio.ui.MediaDataModel.MediaCols;
 import org.olat.modules.portfolio.ui.event.MediaSelectionEvent;
 import org.olat.modules.portfolio.ui.media.CollectFileMediaController;
 import org.olat.modules.portfolio.ui.media.CollectImageMediaController;
 import org.olat.modules.portfolio.ui.media.CollectTextMediaController;
+import org.olat.modules.portfolio.ui.renderer.MediaTypeCellRenderer;
 import org.olat.portfolio.model.artefacts.AbstractArtefact;
 import org.olat.portfolio.ui.EPArtefactPoolRunController;
 import org.olat.portfolio.ui.artefacts.view.EPArtefactChoosenEvent;
+import org.olat.repository.model.SearchMyRepositoryEntryViewParams.OrderBy;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -111,7 +118,7 @@ public class MediaCenterController extends FormBasicController
 		this.select = true;
 		 
 		initForm(ureq);
-		loadModel();
+		loadModel(null);
 	}
 	
 	public MediaCenterController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel) {
@@ -120,7 +127,7 @@ public class MediaCenterController extends FormBasicController
 		this.select = false;
 		 
 		initForm(ureq);
-		loadModel();
+		loadModel(null);
 	}
 	
 	@Override
@@ -145,11 +152,17 @@ public class MediaCenterController extends FormBasicController
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, PortfolioCols.key, "select"));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PortfolioCols.title, "select"));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(PortfolioCols.open));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, MediaCols.key, "select"));
+
+		Map<String, MediaHandler> handlersMap = portfolioService.getMediaHandlers()
+				.stream().collect(Collectors.toMap (h -> h.getType(), h -> h));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(MediaCols.type,
+				new MediaTypeCellRenderer(handlersMap)));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(MediaCols.title, "select"));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(MediaCols.collectionDate, "select"));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(MediaCols.open));
 	
-		model = new MediaDataModel(columnsModel);
+		model = new MediaDataModel(columnsModel, getLocale());
 		tableEl = uifactory.addTableElement(getWindowControl(), "table", model, 20, false, getTranslator(), formLayout);
 		tableEl.setAvailableRendererTypes(FlexiTableRendererType.custom, FlexiTableRendererType.classic);
 		tableEl.setRendererType(FlexiTableRendererType.custom);
@@ -162,24 +175,47 @@ public class MediaCenterController extends FormBasicController
 		tableEl.setRowRenderer(row, this);
 		tableEl.setCssDelegate(new MediaCssDelegate());
 		tableEl.setAndLoadPersistedPreferences(ureq, "media-list");
+		initSorters(tableEl);
+		initFilters(tableEl);
 		
 		mapperThumbnailUrl = registerCacheableMapper(ureq, "media-thumbnail", new ThumbnailMapper(model));
 		row.contextPut("mapperThumbnailUrl", mapperThumbnailUrl);
 	}
+	
+	private void initSorters(FlexiTableElement tableElement) {
+		List<FlexiTableSort> sorters = new ArrayList<>(14);
+		sorters.add(new FlexiTableSort(translate(MediaCols.key.i18nHeaderKey()), MediaCols.key.name()));
+		sorters.add(new FlexiTableSort(translate(MediaCols.type.i18nHeaderKey()), MediaCols.type.name()));
+		sorters.add(new FlexiTableSort(translate(MediaCols.title.i18nHeaderKey()), MediaCols.title.name()));
+		sorters.add(new FlexiTableSort(translate(MediaCols.collectionDate.i18nHeaderKey()), MediaCols.collectionDate.name()));
+		sorters.add(FlexiTableSort.SPACER);
+
+		FlexiTableSortOptions options = new FlexiTableSortOptions(sorters);
+		options.setDefaultOrderBy(new SortKey(OrderBy.title.name(), true));
+		tableElement.setSortSettings(options);
+	}
+	
+	private void initFilters(FlexiTableElement tableElement) {
+		List<FlexiTableFilter> filters = new ArrayList<>(16);
+		filters.add(new FlexiTableFilter(translate("filter.show.all"), "showall"));
+		filters.add(FlexiTableFilter.SPACER);
+		List<MediaHandler> handlers = portfolioService.getMediaHandlers();
+		for(MediaHandler handler:handlers) {
+			filters.add(new FlexiTableFilter(translate("artefact." + handler.getType()), handler.getType()));
+		}
+		tableElement.setFilters(null, filters);
+	}
 
 	@Override
 	public Iterable<Component> getComponents(int row, Object rowObject) {
 		return null;
 	}
 	
-	private void loadModel() {
-		List<MediaRow> currentRows = model.getObjects();
-		Map<Long,MediaRow> currentMap = new HashMap<>();
-		for(MediaRow row:currentRows) {
-			currentMap.put(row.getKey(), row);
-		}
-		
-		List<MediaLight> medias = portfolioService.searchOwnedMedias(getIdentity());
+	private void loadModel(String searchString) {
+		Map<Long,MediaRow> currentMap = model.getObjects()
+				.stream().collect(Collectors.toMap(r -> r.getKey(), r -> r));
+
+		List<MediaLight> medias = portfolioService.searchOwnedMedias(getIdentity(), searchString);
 		List<MediaRow> rows = new ArrayList<>(medias.size());
 		for(MediaLight media:medias) {
 			if(currentMap.containsKey(media.getKey())) {
@@ -194,6 +230,7 @@ public class MediaCenterController extends FormBasicController
 			}
 		}
 		model.setObjects(rows);
+		model.filter(tableEl.getSelectedFilterKey());
 	}
 
 	@Override
@@ -223,6 +260,9 @@ public class MediaCenterController extends FormBasicController
 						}
 					}
 				}
+			} else if(event instanceof FlexiTableSearchEvent) {
+				FlexiTableSearchEvent se = (FlexiTableSearchEvent)event;
+				loadModel(se.getSearch());
 			}
 		} else if(source instanceof FormLink) {
 			FormLink link = (FormLink)source;
@@ -246,21 +286,21 @@ public class MediaCenterController extends FormBasicController
 	public void event(UserRequest ureq, Controller source, Event event) {
 		if(imageUploadCtrl == source) {
 			if(event == Event.DONE_EVENT) {
-				loadModel();
+				loadModel(null);
 				tableEl.reloadData();
 			}
 			cmc.deactivate();
 			cleanUp();
 		} else if(fileUploadCtrl == source) {
 			if(event == Event.DONE_EVENT) {
-				loadModel();
+				loadModel(null);
 				tableEl.reloadData();
 			}
 			cmc.deactivate();
 			cleanUp();
 		} else if(textUploadCtrl == source) {
 			if(event == Event.DONE_EVENT) {
-				loadModel();
+				loadModel(null);
 				tableEl.reloadData();
 			}
 			cmc.deactivate();
@@ -269,7 +309,7 @@ public class MediaCenterController extends FormBasicController
 			if(event instanceof EPArtefactChoosenEvent) {
 				EPArtefactChoosenEvent cEvent = (EPArtefactChoosenEvent)event;
 				doImportArtefactV1(cEvent.getArtefact());
-				loadModel();
+				loadModel(null);
 				tableEl.reloadData();
 			}
 			cmc.deactivate();
@@ -369,7 +409,7 @@ public class MediaCenterController extends FormBasicController
 	private void doChooseArtefactV1(UserRequest ureq) {
 		if(importArtefactv1Ctrl != null) return;
 		
-		importArtefactv1Ctrl = new EPArtefactPoolRunController(ureq, this.getWindowControl(), true);
+		importArtefactv1Ctrl = new EPArtefactPoolRunController(ureq, this.getWindowControl(), true, false);
 		listenTo(importArtefactv1Ctrl);
 		
 		String title = translate("import.artefactV1");
diff --git a/src/main/java/org/olat/modules/portfolio/ui/MediaDataModel.java b/src/main/java/org/olat/modules/portfolio/ui/MediaDataModel.java
index 2dc2685a354..44937d51b97 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/MediaDataModel.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/MediaDataModel.java
@@ -19,9 +19,20 @@
  */
 package org.olat.modules.portfolio.ui;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FilterableFlexiTableModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate;
+import org.olat.core.util.StringHelper;
 import org.olat.modules.portfolio.model.MediaRow;
 
 /**
@@ -30,31 +41,76 @@ import org.olat.modules.portfolio.model.MediaRow;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class MediaDataModel  extends DefaultFlexiTableDataModel<MediaRow>  {
+public class MediaDataModel extends DefaultFlexiTableDataModel<MediaRow>
+	implements SortableFlexiTableDataModel<MediaRow>, FilterableFlexiTableModel {
+	
+	private final Locale locale;
+	private List<MediaRow> backups;
 	
-	public MediaDataModel(FlexiTableColumnModel columnsModel) {
+	public MediaDataModel(FlexiTableColumnModel columnsModel, Locale locale) {
 		super(columnsModel);
+		this.locale = locale;
+	}
+
+	@Override
+	public void filter(String key) {
+		if(StringHelper.containsNonWhitespace(key) && !"showall".equals(key)) {
+			List<MediaRow> filteredRows = new ArrayList<>();
+			for(MediaRow row:backups) {
+				if(key.equals(row.getType())) {
+					filteredRows.add(row);
+				}
+			}
+			super.setObjects(filteredRows);
+		} else {
+			super.setObjects(backups);
+		}
+	}
+
+	@Override
+	public void sort(SortKey orderBy) {
+		if(orderBy != null) {
+			List<MediaRow> views = new MediaDataModelSorterDelegate(orderBy, this, locale).sort();
+			super.setObjects(views);
+		}
 	}
 
 	@Override
 	public Object getValueAt(int row, int col) {
-		MediaRow page = getObject(row);
+		MediaRow media = getObject(row);
+		return getValueAt(media, col);
+	}
+
+	@Override
+	public Object getValueAt(MediaRow media, int col) {
 		switch(MediaCols.values()[col]) {
-			case key: return page.getKey();
-			case title: return page.getTitle();
-			case open: return page.getOpenFormItem();
+			case key: return media.getKey();
+			case title: return media.getTitle();
+			case collectionDate: return media.getCollectionDate();
+			case type: return media;
+			case open: return media.getOpenFormItem();
 		}
 		return null;
 	}
 	
+	
+	
+		@Override
+	public void setObjects(List<MediaRow> objects) {
+		backups = objects;
+		super.setObjects(objects);
+	}
+
 		@Override
 	public DefaultFlexiTableDataModel<MediaRow> createCopyWithEmptyList() {
-		return new MediaDataModel(getTableColumnModel());
+		return new MediaDataModel(getTableColumnModel(), locale);
 	}
 		
 	public enum MediaCols implements FlexiSortableColumnDef {
 		key("table.header.key"),
 		title("table.header.title"),
+		collectionDate("table.header.collection.date"),
+		type("table.header.type"),
 		open("table.header.open");
 
 		private final String i18nKey;
@@ -78,4 +134,37 @@ public class MediaDataModel  extends DefaultFlexiTableDataModel<MediaRow>  {
 			return name();
 		}
 	}
+	
+	public static class MediaDataModelSorterDelegate extends SortableFlexiTableModelDelegate<MediaRow> {
+		
+		public MediaDataModelSorterDelegate(SortKey orderBy, MediaDataModel model, Locale locale) {
+			super(orderBy, model, locale);
+		}
+
+		@Override
+		protected void sort(List<MediaRow> rows) {
+			int columnIndex = getColumnIndex();
+			MediaCols column = MediaCols.values()[columnIndex];
+			switch(column) {
+				case type: Collections.sort(rows, new TypeComparator()); break;
+				default: {
+					super.sort(rows);
+				}
+			}
+		}
+		
+		private class TypeComparator implements Comparator<MediaRow> {
+			@Override
+			public int compare(MediaRow t1, MediaRow t2) {
+				String r1 = t1.getType();
+				String r2 = t2.getType();
+				
+				int compare = compareString(r1, r2);
+				if(compare == 0) {
+					compare = compareString(t1.getTitle(), t2.getTitle());
+				}
+				return compare;
+			}
+		}
+	}
 }
diff --git a/src/main/java/org/olat/modules/portfolio/ui/PageEditController.java b/src/main/java/org/olat/modules/portfolio/ui/PageEditController.java
index 0dec547508d..52233f6abf7 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/PageEditController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/PageEditController.java
@@ -230,12 +230,12 @@ public class PageEditController extends BasicController {
 			return mediaPart;
 		}
 
-		@Autowired
+		@Override
 		public AddElementInfos getUserObject() {
 			return userObject;
 		}
 
-		@Autowired
+		@Override
 		public void setUserObject(AddElementInfos userObject) {
 			this.userObject = userObject;
 		}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/PageListDataModel.java b/src/main/java/org/olat/modules/portfolio/ui/PageListDataModel.java
index c58239921df..2e78790a2c6 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/PageListDataModel.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/PageListDataModel.java
@@ -36,7 +36,7 @@ import org.olat.modules.portfolio.model.PageRow;
  *
  */
 public class PageListDataModel extends DefaultFlexiTableDataModel<PageRow>
-	implements SortableFlexiTableDataModel<PageRow>{
+	implements SortableFlexiTableDataModel<PageRow> {
 	
 	public PageListDataModel(FlexiTableColumnModel columnModel) {
 		super(columnModel);
diff --git a/src/main/java/org/olat/modules/portfolio/ui/PageMetadataController.java b/src/main/java/org/olat/modules/portfolio/ui/PageMetadataController.java
index f780d13be71..9420474f4e5 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/PageMetadataController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/PageMetadataController.java
@@ -21,8 +21,11 @@ package org.olat.modules.portfolio.ui;
 
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import org.olat.basesecurity.GroupRoles;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
@@ -31,11 +34,13 @@ import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.id.Identity;
 import org.olat.modules.portfolio.BinderSecurityCallback;
 import org.olat.modules.portfolio.Category;
 import org.olat.modules.portfolio.Page;
 import org.olat.modules.portfolio.PortfolioService;
 import org.olat.modules.portfolio.ui.event.PublishEvent;
+import org.olat.user.UserManager;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -48,6 +53,8 @@ public class PageMetadataController extends BasicController {
 	
 	private Link publishButton;
 	
+	@Autowired
+	private UserManager userManager;
 	@Autowired
 	private PortfolioService portfolioService;
 	
@@ -59,6 +66,18 @@ public class PageMetadataController extends BasicController {
 			publishButton = LinkFactory.createButton("publish", mainVC, this);
 		}
 		
+		Set<Identity> owners = new HashSet<>();
+		if(page.getSection() != null && page.getSection().getBinder() != null) {
+			owners.addAll(portfolioService.getMembers(page.getSection().getBinder(), GroupRoles.owner.name()));
+		}
+		owners.addAll(portfolioService.getMembers(page, GroupRoles.owner.name()));
+		
+		StringBuilder ownerSb = new StringBuilder();
+		for(Identity owner:owners) {
+			if(ownerSb.length() > 0) ownerSb.append(", ");
+			ownerSb.append(userManager.getUserDisplayName(owner));
+		}
+		mainVC.contextPut("owners", ownerSb.toString());
 		mainVC.contextPut("pageTitle", page.getTitle());
 		mainVC.contextPut("pageSummary", page.getSummary());
 		mainVC.contextPut("status", page.getPageStatus());
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_content/page_meta.html b/src/main/java/org/olat/modules/portfolio/ui/_content/page_meta.html
index 1d5fc4f3b2d..64e31104928 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_content/page_meta.html
+++ b/src/main/java/org/olat/modules/portfolio/ui/_content/page_meta.html
@@ -1,5 +1,5 @@
 <div class="o_header_with_buttons">
-	<h1>$r.escapeHtml($pageTitle)</h1>
+	<h2>$r.escapeHtml($pageTitle) <small>$r.translate("binder.by",$owners)</small></h2>
 	#if($r.available("publish"))
 	<div class="o_button_group o_button_group_right">
 		$r.render("publish")
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
index 945f0711030..43e76ad86c8 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
@@ -23,7 +23,14 @@ artefact.descr=Beschreibung
 artefact.file=Datei
 artefact.source=Quelle
 artefact.title=Titel
-artefatct.text=Text
+artefact.text=Text
+artefact.image=Bild
+artefact.bc=Datei
+artefact.Forum=Forum Eintrag
+artefact.FileResource.BLOG=Blog Eintrag
+artefact.FileResource.WIKI=Wikiseite
+artefact.EfficiencyStatement=Leistungnachweis
+filter.show.all=Alle anzeigen
 author=Autor
 begin.date=Beginn
 binder.by=von {0}
@@ -148,6 +155,7 @@ table.grading.passed.points=<span class\="o_state o_passed"><i class\="o_icon o_
 table.grading.points={0} Punkt(e)
 table.header.change.status=Status
 table.header.course=Kurs
+table.header.collection.date=Sammelt am
 table.header.date=Datum
 table.header.grading=Bewertung
 table.header.key=ID
@@ -157,6 +165,7 @@ table.header.open=Start
 table.header.passed=Bestanden
 table.header.score=Punkte
 table.header.section=Bereich
+table.header.type=Typ
 table.header.title=Titel
 table.of.contents=Inhaltsverzeichnis
 table.user.login=Benutzername
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
index 9100d32c5fe..87c4d244090 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
@@ -24,7 +24,13 @@ artefact.file=File
 artefact.source=Source
 artefact.text=Text
 artefact.title=Title
-artefatct.text=Text
+artefact.image=Image
+artefact.bc=File
+artefact.Forum=Forum post
+artefact.FileResource.BLOG=Blog entry
+artefact.FileResource.WIKI=Wiki page
+artefact.EfficiencyStatement=Efficiency statement
+filter.show.all=Show all
 author=Author
 begin.date=Begin
 binder.by=by {0}
@@ -149,6 +155,7 @@ table.grading.passed.points=<span class\="o_state o_passed"><i class\="o_icon o_
 table.grading.points={0} Point(s)
 table.header.change.status=Status
 table.header.course=Course
+table.header.collection.date=Collected at
 table.header.date=Date
 table.header.grading=Grading
 table.header.key=ID
@@ -158,6 +165,7 @@ table.header.open=Start
 table.header.passed=Passed
 table.header.score=Points
 table.header.section=Section
+table.header.type=Type
 table.header.title=Title
 table.of.contents=Table of contents {0}
 table.user.login=User name
diff --git a/src/main/java/org/olat/modules/portfolio/ui/editor/PageEditorController.java b/src/main/java/org/olat/modules/portfolio/ui/editor/PageEditorController.java
index 6f392393f2b..1484439ddf4 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/editor/PageEditorController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/editor/PageEditorController.java
@@ -33,6 +33,8 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings.CalloutOrientation;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController;
 import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
 import org.olat.core.util.Util;
@@ -251,10 +253,15 @@ public class PageEditorController extends BasicController {
 		addElementsCtrl = new AddElementsController(ureq, getWindowControl(), provider, referenceFragment, target);
 		listenTo(addElementsCtrl);
 		
-		// data-placement="top"
-
+		CalloutSettings calloutSettings;
+		if(target == PageElementTarget.above) {
+			calloutSettings = new CalloutSettings(true, CalloutOrientation.top);
+		} else {
+			calloutSettings = new CalloutSettings(false);
+		}
 		addCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(),
-				addElementsCtrl.getInitialComponent(), link.getDispatchID(), "", true, "");
+				addElementsCtrl.getInitialComponent(), link.getDispatchID(),
+				"", true, "", calloutSettings);
 		listenTo(addCalloutCtrl);
 		addCalloutCtrl.activate();
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/media/CollectImageMediaController.java b/src/main/java/org/olat/modules/portfolio/ui/media/CollectImageMediaController.java
index 20938486bb3..0abbaed0222 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/media/CollectImageMediaController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/media/CollectImageMediaController.java
@@ -86,12 +86,12 @@ public class CollectImageMediaController extends FormBasicController implements
 		return mediaReference;
 	}
 
-	@Autowired
+	@Override
 	public AddElementInfos getUserObject() {
 		return userObject;
 	}
 
-	@Autowired
+	@Override
 	public void setUserObject(AddElementInfos userObject) {
 		this.userObject = userObject;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/media/CollectTextMediaController.java b/src/main/java/org/olat/modules/portfolio/ui/media/CollectTextMediaController.java
index 1ef98aa8629..92def2ca975 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/media/CollectTextMediaController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/media/CollectTextMediaController.java
@@ -82,12 +82,12 @@ public class CollectTextMediaController extends FormBasicController implements P
 		return mediaReference;
 	}
 	
-	@Autowired
+	@Override
 	public AddElementInfos getUserObject() {
 		return userObject;
 	}
 
-	@Autowired
+	@Override
 	public void setUserObject(AddElementInfos userObject) {
 		this.userObject = userObject;
 	}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/renderer/MediaTypeCellRenderer.java b/src/main/java/org/olat/modules/portfolio/ui/renderer/MediaTypeCellRenderer.java
new file mode 100644
index 00000000000..d42772ec94f
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/renderer/MediaTypeCellRenderer.java
@@ -0,0 +1,58 @@
+/**
+ * <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.portfolio.ui.renderer;
+
+import java.util.Map;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+import org.olat.modules.portfolio.MediaHandler;
+import org.olat.modules.portfolio.model.MediaRow;
+
+/**
+ * 
+ * Initial date: 06.07.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MediaTypeCellRenderer implements FlexiCellRenderer {
+	
+	private Map<String,MediaHandler> handlersMap;
+	
+	public MediaTypeCellRenderer(Map<String,MediaHandler> handlersMap) {
+		this.handlersMap = handlersMap;
+	}
+
+	@Override
+	public void render(Renderer renderer, StringOutput target, Object cellValue,
+			int row, FlexiTableComponent source, URLBuilder ubu, Translator translator) {
+		if(cellValue instanceof MediaRow) {
+			MediaRow mRow = (MediaRow)cellValue;
+			MediaHandler handler = handlersMap.get(mRow.getType());
+			if(handler != null) {
+				target.append("<i class='o_icon o_icon-lg ").append(handler.getIconCssClass(mRow)).append("'> </i>");
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java b/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
index 58bed1e4197..4330b5271e6 100755
--- a/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
+++ b/src/main/java/org/olat/portfolio/ui/EPArtefactPoolRunController.java
@@ -72,6 +72,7 @@ public class EPArtefactPoolRunController extends BasicController implements Acti
 	private EPFilterSettings filterSettings = new EPFilterSettings();
 	private EPAddArtefactController addArtefactCtrl;
 	private boolean artefactChooseMode;
+	private boolean canAddArtefacts;
 	private SegmentViewComponent segmentView;
 	private Link artefactsLink, browseLink, searchLink;
 	private Controller filterSelectCtrl;
@@ -89,11 +90,12 @@ public class EPArtefactPoolRunController extends BasicController implements Acti
 	private PortfolioStructure preSelectedStruct;
 
 	public EPArtefactPoolRunController(UserRequest ureq, WindowControl wControl) {
-		this(ureq, wControl, false);
+		this(ureq, wControl, false, true);
 	}
 	
-	public EPArtefactPoolRunController(UserRequest ureq, WindowControl wControl, boolean artefactChooseMode) {
+	public EPArtefactPoolRunController(UserRequest ureq, WindowControl wControl, boolean artefactChooseMode, boolean canAddArtefacts) {
 		super(ureq, wControl);
+		this.canAddArtefacts = canAddArtefacts;
 		this.artefactChooseMode = artefactChooseMode;
 		Component viewComp = new Panel("empty");
 		Component filterPanel = new Panel("filter");
@@ -136,9 +138,11 @@ public class EPArtefactPoolRunController extends BasicController implements Acti
 		searchLink = LinkFactory.createLink("viewTab.search", vC, this);
 		segmentView.addSegment(searchLink, false);
 		
-		addArtefactCtrl = new EPAddArtefactController(ureq, getWindowControl());
-		listenTo(addArtefactCtrl);
-		vC.put("addArtefactCtrl", addArtefactCtrl.getInitialComponent());
+		if(canAddArtefacts) {
+			addArtefactCtrl = new EPAddArtefactController(ureq, getWindowControl());
+			listenTo(addArtefactCtrl);
+			vC.put("addArtefactCtrl", addArtefactCtrl.getInitialComponent());
+		}
 	}
 	
 	/**
diff --git a/src/main/java/org/olat/portfolio/ui/_content/artefactsmain.html b/src/main/java/org/olat/portfolio/ui/_content/artefactsmain.html
index c6bc767c206..f67543d74f5 100644
--- a/src/main/java/org/olat/portfolio/ui/_content/artefactsmain.html
+++ b/src/main/java/org/olat/portfolio/ui/_content/artefactsmain.html
@@ -1,11 +1,14 @@
 <div class="o_eportfolio o_artefacts">
-	#if (!$artefactChooseMode)
+
+	#if(!$artefactChooseMode && $r.available("addArtefactCtrl"))
 		<div class="o_ep_add_artefact pull-right">
 			$r.render("addArtefactCtrl")
 		</div><br /><br />
 	#else
 		$r.translate("choose.artefact.intro")
-		<p>$r.render("addArtefactCtrl")</p>
+		#if($r.available("addArtefactCtrl"))
+			<p>$r.render("addArtefactCtrl")</p>
+		#end
 	#end
 	
 	<div class="o_ep_content">
diff --git a/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java b/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
index 3ef80b8ebd5..e51c91fa5de 100644
--- a/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
+++ b/src/main/java/org/olat/portfolio/ui/artefacts/view/EPArtefactViewController.java
@@ -204,8 +204,8 @@ public class EPArtefactViewController extends FormBasicController {
 			List<FormLink> selectMapNames = new ArrayList<FormLink>(linkedMaps.size());
 			int count = 0;
 			for (PortfolioStructure ePMap : linkedMaps) {
-				String title = StringHelper.escapeHtml(ePMap.getTitle());
-				FormLink selectMap = uifactory.addFormLink("map-" + count++, "map", title, null, formLayout, Link.NONTRANSLATED);
+				String mapTitle = StringHelper.escapeHtml(ePMap.getTitle());
+				FormLink selectMap = uifactory.addFormLink("map-" + count++, "map", mapTitle, null, formLayout, Link.NONTRANSLATED);
 				selectMap.setUserObject(ePMap.getOlatResource());
 				selectMap.setEnabled(!viewOnlyMode && !artefactChooseMode);
 				selectMapNames.add(selectMap);
diff --git a/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java b/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
index 882cbe9bd65..a6a4a4f4707 100644
--- a/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
+++ b/src/main/java/org/olat/portfolio/ui/structel/EPAddElementsController.java
@@ -196,7 +196,7 @@ public class EPAddElementsController extends BasicController {
 
 	private void popUpAddArtefactBox(UserRequest ureq) {
 		if (artefactPoolCtrl == null) {
-			artefactPoolCtrl = new EPArtefactPoolRunController(ureq, getWindowControl(), true);
+			artefactPoolCtrl = new EPArtefactPoolRunController(ureq, getWindowControl(), true, true);
 			listenTo(artefactPoolCtrl);
 		}
 		artefactBox = new CloseableModalController(getWindowControl(),"close",artefactPoolCtrl.getInitialComponent(),true, translate("choose.artefact.title"));
diff --git a/src/main/java/org/olat/restapi/support/ErrorWindowControl.java b/src/main/java/org/olat/restapi/support/ErrorWindowControl.java
index 9a35746f1ab..2b33897877a 100644
--- a/src/main/java/org/olat/restapi/support/ErrorWindowControl.java
+++ b/src/main/java/org/olat/restapi/support/ErrorWindowControl.java
@@ -22,6 +22,7 @@ package org.olat.restapi.support;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.WindowBackOffice;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.info.WindowControlInfo;
 import org.olat.core.id.context.BusinessControl;
 
@@ -50,7 +51,7 @@ public class ErrorWindowControl implements WindowControl {
 	public void pushAsModalDialog(Component comp) {/* */}
 	
 	@Override
-	public void pushAsCallout(Component comp, String targetId) {/* */}
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings) {/* */}
 
 	@Override
 	public void pop() {/* */}
diff --git a/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java
index 0272c99fd09..0bc285744fa 100644
--- a/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java
+++ b/src/test/java/org/olat/course/nodes/en/EnrollmentManagerConcurrentTest.java
@@ -47,6 +47,7 @@ import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.control.WindowBackOffice;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CalloutSettings;
 import org.olat.core.gui.control.generic.dtabs.DTabs;
 import org.olat.core.gui.control.info.WindowControlInfo;
 import org.olat.core.gui.translator.Translator;
@@ -326,7 +327,7 @@ public class EnrollmentManagerConcurrentTest extends OlatTestCase implements Win
 	public void pushToMainArea(Component comp){}
 	public void pushAsModalDialog(Component comp){}
 	@Override
-	public void pushAsCallout(Component comp, String targetId){}
+	public void pushAsCallout(Component comp, String targetId, CalloutSettings settings){}
 	public void pop(){}
 	public void setInfo(String string){}
 	public void setError(String string){}
-- 
GitLab