/*******************************************************************************
* Australian National University Data Commons
* Copyright (C) 2013 The Australian National University
*
* This file is part of Australian National University Data Commons.
*
* Australian National University Data Commons 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 au.edu.anu.datacommons.publish;
import static java.text.MessageFormat.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.solr.client.solrj.SolrServerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import au.edu.anu.datacommons.data.db.model.FedoraObject;
import au.edu.anu.datacommons.data.db.model.Groups;
import au.edu.anu.datacommons.data.db.model.PublishLocation;
import au.edu.anu.datacommons.data.solr.model.SolrSearchResult;
import au.edu.anu.datacommons.exception.ValidateException;
import au.edu.anu.datacommons.publish.service.LocationValidationMessage;
import au.edu.anu.datacommons.publish.service.PublishService;
import au.edu.anu.datacommons.security.acl.CustomACLPermission;
import au.edu.anu.datacommons.security.acl.PermissionService;
import au.edu.anu.datacommons.security.service.FedoraObjectService;
import au.edu.anu.datacommons.services.ListResource;
import au.edu.anu.datacommons.util.Util;
import com.sun.jersey.api.view.Viewable;
/**
* FedoraObjectService
*
* Australian National University Data Commons
*
* Resource for publishing objects to various sources such as ANU and ANDS
*
* JUnit Coverage:
* None
*
* <pre>
* Version Date Developer Description
* 0.1 26/04/2012 Genevieve Turner (GT) Initial
* 0.2 08/06/2012 Genevieve Turner (GT) Fixed issue with an exception when publish button is clicked and no optiosn have been selected
* 0.3 02/07/2012 Genevieve Turner (GT) Updated to have the pid in the path
* 0.4 19/09/2012 Genevieve Turner (GT) Updated to redirect to the display page after publishing
* 0.5 27/09/2012 Genevieve Turner (GT) Updated to redirect to display page rather than edit
* 0.6 15/10/2012 Genevieve Turner (GT) Added the availablity of validation checks
* 0.7 10/12/2012 Genevieve Turner (GT) Added mass publication and validation functionality, also moved some functions from the fedora object service to the publish service
* 0.8 02/01/2012 Genevieve Turner (GT) Updated to allow for changes to error handling
* </pre>
*
*/
@Component
@Scope("request")
@Path("publish")
public class PublishResource {
static final Logger LOGGER = LoggerFactory.getLogger(ListResource.class);
@Resource(name="fedoraObjectServiceImpl")
private FedoraObjectService fedoraObjectService;
@Resource(name="publishServiceImpl")
private PublishService publishService;
@Resource(name="permissionService")
private PermissionService permissionService;
/**
* getPublishers
*
* Returns a list of publishers.
*
* <pre>
* Version Date Developer Description
* 0.1 15/05/2012 Genevieve Turner (GT) Initial
* 0.3 02/07/2012 Genevieve Turner (GT) Updated to have the pid in the path
* 0.7 10/12/2012 Genevieve Turner (GT) Updated to use publishService rather htan fedoraObjectService
* </pre>
*
* @return The web page for publishers
*/
@GET
@Path("{item}")
@Produces(MediaType.TEXT_HTML)
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response getPublishers(@PathParam("item") String pid) {
LOGGER.info("User {} requested publish location selection page.", getCurUsername());
FedoraObject fedoraObject = fedoraObjectService.getItemByPid(pid);
List<PublishLocation> publishLocations = publishService.getPublishers(fedoraObject);
Map<String, Object> model = new HashMap<String, Object>();
model.put("publishLocations", publishLocations);
Viewable viewable = new Viewable("/publish.jsp", model);
return Response.ok(viewable).build();
}
/**
* savePublishers
*
* Publishes the object to the given list of publishers
*
* <pre>
* Version Date Developer Description
* 0.1 15/05/2012 Genevieve Turner (GT) Initial
* 0.2 08/06/2012 Genevieve Turner (GT) Fixed issue with an exception when publish button is clicked and no optiosn have been selected
* 0.3 02/07/2012 Genevieve Turner (GT) Updated to have the pid in the path
* 0.4 19/09/2012 Genevieve Turner (GT) Updated to redirect to the display page
* 0.5 27/09/2012 Genevieve Turner (GT) Updated to redirect to display page rather than edit
* 0.7 10/12/2012 Genevieve Turner (GT) Updated to use publishService rather than fedoraObjectService
* 0.8 02/01/2012 Genevieve Turner (GT) Updated to allow for changes to error handling
* </pre>
*
* @param item The item to publish
* @param request The http request that is occuring
* @return The web page for publishers.
*/
@POST
@Path("{item}")
@Produces(MediaType.TEXT_HTML)
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response publishItem(@PathParam("item") String item, @QueryParam("layout") String layout,
@QueryParam("tmplt") String tmplt, @Context HttpServletRequest request) {
FedoraObject fedoraObject = fedoraObjectService.getItemByPid(item);
Map<String, List<String>> form = Util.convertArrayValueToList(request.getParameterMap());
List<String> publishers = form.get("publish");
LOGGER.debug("Locations to publish to: {}", publishers);
if (publishers != null && publishers.size() > 0) {
publishService.publish(fedoraObject, publishers);
}
UriBuilder uriBuilder = UriBuilder.fromPath("/display").path(item).queryParam("layout", layout);
if (Util.isNotEmpty(tmplt)) {
uriBuilder = uriBuilder.queryParam("tmplt", tmplt);
}
return Response.seeOther(uriBuilder.build()).build();
}
@GET
@Path("/mintdoi/{pid}")
@Produces(MediaType.TEXT_HTML)
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response generateDoi(@PathParam("pid") String pid, @QueryParam("tmplt") String tmplt, @Context UriInfo uriInfo)
{
Response resp = null;
UriBuilder redirUri = UriBuilder.fromPath("/display").path(pid).queryParam("layout", "def:display").queryParam("tmplt", tmplt);
FedoraObject fedoraObject = fedoraObjectService.getItemByPid(pid);
if (!permissionService.checkPermission(fedoraObject, CustomACLPermission.PUBLISH)) {
throw new AccessDeniedException(format("User does not have Publish permissions for {0}.", pid));
}
try
{
LOGGER.info("User {} requested a DOI to be minted for {}", getCurUsername(), pid);
fedoraObjectService.generateDoi(pid, tmplt, null);
resp = Response.seeOther(redirUri.build()).build();
}
catch (Exception e)
{
LOGGER.error("DOI Minting failed for " + pid, e);
resp = Response.seeOther(redirUri.queryParam("emsg", e.getMessage()).build()).build();
}
return resp;
}
/**
* getValidationCheckScreen
*
* Retrieve the validation page.
*
* <pre>
* Version Date Developer Description
* 0.6 15/10/2012 Genevieve Turner(GT) Initial
* 0.7 11/12/2012 Genevieve Turner (GT) Updated to use the publishing service
* </pre>
*
* @param item The item to check validation on
* @return The validation page
*/
@GET
@Produces(MediaType.TEXT_HTML)
@Path("validate/{item}")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response getValidationCheckScreen(@PathParam("item") String item) {
List<PublishLocation> publishLocations = publishService.getPublishers();
Map<String, Object> model = new HashMap<String, Object>();
model.put("publishLocations", publishLocations);
model.put("item", item);
Viewable viewable = new Viewable("/validate.jsp", model);
return Response.ok(viewable).build();
}
/**
* validateItem
*
* Check the validation and verify the page
*
* <pre>
* Version Date Developer Description
* 0.6 15/10/2012 Genevieve Turner(GT) Initial
* 0.7 10/12/2012 Genevieve Turner (GT) Updated to use publishService rather htan fedoraObjectService
* 0.8 02/01/2012 Genevieve Turner (GT) Updated to allow for changes to error handling and fixed a null pointer exception if no publishers had been selected
* </pre>
*
* @param item The item to check validation on
* @param request The request information
* @return The validation reponse page
*/
@POST
@Produces(MediaType.TEXT_HTML)
@Path("validate/{item}")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response validateItem(@PathParam("item") String item, @Context HttpServletRequest request) {
Map<String, List<String>> form = Util.convertArrayValueToList(request.getParameterMap());
List<String> publishers = form.get("publish");
Map<String, Object> model = new HashMap<String, Object>();
if (publishers != null && publishers.size() > 0) {
FedoraObject fedoraObject = fedoraObjectService.getItemByPid(item);
List<String> messages = publishService.validatePublishLocation(fedoraObject, publishers);
model.put("validateMessages", messages);
}
else {
throw new ValidateException("No publish location specified");
}
List<PublishLocation> publishLocations = publishService.getPublishers();
model.put("publishLocations", publishLocations);
model.put("item", item);
Viewable viewable = new Viewable("/validate.jsp", model);
return Response.ok(viewable).build();
}
/**
* getMultipleItemValidationScreen
*
* Retrieve the page to select items and locations to validate against
*
* <pre>
* Version Date Developer Description
* 0.7 10/12/2012 Genevieve Turner(GT) Initial
* </pre>
*
* @param groupId The group to find records for
* @param page The page number to get records for
* @return A page that contains groups that the user can validate for, also locations and items that they can validate
*/
@GET
@Produces(MediaType.TEXT_HTML)
@Path("validate/multiple")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response getMultipleItemValidationScreen(@QueryParam("group") Long groupId, @QueryParam("page") Integer page) {
List<Groups> groups = publishService.getValidationGroups();
Map<String, Object> model = new HashMap<String, Object>();
model.put("groups", groups);
if (groupId != null) {
List<PublishLocation> publishers = publishService.getPublishers();
model.put("publishers", publishers);
try {
SolrSearchResult results = publishService.getGroupObjects(groupId, page);
model.put("results", results);
}
catch (SolrServerException e) {
LOGGER.error("Exception querying solr", e);
}
}
return Response.ok(new Viewable("/validate_multiple.jsp", model)).build();
}
/**
* executeMultipleItemValidation
*
* Perform validation against multiple records
*
* <pre>
* Version Date Developer Description
* 0.7 10/12/2012 Genevieve Turner(GT) Initial
* 0.8 02/01/2012 Genevieve Turner (GT) Updated to allow for changes to error handling
* </pre>
*
* @param request The http request information
* @return A page containing the validated records
*/
@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("validate/multiple")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response executeMultipleItemValidation(@Context HttpServletRequest request) {
Map<String, Object> model = new HashMap<String, Object>();
String[] ids = request.getParameterValues("ids");
String[] publishLocations = request.getParameterValues("publishLocation");
if (publishLocations == null || publishLocations.length == 0 || ids == null || ids.length == 0) {
throw new ValidateException("You must select at least one publish location and one record");
}
try {
Map<String, List<LocationValidationMessage>> messages = publishService.validateMultiple(publishLocations, ids);
model.put("validationMessages", messages);
}
catch (Exception e) {
LOGGER.error("Exception validating messages", e);
throw new ValidateException("Exception performing validation on the selected records");
}
try {
SolrSearchResult information = publishService.getItemInformation(ids);
model.put("information", information);
}
catch (SolrServerException e) {
LOGGER.error("Error searching solr", e);
}
return Response.ok(new Viewable("/validate_display.jsp", model)).build();
}
/**
* getMultipleItemPublishScreen
*
* Retrieves the screen for records available publishing records
*
* <pre>
* Version Date Developer Description
* 0.7 10/12/2012 Genevieve Turner(GT) Initial
* </pre>
*
* @param groupId The group to find items to publish for
* @param page The page number of the items
* @return A page that contains the groups, locations and items the user can publish
*/
@GET
@Produces(MediaType.TEXT_HTML)
@Path("multiple")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response getMultipleItemPublishScreen(@QueryParam("group") Long groupId, @QueryParam("page") Integer page) {
List<Groups> groups = publishService.getMultiplePublishGroups();
LOGGER.info("Number of groups: {}", groups.size());
Map<String, Object> model = new HashMap<String, Object>();
model.put("groups", groups);
if (groupId != null) {
List<PublishLocation> publishers = publishService.getPublishers();
model.put("publishers", publishers);
try {
SolrSearchResult results = publishService.getGroupObjects(groupId, page);
model.put("results", results);
}
catch (SolrServerException e) {
LOGGER.error("Exception querying solr", e);
}
}
return Response.ok(new Viewable("/publish_multiple.jsp", model)).build();
}
/**
* executeMultiplePublish
*
* Publish the selected items to the given locations
*
* <pre>
* Version Date Developer Description
* 0.7 10/12/2012 Genevieve Turner(GT) Initial
* 0.8 02/01/2012 Genevieve Turner (GT) Updated to allow for changes to error handling
* </pre>
*
* @param request The http request
* @return A page indicating whether the items have been successfully published
*/
@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("multiple")
@PreAuthorize("hasRole('ROLE_ANU_USER')")
public Response executeMultiplePublish(@Context HttpServletRequest request, @QueryParam("groupId") Long id) {
Map<String, Object> model = new HashMap<String, Object>();
String[] ids = request.getParameterValues("ids");
String[] publishLocations = request.getParameterValues("publishLocation");
if (publishLocations == null || publishLocations.length == 0 || ids == null || ids.length == 0) {
throw new ValidateException("At least one publish location and record must be selected");
}
Map<String, String> published = publishService.publishMultiple(ids, publishLocations);
if (published == null) {
return Response.status(400).entity(new Viewable("/publish_multiple_validation_error.jsp", model)).build();
}
model.put("published", published);
try {
SolrSearchResult information = publishService.getItemInformation(ids);
model.put("information", information);
}
catch (SolrServerException e) {
LOGGER.error("Error searching solr", e);
}
return Response.ok(new Viewable("/publish_multiple_display.jsp", model)).build();
}
private String getCurUsername() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}