/* documentr - Edit, maintain, and present software documentation on the web. Copyright (C) 2012-2013 Maik Schreiber This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.blizzy.documentr.web.attachment; import java.io.IOException; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; 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.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import de.blizzy.documentr.DocumentrConstants; import de.blizzy.documentr.access.AuthenticationUtil; import de.blizzy.documentr.access.User; import de.blizzy.documentr.access.UserStore; import de.blizzy.documentr.page.IPageStore; import de.blizzy.documentr.page.Page; import de.blizzy.documentr.page.PageMetadata; import de.blizzy.documentr.page.PageNotFoundException; import de.blizzy.documentr.page.PageUtil; import de.blizzy.documentr.util.Util; @Controller @RequestMapping("/attachment") @Slf4j public class AttachmentController { @Autowired private IPageStore pageStore; @Autowired private ServletContext servletContext; @Autowired private UserStore userStore; @RequestMapping(value="/list/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}", method=RequestMethod.GET) @PreAuthorize("isAuthenticated() and hasPagePermission(#projectName, #branchName, #pagePath, VIEW)") public String getAttachments(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, Model model) { model.addAttribute("projectName", projectName); //$NON-NLS-1$ model.addAttribute("branchName", branchName); //$NON-NLS-1$ pagePath = Util.toRealPagePath(pagePath); model.addAttribute("pagePath", pagePath); //$NON-NLS-1$ return "/project/branch/page/attachments"; //$NON-NLS-1$ } @RequestMapping(value="/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/" + "{name:.*}", method={ RequestMethod.GET, RequestMethod.HEAD }) @PreAuthorize("hasPagePermission(#projectName, #branchName, #pagePath, VIEW)") public ResponseEntity<byte[]> getAttachment(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, @PathVariable String name, @RequestParam(required=false) boolean download, HttpServletRequest request) throws IOException { try { pagePath = Util.toRealPagePath(pagePath); PageMetadata metadata = pageStore.getAttachmentMetadata(projectName, branchName, pagePath, name); HttpHeaders headers = new HttpHeaders(); long lastEdited = metadata.getLastEdited().getTime(); long authenticationCreated = AuthenticationUtil.getAuthenticationCreationTime(request.getSession()); long lastModified = Math.max(lastEdited, authenticationCreated); if (!download) { long projectEditTime = PageUtil.getProjectEditTime(projectName); if (projectEditTime >= 0) { lastModified = Math.max(lastModified, projectEditTime); } long modifiedSince = request.getDateHeader("If-Modified-Since"); //$NON-NLS-1$ if ((modifiedSince >= 0) && (lastModified <= modifiedSince)) { return new ResponseEntity<byte[]>(headers, HttpStatus.NOT_MODIFIED); } } headers.setLastModified(lastModified); headers.setExpires(0); headers.setCacheControl("must-revalidate, private"); //$NON-NLS-1$ if (download) { headers.set("Content-Disposition", //$NON-NLS-1$ "attachment; filename=\"" + name.replace('"', '_') + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } Page attachment = pageStore.getAttachment(projectName, branchName, pagePath, name); headers.setContentType(MediaType.parseMediaType(attachment.getContentType())); return new ResponseEntity<byte[]>(attachment.getData().getData(), headers, HttpStatus.OK); } catch (PageNotFoundException e) { return new ResponseEntity<byte[]>(HttpStatus.NOT_FOUND); } } @RequestMapping(value="/create/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}", method=RequestMethod.GET) @PreAuthorize("hasPagePermission(#projectName, #branchName, #pagePath, EDIT_PAGE)") public String createAttachment(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, Model model) { model.addAttribute("projectName", projectName); //$NON-NLS-1$ model.addAttribute("branchName", branchName); //$NON-NLS-1$ pagePath = Util.toRealPagePath(pagePath); model.addAttribute("pagePath", pagePath); //$NON-NLS-1$ return "/project/branch/page/editAttachment"; //$NON-NLS-1$ } @RequestMapping(value="/save/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}", method=RequestMethod.POST) @PreAuthorize("hasPagePermission(#projectName, #branchName, #pagePath, EDIT_PAGE)") public String saveAttachment(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, @RequestParam MultipartFile file, Authentication authentication) throws IOException { saveAttachmentInternal(projectName, branchName, pagePath, file, authentication); return "redirect:/attachment/list/" + projectName + "/" + branchName + "/" + pagePath; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } @RequestMapping(value="/saveViaJson/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json", method=RequestMethod.POST) @PreAuthorize("hasPagePermission(#projectName, #branchName, #pagePath, EDIT_PAGE)") @ResponseBody public Map<String, Object> saveAttachmentViaJson(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, @RequestParam MultipartFile file, Authentication authentication) throws IOException { log.info("saving attachment via JSON: {}", file.getOriginalFilename()); //$NON-NLS-1$ saveAttachmentInternal(projectName, branchName, pagePath, file, authentication); Map<String, Object> fileResult = Maps.newHashMap(); fileResult.put("name", file.getOriginalFilename()); //$NON-NLS-1$ fileResult.put("size", file.getSize()); //$NON-NLS-1$ fileResult.put("url", StringUtils.EMPTY); //$NON-NLS-1$ fileResult.put("thumbnail_url", StringUtils.EMPTY); //$NON-NLS-1$ fileResult.put("delete_url", StringUtils.EMPTY); //$NON-NLS-1$ fileResult.put("delete_type", StringUtils.EMPTY); //$NON-NLS-1$ List<Map<String, Object>> filesList = Lists.newArrayList(); filesList.add(fileResult); Map<String, Object> result = Maps.newHashMap(); result.put("files", filesList); //$NON-NLS-1$ return result; } private void saveAttachmentInternal(String projectName, String branchName, String pagePath, MultipartFile file, Authentication authentication) throws IOException { log.debug("saving attachment: {}", file.getOriginalFilename()); //$NON-NLS-1$ byte[] data = IOUtils.toByteArray(file.getInputStream()); String contentType = servletContext.getMimeType(file.getOriginalFilename()); if (StringUtils.isBlank(contentType)) { contentType = DocumentrConstants.DEFAULT_MIME_TYPE; } Page attachment = Page.fromData(data, contentType); pagePath = Util.toRealPagePath(pagePath); User user = userStore.getUser(authentication.getName()); pageStore.saveAttachment(projectName, branchName, pagePath, file.getOriginalFilename(), attachment, user); } @RequestMapping(value="/delete/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" + "{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" + "{pagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/" + "{name:.*}", method=RequestMethod.GET) @PreAuthorize("hasPagePermission(#projectName, #branchName, #pagePath, EDIT_PAGE)") public String deleteAttachment(@PathVariable String projectName, @PathVariable String branchName, @PathVariable String pagePath, @PathVariable String name, Authentication authentication) throws IOException { User user = userStore.getUser(authentication.getName()); pageStore.deleteAttachment(projectName, branchName, Util.toRealPagePath(pagePath), name, user); return "redirect:/attachment/list/" + projectName + "/" + branchName + "/" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Util.toUrlPagePath(pagePath); } }