/**
* <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.controllers.filechooser;
import java.util.regex.Pattern;
import org.olat.core.commons.modules.bc.FileUploadController;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.FormUIFactory;
import org.olat.core.gui.components.form.flexible.elements.Submit;
import org.olat.core.gui.components.form.flexible.elements.TextElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
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.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
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.VFSManager;
/**
* Description:
* <p>
* This controller shows a form where the user can edit the filename of a new
* file and the optional sub-path under which the file is created.
* <p />
* Fires Event.DONE_EVENT when finished and Event.CANCELLED_EVENT otherwise.
* When done, the created file can be retrieved with the getCreatedFile()
* method.
* <p />
*
* Initial date: 15.01.2015<br>
*
* @author dfurrer, dirk.furrer@frentix.com, http://www.frentix.com
* @author gnaegi, gnaegi@frentix.com, http://www.frentix.com
*
*/
public class FileCreatorController extends FormBasicController {
private VFSContainer baseContainer;
private String subfolderPath;
private VFSLeaf createdFile;
private TextElement fileNameElement;
private TextElement targetSubPath;
private static final Pattern validSubPathPattern = Pattern.compile("[\\p{Alnum}-_\\./]*");
/**
* Create a file creator instance
*
* @param ureq
* User request
* @param wControl
* Window control
* @param rootContainer
* The root container in which the file can be created
* @param subfolderPath
* A subfolder path that should be prefilled, can be NULL
*/
public FileCreatorController(UserRequest ureq, WindowControl wControl, VFSContainer rootContainer, String subfolderPath) {
super(ureq, wControl, Util.createPackageTranslator(FileUploadController.class, ureq.getLocale()));
this.baseContainer = rootContainer;
this.subfolderPath = subfolderPath;
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
setFormTitle("filecreator.text.newfile");
// Global target directory
String path = "/ " + baseContainer.getName();
VFSContainer container = baseContainer.getParentContainer();
while (container != null) {
path = "/ " + container.getName() + " " + path;
container = container.getParentContainer();
}
uifactory.addStaticTextElement("ul.target", path,formLayout);
// Sub path, can be modified
targetSubPath = uifactory.addInlineTextElement("ul.target.child", subfolderPath, formLayout, this);
targetSubPath.setLabel("ul.target.child", null);
// The file name of the new file
fileNameElement = FormUIFactory.getInstance().addTextElement("fileName", "filecreator.filename", 50, "", formLayout);
fileNameElement.setPlaceholderKey("filecreator.filename.placeholder", null);
fileNameElement.setMandatory(true);
// Add buttons
FormLayoutContainer buttons = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
formLayout.add(buttons);
Submit createFile = new FormSubmit("submit","button.create");
buttons.add(createFile);
uifactory.addFormCancelButton("cancel", buttons, ureq, getWindowControl());
}
@Override
protected boolean validateFormLogic(UserRequest ureq) {
boolean isFileNmaeValid = true;
boolean isSubDirValid = true;
// 1: Check sub path
String subPath = targetSubPath.getValue();
if (subPath != null) {
// Cleanup first
subPath = subPath.toLowerCase().trim();
if (!validSubPathPattern.matcher(subPath).matches()) {
targetSubPath.setErrorKey("subpath.error.characters", null);
isSubDirValid = false;
} else {
// Fix mess with slashes and dots
// reduce doubled slashes with single slash
subPath = subPath.replaceAll("\\.*\\/+\\.*", "\\/");
// do it a second time to catch the double slashes created by previous replacement
subPath = subPath.replaceAll("\\/+", "\\/");
// remove slash at end
if (subPath.endsWith("/")) {
subPath = subPath.substring(0, subPath.length()-1);
}
// single slash means no sub-directory
if (subPath.length() == 1 && subPath.startsWith("/")) {
subPath = "";
}
// fix missing slash at start
if (subPath.length() > 0 && !subPath.startsWith("/")) {
subPath = "/" + subPath;
}
// update in GUI so user sees how we optimized
targetSubPath.setValue(subPath);
}
// Now check if this path does not already exist
if (isSubDirValid && StringHelper.containsNonWhitespace(subPath)){
// Try to resolve given rel path from current container
VFSItem uploadDir = baseContainer.resolve(subPath);
if (uploadDir != null) {
// already exists. this is fine, as long as it is a directory and not a file
if (!(uploadDir instanceof VFSContainer)) {
// error
targetSubPath.setErrorKey("subpath.error.dir.is.file", new String[] {subPath});
isSubDirValid = false;
}
}
}
if (isSubDirValid) {
targetSubPath.clearError();
}
}
// 2: Check file name
String fileName = fileNameElement.getValue();
if(!StringHelper.containsNonWhitespace(fileName)) {
fileNameElement.setErrorKey("mf.error.filename.empty", new String[0]);
isFileNmaeValid = false;
} else {
fileName = fileName.toLowerCase().trim();
if (!FileUtils.validateFilename(fileName)) {
fileNameElement.setErrorKey("mf.error.filename.invalidchars", new String[0]);
isFileNmaeValid = false;
} else if (!fileName.endsWith(".html") && !fileName.endsWith(".htm")) {
fileName = fileName + ".html";
}
// update in GUI so user sees how we optimized
fileNameElement.setValue(fileName);
// check if it already exists
String filePath = fileName;
if (filePath.startsWith("/")) {
filePath = "/" + filePath;
}
if (StringHelper.containsNonWhitespace(targetSubPath.getValue())) {
filePath = targetSubPath.getValue() + filePath;
}
VFSItem vfsItem = baseContainer.resolve(filePath);
if (vfsItem != null) {
fileNameElement.setErrorKey("mf.error.filename.exists", new String[] {filePath});
isFileNmaeValid = false;
}
}
if (isFileNmaeValid) {
fileNameElement.clearError();
}
return isFileNmaeValid && isSubDirValid;
}
@Override
protected void formOK(UserRequest ureq) {
// 1: Get parent container for new file
String uploadRelPath = targetSubPath.getValue();
VFSContainer parentContainer = VFSManager.resolveOrCreateContainerFromPath(baseContainer, uploadRelPath);
if (parentContainer == null) {
logError("Can not create target sub path::" + uploadRelPath + ", fall back to base container", null);
parentContainer = baseContainer;
}
// 2: Create empty file in parent
String fileName = fileNameElement.getValue();
VFSItem resolvedFile = parentContainer.resolve(fileName);
if (resolvedFile == null) {
createdFile = parentContainer.createChildLeaf(fileName);
} else {
createdFile = (VFSLeaf) resolvedFile;
}
// Let others know that we have a great new (empty) file created ready
// for anything you want to do with it
fireEvent(ureq, Event.DONE_EVENT);
}
@Override
protected void formCancelled(UserRequest ureq) {
fireEvent(ureq, Event.CANCELLED_EVENT);
super.formCancelled(ureq);
}
@Override
protected void formResetted(UserRequest ureq) {
fileNameElement.reset();
targetSubPath.reset();
}
/**
* Get the created file or NULL if no file has been created
* @return
*/
public VFSLeaf getCreatedFile(){
return this.createdFile;
}
@Override
protected void doDispose() {
//nothing to dispose
}
}