/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <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> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.commons.controllers.linkchooser; import java.util.HashSet; import java.util.Set; import org.olat.core.commons.controllers.filechooser.FileChoosenEvent; import org.olat.core.commons.controllers.filechooser.FileChooserUIFactory; import org.olat.core.commons.modules.bc.FileUploadController; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.commons.modules.bc.FolderEvent; import org.olat.core.commons.modules.bc.commands.FolderCommandStatus; import org.olat.core.commons.services.image.Size; import org.olat.core.commons.services.video.MovieService; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; 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.controller.BasicController; import org.olat.core.util.FileUtils; import org.olat.core.util.StringHelper; import org.olat.core.util.WebappHelper; import org.olat.core.util.vfs.Quota; import org.olat.core.util.vfs.VFSConstants; 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.VFSItemFileTypeFilter; import org.olat.core.util.vfs.filters.VFSItemFilter; import org.springframework.beans.factory.annotation.Autowired; /** * enclosing_type Description: <br> * this controller is used to generate a component containing the provided * menutree, the tool, and the content. its main use is to standardize the look * and feel of workflows that contain both a menu and a tool * <p> * Events fired by this controller: * <ul> * <li>Event.CANCELLED_EVENT * <li>URLChoosenEvent(URL) containing the selected file URL * </ul> * @author Felix Jost */ public class FileLinkChooserController extends BasicController { private VelocityContainer mainVC; private FileUploadController uploadCtr; private org.olat.core.commons.controllers.filechooser.FileChooserController fileChooserController; private final String fileName; private String[] suffixes; private final String absolutePath; private VFSContainer rootDir; @Autowired private MovieService movieService; /** * @param ureq * @param wControl * @param rootDir The VFS root directory from which the linkable files should be read * @param uploadRelPath The relative path within the rootDir where uploaded * files should be put into. If NULL, the root Dir is used * @param absolutePath * @param suffixes Array of allowed file types * @param uriValidation Set to true if the filename need to be a valid URI * @param fileName the path of the file currently edited (in order to compute * the correct relative paths for links), e.g. bla/blu.html or * index.html */ public FileLinkChooserController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, String uploadRelPath, String absolutePath, String[] suffixes, boolean uriValidation, String fileName) { super(ureq, wControl); this.fileName = fileName; this.suffixes = suffixes; this.rootDir = rootDir; this.absolutePath = absolutePath; this.mainVC = createVelocityContainer("filechooser"); // file uploads are relative to the currently edited file String[] dirs = (this.fileName == null) ? new String[0] : this.fileName.split("/"); VFSContainer fileUploadBase = rootDir; for (String subPath : dirs) { // try to resolve the given file path in the root container VFSItem subFolder = fileUploadBase.resolve(subPath); if (subFolder != null) { if (subFolder instanceof VFSContainer) { // a higher level found, use this one unless a better one is found fileUploadBase = (VFSContainer) subFolder; } else { // it is not a container - leaf reached break; } } else { // resolving was not possible??? stop here break; } } // create directory filter combined with suffix filter String[] dirFilters = { "_courseelementdata" }; VFSItemFilter customFilter = null; VFSItemFilter dirFilter = new VFSItemExcludePrefixFilter(dirFilters); if (suffixes != null) { VFSItemFileTypeFilter typeFilter = new VFSItemFileTypeFilter(suffixes); typeFilter.setCompositeFilter(dirFilter); customFilter = typeFilter; } else { customFilter = dirFilter; } // hide file chooser title, we have our own title fileChooserController = FileChooserUIFactory .createFileChooserControllerWithoutTitle(ureq, getWindowControl(), rootDir, customFilter, true); listenTo(fileChooserController); mainVC.put("stTree", fileChooserController.getInitialComponent()); // convert file endings to mime types as needed by file upload controller Set<String> mimeTypes = null; if (suffixes != null) { mimeTypes = new HashSet<>(); for (String suffix : suffixes) { String mimeType = WebappHelper.getMimeType("dummy." + suffix); if (mimeType != null) { if (!mimeTypes.contains(mimeType)) mimeTypes.add(mimeType); } } } if(fileUploadBase.canWrite() == VFSConstants.YES) { long remainingSpace = Quota.UNLIMITED; long uploadLimit = FolderConfig.getLimitULKB(); if( fileUploadBase.getLocalSecurityCallback() != null && fileUploadBase.getLocalSecurityCallback().getQuota() != null) { Long space = fileUploadBase.getLocalSecurityCallback().getQuota().getRemainingSpace(); if(space != null) { remainingSpace = space.longValue(); } Long limit = fileUploadBase.getLocalSecurityCallback().getQuota().getUlLimitKB(); if(limit != null) { uploadLimit = limit.longValue(); } } uploadCtr = new FileUploadController(wControl, fileUploadBase, ureq, uploadLimit, remainingSpace, mimeTypes, uriValidation, true, false, true, true, false); listenTo(uploadCtr); // set specific upload path uploadCtr.setUploadRelPath(uploadRelPath); mainVC.put("uploader", uploadCtr.getInitialComponent()); } putInitialPanel(mainVC); } @Override public void event(UserRequest ureq, Component source, Event event) { // no events to catch } @Override public void event(UserRequest ureq, Controller source, Event event) { if (source == uploadCtr) { if (event instanceof FolderEvent) { FolderEvent folderEvent = (FolderEvent) event; if (isFileSuffixOk(folderEvent.getFilename())) { Size size = null; VFSItem item = folderEvent.getItem(); String relPath; if(item != null) { size = getSize(item, item.getName()); relPath = FileChooserUIFactory .getSelectedRelativeItemPath(folderEvent, rootDir, fileName); } else { relPath = folderEvent.getFilename(); } if(StringHelper.containsNonWhitespace(absolutePath)) { relPath = absolutePath + relPath; } if(size != null) { fireEvent(ureq, new URLChoosenEvent(relPath, null, null, null, size.getWidth(), size.getHeight())); } else { fireEvent(ureq, new URLChoosenEvent(relPath)); } } else { setErrorMessage(folderEvent.getFilename()); } } if (event == Event.DONE_EVENT) { if (uploadCtr.getStatus() == FolderCommandStatus.STATUS_CANCELED) { fireEvent(ureq, Event.CANCELLED_EVENT); } } else if (event == Event.CANCELLED_EVENT) { fireEvent(ureq, Event.CANCELLED_EVENT); } else if (event == Event.FAILED_EVENT) { fireEvent(ureq, Event.CANCELLED_EVENT); } } else if (source == fileChooserController) { if (event instanceof FileChoosenEvent) { FileChoosenEvent fileEvent = (FileChoosenEvent)event; VFSItem item = fileEvent.getSelectedItem(); Size size = getSize(item, item.getName()); String relPath = FileChooserUIFactory .getSelectedRelativeItemPath(fileEvent, rootDir, fileName); // notify parent controller if(StringHelper.containsNonWhitespace(absolutePath)) { relPath = absolutePath + relPath; } if(size != null) { fireEvent(ureq, new URLChoosenEvent(relPath, null, null, null, size.getWidth(), size.getHeight())); } else { fireEvent(ureq, new URLChoosenEvent(relPath)); } } else if (event == Event.CANCELLED_EVENT) { fireEvent(ureq, Event.CANCELLED_EVENT); } else if (event == Event.FAILED_EVENT) { fireEvent(ureq, Event.CANCELLED_EVENT); } } } private Size getSize(VFSItem item, String filename) { Size size = null; if(item instanceof VFSLeaf) { VFSLeaf leaf = (VFSLeaf)item; String suffix = FileUtils.getFileSuffix(filename); size = movieService.getSize(leaf, suffix); } return size; } private boolean isFileSuffixOk(String filename) { if (suffixes == null) { // no defined suffixes => all allowed return true; } else { // check if suffix one of allowed suffixes String suffix = getSuffix(filename); for (String allowedSuffix : suffixes) { if (allowedSuffix.equals(suffix)) { return true; } } } return false; } private void setErrorMessage(String fileName) { StringBuilder allowedSuffixes = new StringBuilder(); for (String allowedSuffix : suffixes) { allowedSuffixes.append(" ."); allowedSuffixes.append(allowedSuffix); } String suffix = getSuffix(fileName); getWindowControl().setError(getTranslator() .translate("upload.error.incorrect.filetype", new String[] { "." + suffix,allowedSuffixes.toString() })); } private String getSuffix(String filename) { return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase(); } /** * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ @Override protected void doDispose() { // controllers autodisposed by basic controller } }