RepositoryController.java

/*
 * Copyright 2018 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.genesys.server.mvc.admin;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.NoSuchRepositoryFileException;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.filerepository.model.RepositoryFolder;
import org.genesys.filerepository.service.ImageGalleryService;
import org.genesys.filerepository.service.RepositoryService;
import org.genesys.server.mvc.BaseController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.util.UriUtils;

/**
 * The Class RepositoryController.
 */
@Controller("adminRepositoryController")
@RequestMapping(RepositoryController.CONTROLLER_PATH)
@PreAuthorize("hasRole('ADMINISTRATOR')")
public class RepositoryController extends BaseController {
	
	/** The Constant CONTROLLER_PATH. */
	public static final String CONTROLLER_PATH = "/admin/r";
	
	/** The Constant JSP_PATH. */
	public static final String JSP_PATH = "/admin/repository";
	
	/** The Constant LOG. */
	public static final Logger LOG = LoggerFactory.getLogger(RepositoryController.class);

	/** The repository service. */
	@Autowired
	private RepositoryService repositoryService;

	/** The image gallery service. */
	@Autowired
	private ImageGalleryService imageGalleryService;

	/**
	 * List all files.
	 *
	 * @param request the request
	 * @param model the model
	 * @return the string
	 * @throws UnsupportedEncodingException the unsupported encoding exception
	 * @throws InvalidRepositoryPathException the invalid repository path exception
	 */
	@RequestMapping(value = "/files/**", method = RequestMethod.GET)
	public String listAllFiles(HttpServletRequest request, ModelMap model) throws UnsupportedEncodingException, InvalidRepositoryPathException {
		String fullpath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
		fullpath = fullpath.substring(CONTROLLER_PATH.length() + "/files".length());
		
		// The /** mapping does not decode the URL
		fullpath=UriUtils.decode(fullpath, "UTF-8");
		
		return listAllFiles(fullpath, model);
	}

	/**
	 * List all files.
	 *
	 * @param repositoryPath the repository path
	 * @param model the model
	 * @return the string
	 * @throws InvalidRepositoryPathException the invalid repository path exception
	 */
	@RequestMapping(value = "/files", method = RequestMethod.GET)
	public String listAllFiles(@RequestParam(defaultValue = "/") String repositoryPath, ModelMap model) throws InvalidRepositoryPathException {
		Path path = Paths.get(repositoryPath);

		if (LOG.isDebugEnabled()) {
			LOG.debug("Listing files for path={}", path);
		}

		RepositoryFolder currentFolder = repositoryService.getFolder(path);
		List<RepositoryFile> fileList = repositoryService.getFiles(path, RepositoryFile.DEFAULT_SORT);
		model.addAttribute("fileList", fileList);
		model.addAttribute("currentPath", path);
		model.addAttribute("currentFolder", currentFolder);
		model.addAttribute("subPaths", repositoryService.getFolders(path, RepositoryFolder.DEFAULT_SORT));

		model.addAttribute("imageGallery", imageGalleryService.loadImageGallery(path));

		return JSP_PATH + "/index";
	}

	/**
	 * Gets the edits the page.
	 *
	 * @param uuid the uuid
	 * @param model the model
	 * @return the edits the page
	 * @throws NoSuchRepositoryFileException the no such repository file exception
	 */
	@RequestMapping(value = "/edit", method = RequestMethod.GET)
	public String getEditPage(@RequestParam String uuid, ModelMap model) throws NoSuchRepositoryFileException {
		RepositoryFile file = repositoryService.getFile(UUID.fromString(uuid));
		model.addAttribute("file", file);

		return JSP_PATH + "/edit";
	}

	/**
	 * Upload file.
	 *
	 * @param file the file
	 * @param repositoryPath the repository path
	 * @param redirectAttributes the redirect attributes
	 * @return the string
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public String uploadFile(@RequestParam MultipartFile file, @RequestParam String repositoryPath, RedirectAttributes redirectAttributes) throws IOException {
		try {
			repositoryService.addFile(Paths.get(repositoryPath), file.getOriginalFilename(), file.getContentType(), file.getInputStream(), null);
		} catch (InvalidRepositoryPathException e) {
			LOG.error("Invalid repository path!", e);
			redirectAttributes.addFlashAttribute("errorMessage", "Invalid repository path!");

		} catch (InvalidRepositoryFileDataException e) {
			LOG.error("Invalid file data!", e);
			redirectAttributes.addFlashAttribute("errorMessage", "Invalid file data!");
		}

		return "redirect:" + CONTROLLER_PATH + "/files" + Paths.get(repositoryPath);
	}

	/**
	 * Update metadata.
	 *
	 * @param fileData the file data
	 * @param uuid the uuid
	 * @param redirectAttributes the redirect attributes
	 * @return the string
	 * @throws NoSuchRepositoryFileException the no such repository file exception
	 */
	@RequestMapping(value = "/update", method = RequestMethod.POST)
	public String updateMetadata(@ModelAttribute RepositoryFile fileData, @RequestParam String uuid, RedirectAttributes redirectAttributes)
			throws NoSuchRepositoryFileException {
		RepositoryFile updatedFile = repositoryService.updateMetadata(fileData);

		return "redirect:" + CONTROLLER_PATH + "/files" + updatedFile.getFolder().getPath();
	}

	/**
	 * Delete file.
	 *
	 * @param uuid the uuid
	 * @param redirectAttributes the redirect attributes
	 * @return the string
	 * @throws NoSuchRepositoryFileException the no such repository file exception
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	@RequestMapping(value = "/delete", method = RequestMethod.POST)
	public String deleteFile(@RequestParam String uuid, RedirectAttributes redirectAttributes) throws NoSuchRepositoryFileException, IOException {
		RepositoryFile repositoryFile = repositoryService.getFile(UUID.fromString(uuid));
		repositoryService.removeFile(repositoryFile);

		return "redirect:" + CONTROLLER_PATH + "/files" + repositoryFile.getFolder().getPath();
	}

	/**
	 * Inits the binder.
	 *
	 * @param binder the binder
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Instant.class, new CustomDateEditor(dateFormat, false));
	}
}