mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-18 23:43:06 +00:00
DSpace refactored service api
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.disseminate;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.pdfbox.exceptions.COSVisitorException;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.dspace.authorize.AuthorizeException;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.*;
|
||||
import org.dspace.content.Collection;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.content.service.CommunityService;
|
||||
import org.dspace.content.service.ItemService;
|
||||
import org.dspace.core.ConfigurationManager;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.disseminate.service.CitationDocumentService;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Citation Document produces a dissemination package (DIP) that is different that the archival package (AIP).
|
||||
* In this case we append the descriptive metadata to the end (configurable) of the document. i.e. last page of PDF.
|
||||
* So instead of getting the original PDF, you get a cPDF (with citation information added).
|
||||
*
|
||||
* @author Peter Dietz (peter@longsight.com)
|
||||
*/
|
||||
public class CitationDocumentServiceImpl implements CitationDocumentService, InitializingBean {
|
||||
/**
|
||||
* Class Logger
|
||||
*/
|
||||
private static Logger log = Logger.getLogger(CitationDocumentServiceImpl.class);
|
||||
|
||||
/**
|
||||
* A set of MIME types that can have a citation page added to them. That is,
|
||||
* MIME types in this set can be converted to a PDF which is then prepended
|
||||
* with a citation page.
|
||||
*/
|
||||
protected final Set<String> VALID_TYPES = new HashSet<String>(2);
|
||||
|
||||
/**
|
||||
* A set of MIME types that refer to a PDF
|
||||
*/
|
||||
protected final Set<String> PDF_MIMES = new HashSet<String>(2);
|
||||
|
||||
/**
|
||||
* A set of MIME types that refer to a JPEG, PNG, or GIF
|
||||
*/
|
||||
protected final Set<String> RASTER_MIMES = new HashSet<String>();
|
||||
/**
|
||||
* A set of MIME types that refer to a SVG
|
||||
*/
|
||||
protected final Set<String> SVG_MIMES = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Comma separated list of collections handles to enable citation for.
|
||||
* webui.citation.enabled_collections, default empty/none. ex: =1811/123, 1811/345
|
||||
*/
|
||||
protected String citationEnabledCollections = null;
|
||||
|
||||
/**
|
||||
* Comma separated list of community handles to enable citation for.
|
||||
* webui.citation.enabled_communties, default empty/none. ex: =1811/123, 1811/345
|
||||
*/
|
||||
protected String citationEnabledCommunities = null;
|
||||
|
||||
/**
|
||||
* List of all enabled collections, inherited/determined for those under communities.
|
||||
*/
|
||||
protected ArrayList<String> citationEnabledCollectionsList;
|
||||
|
||||
protected File tempDir;
|
||||
|
||||
protected String[] header1;
|
||||
protected String[] header2;
|
||||
protected String[] fields;
|
||||
protected String footer;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected AuthorizeService authorizeService;
|
||||
@Autowired(required = true)
|
||||
protected BitstreamService bitstreamService;
|
||||
@Autowired(required = true)
|
||||
protected CommunityService communityService;
|
||||
@Autowired(required = true)
|
||||
protected ItemService itemService;
|
||||
|
||||
@Autowired(required = true)
|
||||
protected HandleService handleService;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// Add valid format MIME types to set. This could be put in the Schema
|
||||
// instead.
|
||||
//Populate RASTER_MIMES
|
||||
SVG_MIMES.add("image/jpeg");
|
||||
SVG_MIMES.add("image/pjpeg");
|
||||
SVG_MIMES.add("image/png");
|
||||
SVG_MIMES.add("image/gif");
|
||||
//Populate SVG_MIMES
|
||||
SVG_MIMES.add("image/svg");
|
||||
SVG_MIMES.add("image/svg+xml");
|
||||
|
||||
|
||||
//Populate PDF_MIMES
|
||||
PDF_MIMES.add("application/pdf");
|
||||
PDF_MIMES.add("application/x-pdf");
|
||||
|
||||
//Populate VALID_TYPES
|
||||
VALID_TYPES.addAll(PDF_MIMES);
|
||||
|
||||
|
||||
//Load enabled collections
|
||||
citationEnabledCollections = ConfigurationManager.getProperty("disseminate-citation", "enabled_collections");
|
||||
citationEnabledCollectionsList = new ArrayList<String>();
|
||||
if(citationEnabledCollections != null && citationEnabledCollections.length() > 0) {
|
||||
String[] collectionChunks = citationEnabledCollections.split(",");
|
||||
for(String collectionString : collectionChunks) {
|
||||
citationEnabledCollectionsList.add(collectionString.trim());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Load enabled communities, and add to collection-list
|
||||
citationEnabledCommunities = ConfigurationManager.getProperty("disseminate-citation", "enabled_communities");
|
||||
if(citationEnabledCollectionsList == null) {
|
||||
citationEnabledCollectionsList = new ArrayList<String>();
|
||||
}
|
||||
|
||||
if(citationEnabledCommunities != null && citationEnabledCommunities.length() > 0) {
|
||||
try {
|
||||
String[] communityChunks = citationEnabledCommunities.split(",");
|
||||
for(String communityString : communityChunks) {
|
||||
Context context = new Context();
|
||||
DSpaceObject dsoCommunity = handleService.resolveToObject(context, communityString.trim());
|
||||
if(dsoCommunity instanceof Community) {
|
||||
Community community = (Community)dsoCommunity;
|
||||
List<Collection> collections = communityService.getAllCollections(context, community);
|
||||
|
||||
for(Collection collection : collections) {
|
||||
citationEnabledCollectionsList.add(collection.getHandle());
|
||||
}
|
||||
} else {
|
||||
log.error("Invalid community for citation.enabled_communities, value:" + communityString.trim());
|
||||
}
|
||||
context.abort();
|
||||
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Configurable text/fields, we'll set sane defaults
|
||||
String header1Config = ConfigurationManager.getProperty("disseminate-citation", "header1");
|
||||
if(StringUtils.isNotBlank(header1Config)) {
|
||||
header1 = header1Config.split(",");
|
||||
} else {
|
||||
header1 = new String[]{"DSpace Institution", ""};
|
||||
}
|
||||
|
||||
String header2Config = ConfigurationManager.getProperty("disseminate-citation", "header2");
|
||||
if(StringUtils.isNotBlank(header2Config)) {
|
||||
header2 = header2Config.split(",");
|
||||
} else {
|
||||
header2 = new String[]{"DSpace Repository", "http://dspace.org"};
|
||||
}
|
||||
|
||||
String fieldsConfig = ConfigurationManager.getProperty("disseminate-citation", "fields");
|
||||
if(StringUtils.isNotBlank(fieldsConfig)) {
|
||||
fields = fieldsConfig.split(",");
|
||||
} else {
|
||||
fields = new String[]{"dc.date.issued", "dc.title", "dc.creator", "dc.contributor.author", "dc.publisher", "_line_", "dc.identifier.citation", "dc.identifier.uri"};
|
||||
}
|
||||
|
||||
String footerConfig = ConfigurationManager.getProperty("disseminate-citation", "footer");
|
||||
if(StringUtils.isNotBlank(footerConfig)) {
|
||||
footer = footerConfig;
|
||||
} else {
|
||||
footer = "Downloaded from DSpace Repository, DSpace Institution's institutional repository";
|
||||
}
|
||||
|
||||
//Ensure a temp directory is available
|
||||
String tempDirString = ConfigurationManager.getProperty("dspace.dir") + "/temp";
|
||||
tempDir = new File(tempDirString);
|
||||
if(!tempDir.exists()) {
|
||||
boolean success = tempDir.mkdir();
|
||||
if(success) {
|
||||
log.info("Created temp directory at: " + tempDirString);
|
||||
} else {
|
||||
log.info("Unable to create temp directory at: " + tempDirString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected CitationDocumentServiceImpl() {}
|
||||
|
||||
/**
|
||||
* Boolean to determine is citation-functionality is enabled globally for entire site.
|
||||
* config/module/disseminate-citation: enable_globally, default false. true=on, false=off
|
||||
*/
|
||||
protected Boolean citationEnabledGlobally = null;
|
||||
|
||||
protected boolean isCitationEnabledGlobally() {
|
||||
if(citationEnabledGlobally == null) {
|
||||
citationEnabledGlobally = ConfigurationManager.getBooleanProperty("disseminate-citation", "enable_globally", false);
|
||||
}
|
||||
|
||||
return citationEnabledGlobally;
|
||||
}
|
||||
|
||||
protected boolean isCitationEnabledThroughCollection(Context context, Bitstream bitstream) throws SQLException {
|
||||
//Reject quickly if no-enabled collections
|
||||
if(citationEnabledCollectionsList.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DSpaceObject owningDSO = bitstreamService.getParentObject(context, bitstream);
|
||||
if(owningDSO instanceof Item) {
|
||||
Item item = (Item)owningDSO;
|
||||
|
||||
List<Collection> collections = item.getCollections();
|
||||
|
||||
for(Collection collection : collections) {
|
||||
if(citationEnabledCollectionsList.contains(collection.getHandle())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If previous logic didn't return true, then we're false
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isCitationEnabledForBitstream(Bitstream bitstream, Context context) throws SQLException {
|
||||
if(isCitationEnabledGlobally() || isCitationEnabledThroughCollection(context, bitstream)) {
|
||||
|
||||
boolean adminUser = authorizeService.isAdmin(context);
|
||||
|
||||
if(!adminUser && canGenerateCitationVersion(context, bitstream)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If previous logic didn't return true, then we're false.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the citation page be the first page of the document, or the last page?
|
||||
* default => true. true => first page, false => last page
|
||||
* citation_as_first_page=true
|
||||
*/
|
||||
protected Boolean citationAsFirstPage = null;
|
||||
|
||||
protected Boolean isCitationFirstPage() {
|
||||
if(citationAsFirstPage == null) {
|
||||
citationAsFirstPage = ConfigurationManager.getBooleanProperty("disseminate-citation", "citation_as_first_page", true);
|
||||
}
|
||||
|
||||
return citationAsFirstPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGenerateCitationVersion(Context context, Bitstream bitstream) throws SQLException
|
||||
{
|
||||
return VALID_TYPES.contains(bitstream.getFormat(context).getMIMEType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File makeCitedDocument(Context context, Bitstream bitstream)
|
||||
throws IOException, SQLException, AuthorizeException, COSVisitorException {
|
||||
PDDocument document = new PDDocument();
|
||||
PDDocument sourceDocument = new PDDocument();
|
||||
try {
|
||||
Item item = (Item) bitstreamService.getParentObject(context, bitstream);
|
||||
sourceDocument = sourceDocument.load(bitstreamService.retrieve(context, bitstream));
|
||||
PDPage coverPage = new PDPage(PDPage.PAGE_SIZE_LETTER);
|
||||
generateCoverPage(context, document, coverPage, item);
|
||||
addCoverPageToDocument(document, sourceDocument, coverPage);
|
||||
|
||||
document.save(tempDir.getAbsolutePath() + "/bitstream.cover.pdf");
|
||||
return new File(tempDir.getAbsolutePath() + "/bitstream.cover.pdf");
|
||||
} finally {
|
||||
sourceDocument.close();
|
||||
document.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void generateCoverPage(Context context, PDDocument document, PDPage coverPage, Item item) throws IOException, COSVisitorException {
|
||||
PDPageContentStream contentStream = new PDPageContentStream(document, coverPage);
|
||||
try {
|
||||
int ypos = 760;
|
||||
int xpos = 30;
|
||||
int xwidth = 550;
|
||||
int ygap = 20;
|
||||
|
||||
PDFont fontHelvetica = PDType1Font.HELVETICA;
|
||||
PDFont fontHelveticaBold = PDType1Font.HELVETICA_BOLD;
|
||||
PDFont fontHelveticaOblique = PDType1Font.HELVETICA_OBLIQUE;
|
||||
contentStream.setNonStrokingColor(Color.BLACK);
|
||||
|
||||
String[][] content = {header1};
|
||||
drawTable(coverPage, contentStream, ypos, xpos, content, fontHelveticaBold, 11, false);
|
||||
ypos -=(ygap);
|
||||
|
||||
String[][] content2 = {header2};
|
||||
drawTable(coverPage, contentStream, ypos, xpos, content2, fontHelveticaBold, 11, false);
|
||||
ypos -=ygap;
|
||||
|
||||
contentStream.fillRect(xpos, ypos, xwidth, 1);
|
||||
contentStream.closeAndStroke();
|
||||
|
||||
String[][] content3 = {{getOwningCommunity(context, item), getOwningCollection(item)}};
|
||||
drawTable(coverPage, contentStream, ypos, xpos, content3, fontHelvetica, 9, false);
|
||||
ypos -=ygap;
|
||||
|
||||
contentStream.fillRect(xpos, ypos, xwidth, 1);
|
||||
contentStream.closeAndStroke();
|
||||
ypos -=(ygap*2);
|
||||
|
||||
for(String field : fields) {
|
||||
field = field.trim();
|
||||
PDFont font = fontHelvetica;
|
||||
int fontSize = 11;
|
||||
if(field.contains("title")) {
|
||||
fontSize = 26;
|
||||
ypos -= ygap;
|
||||
} else if(field.contains("creator") || field.contains("contributor")) {
|
||||
fontSize = 16;
|
||||
}
|
||||
|
||||
if(field.equals("_line_")) {
|
||||
contentStream.fillRect(xpos, ypos, xwidth, 1);
|
||||
contentStream.closeAndStroke();
|
||||
ypos -=(ygap);
|
||||
|
||||
} else if(StringUtils.isNotEmpty(itemService.getMetadata(item, field))) {
|
||||
ypos = drawStringWordWrap(coverPage, contentStream, itemService.getMetadata(item, field), xpos, ypos, font, fontSize);
|
||||
}
|
||||
|
||||
if(field.contains("title")) {
|
||||
ypos -=ygap;
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(fontHelveticaOblique, 11);
|
||||
contentStream.moveTextPositionByAmount(xpos, ypos);
|
||||
contentStream.drawString(footer);
|
||||
contentStream.endText();
|
||||
} finally {
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void addCoverPageToDocument(PDDocument document, PDDocument sourceDocument, PDPage coverPage) {
|
||||
List<PDPage> sourcePageList = sourceDocument.getDocumentCatalog().getAllPages();
|
||||
|
||||
if (isCitationFirstPage()) {
|
||||
//citation as cover page
|
||||
document.addPage(coverPage);
|
||||
for (PDPage sourcePage : sourcePageList) {
|
||||
document.addPage(sourcePage);
|
||||
}
|
||||
} else {
|
||||
//citation as tail page
|
||||
for (PDPage sourcePage : sourcePageList) {
|
||||
document.addPage(sourcePage);
|
||||
}
|
||||
document.addPage(coverPage);
|
||||
}
|
||||
sourcePageList.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drawStringWordWrap(PDPage page, PDPageContentStream contentStream, String text,
|
||||
int startX, int startY, PDFont pdfFont, float fontSize) throws IOException {
|
||||
float leading = 1.5f * fontSize;
|
||||
|
||||
PDRectangle mediabox = page.findMediaBox();
|
||||
float margin = 72;
|
||||
float width = mediabox.getWidth() - 2*margin;
|
||||
|
||||
List<String> lines = new ArrayList<>();
|
||||
int lastSpace = -1;
|
||||
while (text.length() > 0)
|
||||
{
|
||||
int spaceIndex = text.indexOf(' ', lastSpace + 1);
|
||||
if (spaceIndex < 0)
|
||||
{
|
||||
lines.add(text);
|
||||
text = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
String subString = text.substring(0, spaceIndex);
|
||||
float size = fontSize * pdfFont.getStringWidth(subString) / 1000;
|
||||
if (size > width)
|
||||
{
|
||||
if (lastSpace < 0) // So we have a word longer than the line... draw it anyways
|
||||
lastSpace = spaceIndex;
|
||||
subString = text.substring(0, lastSpace);
|
||||
lines.add(subString);
|
||||
text = text.substring(lastSpace).trim();
|
||||
lastSpace = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastSpace = spaceIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(pdfFont, fontSize);
|
||||
contentStream.moveTextPositionByAmount(startX, startY);
|
||||
int currentY = startY;
|
||||
for (String line: lines)
|
||||
{
|
||||
contentStream.drawString(line);
|
||||
currentY -= leading;
|
||||
contentStream.moveTextPositionByAmount(0, -leading);
|
||||
}
|
||||
contentStream.endText();
|
||||
return currentY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwningCommunity(Context context, Item item) {
|
||||
try {
|
||||
List<Community> comms = itemService.getCommunities(context, item);
|
||||
if(comms.size() > 0) {
|
||||
return comms.get(0).getName();
|
||||
} else {
|
||||
return " ";
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error(e.getMessage());
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwningCollection(Item item) {
|
||||
return item.getOwningCollection().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAllMetadataSeparated(Item item, String metadataKey) {
|
||||
List<MetadataValue> dcValues = itemService.getMetadataByMetadataString(item, metadataKey);
|
||||
|
||||
ArrayList<String> valueArray = new ArrayList<String>();
|
||||
|
||||
for(MetadataValue dcValue : dcValues) {
|
||||
if(StringUtils.isNotBlank(dcValue.getValue())) {
|
||||
valueArray.add(dcValue.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtils.join(valueArray.toArray(), "; ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTable(PDPage page, PDPageContentStream contentStream,
|
||||
float y, float margin,
|
||||
String[][] content, PDFont font, int fontSize, boolean cellBorders) throws IOException {
|
||||
final int rows = content.length;
|
||||
final int cols = content[0].length;
|
||||
final float rowHeight = 20f;
|
||||
final float tableWidth = page.findMediaBox().getWidth()-(2*margin);
|
||||
final float tableHeight = rowHeight * rows;
|
||||
final float colWidth = tableWidth/(float)cols;
|
||||
final float cellMargin=5f;
|
||||
|
||||
if(cellBorders) {
|
||||
//draw the rows
|
||||
float nexty = y ;
|
||||
for (int i = 0; i <= rows; i++) {
|
||||
contentStream.drawLine(margin,nexty,margin+tableWidth,nexty);
|
||||
nexty-= rowHeight;
|
||||
}
|
||||
|
||||
//draw the columns
|
||||
float nextx = margin;
|
||||
for (int i = 0; i <= cols; i++) {
|
||||
contentStream.drawLine(nextx,y,nextx,y-tableHeight);
|
||||
nextx += colWidth;
|
||||
}
|
||||
}
|
||||
|
||||
//now add the text
|
||||
contentStream.setFont(font, fontSize);
|
||||
|
||||
float textx = margin+cellMargin;
|
||||
float texty = y-15;
|
||||
for(int i = 0; i < content.length; i++){
|
||||
for(int j = 0 ; j < content[i].length; j++){
|
||||
String text = content[i][j];
|
||||
contentStream.beginText();
|
||||
contentStream.moveTextPositionByAmount(textx,texty);
|
||||
contentStream.drawString(text);
|
||||
contentStream.endText();
|
||||
textx += colWidth;
|
||||
}
|
||||
texty-=rowHeight;
|
||||
textx = margin+cellMargin;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user