/**
* Copyright (C) 2011 JTalks.org Team
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jtalks.jcommune.web.controller;
import org.joda.time.DateTime;
import org.jtalks.common.model.entity.Component;
import org.jtalks.jcommune.service.ComponentService;
import org.jtalks.jcommune.service.exceptions.ImageProcessException;
import org.jtalks.jcommune.service.nontransactional.Base64Wrapper;
import org.jtalks.jcommune.service.transactional.TransactionalComponentService;
import org.jtalks.jcommune.web.util.ImageControllerUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author Andrei Alikov
*/
@Controller
public class AdministrationImagesController extends ImageUploadController {
private static final Logger LOGGER = LoggerFactory.getLogger(AdministrationImagesController.class);
private final ImageControllerUtils logoControllerUtils;
private final ImageControllerUtils favIconPngControllerUtils;
private final ImageControllerUtils favIconIcoControllerUtils;
private final ComponentService componentService;
/**
* We need this start time because there might be case when time of the last modification
* of the forum information (logo, icon) was not set (may be even there is no Component for the forum),
* but we need to use some constant time to decide if we should return "Not Modified" in response to
* logo or fav icon request.
*/
private final Date startTime;
/**
* Creates instance of the service
*
* @param componentService service to work with the forum component
* @param logoControllerUtils utility object for logo converting functions
* @param favIconPngControllerUtils utility object for fav icon converting (to PNG format) functions
* @param favIconIcoControllerUtils utility object for fav icon converting (to ICO format) functions
* @param messageSource to resolve locale-dependent messages
*/
@Autowired
public AdministrationImagesController(ComponentService componentService,
@Qualifier("forumLogoControllerUtils")
ImageControllerUtils logoControllerUtils,
@Qualifier("favIconPngControllerUtils")
ImageControllerUtils favIconPngControllerUtils,
@Qualifier("favIconIcoControllerUtils")
ImageControllerUtils favIconIcoControllerUtils,
MessageSource messageSource) {
super(messageSource);
this.componentService = componentService;
this.logoControllerUtils = logoControllerUtils;
this.favIconIcoControllerUtils = favIconIcoControllerUtils;
this.favIconPngControllerUtils = favIconPngControllerUtils;
DateTime now = new DateTime();
startTime = now.withMillisOfSecond(0).toDate();
}
/**
* Gets current forum logo
*
* @param request http request
* @param response http response
*/
@RequestMapping(value = "/admin/logo", method = RequestMethod.GET)
public void getForumLogo(HttpServletRequest request, HttpServletResponse response) {
processImageRequest(request, response, TransactionalComponentService.LOGO_PROPERTY,
logoControllerUtils, "image/jpeg");
}
/**
* Gets default logo in JSON containing image data in String64 format
*
* @return JSON string containing default logo in String64 format
* @throws java.io.IOException
* @throws org.jtalks.jcommune.service.exceptions.ImageProcessException
*
*/
@RequestMapping(value = "/admin/defaultLogo", method = RequestMethod.GET)
@ResponseBody
public String getDefaultLogoInJson() throws IOException, ImageProcessException {
return getDefaultImageInJSON(logoControllerUtils);
}
private String getDefaultImageInJSON(ImageControllerUtils imageUtils) throws ImageProcessException, IOException {
Map<String, String> responseContent = new HashMap<String, String>();
imageUtils.prepareNormalResponse(imageUtils.getDefaultImage(), responseContent);
return imageUtils.getResponceJSONString(responseContent);
}
/**
* Process Logo file from request and return logo preview in response.
* Used for IE, Opera specific request processing.
*
* @param file file, that contains uploaded image
* @return ResponseEntity
* @throws IOException defined in the JsonFactory implementation,
* caller must implement exception processing
* @throws org.jtalks.jcommune.service.exceptions.ImageProcessException
* if error occurred while image processing
*/
@RequestMapping(value = "/admin/logo/IFrameLogoPreview", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<String> uploadLogo(
@RequestParam(value = "qqfile") MultipartFile file) throws IOException, ImageProcessException {
return createPreviewOfImage(file, logoControllerUtils);
}
/**
* Process logo file from request and return logo preview in response.
* Used for FF, Chrome specific request processing
*
* @param bytes input logo data
* @param response servlet response
* @return response content
* @throws ImageProcessException if error occurred while image processing
*/
@RequestMapping(value = "/admin/logo/XHRlogoPreview", method = RequestMethod.POST)
@ResponseBody
public Map<String, String> uploadLogo(@RequestBody byte[] bytes,
HttpServletResponse response) throws ImageProcessException {
return createPreviewOfImage(bytes, response, logoControllerUtils);
}
/**
* Gets current forum fav icon in the IE format
*
* @param request http request
* @param response http response
*/
@RequestMapping(value = "/admin/icon/ico", method = RequestMethod.GET)
public void getFavIconICO(HttpServletRequest request, HttpServletResponse response) {
processImageRequest(request, response, TransactionalComponentService.COMPONENT_FAVICON_ICO_PARAM,
favIconIcoControllerUtils, "image/x-icon");
}
/**
* Gets current forum fav icon in the PNG format (for all browsers except IE)
*
* @param request http request
* @param response http response
*/
@RequestMapping(value = "/admin/icon/png", method = RequestMethod.GET)
public void getFavIconPNG(HttpServletRequest request, HttpServletResponse response) {
processImageRequest(request, response, TransactionalComponentService.COMPONENT_FAVICON_PNG_PARAM,
favIconPngControllerUtils, "image/png");
}
private void processImageRequest(HttpServletRequest request, HttpServletResponse response,
String propertyName, ImageControllerUtils imageControllerUtils,
String contentType) {
Date forumModificationDate = componentService.getComponentModificationTime();
if (forumModificationDate == null) {
forumModificationDate = startTime;
}
Date ifModifiedDate = getIfModifiedSinceDate(request.getHeader(IF_MODIFIED_SINCE_HEADER));
if (!forumModificationDate.after(ifModifiedDate)) {
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
} else {
byte[] image = loadImageFromProperties(propertyName, imageControllerUtils);
response.setContentType(contentType);
response.setContentLength(image.length);
try {
response.getOutputStream().write(image);
} catch (IOException e) {
LOGGER.error("Can't write image to the output stream. ", e);
}
}
setupAvatarHeaders(response, forumModificationDate);
}
private byte[] loadImageFromProperties(String propertyName, ImageControllerUtils imageControllerUtils) {
Component forumComponent = componentService.getComponentOfForum();
String imageProperty = null;
if (forumComponent != null) {
imageProperty = forumComponent.getProperty(propertyName);
}
byte[] imageBytes = null;
if (imageProperty == null || imageProperty.isEmpty()) {
imageBytes = imageControllerUtils.getDefaultImage();
} else {
Base64Wrapper wrapper = new Base64Wrapper();
imageBytes = wrapper.decodeB64Bytes(imageProperty);
}
return imageBytes;
}
/**
* Gets default fav icon in JSON containing image data in String64 format
*
* @return JSON string containing default fav icon in String64 format
* @throws IOException
* @throws ImageProcessException
*/
@RequestMapping(value = "/admin/defaultIcon", method = RequestMethod.GET)
@ResponseBody
public String getDefaultIconInJson() throws IOException, ImageProcessException {
return getDefaultImageInJSON(favIconPngControllerUtils);
}
/**
* Process Fav Icon file from request and return fav icon preview in response.
* Used for IE, Opera specific request processing.
*
* @param file file, that contains uploaded image
* @return ResponseEntity
* @throws IOException defined in the JsonFactory implementation,
* caller must implement exception processing
* @throws org.jtalks.jcommune.service.exceptions.ImageProcessException
* if error occurred while image processing
*/
@RequestMapping(value = "/admin/icon/IFrameFavIconPreview", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<String> uploadFavIcon(
@RequestParam(value = "qqfile") MultipartFile file) throws IOException, ImageProcessException {
return createPreviewOfImage(file, favIconPngControllerUtils);
}
/**
* Process Fav Icon file from request and return icon preview in response.
* Used for FF, Chrome specific request processing
*
* @param bytes input icon data
* @param response servlet response
* @return response content
* @throws ImageProcessException if error occurred while image processing
*/
@RequestMapping(value = "/admin/icon/XHRFavIconPreview", method = RequestMethod.POST)
@ResponseBody
public Map<String, String> uploadFavIcon(@RequestBody byte[] bytes,
HttpServletResponse response) throws ImageProcessException {
return createPreviewOfImage(bytes, response, favIconPngControllerUtils);
}
}