From b5e39f9aa45a989a8f4de5dabb9530633c41c2e6 Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Fri, 5 Apr 2019 15:40:57 +0200
Subject: [PATCH] OO-3934: briefcase peekview use metadata only if migrated

Refactor the peek view of the briefcase course element to use the
metadata but don't force migration to prevent overloading large
instances
---
 .../services/vfs/VFSRepositoryService.java    |   8 ++
 .../vfs/manager/VFSRepositoryServiceImpl.java |   5 +
 .../ui/media/VFSMetadataMediaResource.java    |  56 ++++++++
 .../ui/media/VFSRevisionMediaResource.java    |   6 -
 .../download/DownloadComponent.java           |  18 +++
 .../olat/core/util/vfs/VFSMediaResource.java  |  27 ++--
 .../course/nodes/bc/BCPeekviewController.java | 130 ++++++++++--------
 .../course/nodes/bc/_content/peekview.html    |   8 +-
 8 files changed, 180 insertions(+), 78 deletions(-)
 create mode 100644 src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSMetadataMediaResource.java

diff --git a/src/main/java/org/olat/core/commons/services/vfs/VFSRepositoryService.java b/src/main/java/org/olat/core/commons/services/vfs/VFSRepositoryService.java
index 4e626e6ab5c..1a5248e61c5 100644
--- a/src/main/java/org/olat/core/commons/services/vfs/VFSRepositoryService.java
+++ b/src/main/java/org/olat/core/commons/services/vfs/VFSRepositoryService.java
@@ -71,6 +71,14 @@ public interface VFSRepositoryService {
 	
 	public List<VFSMetadata> getMostDownloaded(VFSMetadata ancestorMetadata, int maxResults);
 	
+	/**
+	 * 
+	 * @param ancestorMetadata
+	 * @param maxResults
+	 * @return
+	 */
+	public List<VFSMetadata> getNewest(VFSMetadata ancestorMetadata, int maxResults);
+	
 	public void itemSaved(VFSLeaf leaf);
 	
 	public VFSMetadata updateMetadata(VFSMetadata data);
diff --git a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java
index 6c2ad14c641..289683fc957 100644
--- a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java
+++ b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java
@@ -288,6 +288,11 @@ public class VFSRepositoryServiceImpl implements VFSRepositoryService, GenericEv
 		return metadataDao.getMetadatas(parentMetadata);
 	}
 
+	@Override
+	public List<VFSMetadata> getNewest(VFSMetadata ancestorMetadata, int maxResults) {
+		return metadataDao.getNewest(ancestorMetadata.getRelativePath(), maxResults);
+	}
+
 	@Override
 	public List<VFSMetadata> getMostDownloaded(VFSMetadata ancestorMetadata, int maxResults) {
 		return metadataDao.getMostDownloaded(ancestorMetadata.getRelativePath(), maxResults);
diff --git a/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSMetadataMediaResource.java b/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSMetadataMediaResource.java
new file mode 100644
index 00000000000..219aa9530fc
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSMetadataMediaResource.java
@@ -0,0 +1,56 @@
+/**
+ * <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.commons.services.vfs.ui.media;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.services.vfs.VFSMetadata;
+import org.olat.core.commons.services.vfs.VFSRepositoryService;
+import org.olat.core.util.vfs.VFSItem;
+import org.olat.core.util.vfs.VFSLeaf;
+import org.olat.core.util.vfs.VFSMediaResource;
+
+/**
+ * 
+ * Initial date: 5 avr. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class VFSMetadataMediaResource extends VFSMediaResource {
+
+	private final VFSMetadata metadata;
+
+	public VFSMetadataMediaResource(VFSMetadata metadata) {
+		super(null);
+		this.metadata = metadata;
+	}
+
+	@Override
+	public VFSLeaf getLeaf() {
+		VFSLeaf leaf = super.getLeaf();
+		if(leaf == null) {
+			VFSItem revFile = CoreSpringFactory.getImpl(VFSRepositoryService.class).getItemFor(metadata);
+			if(revFile instanceof VFSLeaf) {
+				leaf = (VFSLeaf)revFile;
+				setLeaf(leaf);
+			}
+		}
+		return leaf;
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSRevisionMediaResource.java b/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSRevisionMediaResource.java
index 37d507113f3..ba110d5c055 100644
--- a/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSRevisionMediaResource.java
+++ b/src/main/java/org/olat/core/commons/services/vfs/ui/media/VFSRevisionMediaResource.java
@@ -50,7 +50,6 @@ public class VFSRevisionMediaResource implements MediaResource {
 	private static final String MIME_TYPE_OCTET_STREAM = "application/octet-stream";
 	private static final OLog log = Tracing.createLoggerFor(VFSRevisionMediaResource.class);
 
-	private String encoding;
 	private final VFSRevision revision;
 	private final VFSMetadata metadata;
 
@@ -74,11 +73,6 @@ public class VFSRevisionMediaResource implements MediaResource {
 		String mimeType = WebappHelper.getMimeType(metadata.getFilename());
 		if (mimeType == null) {
 			mimeType = MIME_TYPE_OCTET_STREAM;
-		} else {
-			// if any encoding is set, append it for the browser
-			if (encoding != null) {
-				mimeType = mimeType + ";charset=" + encoding;
-			}
 		}
 		return mimeType;
 	}
diff --git a/src/main/java/org/olat/core/gui/components/download/DownloadComponent.java b/src/main/java/org/olat/core/gui/components/download/DownloadComponent.java
index 94dd7ff2968..0f07ff3f92a 100644
--- a/src/main/java/org/olat/core/gui/components/download/DownloadComponent.java
+++ b/src/main/java/org/olat/core/gui/components/download/DownloadComponent.java
@@ -97,6 +97,24 @@ public class DownloadComponent extends AbstractComponent {
 		this.setDomReplacementWrapperRequired(false);
 	}
 	
+	/**
+	 * @param name The component name
+	 * @param downloadItem The resource to download
+	 * @param linkText An optional link text
+	 * @param linkToolTip An optional tool tip
+	 * @param linkCssIconClass An optional icon class
+	 */
+	public DownloadComponent(String name, MediaResource downloadItem, String linkText,
+			String linkToolTip, String linkCssIconClass) {
+		super(name);
+		setMediaResource(downloadItem);
+		setLinkText(linkText);
+		setLinkToolTip(linkToolTip);
+		setLinkCssIconClass(linkCssIconClass);
+		// renderer puts dispatch ID in a tag
+		this.setDomReplacementWrapperRequired(false);
+	}
+	
 	public DownloadLink getFormItem() {
 		return delegate;
 	}
diff --git a/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java b/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
index 57b49b50dfd..23c533ba75d 100644
--- a/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
+++ b/src/main/java/org/olat/core/util/vfs/VFSMediaResource.java
@@ -58,12 +58,20 @@ public class VFSMediaResource implements MediaResource {
 	public boolean acceptRanges() {
 		return true;
 	}
+	
+	public VFSLeaf getLeaf() {
+		return vfsLeaf;
+	}
+	
+	public void setLeaf(VFSLeaf vfsLeaf) {
+		this.vfsLeaf = vfsLeaf;
+	}
 
 	@Override
 	public String getContentType() {
 		String mimeType;
 		if(downloadable) {
-			mimeType = WebappHelper.getMimeType(vfsLeaf.getName());
+			mimeType = WebappHelper.getMimeType(getLeaf().getName());
 			//html, xhtml and javascript are set to force download
 			if (mimeType == null || "text/html".equals(mimeType)
 					|| "application/xhtml+xml".equals(mimeType)
@@ -75,7 +83,7 @@ public class VFSMediaResource implements MediaResource {
 				mimeType = mimeType + ";charset=" + encoding;
 			}
 		} else {
-			mimeType = WebappHelper.getMimeType(vfsLeaf.getName());
+			mimeType = WebappHelper.getMimeType(getLeaf().getName());
 			if (mimeType == null) {
 				mimeType = MIME_TYPE_OCTET_STREAM;
 				unknownMimeType = true;
@@ -92,27 +100,24 @@ public class VFSMediaResource implements MediaResource {
 
 	@Override
 	public Long getSize() {
-		long size = vfsLeaf.getSize();
-		return (size == VFSConstants.UNDEFINED) ? null : new Long(size);
+		long size = getLeaf().getSize();
+		return (size == VFSConstants.UNDEFINED) ? null : Long.valueOf(size);
 	}
 
 	@Override
 	public InputStream getInputStream() {
-		return vfsLeaf.getInputStream();
+		return getLeaf().getInputStream();
 	}
 
 	@Override
 	public Long getLastModified() {
-		long lastModified = vfsLeaf.getLastModified();
-		return (lastModified == VFSConstants.UNDEFINED) ? null : new Long(lastModified);
+		long lastModified = getLeaf().getLastModified();
+		return (lastModified == VFSConstants.UNDEFINED) ? null : Long.valueOf(lastModified);
 	}
 
-	/**
-	 * @see org.olat.core.gui.media.MediaResource#prepare(javax.servlet.http.HttpServletResponse)
-	 */
 	@Override
 	public void prepare(HttpServletResponse hres) {
-		String filename = StringHelper.urlEncodeUTF8(vfsLeaf.getName());
+		String filename = StringHelper.urlEncodeUTF8(getLeaf().getName());
 		if (unknownMimeType || downloadable) {
 			hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + filename);
 		} else {
diff --git a/src/main/java/org/olat/course/nodes/bc/BCPeekviewController.java b/src/main/java/org/olat/course/nodes/bc/BCPeekviewController.java
index 4ebf880e84e..9bf9ea2bfa4 100644
--- a/src/main/java/org/olat/course/nodes/bc/BCPeekviewController.java
+++ b/src/main/java/org/olat/course/nodes/bc/BCPeekviewController.java
@@ -24,9 +24,10 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.modules.bc.FolderModule;
-import org.olat.core.commons.modules.bc.components.FolderComponent;
+import org.olat.core.commons.services.vfs.VFSMetadata;
+import org.olat.core.commons.services.vfs.VFSRepositoryService;
+import org.olat.core.commons.services.vfs.ui.media.VFSMetadataMediaResource;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.download.DownloadComponent;
@@ -39,12 +40,11 @@ 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.util.CSSHelper;
-import org.olat.core.util.vfs.LocalFileImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSItem;
 import org.olat.core.util.vfs.VFSLeaf;
-import org.olat.core.util.vfs.filters.VFSItemExcludePrefixFilter;
-import org.olat.core.util.vfs.filters.VFSItemFilter;
+import org.olat.core.util.vfs.filters.VFSSystemItemFilter;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * <h3>Description:</h3> The folder peekview controller displays the configurable
@@ -60,16 +60,17 @@ import org.olat.core.util.vfs.filters.VFSItemFilter;
  * @author gnaegi, gnaegi@frentix.com, www.frentix.com
  */
 public class BCPeekviewController extends BasicController implements Controller {
-	// comparator to sort the messages list by creation date
-	private static final Comparator<VFSLeaf> dateSortingComparator = new Comparator<VFSLeaf>(){
-		public int compare(final VFSLeaf leaf1, final VFSLeaf leaf2) {
-			return Long.valueOf(leaf2.getLastModified()).compareTo(leaf1.getLastModified()); //last first
-		}};
-	// the current course node id
+	
+	private int count = 0;
 	private final String nodeId;
+	private final boolean forceDownload;
 	
-	private static final VFSItemFilter attachmentExcludeFilter = new VFSItemExcludePrefixFilter(FolderComponent.ATTACHMENT_EXCLUDE_PREFIXES);
+	private final VelocityContainer mainVC;
 
+	@Autowired
+	private FolderModule folderModule;
+	@Autowired
+	private VFSRepositoryService vfsRepositoryService;
 
 	/**
 	 * Constructor
@@ -82,46 +83,27 @@ public class BCPeekviewController extends BasicController implements Controller
 	public BCPeekviewController(UserRequest ureq, WindowControl wControl, VFSContainer rootFolder, String nodeId, int itemsToDisplay) {		
 		super(ureq, wControl);
 		this.nodeId = nodeId;
-	
-		VelocityContainer peekviewVC = createVelocityContainer("peekview");
-		// add items, only as many as configured
-		List<VFSLeaf> allLeafs = new ArrayList<VFSLeaf>();
-		addItems(rootFolder, allLeafs);
-		// Sort messages by last modified date
-		Collections.sort(allLeafs, dateSortingComparator);
-		boolean forceDownload = CoreSpringFactory.getImpl(FolderModule.class).isForceDownload();
+		mainVC = createVelocityContainer("peekview");
+		forceDownload = folderModule.isForceDownload();
 		
-		// only take the configured amount of messages
-		List<VFSLeaf> leafs = new ArrayList<VFSLeaf>();
-		for (int i = 0; i < allLeafs.size(); i++) {
-			if (leafs.size() == itemsToDisplay) {
-				break;
-			}
-			VFSLeaf leaf = allLeafs.get(i);
-			leafs.add(leaf);
-			// add link to item
-			// Add link to jump to course node
-			if (leaf instanceof LocalFileImpl) {
-				DownloadComponent dlComp = new DownloadComponent("nodeLinkDL_"+(i+1), leaf, forceDownload,
-						leaf.getName(), translate("preview.downloadfile"),
-						CSSHelper.createFiletypeIconCssClassFor(leaf.getName()));
-				dlComp.setElementCssClass("o_gotoNode");
-				peekviewVC.put("nodeLinkDL_"+(i+1),dlComp);
-			} else {
-				// hu? don't konw how to work with non-local impls
-			}
+		List<DownloadComponent> links = new ArrayList<>(itemsToDisplay);
+		
+		VFSMetadata metadata = vfsRepositoryService.getMetadataFor(rootFolder);
+		// don't force migration here to prevent overloading big OpenOLAT instances
+		if("migrated".equals(metadata.getMigrated())) {
+			fileMetadata(links, metadata, itemsToDisplay);
+		} else {
+			fileFallback(links, rootFolder, itemsToDisplay);
 		}
-		peekviewVC.contextPut("leafs", leafs);
+		mainVC.contextPut("links", links);
+		
 		// Add link to show all items (go to node)
-		Link allItemsLink = LinkFactory.createLink("peekview.allItemsLink", peekviewVC, this);
+		Link allItemsLink = LinkFactory.createLink("peekview.allItemsLink", mainVC, this);
 		allItemsLink.setIconRightCSS("o_icon o_icon_start");
 		allItemsLink.setElementCssClass("pull-right");
-		putInitialPanel(peekviewVC);
+		putInitialPanel(mainVC);
 	}
 
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
-	 */
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
 		if (source instanceof Link) {
@@ -135,13 +117,48 @@ public class BCPeekviewController extends BasicController implements Controller
 		}
 	}
 
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#doDispose()
-	 */
 	@Override
 	protected void doDispose() {
 		// nothing to dispose
 	}
+	
+	private void fileMetadata(List<DownloadComponent> links, VFSMetadata metadata, int itemsToDisplay) {
+		List<VFSMetadata> newestData = vfsRepositoryService.getNewest(metadata, itemsToDisplay);
+		for(VFSMetadata newData:newestData) {
+			String name = "nodeLinkDL_"+ (count++);
+			VFSMetadataMediaResource media = new VFSMetadataMediaResource(newData);
+			media.setDownloadable(forceDownload);
+			DownloadComponent dlComp = new DownloadComponent(name, media, newData.getFilename(),
+					translate("preview.downloadfile"), CSSHelper.createFiletypeIconCssClassFor(newData.getFilename()));
+			dlComp.setElementCssClass("o_gotoNode");
+			mainVC.put(name, dlComp);
+			links.add(dlComp);
+		}
+	}
+	
+	private void fileFallback(List<DownloadComponent> links, VFSContainer rootFolder, int itemsToDisplay) {
+		List<VFSLeaf> leafs = collectFiles(rootFolder, itemsToDisplay);
+		for(VFSLeaf leaf:leafs) {
+			String name = "nodeLinkDL_"+ (count++);
+			DownloadComponent dlComp = new DownloadComponent(name, leaf, forceDownload, leaf.getName(),
+					translate("preview.downloadfile"), CSSHelper.createFiletypeIconCssClassFor(leaf.getName()));
+			dlComp.setElementCssClass("o_gotoNode");
+			mainVC.put(name, dlComp);
+			links.add(dlComp);
+		}
+	}
+	
+	private List<VFSLeaf> collectFiles(VFSContainer rootFolder, int itemsToDisplay) {
+		// add items, only as many as configured
+		List<VFSLeaf> allLeafs = new ArrayList<>();
+		addItems(rootFolder, allLeafs);
+		// Sort messages by last modified date
+		Collections.sort(allLeafs, new LastModifiedcomparator());
+		
+		int size = Math.min(allLeafs.size(), itemsToDisplay);
+		List<VFSLeaf> lastLeafs = allLeafs.subList(0, size);
+		return new ArrayList<>(lastLeafs);
+	}
 
 	/**
 	 * Private helper method to get all files in a directory. Traverses the directory tree recursively
@@ -149,19 +166,20 @@ public class BCPeekviewController extends BasicController implements Controller
 	 * @param allLeafs
 	 */
 	private void addItems(VFSContainer container, List<VFSLeaf> allLeafs) {
-		// exclude files which are also excluded in FolderComponent
-		for (VFSItem vfsItem : container.getItems(attachmentExcludeFilter)) {
+		for (VFSItem vfsItem : container.getItems(new VFSSystemItemFilter())) {
 			if (vfsItem instanceof VFSLeaf) {
-				// add leaf to our list
-				VFSLeaf leaf = (VFSLeaf) vfsItem;
-				allLeafs.add(leaf);
+				allLeafs.add((VFSLeaf)vfsItem);
 			} else if (vfsItem instanceof VFSContainer) {
-				// do it recursively for all children
 				VFSContainer childContainer = (VFSContainer) vfsItem;
 				addItems(childContainer, allLeafs);
-			} else {
-				// hu?
 			}
 		}
 	}
+	
+	private static class LastModifiedcomparator implements Comparator<VFSLeaf> {
+		@Override
+		public int compare(final VFSLeaf leaf1, final VFSLeaf leaf2) {
+			return Long.compare(leaf2.getLastModified(), leaf1.getLastModified());
+		}
+	}
 }
diff --git a/src/main/java/org/olat/course/nodes/bc/_content/peekview.html b/src/main/java/org/olat/course/nodes/bc/_content/peekview.html
index 1728eb41385..314c512171c 100644
--- a/src/main/java/org/olat/course/nodes/bc/_content/peekview.html
+++ b/src/main/java/org/olat/course/nodes/bc/_content/peekview.html
@@ -1,8 +1,6 @@
 <div class="o_briefcase_peekview clearfix">
-#foreach( $leaf in $leafs ) 	
-	<div class="o_briefcase_peekview_file">
-	$r.render("nodeLinkDL_${foreach.count}")
-	</div>
+#foreach($link in $links) 	
+	<div class="o_briefcase_peekview_file">$r.render($link.componentName)</div>
 #end
-	$r.render("peekview.allItemsLink")
+$r.render("peekview.allItemsLink")
 </div>
\ No newline at end of file
-- 
GitLab