Merge pull request #72 from atmire/dspace3-versioning

[DS-1194] Atmire dspace 3.0 versioning contribution
This commit is contained in:
Mark Diggory
2012-09-19 16:29:33 -07:00
73 changed files with 6263 additions and 168 deletions

View File

@@ -106,13 +106,35 @@
</execution>
</executions>
</plugin>
<!--
The ant plugin below ensures that the dspace "woven" configuration file ends up in the dspace directory
The dspace service manager needs this "woven" configuration file when it starts
-->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<configuration>
<tasks>
<copy file="${project.build.directory}/testing/dspace.cfg.woven" tofile="${project.build.directory}/testing/dspace/config/dspace.cfg"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<dspace.dir>${project.build.directory}/testing/dspace</dspace.dir>
<dspace.dir.static>${basedir}/src/test/data/dspaceFolder</dspace.dir.static>
<dspace.configuration>${project.build.directory}/testing/dspace.cfg.woven</dspace.configuration>
<dspace.configuration>${project.build.directory}/testing/dspace/config/dspace.cfg</dspace.configuration>
<db.schema.path>${project.build.directory}/testing/dspace/etc/h2/database_schema.sql</db.schema.path>
<dspace.log.init.disable>true</dspace.log.init.disable>
</systemPropertyVariables>

View File

@@ -73,7 +73,7 @@ public class BrowseConsumer implements Consumer
// If an Item is created or its metadata is modified..
case Constants.ITEM:
if (et == Event.MODIFY_METADATA || et == Event.CREATE)
if (et == Event.MODIFY_METADATA || et == Event.CREATE || et == Event.MODIFY)
{
Item subj = (Item)event.getSubject(ctx);
if (subj != null)

View File

@@ -8,6 +8,7 @@
package org.dspace.browse;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.*;
import org.dspace.core.Constants;
@@ -384,6 +385,12 @@ public class BrowseItem extends DSpaceObject
}
}
@Override
public void update() throws SQLException, AuthorizeException
{
}
@Override
public void updateLastModified()
{

View File

@@ -321,7 +321,7 @@ public class IndexBrowse
{
indexItem(new ItemMetadataProxy(item), addingNewItem);
}
else if (item.isWithdrawn())
else if (item.isWithdrawn() || !item.isArchived())
{
indexItem(new ItemMetadataProxy(item), false);
}

View File

@@ -974,7 +974,7 @@ public class Collection extends DSpaceObject
* @throws IOException
* @throws AuthorizeException
*/
public void update() throws SQLException, IOException, AuthorizeException
public void update() throws SQLException, AuthorizeException
{
// Check authorisation
canEdit(true);

View File

@@ -509,7 +509,7 @@ public class Community extends DSpaceObject
/**
* Update the community metadata (including logo) to the database.
*/
public void update() throws SQLException, IOException, AuthorizeException
public void update() throws SQLException, AuthorizeException
{
// Check authorisation
canEdit();

View File

@@ -7,13 +7,14 @@
*/
package org.dspace.content;
import java.sql.SQLException;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import java.sql.SQLException;
/**
* Abstract base class for DSpace objects
*/
@@ -171,5 +172,7 @@ public abstract class DSpaceObject
return null;
}
public abstract void update() throws SQLException, AuthorizeException;
public abstract void updateLastModified();
}

View File

@@ -37,7 +37,7 @@ public interface InProgressSubmission
/**
* Update the submission, including the unarchived item.
*/
void update() throws SQLException, IOException, AuthorizeException;
void update() throws SQLException, AuthorizeException;
/**
* Get the incomplete item object

View File

@@ -15,7 +15,9 @@ import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.embargo.EmbargoManager;
import org.dspace.event.Event;
import org.dspace.handle.HandleManager;
import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.IdentifierService;
import org.dspace.utils.DSpace;
/**
* Support to install an Item in the archive.
@@ -58,21 +60,18 @@ public class InstallItem
IOException, AuthorizeException
{
Item item = is.getItem();
String handle;
// if no previous handle supplied, create one
if (suppliedHandle == null)
{
// create a new handle for this item
handle = HandleManager.createHandle(c, item);
}
else
{
// assign the supplied handle to this item
handle = HandleManager.createHandle(c, item, suppliedHandle);
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
try {
if(suppliedHandle == null)
{
identifierService.register(c, item);
}else{
identifierService.register(c, item, suppliedHandle);
}
} catch (IdentifierException e) {
throw new RuntimeException("Can't create an Identifier!");
}
populateHandleMetadata(item, handle);
populateMetadata(c, item);
@@ -102,21 +101,17 @@ public class InstallItem
throws SQLException, IOException, AuthorizeException
{
Item item = is.getItem();
String handle;
// if no handle supplied
if (suppliedHandle == null)
{
// create a new handle for this item
handle = HandleManager.createHandle(c, item);
//only populate handle metadata for new handles
// (existing handles should already be in the metadata -- as it was restored by ingest process)
populateHandleMetadata(item, handle);
}
else
{
// assign the supplied handle to this item
handle = HandleManager.createHandle(c, item, suppliedHandle);
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
try {
if(suppliedHandle == null)
{
identifierService.register(c, item);
}else{
identifierService.register(c, item, suppliedHandle);
}
} catch (IdentifierException e) {
throw new RuntimeException("Can't create an Identifier!");
}
// Even though we are restoring an item it may not have the proper dates. So let's
@@ -146,28 +141,6 @@ public class InstallItem
return finishItem(c, item, is);
}
private static void populateHandleMetadata(Item item, String handle)
throws SQLException, IOException, AuthorizeException
{
String handleref = HandleManager.getCanonicalForm(handle);
// Add handle as identifier.uri DC value.
// First check that identifier doesn't already exist.
boolean identifierExists = false;
DCValue[] identifiers = item.getDC("identifier", "uri", Item.ANY);
for (DCValue identifier : identifiers)
{
if (handleref.equals(identifier.value))
{
identifierExists = true;
}
}
if (!identifierExists)
{
item.addDC("identifier", "uri", null, handleref);
}
}
private static void populateMetadata(Context c, Item item)
throws SQLException, IOException, AuthorizeException

View File

@@ -38,9 +38,13 @@ import org.dspace.event.Event;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.handle.HandleManager;
import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.IdentifierService;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
import org.dspace.utils.DSpace;
import org.dspace.versioning.VersioningService;
/**
* Class representing an item in DSpace.
@@ -2039,10 +2043,30 @@ public class Item extends DSpaceObject
// Remove any Handle
HandleManager.unbindHandle(ourContext, this);
// remove version attached to the item
removeVersion();
// Finally remove item row
DatabaseManager.delete(ourContext, itemRow);
}
private void removeVersion() throws AuthorizeException, SQLException
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
if(versioningService.getVersion(ourContext, this)!=null)
{
versioningService.removeVersion(ourContext, this);
}else{
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
try {
identifierService.delete(ourContext, this);
} catch (IdentifierException e) {
throw new RuntimeException(e);
}
}
}
/**
* Remove item and all its sub-structure from the context cache.
* Useful in batch processes where a single context has a long,

View File

@@ -96,7 +96,7 @@ public class Site extends DSpaceObject
}
public void update()
throws SQLException, AuthorizeException, IOException
throws SQLException, AuthorizeException
{
}

View File

@@ -506,7 +506,7 @@ public class WorkspaceItem implements InProgressSubmission
/**
* Update the workspace item, including the unarchived item.
*/
public void update() throws SQLException, AuthorizeException, IOException
public void update() throws SQLException, AuthorizeException
{
// Authorisation is checked by the item.update() method below

View File

@@ -9,13 +9,7 @@ package org.dspace.core;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Stack;
import java.util.*;
import org.apache.log4j.Logger;
import org.dspace.eperson.EPerson;
@@ -24,6 +18,7 @@ import org.dspace.event.Dispatcher;
import org.dspace.event.Event;
import org.dspace.event.EventManager;
import org.dspace.storage.rdbms.DatabaseManager;
import org.springframework.util.CollectionUtils;
/**
* Class representing the context of a particular DSpace operation. This stores
@@ -77,7 +72,7 @@ public class Context
private List<Integer> specialGroups;
/** Content events */
private List<Event> events = null;
private LinkedList<Event> events = null;
/** Event dispatcher name */
private String dispName = null;
@@ -375,7 +370,7 @@ public class Context
{
if (events == null)
{
events = new ArrayList<Event>();
events = new LinkedList<Event>();
}
events.add(event);
@@ -385,17 +380,32 @@ public class Context
* Get the current event list. If there is a separate list of events from
* already-committed operations combine that with current list.
*
* TODO WARNING: events uses an ArrayList, a class not ready for concurrency.
* Read http://download.oracle.com/javase/6/docs/api/java/util/Collections.html#synchronizedList%28java.util.List%29
* on how to properly synchronize the class when calling this method
*
* @return List of all available events.
*/
public List<Event> getEvents()
public LinkedList<Event> getEvents()
{
return events;
}
public boolean hasEvents()
{
return !CollectionUtils.isEmpty(events);
}
/**
* Retrieves the first element in the events list & removes it from the list of events once retrieved
* @return The first event of the list or <code>null</code> if the list is empty
*/
public Event pollEvent()
{
if(hasEvents())
{
return events.poll();
}else{
return null;
}
}
/**
* Close the context, without committing any of the changes performed using
* this context. The database connection is freed. No exception is thrown if

View File

@@ -7,9 +7,7 @@
*/
package org.dspace.event;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
@@ -72,9 +70,8 @@ public class BasicDispatcher extends Dispatcher
{
if (!consumers.isEmpty())
{
List<Event> events = Collections.synchronizedList(ctx.getEvents());
if (events == null)
if (!ctx.hasEvents())
{
return;
}
@@ -82,7 +79,7 @@ public class BasicDispatcher extends Dispatcher
if (log.isDebugEnabled())
{
log.debug("Processing queue of "
+ String.valueOf(events.size()) + " events.");
+ String.valueOf(ctx.getEvents().size()) + " events.");
}
// transaction identifier applies to all events created in
@@ -90,8 +87,9 @@ public class BasicDispatcher extends Dispatcher
// some letters so RDF readers don't mistake it for an integer.
String tid = "TX" + Utils.generateKey();
for (Event event : events)
while (ctx.hasEvents())
{
Event event = ctx.pollEvent();
event.setDispatcher(getIdentifier());
event.setTransactionID(tid);

View File

@@ -27,13 +27,13 @@ import org.dspace.storage.rdbms.TableRowIterator;
/**
* Interface to the <a href="http://www.handle.net" target=_new>CNRI Handle
* System </a>.
*
*
* <p>
* Currently, this class simply maps handles to local facilities; handles which
* are owned by other sites (including other DSpaces) are treated as
* non-existent.
* </p>
*
*
* @author Peter Breton
* @version $Revision$
*/
@@ -52,10 +52,10 @@ public class HandleManager
/**
* Return the local URL for handle, or null if handle cannot be found.
*
*
* The returned URL is a (non-handle-based) location where a dissemination
* of the object referred to by handle can be obtained.
*
*
* @param context
* DSpace context
* @param handle
@@ -87,17 +87,17 @@ public class HandleManager
/**
* Transforms handle into the canonical form <em>hdl:handle</em>.
*
*
* No attempt is made to verify that handle is in fact valid.
*
*
* @param handle
* The handle
* @return The canonical form
*/
public static String getCanonicalForm(String handle)
{
// Let the admin define a new prefix, if not then we'll use the
// Let the admin define a new prefix, if not then we'll use the
// CNRI default. This allows the admin to use "hdl:" if they want to or
// use a locally branded prefix handle.myuni.edu.
String handlePrefix = ConfigurationManager.getProperty("handle.canonical.prefix");
@@ -105,7 +105,7 @@ public class HandleManager
{
handlePrefix = "http://hdl.handle.net/";
}
return handlePrefix + handle;
}
@@ -126,7 +126,7 @@ public class HandleManager
/**
* Creates a new handle in the database.
*
*
* @param context
* DSpace context
* @param dso
@@ -158,7 +158,7 @@ public class HandleManager
/**
* Creates a handle entry, but with a handle supplied by the caller (new
* Handle not generated)
*
*
* @param context
* DSpace context
* @param dso
@@ -234,21 +234,24 @@ public class HandleManager
public static void unbindHandle(Context context, DSpaceObject dso)
throws SQLException
{
TableRow row = getHandleInternal(context, dso.getType(), dso.getID());
if (row != null)
TableRowIterator rows = getInternalHandles(context, dso.getType(), dso.getID());
if (rows != null)
{
//Only set the "resouce_id" column to null when unbinding a handle.
// We want to keep around the "resource_type_id" value, so that we
// can verify during a restore whether the same *type* of resource
// is reusing this handle!
row.setColumnNull("resource_id");
DatabaseManager.update(context, row);
if(log.isDebugEnabled())
while (rows.hasNext())
{
log.debug("Unbound Handle " + row.getStringColumn("handle") + " from object " + Constants.typeText[dso.getType()] + " id=" + dso.getID());
}
TableRow row = rows.next();
//Only set the "resouce_id" column to null when unbinding a handle.
// We want to keep around the "resource_type_id" value, so that we
// can verify during a restore whether the same *type* of resource
// is reusing this handle!
row.setColumnNull("resource_id");
DatabaseManager.update(context, row);
if(log.isDebugEnabled())
{
log.debug("Unbound Handle " + row.getStringColumn("handle") + " from object " + Constants.typeText[dso.getType()] + " id=" + dso.getID());
}
}
}
else
{
@@ -259,7 +262,7 @@ public class HandleManager
/**
* Return the object which handle maps to, or null. This is the object
* itself, not a URL which points to it.
*
*
* @param context
* DSpace context
* @param handle
@@ -288,7 +291,7 @@ public class HandleManager
}
// check if handle was allocated previously, but is currently not
// associated with a DSpaceObject
// associated with a DSpaceObject
// (this may occur when 'unbindHandle()' is called for an obj that was removed)
if ((dbhandle.isColumnNull("resource_type_id"))
|| (dbhandle.isColumnNull("resource_id")))
@@ -344,7 +347,7 @@ public class HandleManager
/**
* Return the handle for an Object, or null if the Object has no handle.
*
*
* @param context
* DSpace context
* @param dso
@@ -356,8 +359,8 @@ public class HandleManager
public static String findHandle(Context context, DSpaceObject dso)
throws SQLException
{
TableRow row = getHandleInternal(context, dso.getType(), dso.getID());
if (row == null)
TableRowIterator rows = getInternalHandles(context, dso.getType(), dso.getID());
if (rows == null || !rows.hasNext())
{
if (dso.getType() == Constants.SITE)
{
@@ -370,13 +373,27 @@ public class HandleManager
}
else
{
return row.getStringColumn("handle");
//TODO: Move this code away from the HandleManager & into the Identifier provider
//Attempt to retrieve a handle that does NOT look like {handle.part}/{handle.part}.{version}
String result = rows.next().getStringColumn("handle");
while (rows.hasNext())
{
TableRow row = rows.next();
//Ensure that the handle doesn't look like this 12346/213.{version}
//If we find a match that indicates that we have a proper handle
if(!row.getStringColumn("handle").matches(".*/.*\\.\\d+"))
{
result = row.getStringColumn("handle");
}
}
return result;
}
}
/**
* Return all the handles which start with prefix.
*
*
* @param context
* DSpace context
* @param prefix
@@ -434,7 +451,7 @@ public class HandleManager
/**
* Return the handle for an Object, or null if the Object has no handle.
*
*
* @param context
* DSpace context
* @param type
@@ -445,18 +462,18 @@ public class HandleManager
* @exception SQLException
* If a database error occurs
*/
private static TableRow getHandleInternal(Context context, int type, int id)
private static TableRowIterator getInternalHandles(Context context, int type, int id)
throws SQLException
{
{
String sql = "SELECT * FROM Handle WHERE resource_type_id = ? " +
"AND resource_id = ?";
return DatabaseManager.querySingleTable(context, "Handle", sql, type, id);
return DatabaseManager.queryTable(context, "Handle", sql, type, id);
}
/**
* Find the database row corresponding to handle.
*
*
* @param context
* DSpace context
* @param handle

View File

@@ -0,0 +1,18 @@
/**
* 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.identifier;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class Handle implements Identifier {
}

View File

@@ -0,0 +1,448 @@
/**
* 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.identifier;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.*;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.sql.SQLException;
/**
* The old DSpace handle identifier service, used to create handles or retrieve objects based on their handle
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
@Component
public class HandleIdentifierProvider extends IdentifierProvider {
/** log4j category */
private static Logger log = Logger.getLogger(HandleIdentifierProvider.class);
/** Prefix registered to no one */
protected static final String EXAMPLE_PREFIX = "123456789";
protected String[] supportedPrefixes = new String[]{"info:hdl", "hdl", "http://"};
@Override
public boolean supports(Class<? extends Identifier> identifier) {
return Handle.class.isAssignableFrom(identifier);
}
public boolean supports(String identifier)
{
for(String prefix : supportedPrefixes){
if(identifier.startsWith(prefix))
{
return true;
}
}
try {
String outOfUrl = retrieveHandleOutOfUrl(identifier);
if(outOfUrl != null)
{
return true;
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
return false;
}
public String register(Context context, DSpaceObject dso) {
try{
String id = mint(context, dso);
// move canonical to point the latest version
if(dso instanceof Item)
{
Item item = (Item)dso;
populateHandleMetadata(item, id);
}
return id;
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID(), e);
}
}
public void register(Context context, DSpaceObject dso, String identifier) {
try{
createNewIdentifier(context, dso, identifier);
if(dso instanceof Item)
{
Item item = (Item)dso;
populateHandleMetadata(item, identifier);
}
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID(), e);
}
}
public void reserve(Context context, DSpaceObject dso, String identifier) {
try{
TableRow handle = DatabaseManager.create(context, "Handle");
modifyHandleRecord(context, dso, handle, identifier);
}catch(Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID());
}
}
/**
* Creates a new handle in the database.
*
* @param context DSpace context
* @param dso The DSpaceObject to create a handle for
* @return The newly created handle
* @exception java.sql.SQLException If a database error occurs
*/
public String mint(Context context, DSpaceObject dso) {
if(dso.getHandle() != null)
{
return dso.getHandle();
}
try{
return createNewIdentifier(context, dso, null);
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID());
}
}
public DSpaceObject resolve(Context context, String identifier, String... attributes) {
// We can do nothing with this, return null
try
{
TableRow dbhandle = findHandleInternal(context, identifier);
if (dbhandle == null)
{
//Check for an url
identifier = retrieveHandleOutOfUrl(identifier);
if(identifier != null)
{
dbhandle = findHandleInternal(context, identifier);
}
if(dbhandle == null)
{
return null;
}
}
if ((dbhandle.isColumnNull("resource_type_id")) || (dbhandle.isColumnNull("resource_id")))
{
throw new IllegalStateException("No associated resource type");
}
// What are we looking at here?
int handletypeid = dbhandle.getIntColumn("resource_type_id");
int resourceID = dbhandle.getIntColumn("resource_id");
if (handletypeid == Constants.ITEM)
{
Item item = Item.find(context, resourceID);
if (log.isDebugEnabled()) {
log.debug("Resolved handle " + identifier + " to item "
+ ((item == null) ? (-1) : item.getID()));
}
return item;
}
else if (handletypeid == Constants.COLLECTION)
{
Collection collection = Collection.find(context, resourceID);
if (log.isDebugEnabled()) {
log.debug("Resolved handle " + identifier + " to collection "
+ ((collection == null) ? (-1) : collection.getID()));
}
return collection;
}
else if (handletypeid == Constants.COMMUNITY)
{
Community community = Community.find(context, resourceID);
if (log.isDebugEnabled())
{
log.debug("Resolved handle " + identifier + " to community "
+ ((community == null) ? (-1) : community.getID()));
}
return community;
}
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while resolving handle to item", "handle: " + identifier), e);
}
// throw new IllegalStateException("Unsupported Handle Type "
// + Constants.typeText[handletypeid]);
return null;
}
@Override
public String lookup(Context context, DSpaceObject dso) throws IdentifierNotFoundException, IdentifierNotResolvableException {
try
{
TableRow row = getHandleInternal(context, dso.getType(), dso.getID());
if (row == null)
{
if (dso.getType() == Constants.SITE)
{
return Site.getSiteHandle();
}
else
{
return null;
}
}
else
{
return row.getStringColumn("handle");
}
}catch(SQLException sqe){
throw new IdentifierNotResolvableException(sqe.getMessage(),sqe);
}
}
@Override
public void delete(Context context, DSpaceObject dso, String identifier) throws IdentifierException {
delete(context, dso);
}
public void delete(Context context, DSpaceObject dso) throws IdentifierException {
try{
TableRow row = getHandleInternal(context, dso.getType(), dso.getID());
if (row != null)
{
//Only set the "resouce_id" column to null when unbinding a handle.
// We want to keep around the "resource_type_id" value, so that we
// can verify during a restore whether the same *type* of resource
// is reusing this handle!
row.setColumnNull("resource_id");
DatabaseManager.update(context, row);
if(log.isDebugEnabled())
{
log.debug("Unbound Handle " + row.getStringColumn("handle") + " from object " + Constants.typeText[dso.getType()] + " id=" + dso.getID());
}
}
else
{
log.warn("Cannot find Handle entry to unbind for object " + Constants.typeText[dso.getType()] + " id=" + dso.getID());
}
}catch(SQLException sqe)
{
throw new IdentifierException(sqe.getMessage(),sqe);
}
}
public static String retrieveHandleOutOfUrl(String url)
throws SQLException {
// We can do nothing with this, return null
if (!url.contains("/"))
{
return null;
}
String[] splitUrl = url.split("/");
return splitUrl[splitUrl.length - 2] + "/" + splitUrl[splitUrl.length - 1];
}
/**
* Get the configured Handle prefix string, or a default
* @return configured prefix or "123456789"
*/
public static String getPrefix()
{
String prefix = ConfigurationManager.getProperty("handle.prefix");
if (null == prefix)
{
prefix = EXAMPLE_PREFIX; // XXX no good way to exit cleanly
log.error("handle.prefix is not configured; using " + prefix);
}
return prefix;
}
protected static String getCanonicalForm(String handle)
{
// Let the admin define a new prefix, if not then we'll use the
// CNRI default. This allows the admin to use "hdl:" if they want to or
// use a locally branded prefix handle.myuni.edu.
String handlePrefix = ConfigurationManager.getProperty("handle.canonical.prefix");
if (handlePrefix == null || handlePrefix.length() == 0)
{
handlePrefix = "http://hdl.handle.net/";
}
return handlePrefix + handle;
}
protected String createNewIdentifier(Context context, DSpaceObject dso, String handleId) throws SQLException {
TableRow handle=null;
if(handleId != null)
{
handle = findHandleInternal(context, handleId);
if(handle!=null && !handle.isColumnNull("resource_id"))
{
//Check if this handle is already linked up to this specified DSpace Object
if(handle.getIntColumn("resource_id")==dso.getID() &&
handle.getIntColumn("resource_type_id")==dso.getType())
{
//This handle already links to this DSpace Object -- so, there's nothing else we need to do
return handleId;
}
else
{
//handle found in DB table & already in use by another existing resource
throw new IllegalStateException("Attempted to create a handle which is already in use: " + handleId);
}
}
}
else if(handle!=null && !handle.isColumnNull("resource_type_id"))
{
//If there is a 'resource_type_id' (but 'resource_id' is empty), then the object using
// this handle was previously unbound (see unbindHandle() method) -- likely because object was deleted
int previousType = handle.getIntColumn("resource_type_id");
//Since we are restoring an object to a pre-existing handle, double check we are restoring the same *type* of object
// (e.g. we will not allow an Item to be restored to a handle previously used by a Collection)
if(previousType != dso.getType())
{
throw new IllegalStateException("Attempted to reuse a handle previously used by a " +
Constants.typeText[previousType] + " for a new " +
Constants.typeText[dso.getType()]);
}
}
if(handle==null){
handle = DatabaseManager.create(context, "Handle");
handleId = createId(handle.getIntColumn("handle_id"));
}
modifyHandleRecord(context, dso, handle, handleId);
return handleId;
}
protected String modifyHandleRecord(Context context, DSpaceObject dso, TableRow handle, String handleId) throws SQLException {
handle.setColumn("handle", handleId);
handle.setColumn("resource_type_id", dso.getType());
handle.setColumn("resource_id", dso.getID());
DatabaseManager.update(context, handle);
if (log.isDebugEnabled())
{
log.debug("Created new handle for "
+ Constants.typeText[dso.getType()] + " " + handleId);
}
return handleId;
}
/**
* Return the handle for an Object, or null if the Object has no handle.
*
* @param context
* DSpace context
* @param type
* The type of object
* @param id
* The id of object
* @return The handle for object, or null if the object has no handle.
* @exception java.sql.SQLException
* If a database error occurs
*/
protected static TableRow getHandleInternal(Context context, int type, int id)
throws SQLException
{
String sql = "SELECT * FROM Handle WHERE resource_type_id = ? " +
"AND resource_id = ?";
return DatabaseManager.querySingleTable(context, "Handle", sql, type, id);
}
/**
* Find the database row corresponding to handle.
*
* @param context DSpace context
* @param handle The handle to resolve
* @return The database row corresponding to the handle
* @exception java.sql.SQLException If a database error occurs
*/
protected static TableRow findHandleInternal(Context context, String handle)
throws SQLException {
if (handle == null)
{
throw new IllegalArgumentException("Handle is null");
}
return DatabaseManager.findByUnique(context, "Handle", "handle", handle);
}
/**
* Create a new handle id. The implementation uses the PK of the RDBMS
* Handle table.
*
* @return A new handle id
* @exception java.sql.SQLException
* If a database error occurs
*/
protected static String createId(int id) throws SQLException
{
String handlePrefix = getPrefix();
return handlePrefix + (handlePrefix.endsWith("/") ? "" : "/") + id;
}
protected void populateHandleMetadata(Item item, String handle)
throws SQLException, IOException, AuthorizeException
{
String handleref = getCanonicalForm(handle);
// Add handle as identifier.uri DC value.
// First check that identifier doesn't already exist.
boolean identifierExists = false;
DCValue[] identifiers = item.getDC("identifier", "uri", Item.ANY);
for (DCValue identifier : identifiers)
{
if (handleref.equals(identifier.value))
{
identifierExists = true;
}
}
if (!identifierExists)
{
item.addDC("identifier", "uri", null, handleref);
}
}
}

View File

@@ -0,0 +1,18 @@
/**
* 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.identifier;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface Identifier {
}

View File

@@ -0,0 +1,33 @@
/**
* 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.identifier;
/**
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IdentifierException extends Exception{
public IdentifierException() {
super();
}
public IdentifierException(String message) {
super(message);
}
public IdentifierException(String message, Throwable cause) {
super(message, cause);
}
public IdentifierException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,34 @@
/**
* 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.identifier;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IdentifierNotFoundException extends IdentifierException {
public IdentifierNotFoundException() {
super();
}
public IdentifierNotFoundException(String message) {
super(message);
}
public IdentifierNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public IdentifierNotFoundException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,34 @@
/**
* 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.identifier;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IdentifierNotResolvableException extends IdentifierException {
public IdentifierNotResolvableException() {
super();
}
public IdentifierNotResolvableException(String message) {
super(message);
}
public IdentifierNotResolvableException(String message, Throwable cause) {
super(message, cause);
}
public IdentifierNotResolvableException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,58 @@
/**
* 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.identifier;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public abstract class IdentifierProvider {
protected IdentifierService parentService;
protected ConfigurationService configurationService;
@Autowired
@Required
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
public void setParentService(IdentifierService parentService) {
this.parentService = parentService;
}
public abstract boolean supports(Class<? extends Identifier> identifier);
public abstract boolean supports(String identifier);
public abstract String register(Context context, DSpaceObject item) throws IdentifierException;
public abstract String mint(Context context, DSpaceObject dso) throws IdentifierException;
public abstract DSpaceObject resolve(Context context, String identifier, String... attributes) throws IdentifierNotFoundException, IdentifierNotResolvableException;;
public abstract String lookup(Context context, DSpaceObject object) throws IdentifierNotFoundException, IdentifierNotResolvableException;;
public abstract void delete(Context context, DSpaceObject dso) throws IdentifierException;
public abstract void delete(Context context, DSpaceObject dso, String identifier) throws IdentifierException;
public abstract void reserve(Context context, DSpaceObject dso, String identifier) throws IdentifierException;
public abstract void register(Context context, DSpaceObject object, String identifier);
}

View File

@@ -0,0 +1,130 @@
/**
* 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.identifier;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface IdentifierService {
/**
*
* @param context
* @param dso
* @param identifier
* @return
*/
String lookup(Context context, DSpaceObject dso, Class<? extends Identifier> identifier);
/**
*
* This will resolve a DSpaceObject based on a provided Identifier. The Service will interrogate the providers in
* no particular order and return the first successful result discovered. If no resolution is successful,
* the method will return null if no object is found.
*
* TODO: Verify null is returned.
*
* @param context
* @param identifier
* @return
* @throws IdentifierNotFoundException
* @throws IdentifierNotResolvableException
*/
DSpaceObject resolve(Context context, String identifier) throws IdentifierNotFoundException, IdentifierNotResolvableException;
/**
*
* Reserves any identifiers necessary based on the capabilities of all providers in the service.
*
* @param context
* @param dso
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void reserve(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException;
/**
*
* Used to Reserve a Specific Identifier (for example a Handle, hdl:1234.5/6) The provider is responsible for
* Detecting and Processing the appropriate identifier, all Providers are interrogated, multiple providers
* can process the same identifier.
*
* @param context
* @param dso
* @param identifier
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void reserve(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException;
/**
*
* @param context
* @param dso
* @return
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void register(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException;
/**
*
* Used to Register a Specific Identifier (for example a Handle, hdl:1234.5/6) The provider is responsible for
* Detecting and Processing the appropriate identifier, all Providers are interrogated, multiple providers
* can process the same identifier.
*
* @param context
* @param dso
* @param identifier
* @return
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void register(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException;
/**
* Delete (Unbind) all identifiers registered for a specific DSpace item. Identifiers are "unbound" across
* all providers in no particular order.
*
* @param context
* @param dso
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void delete(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException;
/**
* Used to Delete a Specific Identifier (for example a Handle, hdl:1234.5/6) The provider is responsible for
* Detecting and Processing the appropriate identifier, all Providers are interrogated, multiple providers
* can process the same identifier.
*
* @param context
* @param dso
* @param identifier
* @throws org.dspace.authorize.AuthorizeException
* @throws java.sql.SQLException
* @throws IdentifierException
*/
void delete(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException;
}

View File

@@ -0,0 +1,165 @@
/**
* 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.identifier;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import java.sql.SQLException;
import java.util.List;
/**
* The main service class used to reserve, register and resolve identifiers
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IdentifierServiceImpl implements IdentifierService {
private List<IdentifierProvider> providers;
/** log4j category */
private static Logger log = Logger.getLogger(IdentifierServiceImpl.class);
@Autowired
@Required
public void setProviders(List<IdentifierProvider> providers)
{
this.providers = providers;
for(IdentifierProvider p : providers)
{
p.setParentService(this);
}
}
/**
* Reserves identifiers for the item
* @param context dspace context
* @param dso dspace object
*/
public void reserve(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException {
for (IdentifierProvider service : providers)
{
service.mint(context, dso);
}
//Update our item
dso.update();
}
@Override
public void reserve(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException {
// Next resolve all other services
for (IdentifierProvider service : providers)
{
if(service.supports(identifier))
{
service.reserve(context, dso, identifier);
}
}
//Update our item
dso.update();
}
@Override
public void register(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException {
//We need to commit our context because one of the providers might require the handle created above
// Next resolve all other services
for (IdentifierProvider service : providers)
{
service.register(context, dso);
}
//Update our item
dso.update();
}
@Override
public void register(Context context, DSpaceObject object, String identifier) throws AuthorizeException, SQLException, IdentifierException {
//We need to commit our context because one of the providers might require the handle created above
// Next resolve all other services
for (IdentifierProvider service : providers)
{
service.register(context, object, identifier);
}
//Update our item
object.update();
}
@Override
public String lookup(Context context, DSpaceObject dso, Class<? extends Identifier> identifier) {
for (IdentifierProvider service : providers)
{
if(service.supports(identifier))
{
try{
String result = service.lookup(context, dso);
if (result != null){
return result;
}
} catch (IdentifierException e) {
log.error(e.getMessage(),e);
}
}
}
return null;
}
public DSpaceObject resolve(Context context, String identifier) throws IdentifierNotFoundException, IdentifierNotResolvableException{
for (IdentifierProvider service : providers)
{
if(service.supports(identifier))
{ try
{
DSpaceObject result = service.resolve(context, identifier);
if (result != null)
{
return result;
}
} catch (IdentifierException e) {
log.error(e.getMessage(),e);
}
}
}
return null;
}
public void delete(Context context, DSpaceObject dso) throws AuthorizeException, SQLException, IdentifierException {
for (IdentifierProvider service : providers)
{
try
{
service.delete(context, dso);
} catch (IdentifierException e) {
log.error(e.getMessage(),e);
}
}
}
@Override
public void delete(Context context, DSpaceObject dso, String identifier) throws AuthorizeException, SQLException, IdentifierException {
for (IdentifierProvider service : providers)
{
try
{
if(service.supports(identifier))
{
service.delete(context, dso, identifier);
}
} catch (IdentifierException e) {
log.error(e.getMessage(),e);
}
}
}
}

View File

@@ -0,0 +1,689 @@
/**
* 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.identifier;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.*;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.utils.DSpace;
import org.dspace.versioning.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Date;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
@Component
public class VersionedHandleIdentifierProvider extends IdentifierProvider {
/** log4j category */
private static Logger log = Logger.getLogger(VersionedHandleIdentifierProvider.class);
/** Prefix registered to no one */
static final String EXAMPLE_PREFIX = "123456789";
private static final char DOT = '.';
private String[] supportedPrefixes = new String[]{"info:hdl", "hdl", "http://"};
private VersionDAO versionDAO;
private VersionHistoryDAO versionHistoryDAO;
@Override
public boolean supports(Class<? extends Identifier> identifier)
{
return Handle.class.isAssignableFrom(identifier);
}
public boolean supports(String identifier)
{
for(String prefix : supportedPrefixes)
{
if(identifier.startsWith(prefix))
{
return true;
}
}
try {
String outOfUrl = retrieveHandleOutOfUrl(identifier);
if(outOfUrl != null)
{
return true;
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
return false;
}
public String register(Context context, DSpaceObject dso)
{
try
{
String id = mint(context, dso);
// move canonical to point the latest version
if(dso != null && dso.getType() == Constants.ITEM)
{
Item item = (Item)dso;
VersionHistory history = retrieveVersionHistory(context, (Item)dso);
if(history!=null)
{
String canonical = getCanonical(item);
// Modify Canonical: 12345/100 will point to the new item
TableRow canonicalRecord = findHandleInternal(context, canonical);
modifyHandleRecord(context, dso, canonicalRecord, canonical);
// in case of first version we have to modify the previous metadata to be xxxx.1
Version version = history.getVersion(item);
Version previous = history.getPrevious(version);
if (history.isFirstVersion(previous))
{
modifyHandleMetadata(previous.getItem(), (canonical + DOT + 1));
}
// Check if our previous item hasn't got a handle anymore.
// This only occurs when a switch has been made from the standard handle identifier provider
// to the versioned one, in this case no "versioned handle" is reserved so we need to create one
if(previous != null && getHandleInternal(context, Constants.ITEM, previous.getItemID()) == null){
makeIdentifierBasedOnHistory(context, previous.getItem(), canonical, history);
}
}
populateHandleMetadata(item);
}
return id;
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + (dso != null ? dso.getID() : "")), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + (dso != null ? dso.getID() : ""));
}
}
public void register(Context context, DSpaceObject dso, String identifier)
{
try
{
Item item = (Item) dso;
// if for this identifier is already present a record in the Handle table and the corresponding item
// has an history someone is trying to restore the latest version for the item. When
// trying to restore the latest version the identifier in input doesn't have the for 1234/123.latestVersion
// it is the canonical 1234/123
VersionHistory itemHistory = getHistory(context, identifier);
if(!identifier.matches(".*/.*\\.\\d+") && itemHistory!=null){
int newVersionNumber = itemHistory.getLatestVersion().getVersionNumber()+1;
String canonical = identifier;
identifier = identifier.concat(".").concat("" + newVersionNumber);
restoreItAsVersion(context, dso, identifier, item, canonical, itemHistory);
}
// if identifier == 1234.5/100.4 reinstate the version 4 in the version table if absent
else if(identifier.matches(".*/.*\\.\\d+"))
{
// if it is a version of an item is needed to put back the record
// in the versionitem table
String canonical = getCanonical(identifier);
DSpaceObject canonicalItem = this.resolve(context, canonical);
if(canonicalItem==null){
restoreItAsCanonical(context, dso, identifier, item, canonical);
}
else{
VersionHistory history = retrieveVersionHistory(context, (Item)canonicalItem);
if(history==null){
restoreItAsCanonical(context, dso, identifier, item, canonical);
}
else
{
restoreItAsVersion(context, dso, identifier, item, canonical, history);
}
}
}
else
{
//A regular handle
createNewIdentifier(context, dso, identifier);
if(dso instanceof Item)
{
populateHandleMetadata(item);
}
}
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID(), e);
}
}
private VersionHistory getHistory(Context context, String identifier)
{
DSpaceObject item = this.resolve(context, identifier);
if(item!=null){
VersionHistory history = retrieveVersionHistory(context, (Item)item);
return history;
}
return null;
}
private void restoreItAsVersion(Context context, DSpaceObject dso, String identifier, Item item, String canonical, VersionHistory history) throws SQLException, IOException, AuthorizeException
{
createNewIdentifier(context, dso, identifier);
populateHandleMetadata(item);
int versionNumber = Integer.parseInt(identifier.substring(identifier.lastIndexOf(".") + 1));
createVersion(context, history, item, "Restoring from AIP Service", new Date(), versionNumber);
Version latest = history.getLatestVersion();
// if restoring the lastest version: needed to move the canonical
if(latest.getVersionNumber() < versionNumber){
TableRow canonicalRecord = findHandleInternal(context, canonical);
modifyHandleRecord(context, dso, canonicalRecord, canonical);
}
}
private void restoreItAsCanonical(Context context, DSpaceObject dso, String identifier, Item item, String canonical) throws SQLException, IOException, AuthorizeException
{
createNewIdentifier(context, dso, identifier);
populateHandleMetadata(item);
int versionNumber = Integer.parseInt(identifier.substring(identifier.lastIndexOf(".")+1));
VersionHistory history=versionHistoryDAO.create(context);
createVersion(context, history, item, "Restoring from AIP Service", new Date(), versionNumber);
TableRow canonicalRecord = findHandleInternal(context, canonical);
modifyHandleRecord(context, dso, canonicalRecord, canonical);
}
public void reserve(Context context, DSpaceObject dso, String identifier)
{
try{
TableRow handle = DatabaseManager.create(context, "Handle");
modifyHandleRecord(context, dso, handle, identifier);
}catch(Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID());
}
}
/**
* Creates a new handle in the database.
*
* @param context DSpace context
* @param dso The DSpaceObject to create a handle for
* @return The newly created handle
*/
public String mint(Context context, DSpaceObject dso)
{
if(dso.getHandle() != null)
{
return dso.getHandle();
}
try{
String handleId = null;
VersionHistory history = null;
if(dso instanceof Item)
{
history = retrieveVersionHistory(context, (Item)dso);
}
if(history!=null)
{
handleId = makeIdentifierBasedOnHistory(context, dso, handleId, history);
}else{
handleId = createNewIdentifier(context, dso, null);
}
return handleId;
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while attempting to create handle", "Item id: " + dso.getID()), e);
throw new RuntimeException("Error while attempting to create identifier for Item id: " + dso.getID());
}
}
public DSpaceObject resolve(Context context, String identifier, String... attributes)
{
// We can do nothing with this, return null
try{
TableRow dbhandle = findHandleInternal(context, identifier);
if (dbhandle == null)
{
//Check for an url
identifier = retrieveHandleOutOfUrl(identifier);
if(identifier != null)
{
dbhandle = findHandleInternal(context, identifier);
}
if(dbhandle == null)
{
return null;
}
}
if ((dbhandle.isColumnNull("resource_type_id"))
|| (dbhandle.isColumnNull("resource_id")))
{
throw new IllegalStateException("No associated resource type");
}
// What are we looking at here?
int handletypeid = dbhandle.getIntColumn("resource_type_id");
int resourceID = dbhandle.getIntColumn("resource_id");
if (handletypeid == Constants.ITEM)
{
Item item = Item.find(context, resourceID);
if (log.isDebugEnabled())
{
log.debug("Resolved handle " + identifier + " to item "
+ ((item == null) ? (-1) : item.getID()));
}
return item;
}
else if (handletypeid == Constants.COLLECTION)
{
Collection collection = Collection.find(context, resourceID);
if (log.isDebugEnabled()) {
log.debug("Resolved handle " + identifier + " to collection "
+ ((collection == null) ? (-1) : collection.getID()));
}
return collection;
}
else if (handletypeid == Constants.COMMUNITY)
{
Community community = Community.find(context, resourceID);
if (log.isDebugEnabled()) {
log.debug("Resolved handle " + identifier + " to community "
+ ((community == null) ? (-1) : community.getID()));
}
return community;
}
}catch (Exception e){
log.error(LogManager.getHeader(context, "Error while resolving handle to item", "handle: " + identifier), e);
}
// throw new IllegalStateException("Unsupported Handle Type "
// + Constants.typeText[handletypeid]);
return null;
}
@Override
public String lookup(Context context, DSpaceObject dso) throws IdentifierNotFoundException, IdentifierNotResolvableException {
try
{
TableRow row = getHandleInternal(context, dso.getType(), dso.getID());
if (row == null)
{
if (dso.getType() == Constants.SITE)
{
return Site.getSiteHandle();
}
else
{
return null;
}
}
else
{
return row.getStringColumn("handle");
}
}catch(SQLException sqe){
throw new IdentifierNotResolvableException(sqe.getMessage(),sqe);
}
}
@Override
public void delete(Context context, DSpaceObject dso, String identifier) throws IdentifierException {
delete(context, dso);
}
public void delete(Context context, DSpaceObject dso) throws IdentifierException {
try {
if (dso instanceof Item)
{
Item item = (Item) dso;
// If it is the most current version occurs to move the canonical to the previous version
VersionHistory history = retrieveVersionHistory(context, item);
if(history!=null && history.getLatestVersion().getItem().equals(item) && history.size() > 1)
{
Item previous = history.getPrevious(history.getLatestVersion()).getItem();
// Modify Canonical: 12345/100 will point to the new item
String canonical = getCanonical(previous);
TableRow canonicalRecord = findHandleInternal(context, canonical);
modifyHandleRecord(context, previous, canonicalRecord, canonical);
}
}
} catch (Exception e) {
log.error(LogManager.getHeader(context, "Error while attempting to register doi", "Item id: " + dso.getID()), e);
throw new IdentifierException("Error while moving doi identifier", e);
}
}
public static String retrieveHandleOutOfUrl(String url) throws SQLException
{
// We can do nothing with this, return null
if (!url.contains("/")) return null;
String[] splitUrl = url.split("/");
return splitUrl[splitUrl.length - 2] + "/" + splitUrl[splitUrl.length - 1];
}
/**
* Get the configured Handle prefix string, or a default
* @return configured prefix or "123456789"
*/
public static String getPrefix()
{
String prefix = ConfigurationManager.getProperty("handle.prefix");
if (null == prefix)
{
prefix = EXAMPLE_PREFIX; // XXX no good way to exit cleanly
log.error("handle.prefix is not configured; using " + prefix);
}
return prefix;
}
protected static String getCanonicalForm(String handle)
{
// Let the admin define a new prefix, if not then we'll use the
// CNRI default. This allows the admin to use "hdl:" if they want to or
// use a locally branded prefix handle.myuni.edu.
String handlePrefix = ConfigurationManager.getProperty("handle.canonical.prefix");
if (handlePrefix == null || handlePrefix.length() == 0)
{
handlePrefix = "http://hdl.handle.net/";
}
return handlePrefix + handle;
}
protected String createNewIdentifier(Context context, DSpaceObject dso, String handleId) throws SQLException {
TableRow handle=null;
if(handleId != null)
{
handle = findHandleInternal(context, handleId);
if(handle!=null && !handle.isColumnNull("resource_id"))
{
//Check if this handle is already linked up to this specified DSpace Object
int resourceID = handle.getIntColumn("resource_id");
int resourceType = handle.getIntColumn("resource_type_id");
if(resourceID==dso.getID() && resourceType ==dso.getType())
{
//This handle already links to this DSpace Object -- so, there's nothing else we need to do
return handleId;
}
else
{
//handle found in DB table & already in use by another existing resource
throw new IllegalStateException("Attempted to create a handle which is already in use: " + handleId);
}
}
}
else if(handle!=null && !handle.isColumnNull("resource_type_id"))
{
//If there is a 'resource_type_id' (but 'resource_id' is empty), then the object using
// this handle was previously unbound (see unbindHandle() method) -- likely because object was deleted
int previousType = handle.getIntColumn("resource_type_id");
//Since we are restoring an object to a pre-existing handle, double check we are restoring the same *type* of object
// (e.g. we will not allow an Item to be restored to a handle previously used by a Collection)
if(previousType != dso.getType())
{
throw new IllegalStateException("Attempted to reuse a handle previously used by a " +
Constants.typeText[previousType] + " for a new " +
Constants.typeText[dso.getType()]);
}
}
if(handle==null){
handle = DatabaseManager.create(context, "Handle");
handleId = createId(handle.getIntColumn("handle_id"));
}
modifyHandleRecord(context, dso, handle, handleId);
return handleId;
}
protected String makeIdentifierBasedOnHistory(Context context, DSpaceObject dso, String handleId, VersionHistory history) throws AuthorizeException, SQLException
{
Item item = (Item)dso;
// FIRST time a VERSION is created 2 identifiers will be minted and the canonical will be updated to point to the newer URL:
// - id.1-->old URL
// - id.2-->new URL
Version version = history.getVersion(item);
Version previous = history.getPrevious(version);
String canonical = getCanonical(previous.getItem());
if (history.isFirstVersion(previous))
{
// add a new Identifier for previous item: 12345/100.1
String identifierPreviousItem=canonical + DOT + 1;
TableRow handle = DatabaseManager.create(context, "Handle");
modifyHandleRecord(context, previous.getItem(), handle, identifierPreviousItem);
}
// add a new Identifier for this item: 12345/100.x
String idNew = canonical + DOT + version.getVersionNumber();
TableRow handle = DatabaseManager.create(context, "Handle");
modifyHandleRecord(context, dso, handle, idNew);
return handleId;
}
protected String modifyHandleRecord(Context context, DSpaceObject dso, TableRow handle, String handleId) throws SQLException
{
handle.setColumn("handle", handleId);
handle.setColumn("resource_type_id", dso.getType());
handle.setColumn("resource_id", dso.getID());
DatabaseManager.update(context, handle);
if (log.isDebugEnabled())
{
log.debug("Created new handle for "
+ Constants.typeText[dso.getType()] + " " + handleId);
}
return handleId;
}
protected String getCanonical(Item item)
{
String canonical = item.getHandle();
if( canonical.lastIndexOf(DOT)!=-1)
{
canonical = canonical.substring(0, canonical.lastIndexOf(DOT));
}
return canonical;
}
protected String getCanonical(String identifier)
{
String canonical = identifier;
if( canonical.lastIndexOf(DOT)!=-1)
{
canonical = canonical.substring(0, canonical.lastIndexOf(DOT));
}
return canonical;
}
/**
* Find the database row corresponding to handle.
*
* @param context DSpace context
* @param handle The handle to resolve
* @return The database row corresponding to the handle
* @exception java.sql.SQLException If a database error occurs
*/
protected static TableRow findHandleInternal(Context context, String handle)
throws SQLException {
if (handle == null)
{
throw new IllegalArgumentException("Handle is null");
}
return DatabaseManager.findByUnique(context, "Handle", "handle", handle);
}
/**
* Return the handle for an Object, or null if the Object has no handle.
*
* @param context
* DSpace context
* @param type
* The type of object
* @param id
* The id of object
* @return The handle for object, or null if the object has no handle.
* @exception java.sql.SQLException
* If a database error occurs
*/
protected static TableRow getHandleInternal(Context context, int type, int id)
throws SQLException
{
String sql = "SELECT * FROM Handle WHERE resource_type_id = ? AND resource_id = ?";
return DatabaseManager.querySingleTable(context, "Handle", sql, type, id);
}
/**
* Create a new handle id. The implementation uses the PK of the RDBMS
* Handle table.
*
* @return A new handle id
* @exception java.sql.SQLException
* If a database error occurs
*/
protected static String createId(int id) throws SQLException
{
String handlePrefix = getPrefix();
return handlePrefix + (handlePrefix.endsWith("/") ? "" : "/") + id;
}
protected VersionHistory retrieveVersionHistory(Context c, Item item)
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
return versioningService.findVersionHistory(c, item.getID());
}
protected void populateHandleMetadata(Item item)
throws SQLException, IOException, AuthorizeException
{
String handleref = getCanonicalForm(getCanonical(item));
// Add handle as identifier.uri DC value.
// First check that identifier doesn't already exist.
boolean identifierExists = false;
DCValue[] identifiers = item.getDC("identifier", "uri", Item.ANY);
for (DCValue identifier : identifiers)
{
if (handleref.equals(identifier.value))
{
identifierExists = true;
}
}
if (!identifierExists)
{
item.addDC("identifier", "uri", null, handleref);
}
}
protected void modifyHandleMetadata(Item item, String handle)
throws SQLException, IOException, AuthorizeException
{
String handleref = getCanonicalForm(handle);
item.clearMetadata("dc", "identifier", "uri", Item.ANY);
item.addDC("identifier", "uri", null, handleref);
item.update();
}
protected VersionImpl createVersion(Context c, VersionHistory vh, Item item, String summary, Date date, int versionNumber) {
try {
VersionImpl version = versionDAO.create(c);
// check if an equals versionNumber is already present in the DB (at this point it should never happen).
if(vh!=null && vh.getVersions()!=null){
for(Version v : vh.getVersions()){
if(v.getVersionNumber()==versionNumber){
throw new RuntimeException("A Version for this versionNumber is already present. Impossible complete the operation.");
}
}
}
version.setVersionNumber(versionNumber);
version.setVersionDate(date);
version.setEperson(item.getSubmitter());
version.setItemID(item.getID());
version.setSummary(summary);
version.setVersionHistory(vh.getVersionHistoryId());
versionDAO.update(version);
return version;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
protected int getNextVersionNumer(Version latest){
if(latest==null) return 1;
return latest.getVersionNumber()+1;
}
public void setVersionDAO(VersionDAO versionDAO)
{
this.versionDAO = versionDAO;
}
public void setVersionHistoryDAO(VersionHistoryDAO versionHistoryDAO)
{
this.versionHistoryDAO = versionHistoryDAO;
}
}

View File

@@ -637,32 +637,38 @@ public class BitstreamStorageManager
continue; // do not delete registered bitstreams
}
boolean success = file.delete();
String message = ("Deleted bitstream " + bid + " (file "
+ file.getAbsolutePath() + ") with result "
+ success);
if (log.isDebugEnabled())
// Since versioning allows for multiple bitstreams, check if the internal identifier isn't used on another place
TableRow duplicateBitRow = DatabaseManager.querySingleTable(context, "Bitstream", "SELECT * FROM Bitstream WHERE internal_id = ? AND bitstream_id <> ?", row.getStringColumn("internal_id"), bid);
if(duplicateBitRow == null)
{
log.debug(message);
}
if (verbose)
{
System.out.println(message);
boolean success = file.delete();
String message = ("Deleted bitstream " + bid + " (file "
+ file.getAbsolutePath() + ") with result "
+ success);
if (log.isDebugEnabled())
{
log.debug(message);
}
if (verbose)
{
System.out.println(message);
}
// if the file was deleted then
// try deleting the parents
// Otherwise the cleanup script is set to
// leave the db records then the file
// and directories have already been deleted
// if this is turned off then it still looks like the
// file exists
if( success )
{
deleteParents(file);
}
}
// if the file was deleted then
// try deleting the parents
// Otherwise the cleanup script is set to
// leave the db records then the file
// and directories have already been deleted
// if this is turned off then it still looks like the
// file exists
if( success )
{
deleteParents(file);
}
// Make sure to commit our outstanding work every 100
// iterations. Otherwise you risk losing the entire transaction
// if we hit an exception, which isn't useful at all for large
@@ -701,6 +707,22 @@ public class BitstreamStorageManager
}
}
/**
*
* @param context
* @param id of the bitstream to clone
* @return
* @throws SQLException
*/
public static int clone(Context context, int id) throws SQLException
{
TableRow row = DatabaseManager.find(context, "bitstream", id);
row.setColumn("bitstream_id", -1);
DatabaseManager.insert(context, row);
return row.getIntColumn("bitstream_id");
}
////////////////////////////////////////
// Internal methods
////////////////////////////////////////

View File

@@ -0,0 +1,70 @@
/**
* 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.versioning;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.*;
import org.dspace.core.Context;
import org.dspace.storage.bitstore.BitstreamStorageManager;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public abstract class AbstractVersionProvider {
protected void copyMetadata(Item itemNew, Item nativeItem){
DCValue[] md = nativeItem.getMetadata(Item.ANY, Item.ANY, Item.ANY, Item.ANY);
for (DCValue aMd : md) {
if ((aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("relation") && (aMd.qualifier != null && aMd.qualifier.equals("haspart")))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("relation") && (aMd.qualifier != null && aMd.qualifier.equals("ispartof")))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("identifier"))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("relation") && (aMd.qualifier != null && aMd.qualifier.equals("isversionof")))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("date") && (aMd.qualifier != null && aMd.qualifier.equals("accessioned")))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("date") && (aMd.qualifier != null && aMd.qualifier.equals("available")))
|| (aMd.schema.equals(MetadataSchema.DC_SCHEMA) && aMd.element.equals("description") && (aMd.qualifier != null && aMd.qualifier.equals("provenance"))))
{
continue;
}
itemNew.addMetadata(aMd.schema, aMd.element, aMd.qualifier, aMd.language, aMd.value);
}
}
protected void createBundlesAndAddBitstreams(Context c, Item itemNew, Item nativeItem) throws SQLException, AuthorizeException {
for(Bundle nativeBundle : nativeItem.getBundles())
{
Bundle bundleNew = itemNew.createBundle(nativeBundle.getName());
for(Bitstream nativeBitstream : nativeBundle.getBitstreams())
{
Bitstream bitstreamNew = createBitstream(c, nativeBitstream);
bundleNew.addBitstream(bitstreamNew);
if(nativeBundle.getPrimaryBitstreamID() == nativeBitstream.getID())
{
bundleNew.setPrimaryBitstreamID(bitstreamNew.getID());
}
}
}
}
protected Bitstream createBitstream(Context context, Bitstream nativeBitstream) throws AuthorizeException, SQLException {
int idNew = BitstreamStorageManager.clone(context, nativeBitstream.getID());
return Bitstream.find(context, idNew);
}
}

View File

@@ -0,0 +1,95 @@
/**
* 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.versioning;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context;
import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.IdentifierService;
import org.dspace.utils.DSpace;
import java.io.IOException;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class DefaultItemVersionProvider extends AbstractVersionProvider implements ItemVersionProvider
{
public Item createNewItemAndAddItInWorkspace(Context context, Item nativeItem) {
try
{
WorkspaceItem workspaceItem = WorkspaceItem.create(context, nativeItem.getOwningCollection(), false);
Item itemNew = workspaceItem.getItem();
itemNew.update();
return itemNew;
}catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}catch (AuthorizeException e) {
throw new RuntimeException(e.getMessage(), e);
}catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void deleteVersionedItem(Context c, Version versionToDelete, VersionHistory history)
{
try
{
// if versionToDelete is the current version we have to reinstate the previous version
// and reset canonical
if(history.isLastVersion(versionToDelete) && history.size() > 1)
{
// reset the previous version to archived
Item item = history.getPrevious(versionToDelete).getItem();
item.setArchived(true);
item.update();
}
// assign tombstone to the Identifier and reset canonical to the previous version only if there is a previous version
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
Item itemToDelete=versionToDelete.getItem();
identifierService.delete(c, itemToDelete);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (AuthorizeException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (IdentifierException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public Item updateItemState(Context c, Item itemNew, Item previousItem)
{
try
{
copyMetadata(itemNew, previousItem);
createBundlesAndAddBitstreams(c, itemNew, previousItem);
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
try
{
identifierService.reserve(c, itemNew);
} catch (IdentifierException e) {
throw new RuntimeException("Can't create Identifier!");
}
itemNew.update();
return itemNew;
}catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (AuthorizeException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,24 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Context;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface ItemVersionProvider {
public Item createNewItemAndAddItInWorkspace(Context c, Item item);
public void deleteVersionedItem(Context c, Version versionToDelete, VersionHistory history);
public Item updateItemState(Context c, Item itemNew, Item previousItem);
}

View File

@@ -0,0 +1,33 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.eperson.EPerson;
import java.util.Date;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface Version
{
public EPerson getEperson();
public int getItemID();
public Date getVersionDate();
public int getVersionNumber();
public String getSummary();
public int getVersionHistoryID();
public int getVersionId();
public Item getItem();
}

View File

@@ -0,0 +1,169 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionDAO
{
protected final static String TABLE_NAME = "versionitem";
protected final static String VERSION_ID = "versionitem_id";
protected final static String ITEM_ID = "item_id";
protected final static String VERSION_NUMBER = "version_number";
protected final static String EPERSON_ID = "eperson_id";
protected final static String VERSION_DATE = "version_date";
protected final static String VERSION_SUMMARY = "version_summary";
protected final static String HISTORY_ID = "versionhistory_id";
public VersionImpl find(Context context, int id) {
try
{
TableRow row = DatabaseManager.findByUnique(context, TABLE_NAME, VERSION_ID, id);
if (row == null)
{
return null;
}
return new VersionImpl(context, row);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public VersionImpl findByItem(Context c, Item item) {
return findByItemId(c, item.getID());
}
public VersionImpl findByItemId(Context context, int itemId) {
try {
if (itemId == 0 || itemId == -1)
{
return null;
}
VersionImpl fromCache = (VersionImpl) context.fromCache(VersionImpl.class, itemId);
if (fromCache != null)
{
return fromCache;
}
TableRow row = DatabaseManager.findByUnique(context, TABLE_NAME, ITEM_ID, itemId);
if (row == null)
{
return null;
}
return new VersionImpl(context, row);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public List<Version> findByVersionHistory(Context context, int versionHistoryId) {
TableRowIterator tri = null;
try {
tri = DatabaseManager.query(context, "SELECT * FROM " + TABLE_NAME + " where " + HISTORY_ID + "=" + versionHistoryId + " order by " + VERSION_NUMBER + " desc");
List<Version> versions = new ArrayList<Version>();
while (tri.hasNext())
{
TableRow tr = tri.next();
VersionImpl fromCache = (VersionImpl) context.fromCache(VersionImpl.class, tr.getIntColumn(VERSION_ID));
if (fromCache != null)
{
versions.add(fromCache);
}else{
versions.add(new VersionImpl(context, tr));
}
}
return versions;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
if (tri != null)
{
tri.close();
}
}
}
public VersionImpl create(Context context) {
try {
TableRow row = DatabaseManager.create(context, TABLE_NAME);
VersionImpl v = new VersionImpl(context, row);
//TODO Do I have to manage the event?
//context.addEvent(new Event(Event.CREATE, Constants.VERSION, e.getID(), null));
return v;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void delete(Context c, int versionID) {
try {
//TODO Do I have to manage the event?
//context.addEvent(new Event(Event.DELETE, Constants.VERSION, getID(), getEmail()));
// Remove ourself
VersionImpl version = find(c, versionID);
if(version!=null){
//Remove ourself from our cache first !
c.removeCached(version, version.getVersionId());
DatabaseManager.delete(c, version.getMyRow());
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void update(VersionImpl version) {
try {
//TODO Do I have to manage the event?
DatabaseManager.update(version.getMyContext(), version.getMyRow());
// if (modified)
// {
// myContext.addEvent(new Event(Event.MODIFY, Constants.EPERSON, getID(), null));
// modified = false;
// }
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,36 @@
/**
* 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.versioning;
import java.util.List;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface VersionHistory {
public Version getLatestVersion();
public Version getFirstVersion();
public List<Version> getVersions();
public int getVersionHistoryId();
public Version getPrevious(Version version);
public Version getNext(Version version);
public boolean hasNext(Version version);
public void add(Version version);
public Version getVersion(org.dspace.content.Item item);
public boolean hasNext(org.dspace.content.Item item);
public boolean isFirstVersion(Version version);
public boolean isLastVersion(Version version);
public void remove(Version version);
public boolean isEmpty();
public int size();
}

View File

@@ -0,0 +1,115 @@
/**
* 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.versioning;
import org.dspace.core.Context;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import java.sql.SQLException;
import java.util.List;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionHistoryDAO
{
protected final static String TABLE_NAME="versionhistory";
protected final static String VERSION_HISTORY_ID = "versionhistory_id";
public VersionHistoryImpl create(Context context)
{
try {
TableRow row = DatabaseManager.create(context, TABLE_NAME);
VersionHistoryImpl vh = new VersionHistoryImpl(context, row);
//TODO Do I have to manage the event?
//context.addEvent(new Event(Event.CREATE, Constants.EPERSON, e.getID(), null));
return vh;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public VersionHistoryImpl find(Context context, int itemID, VersionDAO versionDAO)
{
try {
Version version = versionDAO.findByItemId(context, itemID);
if(version==null)
{
return null;
}
VersionHistoryImpl fromCache = (VersionHistoryImpl) context.fromCache(VersionHistoryImpl.class, version.getVersionHistoryID());
if (fromCache != null)
{
return fromCache;
}
TableRow row = DatabaseManager.find(context, TABLE_NAME, version.getVersionHistoryID());
VersionHistoryImpl vh = new VersionHistoryImpl(context, row);
List<Version> versions= versionDAO.findByVersionHistory(context, vh.getVersionHistoryId());
vh.setVersions(versions);
return vh;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public VersionHistoryImpl findById(Context context, int id, VersionDAO versionDAO)
{
try {
TableRow row = DatabaseManager.find(context, TABLE_NAME, id);
if (row == null) return null;
VersionHistoryImpl fromCache = (VersionHistoryImpl) context.fromCache(VersionHistoryImpl.class, row.getIntColumn(VERSION_HISTORY_ID));
if (fromCache != null)
{
return fromCache;
}
VersionHistoryImpl versionHistoryImpl = new VersionHistoryImpl(context, row);
List<Version> versions= versionDAO.findByVersionHistory(context, versionHistoryImpl.getVersionHistoryId());
versionHistoryImpl.setVersions(versions);
return versionHistoryImpl;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void delete(Context c, int versionHistoryID, VersionDAO versionDAO)
{
try {
VersionHistoryImpl history = findById(c, versionHistoryID, versionDAO);
DatabaseManager.delete(c, history.getMyRow());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,192 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.storage.rdbms.TableRow;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionHistoryImpl implements VersionHistory
{
private int versionHistoryId;
private List<Version>versions;
private Context myContext;
private TableRow myRow;
protected VersionHistoryImpl(VersionHistoryDAO vhDAO){
}
protected VersionHistoryImpl(Context c, TableRow row)
{
myContext = c;
myRow = row;
c.cache(this, row.getIntColumn(VersionHistoryDAO.VERSION_HISTORY_ID));
}
public int getVersionHistoryId() {
return myRow.getIntColumn(VersionHistoryDAO.VERSION_HISTORY_ID);
}
// LIST order: descending
public Version getPrevious(Version version) {
int index = versions.indexOf(version);
if( (index+1)==versions.size()) return null;
return versions.get(index+1);
}
// LIST order: descending
public Version getNext(Version version)
{
int index = versions.indexOf(version);
if(index==0)
{
return null;
}
return versions.get(index-1);
}
public Version getVersion(Item item) {
for(Version v : versions)
{
if(v.getItem().getID()==item.getID())
{
return v;
}
}
return null;
}
public boolean hasNext(Item item)
{
Version version = getVersion(item);
return hasNext(version);
}
public boolean hasNext(Version version)
{
return getNext(version)!=null;
}
public List<Version> getVersions()
{
return versions;
}
public void setVersions(List<Version> versions)
{
this.versions = versions;
}
public void add(Version version)
{
if(versions==null) versions=new ArrayList<Version>();
versions.add(0, version);
}
public Version getLatestVersion()
{
if(versions==null || versions.size()==0)
{
return null;
}
return versions.get(0);
}
public Version getFirstVersion()
{
if(versions==null || versions.size()==0)
{
return null;
}
return versions.get(versions.size()-1);
}
public boolean isFirstVersion(Version version)
{
Version first = versions.get(versions.size()-1);
return first.equals(version);
}
public boolean isLastVersion(Version version)
{
Version last = versions.get(0);
return last.equals(version);
}
public void remove(Version version)
{
versions.remove(version);
}
public boolean isEmpty()
{
return versions.size()==0;
}
public int size()
{
return versions.size();
}
protected TableRow getMyRow()
{
return myRow;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
VersionHistoryImpl that = (VersionHistoryImpl) o;
return versionHistoryId == that.versionHistoryId;
}
@Override
public int hashCode()
{
int hash=7;
hash=79*hash+ (this.getVersionHistoryId() ^ (this.getVersionHistoryId() >>> 32));
return hash;
}
}

View File

@@ -0,0 +1,168 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.storage.rdbms.TableRow;
import java.sql.SQLException;
import java.util.Date;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionImpl implements Version {
private int versionId;
private EPerson eperson;
private int itemID=-1;
private Date versionDate;
private int versionNumber;
private String summary;
private int versionHistoryID;
private Context myContext;
private TableRow myRow;
protected VersionImpl(Context c, TableRow row)
{
myContext = c;
myRow = row;
c.cache(this, row.getIntColumn(VersionDAO.VERSION_ID));
}
public int getVersionId()
{
return myRow.getIntColumn(VersionDAO.VERSION_ID);
}
protected void setVersionId(int versionId)
{
this.versionId = versionId;
}
public EPerson getEperson(){
try {
if (eperson == null)
{
return EPerson.find(myContext, myRow.getIntColumn(VersionDAO.EPERSON_ID));
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
return eperson;
}
public void setEperson(EPerson ePerson) {
this.eperson = ePerson;
myRow.setColumn(VersionDAO.EPERSON_ID, ePerson.getID());
}
public int getItemID() {
return myRow.getIntColumn(VersionDAO.ITEM_ID);
}
public Item getItem(){
try{
if(getItemID()==-1)
{
return null;
}
return Item.find(myContext, getItemID());
}catch(SQLException e){
throw new RuntimeException(e.getMessage(), e);
}
}
public void setItemID(int itemID)
{
this.itemID = itemID;
myRow.setColumn(VersionDAO.ITEM_ID, itemID);
}
public Date getVersionDate() {
return myRow.getDateColumn(VersionDAO.VERSION_DATE);
}
public void setVersionDate(Date versionDate) {
this.versionDate = versionDate;
myRow.setColumn(VersionDAO.VERSION_DATE, versionDate);
}
public int getVersionNumber() {
return myRow.getIntColumn(VersionDAO.VERSION_NUMBER);
}
public void setVersionNumber(int versionNumber) {
this.versionNumber = versionNumber;
myRow.setColumn(VersionDAO.VERSION_NUMBER, versionNumber);
}
public String getSummary() {
return myRow.getStringColumn(VersionDAO.VERSION_SUMMARY);
}
public void setSummary(String summary) {
this.summary = summary;
myRow.setColumn(VersionDAO.VERSION_SUMMARY, summary);
}
public int getVersionHistoryID() {
return myRow.getIntColumn(VersionDAO.HISTORY_ID);
}
public void setVersionHistory(int versionHistoryID) {
this.versionHistoryID = versionHistoryID;
myRow.setColumn(VersionDAO.HISTORY_ID, versionHistoryID);
}
public Context getMyContext(){
return myContext;
}
protected TableRow getMyRow(){
return myRow;
}
@Override
public boolean equals(Object o) {
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
VersionImpl version = (VersionImpl) o;
return getVersionId() == version.getVersionId();
}
@Override
public int hashCode() {
int hash=7;
hash=79*hash+(int) (this.getVersionId() ^ (this.getVersionId() >>> 32));
return hash;
}
}

View File

@@ -0,0 +1,86 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import org.dspace.utils.DSpace;
import java.util.HashSet;
import java.util.Set;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersioningConsumer implements Consumer {
private static Set<Item> itemsToProcess;
public void initialize() throws Exception {}
public void finish(Context ctx) throws Exception {}
public void consume(Context ctx, Event event) throws Exception {
if(itemsToProcess == null){
itemsToProcess = new HashSet<Item>();
}
int st = event.getSubjectType();
int et = event.getEventType();
if(st == Constants.ITEM && et == Event.INSTALL){
Item item = (Item) event.getSubject(ctx);
if (item != null && item.isArchived()) {
VersionHistory history = retrieveVersionHistory(ctx, item);
if (history != null) {
Version latest = history.getLatestVersion();
Version previous = history.getPrevious(latest);
if(previous != null){
Item previousItem = previous.getItem();
if(previousItem != null){
itemsToProcess.add(previousItem);
//Fire a new modify event for our previous item
//Due to the need to reindex the item in the search & browse index we need to fire a new event
ctx.addEvent(new Event(Event.MODIFY, previousItem.getType(), previousItem.getID(), null));
}
}
}
}
}
}
public void end(Context ctx) throws Exception {
if(itemsToProcess != null){
for(Item item : itemsToProcess){
ctx.turnOffAuthorisationSystem();
try {
item.setArchived(false);
item.update();
} finally {
ctx.restoreAuthSystemState();
}
}
ctx.getDBConnection().commit();
}
itemsToProcess = null;
}
private static org.dspace.versioning.VersionHistory retrieveVersionHistory(Context c, Item item) {
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
return versioningService.findVersionHistory(c, item.getID());
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.versioning;
import org.dspace.content.Item;
import org.dspace.core.Context;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface VersioningService {
Version createNewVersion(Context c, int itemId);
Version createNewVersion(Context c, int itemId, String summary);
void removeVersion(Context c, int versionID);
void removeVersion(Context c, Item item);
Version getVersion(Context c, int versionID);
Version restoreVersion(Context c, int versionID);
Version restoreVersion(Context c, int versionID, String summary);
VersionHistory findVersionHistory(Context c, int itemId);
Version updateVersion(Context c, int itemId, String summary);
Version getVersion(Context c, Item item);
}

View File

@@ -0,0 +1,188 @@
/**
* 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.versioning;
import org.dspace.content.Collection;
import org.dspace.content.DCDate;
import org.dspace.content.DCValue;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Required;
import java.sql.SQLException;
import java.util.Date;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersioningServiceImpl implements VersioningService{
private VersionHistoryDAO versionHistoryDAO;
private VersionDAO versionDAO;
private DefaultItemVersionProvider provider;
/** Service Methods */
public Version createNewVersion(Context c, int itemId){
return createNewVersion(c, itemId, null);
}
public Version createNewVersion(Context c, int itemId, String summary) {
try{
Item item = Item.find(c, itemId);
VersionHistory vh = versionHistoryDAO.find(c, item.getID(), versionDAO);
if(vh==null)
{
// first time: create 2 versions, .1(old version) and .2(new version)
vh=versionHistoryDAO.create(c);
// get dc:date.accessioned to be set as first version date...
DCValue[] values = item.getMetadata("dc", "date", "accessioned", Item.ANY);
Date versionDate = new Date();
if(values!=null && values.length > 0){
String date = values[0].value;
versionDate = new DCDate(date).toDate();
}
createVersion(c, vh, item, "", versionDate);
}
// Create new Item
Item itemNew = provider.createNewItemAndAddItInWorkspace(c, item);
// create new version
Version version = createVersion(c, vh, itemNew, summary, new Date());
// Complete any update of the Item and new Identifier generation that needs to happen
provider.updateItemState(c, itemNew, item);
return version;
}catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void removeVersion(Context c, int versionID) {
Version version = versionDAO.find(c, versionID);
if(version!=null){
removeVersion(c, version);
}
}
public void removeVersion(Context c, Item item) {
Version version = versionDAO.findByItem(c, item);
if(version!=null){
removeVersion(c, version);
}
}
protected void removeVersion(Context c, Version version) {
try{
VersionHistory history = versionHistoryDAO.findById(c, version.getVersionHistoryID(), versionDAO);
provider.deleteVersionedItem(c, version, history);
versionDAO.delete(c, version.getVersionId());
history.remove(version);
if(history.isEmpty()){
versionHistoryDAO.delete(c, version.getVersionHistoryID(), versionDAO);
}
//Delete the item linked to the version
Item item = version.getItem();
Collection[] collections = item.getCollections();
// Remove item from all the collections it's in (so our item is also deleted)
for (Collection collection : collections)
{
collection.removeItem(item);
}
}catch (Exception e) {
c.abort();
throw new RuntimeException(e.getMessage(), e);
}
}
public Version getVersion(Context c, int versionID) {
return versionDAO.find(c, versionID);
}
public Version restoreVersion(Context c, int versionID){
return restoreVersion(c, versionID, null);
}
public Version restoreVersion(Context c, int versionID, String summary) {
return null;
}
public VersionHistory findVersionHistory(Context c, int itemId){
return versionHistoryDAO.find(c, itemId, versionDAO);
}
public Version updateVersion(Context c, int itemId, String summary) {
VersionImpl version = versionDAO.findByItemId(c, itemId);
version.setSummary(summary);
versionDAO.update(version);
return version;
}
public Version getVersion(Context c, Item item){
return versionDAO.findByItemId(c, item.getID());
}
// **** PROTECTED METHODS!!
protected VersionImpl createVersion(Context c, VersionHistory vh, Item item, String summary, Date date) {
try {
VersionImpl version = versionDAO.create(c);
version.setVersionNumber(getNextVersionNumer(vh.getLatestVersion()));
version.setVersionDate(date);
version.setEperson(item.getSubmitter());
version.setItemID(item.getID());
version.setSummary(summary);
version.setVersionHistory(vh.getVersionHistoryId());
versionDAO.update(version);
vh.add(version);
return version;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
protected int getNextVersionNumer(Version latest){
if(latest==null) return 1;
return latest.getVersionNumber()+1;
}
public VersionHistoryDAO getVersionHistoryDAO() {
return versionHistoryDAO;
}
public void setVersionHistoryDAO(VersionHistoryDAO versionHistoryDAO) {
this.versionHistoryDAO = versionHistoryDAO;
}
public VersionDAO getVersionDAO() {
return versionDAO;
}
public void setVersionDAO(VersionDAO versionDAO) {
this.versionDAO = versionDAO;
}
@Required
public void setProvider(DefaultItemVersionProvider provider) {
this.provider = provider;
}
}

View File

@@ -355,7 +355,7 @@ public class WorkflowItem implements InProgressSubmission
/**
* Update the workflow item, including the unarchived item.
*/
public void update() throws SQLException, IOException, AuthorizeException
public void update() throws SQLException, AuthorizeException
{
// FIXME check auth
log.info(LogManager.getHeader(ourContext, "update_workflow_item",

View File

@@ -63,7 +63,7 @@ public class XmlWorkflowItem implements InProgressSubmission {
*/
// private ArrayList<StepRecord> activeSteps;
XmlWorkflowItem(Context context, TableRow row) throws SQLException {
XmlWorkflowItem(Context context, TableRow row) throws SQLException, AuthorizeException, IOException {
ourContext = context;
wfRow = row;
// activeSteps = new ArrayList<StepRecord>();
@@ -360,35 +360,36 @@ public class XmlWorkflowItem implements InProgressSubmission {
/**
* Check to see if a particular item is currently under Workflow.
* If so, its XmlWorkflowItem is returned. If not, null is returned
* If so, its WorkflowItem is returned. If not, null is returned
*
* @param context
* the context object
* @param i
* @param item
* the item
*
* @return XmlWorkflow item corresponding to the item, or null
* @return workflow item corresponding to the item, or null
*/
public static XmlWorkflowItem findByItem(Context context, Item i)
throws SQLException
{
// Look for the unique workflowitem entry where 'item_id' references this item
TableRow row = DatabaseManager.findByUnique(context, "cwf_workflowitem", "item_id", i.getID());
public static XmlWorkflowItem findByItem(Context context, Item item) throws SQLException {
TableRow row = DatabaseManager.findByUnique(context, "cwf_workflowitem", "item_id", item.getID());
if (row == null)
{
return null;
}
else
{
return new XmlWorkflowItem(context, row);
XmlWorkflowItem wi = null;
if(row != null){
// Check the cache
wi = (XmlWorkflowItem) context.fromCache(XmlWorkflowItem.class, row.getIntColumn("workflowitem_id"));
// not in cache? turn row into workflowitem
if (wi == null)
{
wi = new XmlWorkflowItem(context, row);
}
}
return wi;
}
/**
* Update the workflow item, including the unarchived item.
*/
public void update() throws SQLException, IOException, AuthorizeException {
public void update() throws SQLException, AuthorizeException {
// FIXME check auth
log.info(LogManager.getHeader(ourContext, "update_workflow_item",
"workflowitem_id=" + getID()));

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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/
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- Identifier Service Application Interface. Will be autowired with
any Identifier Providers present in Spring context.
-->
<bean id="org.dspace.identifier.IdentifierService"
class="org.dspace.identifier.IdentifierServiceImpl"
autowire="byType"
scope="singleton"/>
<!-- provider for exposing default Handle services implementaion. -->
<!--bean id="org.dspace.identifier.HandleIdentifierProvider" class="org.dspace.identifier.HandleIdentifierProvider"
scope="singleton">
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
</bean-->
<bean id="org.dspace.identifier.VersionedHandleIdentifierProvider" class="org.dspace.identifier.VersionedHandleIdentifierProvider"
scope="singleton">
<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>
<property name="versionDAO">
<bean class="org.dspace.versioning.VersionDAO"/>
</property>
<property name="versionHistoryDAO">
<bean class="org.dspace.versioning.VersionHistoryDAO"/>
</property>
</bean>
<!-- !!!!!!!!!!!!!!!!!!!!! ITEM LEVEL VERSIONING SERVICES !!!!!!!!!!!!!!!!!!!!! -->
<!-- Versioning Service Application Interface for DSpace Will be autowired with
a Versioning Provider present in Spring context.-->
<bean id="org.dspace.versioning.VersioningService"
class="org.dspace.versioning.VersioningServiceImpl"
autowire="byType"
scope="singleton">
<property name="versionDAO">
<bean class="org.dspace.versioning.VersionDAO"/>
</property>
<property name="versionHistoryDAO">
<bean class="org.dspace.versioning.VersionHistoryDAO"/>
</property>
<property name="provider">
<!-- Default Item Versioning Provider, defines behavior for replicating
Item, Metadata, BUndles and Bitstreams. Autowired at this time. -->
<bean class="org.dspace.versioning.DefaultItemVersionProvider"/>
</property>
</bean>
</beans>

View File

@@ -10,11 +10,8 @@ package org.dspace;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.sql.SQLException;
import java.util.Properties;
import java.util.TimeZone;
@@ -38,6 +35,8 @@ import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.search.DSIndexer;
import org.dspace.servicemanager.DSpaceKernelImpl;
import org.dspace.servicemanager.DSpaceKernelInit;
import org.dspace.storage.rdbms.MockDatabaseManager;
import org.junit.After;
import org.junit.AfterClass;
@@ -78,6 +77,7 @@ public class AbstractUnitTest
*/
protected static EPerson eperson;
protected static DSpaceKernelImpl kernelImpl;
/**
* This method will be run before the first test as per @BeforeClass. It will
@@ -111,6 +111,13 @@ public class AbstractUnitTest
//load the test configuration file
ConfigurationManager.loadConfig(null);
// Initialise the service manager kernel
kernelImpl = DSpaceKernelInit.getKernel(null);
if (!kernelImpl.isRunning())
{
kernelImpl.start(ConfigurationManager.getProperty("dspace.dir"));
}
// Load the default registries. This assumes the temporary
// filesystem is working and the in-memory DB in place.
Context ctx = new Context();

View File

@@ -0,0 +1,139 @@
/**
* 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.content;
import org.apache.log4j.Logger;
import org.dspace.AbstractUnitTest;
import org.dspace.authorize.AuthorizeException;
import org.dspace.handle.HandleManager;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.VersioningService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.sql.SQLException;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* Unit Tests for versioning
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersioningTest extends AbstractUnitTest {
private static final Logger log = Logger.getLogger(VersioningTest.class);
private Item originalItem;
private Item versionedItem;
private String summary = "Unit test version";
private DSpace dspace = new DSpace();
private VersioningService versioningService = dspace.getSingletonService(VersioningService.class);
/**
* This method will be run before every test as per @Before. It will
* initialize resources required for the tests.
*
* Other methods can be annotated with @Before here or in subclasses
* but no execution order is guaranteed
*/
@Before
@Override
public void init()
{
super.init();
try
{
context.turnOffAuthorisationSystem();
Collection col = Collection.create(context);
WorkspaceItem is = WorkspaceItem.create(context, col, false);
originalItem = InstallItem.installItem(context, is);
Version version = versioningService.createNewVersion(context, originalItem.getID(), summary);
WorkspaceItem wsi = WorkspaceItem.findByItem(context, version.getItem());
versionedItem = InstallItem.installItem(context, wsi);
context.restoreAuthSystemState();
context.commit();
}
catch (AuthorizeException ex)
{
log.error("Authorization Error in init", ex);
fail("Authorization Error in init");
}
catch (SQLException ex)
{
log.error("SQL Error in init", ex);
fail("SQL Error in init");
} catch (IOException ex) {
log.error("IO Error in init", ex);
fail("IO Error in init");
}
}
/**
* This method will be run after every test as per @After. It will
* clean resources initialized by the @Before methods.
*
* Other methods can be annotated with @After here or in subclasses
* but no execution order is guaranteed
*/
@After
@Override
public void destroy()
{
super.destroy();
}
@Test
public void testVersionFind(){
VersionHistory versionHistory = versioningService.findVersionHistory(context, originalItem.getID());
assertThat("testFindVersionHistory", versionHistory, notNullValue());
Version version = versionHistory.getVersion(versionedItem);
assertThat("testFindVersion", version, notNullValue());
}
/**
* Test of installItem method, of class InstallItem.
*/
@Test
public void testVersionSummary() throws Exception
{
//Start by creating a new item !
VersionHistory versionHistory = versioningService.findVersionHistory(context, originalItem.getID());
Version version = versionHistory.getVersion(versionedItem);
assertThat("Test_version_summary", summary, equalTo(version.getSummary()));
}
@Test
public void testVersionHandle() throws Exception {
assertThat("Test_version_handle", versionedItem.getHandle(), notNullValue());
}
@Test
public void testVersionDelete() throws Exception {
context.turnOffAuthorisationSystem();
versioningService.removeVersion(context, versionedItem);
assertThat("Test_version_delete", Item.find(context, versionedItem.getID()), nullValue());
assertThat("Test_version_handle_delete", HandleManager.resolveToObject(context, versionedItem.getHandle()), nullValue());
context.restoreAuthSystemState();
}
}

View File

@@ -116,6 +116,8 @@ CREATE SEQUENCE group2group_seq;
CREATE SEQUENCE group2groupcache_seq;
CREATE SEQUENCE harvested_collection_seq;
CREATE SEQUENCE harvested_item_seq;
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;
-------------------------------------------------------
-- BitstreamFormatRegistry table
@@ -787,6 +789,24 @@ CREATE INDEX harvested_item_fk_idx ON harvested_item(item_id);
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;

View File

@@ -82,6 +82,13 @@
<artifactId>gson</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,144 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.apache.commons.lang.StringUtils;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCDate;
import org.dspace.content.DCValue;
import org.dspace.content.MetadataSchema;
import org.dspace.eperson.EPerson;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersioningService;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class DeleteVersionsConfirm extends AbstractDSpaceTransformer {
/** Language strings */
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
private static final Message T_title = message("xmlui.aspect.versioning.DeleteVersionsConfirm.title");
private static final Message T_trail = message("xmlui.aspect.versioning.DeleteVersionsConfirm.trail");
private static final Message T_head1 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.head1");
private static final Message T_para1 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.para1");
private static final Message T_para2 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.para2");
private static final Message T_column1 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.column1");
private static final Message T_column2 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.column2");
private static final Message T_column3 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.column3");
private static final Message T_column4 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.column4");
private static final Message T_column5 = message("xmlui.aspect.versioning.DeleteVersionsConfirm.column5");
private static final Message T_submit_delete = message("xmlui.general.delete");
private static final Message T_submit_cancel = message("xmlui.general.cancel");
public void addPageMeta(PageMeta pageMeta) throws WingException {
pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
//pageMeta.addTrailLink(contextPath + "/admin/item",T_item_trail);
pageMeta.addTrail().addContent(T_trail);
}
public void addBody(Body body) throws WingException, AuthorizeException {
Division main = createMainDivision(body);
createTable(main);
addButtons(main);
main.addHidden("versioning-continue").setValue(knot.getId());
}
private Division createMainDivision(Body body) throws WingException {
Division main = body.addInteractiveDivision("versions-confirm-delete", contextPath+"/item/versionhistory", Division.METHOD_POST, "delete version");
main.setHead(T_head1);
Para helpPara = main.addPara();
helpPara.addContent(T_para1);
helpPara.addHighlight("bold").addContent(T_para2);
return main;
}
private void createTable(Division main) throws WingException {
// Get all our parameters
String idsString = parameters.getParameter("versionIDs", null);
Table table = main.addTable("versions-confirm-delete", 1, 1);
Row header = table.addRow(Row.ROLE_HEADER);
header.addCellContent(T_column1);
header.addCellContent(T_column2);
header.addCellContent(T_column3);
header.addCellContent(T_column4);
header.addCellContent(T_column5);
for (String id : idsString.split(","))
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
Version version = null;
if(StringUtils.isNotBlank(id))
{
version = versioningService.getVersion(context, Integer.parseInt(id));
}
if(version!=null)
{
Row row = table.addRow();
row.addCell().addContent(version.getVersionNumber());
addItemIdentifier(row.addCell(), version.getItem());
EPerson editor = version.getEperson();
row.addCell().addXref("mailto:" + editor.getEmail(), editor.getFullName());
row.addCell().addContent(new DCDate(version.getVersionDate()).toString());
row.addCell().addContent(version.getSummary());
}
}
}
private void addButtons(Division main) throws WingException {
Para buttons = main.addPara();
buttons.addButton("submit_confirm").setValue(T_submit_delete);
buttons.addButton("submit_cancel").setValue(T_submit_cancel);
}
private void addItemIdentifier(Cell cell, org.dspace.content.Item item) throws WingException {
String itemHandle = item.getHandle();
DCValue[] identifiers = item.getMetadata(MetadataSchema.DC_SCHEMA, "identifier", null, org.dspace.content.Item.ANY);
String itemIdentifier=null;
if(identifiers!=null && identifiers.length > 0)
{
itemIdentifier = identifiers[0].value;
}
if(itemIdentifier!=null)
{
cell.addXref(contextPath + "/resource/" + itemIdentifier, itemIdentifier);
}else{
cell.addXref(contextPath + "/handle/" + itemHandle, itemHandle);
}
}
}

View File

@@ -0,0 +1,220 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.apache.cocoon.caching.CacheableProcessingComponent;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.util.HashUtil;
import org.apache.excalibur.source.SourceValidity;
import org.apache.excalibur.source.impl.validity.NOPValidity;
import org.dspace.app.util.Util;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.utils.DSpaceValidity;
import org.dspace.app.xmlui.utils.HandleUtil;
import org.dspace.app.xmlui.utils.UIException;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.List;
import org.dspace.app.xmlui.wing.element.Options;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.eperson.Group;
import org.dspace.utils.DSpace;
import org.dspace.versioning.VersioningService;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
/**
*
* Navigation for Versioning of Items.
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class Navigation extends AbstractDSpaceTransformer implements CacheableProcessingComponent
{
private static final Message T_context_head = message("xmlui.administrative.Navigation.context_head");
private static final Message T_context_create_version= message("xmlui.aspect.versioning.VersioningNavigation.context_create_version");
private static final Message T_context_show_version_history= message("xmlui.aspect.versioning.VersioningNavigation.context_show_version_history");
/** Cached validity object */
private SourceValidity validity;
/**
* Generate the unique cache key.
*
* @return The generated key hashes the src
*/
public Serializable getKey()
{
Request request = ObjectModelHelper.getRequest(objectModel);
// Special case, don't cache anything if the user is logging
// in. The problem occures because of timming, this cache key
// is generated before we know whether the operation has
// succeded or failed. So we don't know whether to cache this
// under the user's specific cache or under the anonymous user.
if (request.getParameter("login_email") != null ||
request.getParameter("login_password") != null ||
request.getParameter("login_realm") != null )
{
return "0";
}
String key;
if (context.getCurrentUser() != null)
{
key = context.getCurrentUser().getEmail();
}
else
key = "anonymous";
return HashUtil.hash(key);
}
/**
* Generate the validity object.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public SourceValidity getValidity()
{
if (this.validity == null)
{
// Only use the DSpaceValidity object is someone is logged in.
if (context.getCurrentUser() != null)
{
try {
DSpaceValidity validity = new DSpaceValidity();
validity.add(eperson);
Group[] groups = Group.allMemberGroups(context, eperson);
for (Group group : groups)
{
validity.add(group);
}
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
if(dso != null)
{
validity.add(dso);
}
this.validity = validity.complete();
}
catch (SQLException sqle)
{
// Just ignore it and return invalid.
}
}
else
{
this.validity = NOPValidity.SHARED_INSTANCE;
}
}
return this.validity;
}
public void addOptions(Options options) throws SAXException, WingException,
UIException, SQLException, IOException, AuthorizeException {
/* Create skeleton menu structure to ensure consistent order between aspects,
* even if they are never used
*/
options.addList("browse");
options.addList("account");
List context = options.addList("context");
// Context Administrative options for Versioning
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
if(dso==null)
{
// case: internal-item http://localhost:8100/internal-item?itemID=3085
// case: admin http://localhost:8100/admin/item?itemID=3340
// retrieve the object from the DB
dso = getItemById();
}
if (dso != null && dso.getType() == Constants.ITEM)
{
Item item = (Item) dso;
if(AuthorizeManager.isAdmin(this.context, item.getOwningCollection()))
{
boolean headAdded=false;
if(isLatest(item) && item.isArchived())
{
context.setHead(T_context_head);
headAdded=true;
context.addItem().addXref(contextPath+"/item/version?itemID="+item.getID(), T_context_create_version);
}
if(hasVersionHistory(item))
{
if(!headAdded)
{
context.setHead(T_context_head);
}
context.addItem().addXref(contextPath+"/item/versionhistory?itemID="+item.getID(), T_context_show_version_history);
}
}
}
}
private Item getItemById() throws SQLException
{
Request request = ObjectModelHelper.getRequest(objectModel);
Item item = null;
int itemId = Util.getIntParameter(request, "itemID");
if (itemId != -1)
{
item = Item.find(this.context, itemId);
}
return item;
}
/**
* recycle
*/
public void recycle()
{
this.validity = null;
super.recycle();
}
private boolean isLatest(Item item)
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
org.dspace.versioning.VersionHistory history = versioningService.findVersionHistory(context, item.getID());
return (history==null || history.getLatestVersion().getItem().getID() == item.getID());
}
private boolean hasVersionHistory(Item item)
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
org.dspace.versioning.VersionHistory history = versioningService.findVersionHistory(context, item.getID());
return (history!=null);
}
}

View File

@@ -0,0 +1,109 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.DCDate;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersioningService;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class RestoreVersionForm extends AbstractDSpaceTransformer
{
/** Language strings */
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
private static final Message T_item_trail = message("xmlui.administrative.item.general.item_trail");
private static final Message T_title = message("xmlui.aspect.versioning.RestoreVersionForm.title");
private static final Message T_trail = message("xmlui.aspect.versioning.RestoreVersionForm.trail");
private static final Message T_head1 = message("xmlui.aspect.versioning.RestoreVersionForm.head1");
private static final Message T_para1 = message("xmlui.aspect.versioning.RestoreVersionForm.para1");
private static final Message T_column1 = message("xmlui.aspect.versioning.RestoreVersionForm.column1");
private static final Message T_column2 = message("xmlui.aspect.versioning.RestoreVersionForm.column2");
private static final Message T_column3 = message("xmlui.aspect.versioning.RestoreVersionForm.column3");
private static final Message T_column4 = message("xmlui.aspect.versioning.RestoreVersionForm.column4");
private static final Message T_submit_restore = message("xmlui.aspect.versioning.RestoreVersionForm.restore");
private static final Message T_submit_cancel = message("xmlui.general.cancel");
public void addPageMeta(PageMeta pageMeta) throws WingException
{
pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
pageMeta.addTrailLink(contextPath + "/admin/item",T_item_trail);
pageMeta.addTrail().addContent(T_trail);
}
public void addBody(Body body) throws WingException, AuthorizeException
{
Division main = createMainDivision(body);
createTable(main);
addButtons(main);
main.addHidden("versioning-continue").setValue(knot.getId());
}
private Division createMainDivision(Body body) throws WingException
{
Division main = body.addInteractiveDivision("restore-version", contextPath+"/item/versionhistory", Division.METHOD_POST, "restore version");
main.setHead(T_head1);
main.addPara(T_para1);
return main;
}
private void createTable(Division main) throws WingException
{
// Get all our parameters
String id = parameters.getParameter("versionID", null);
Table table = main.addTable("version", 1, 1);
Row header = table.addRow(Row.ROLE_HEADER);
header.addCellContent(T_column1);
header.addCellContent(T_column2);
header.addCellContent(T_column3);
header.addCellContent(T_column4);
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
Version version = versioningService.getVersion(context, Integer.parseInt(id));
Row row = table.addRow();
row.addCell().addContent(version.getVersionNumber());
row.addCell().addContent(version.getEperson().getEmail());
row.addCell().addContent(new DCDate(version.getVersionDate()).toString());
row.addCell().addContent(version.getSummary());
List fields = main.addList("fields", List.TYPE_FORM);
Composite addComposite = fields.addItem().addComposite("summary");
addComposite.setLabel(T_column4);
addComposite.addTextArea("summary");
}
private void addButtons(Division main) throws WingException
{
Para buttons = main.addPara();
buttons.addButton("submit_restore").setValue(T_submit_restore);
buttons.addButton("submit_cancel").setValue(T_submit_cancel);
}
}

View File

@@ -0,0 +1,253 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.utils.HandleUtil;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.*;
import org.dspace.content.Item;
import org.dspace.core.ConfigurationManager;
import org.dspace.eperson.EPerson;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.VersioningService;
import org.dspace.workflow.WorkflowItem;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionHistoryForm extends AbstractDSpaceTransformer {
/** Language strings */
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
private static final Message T_head2 = message("xmlui.aspect.versioning.VersionHistoryForm.head2");
private static final Message T_column1 = message("xmlui.aspect.versioning.VersionHistoryForm.column1");
private static final Message T_column2 = message("xmlui.aspect.versioning.VersionHistoryForm.column2");
private static final Message T_column3 = message("xmlui.aspect.versioning.VersionHistoryForm.column3");
private static final Message T_column4 = message("xmlui.aspect.versioning.VersionHistoryForm.column4");
private static final Message T_column5 = message("xmlui.aspect.versioning.VersionHistoryForm.column5");
private static final Message T_column6 = message("xmlui.aspect.versioning.VersionHistoryForm.column6");
private static final Message T_title = message("xmlui.aspect.versioning.VersionHistoryForm.title");
private static final Message T_trail = message("xmlui.aspect.versioning.VersionHistoryForm.trail");
private static final Message T_submit_update = message("xmlui.aspect.versioning.VersionHistoryForm.update");
private static final Message T_submit_cancel = message("xmlui.aspect.versioning.VersionHistoryForm.return");
private static final Message T_submit_delete = message("xmlui.aspect.versioning.VersionHistoryForm.delete");
private static final Message T_legend = message("xmlui.aspect.versioning.VersionHistoryForm.legend");
public void addPageMeta(PageMeta pageMeta) throws WingException, SQLException
{
pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
Item item = getItem();
if(item != null)
{
HandleUtil.buildHandleTrail(item, pageMeta, contextPath);
pageMeta.addTrailLink(contextPath + "/handle/" + item.getHandle(), item.getName());
}
pageMeta.addTrail().addContent(T_trail);
}
public void addBody(Body body) throws WingException, SQLException, AuthorizeException
{
boolean isItemView=parameters.getParameterAsInteger("itemID",-1) == -1;
Item item = getItem();
if(item==null || !AuthorizeManager.isAdmin(this.context, item.getOwningCollection()))
{
if(isItemView)
{
//Check if only administrators can view the item history
if(new DSpace().getConfigurationService().getPropertyAsType("versioning.item.history.view.admin", false))
{
return;
}
}else{
//Only admins can delete versions
throw new AuthorizeException();
}
}
VersionHistory versionHistory = retrieveVersionHistory(item);
if(versionHistory!=null)
{
Division main = createMain(body);
createTable(main, versionHistory, isItemView, item);
if(!isItemView)
{
addButtons(main, versionHistory);
main.addHidden("versioning-continue").setValue(knot.getId());
}
Para note = main.addPara();
note.addContent(T_legend);
}
}
private Item getItem() throws WingException
{
try
{
if(parameters.getParameterAsInteger("itemID",-1) == -1)
{
DSpaceObject dso;
dso = HandleUtil.obtainHandle(objectModel);
if (!(dso instanceof Item))
{
return null;
}
return (Item) dso;
}else{
return Item.find(context, parameters.getParameterAsInteger("itemID", -1));
}
} catch (SQLException e) {
throw new WingException(e);
}
}
private VersionHistory retrieveVersionHistory(Item item) throws WingException
{
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
return versioningService.findVersionHistory(context, item.getID());
}
private Division createMain(Body body) throws WingException
{
Division main = body.addInteractiveDivision("view-verion-history", contextPath+"/item/versionhistory", Division.METHOD_POST, "view version history");
main.setHead(T_head2);
return main;
}
private void createTable(Division main, VersionHistory history, boolean isItemView, Item item) throws WingException, SQLException
{
Table table = main.addTable("versionhistory", 1, 1);
Row header = table.addRow(Row.ROLE_HEADER);
if(!isItemView)
{
header.addCell().addContent("");
}
header.addCell().addContent(T_column1);
header.addCell().addContent(T_column2);
header.addCell().addContent(T_column3);
header.addCell().addContent(T_column4);
header.addCell().addContent(T_column5);
if(!isItemView)
{
header.addCell().addContent(T_column6);
}
if(history != null)
{
for(Version version : history.getVersions())
{
//Skip items currently in submission
if(isItemInSubmission(version.getItem()))
{
continue;
}
Row row = table.addRow(null, Row.ROLE_DATA,"metadata-value");
if(!isItemView)
{
CheckBox remove = row.addCell().addCheckBox("remove");
remove.setLabel("remove");
remove.addOption(version.getVersionId());
}
row.addCell().addContent(version.getVersionNumber());
addItemIdentifier(row.addCell(), item, version);
EPerson editor = version.getEperson();
row.addCell().addXref("mailto:" + editor.getEmail(), editor.getFullName());
row.addCell().addContent(new DCDate(version.getVersionDate()).toString());
row.addCell().addContent(version.getSummary());
if(!isItemView)
{
row.addCell().addXref(contextPath + "/item/versionhistory?versioning-continue="+knot.getId()+"&versionID="+version.getVersionId() +"&itemID="+ version.getItem().getID() + "&submit_update", T_submit_update);
}
}
}
}
private boolean isItemInSubmission(Item item) throws SQLException
{
WorkspaceItem workspaceItem = WorkspaceItem.findByItem(context, item);
InProgressSubmission workflowItem;
if(ConfigurationManager.getProperty("workflow", "workflow.framework").equals("xmlworkflow"))
{
workflowItem = XmlWorkflowItem.findByItem(context, item);
}else{
workflowItem = WorkflowItem.findByItem(context, item);
}
return workspaceItem != null || workflowItem != null;
}
private void addItemIdentifier(Cell cell, Item item, Version version) throws WingException
{
String itemHandle = version.getItem().getHandle();
DCValue[] identifiers = version.getItem().getMetadata(MetadataSchema.DC_SCHEMA, "identifier", null, Item.ANY);
String itemIdentifier=null;
if(identifiers!=null && identifiers.length > 0)
{
itemIdentifier = identifiers[0].value;
}
if(itemIdentifier!=null)
{
cell.addXref(contextPath + "/resource/" + itemIdentifier, itemIdentifier);
}else{
cell.addXref(contextPath + "/handle/" + itemHandle, itemHandle);
}
if(item.getID()==version.getItemID())
{
cell.addContent("*");
}
}
private void addButtons(Division main, VersionHistory history) throws WingException {
Para actions = main.addPara();
if(history!=null && history.getVersions().size() > 0)
{
actions.addButton("submit_delete").setValue(T_submit_delete);
}
actions.addButton("submit_cancel").setValue(T_submit_cancel);
}
}

View File

@@ -0,0 +1,120 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.apache.avalon.framework.parameters.ParameterException;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.utils.HandleUtil;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.Item;
import org.dspace.utils.DSpace;
import org.dspace.versioning.VersioningService;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
// Versioning
public class VersionItemForm extends AbstractDSpaceTransformer {
/** Language strings */
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
private static final Message T_submit_cancel = message("xmlui.general.cancel");
private static final Message T_title = message("xmlui.aspect.versioning.VersionItemForm.title");
private static final Message T_trail = message("xmlui.aspect.versioning.VersionItemForm.trail");
private static final Message T_head1 = message("xmlui.aspect.versioning.VersionItemForm.head1");
private static final Message T_submit_version= message("xmlui.aspect.versioning.VersionItemForm.submit_version");
private static final Message T_submit_update_version= message("xmlui.aspect.versioning.VersionItemForm.submit_update_version");
private static final Message T_summary = message("xmlui.aspect.versioning.VersionItemForm.summary");
public void addPageMeta(PageMeta pageMeta) throws WingException, SQLException {
pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
//pageMeta.addTrailLink(contextPath+"/admin/item", T_item_trail);
Item item = getItem();
if(item != null)
{
HandleUtil.buildHandleTrail(item, pageMeta, contextPath);
pageMeta.addTrailLink(contextPath + "/handle/" + item.getHandle(), item.getName());
}
pageMeta.addTrail().addContent(T_trail);
}
public void addBody(Body body) throws WingException, SQLException, AuthorizeException {
// Get our parameters and state
Item item = getItem();
//Only (collection) admins should be able to create a new version
if(!AuthorizeManager.isAdmin(context, item.getOwningCollection())){
throw new AuthorizeException();
}
String summary;
try {
summary = parameters.getParameter("summary");
} catch (ParameterException e) {
throw new RuntimeException(e);
}
// DIVISION: Main
Division main = body.addInteractiveDivision("version-item", contextPath+"/item/version", Division.METHOD_POST, "primary administrative version");
main.setHead(T_head1.parameterize(item.getHandle()));
// Fields
List fields = main.addList("fields", List.TYPE_FORM);
Composite addComposite = fields.addItem().addComposite("summary");
addComposite.setLabel(T_summary);
TextArea addValue = addComposite.addTextArea("summary");
if(summary!=null)
{
addValue.setValue(summary);
}
// Buttons
Para actions = main.addPara();
org.dspace.versioning.VersionHistory history = retrieveVersionHistory(item);
if(history!=null && history.hasNext(item))
{
actions.addButton("submit_update_version").setValue(T_submit_update_version);
}
else
{
actions.addButton("submit_version").setValue(T_submit_version);
}
actions.addButton("submit_cancel").setValue(T_submit_cancel);
main.addHidden("versioning-continue").setValue(knot.getId());
}
private Item getItem() throws SQLException {
int itemID = parameters.getParameterAsInteger("itemID",-1);
return Item.find(context, itemID);
}
private org.dspace.versioning.VersionHistory retrieveVersionHistory(Item item){
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
return versioningService.findVersionHistory(context, item.getID());
}
}

View File

@@ -0,0 +1,179 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.dspace.app.xmlui.aspect.administrative.FlowResult;
import org.dspace.app.xmlui.utils.UIException;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.Context;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.VersioningService;
import java.io.IOException;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class VersionManager {
private static final Message T_version_created = new Message("default", "The new version has been created.");
private static final Message T_version_delete = new Message("default", "The selected version(s) have been deleted.");
private static final Message T_version_updated = new Message("default", "The version has been updated.");
private static final Message T_version_restored = new Message("default", "The version has been restored.");
/**
* Create a new version of the specified item
*
* @param context The DSpace context
* @param itemID The id of the to-be-versioned item
* @return A result object
*/
// Versioning
public static FlowResult processCreateNewVersion(Context context, int itemID, String summary) throws SQLException, AuthorizeException, IOException {
FlowResult result = new FlowResult();
try {
result.setContinue(false);
Item item = Item.find(context, itemID);
if (AuthorizeManager.isAdmin(context, item) || item.canEdit()) {
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
Version version = versioningService.createNewVersion(context, itemID, summary);
WorkspaceItem wsi = WorkspaceItem.findByItem(context, version.getItem());
context.commit();
result.setParameter("wsid", wsi.getID());
result.setOutcome(true);
result.setContinue(true);
result.setMessage(T_version_created);
result.setParameter("summary", summary);
}
} catch (Exception ex) {
context.abort();
throw new RuntimeException(ex);
}
return result;
}
/**
* Modify latest version
*
* @param context The DSpace context
* @param itemID The id of the to-be-versioned item
* @return A result object
*/
// Versioning
public static FlowResult processUpdateVersion(Context context, int itemID, String summary) throws SQLException, AuthorizeException, IOException {
FlowResult result = new FlowResult();
try {
result.setContinue(false);
Item item = Item.find(context, itemID);
if (AuthorizeManager.isAdmin(context, item)) {
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
versioningService.updateVersion(context, itemID, summary);
context.commit();
result.setOutcome(true);
result.setContinue(true);
result.setMessage(T_version_updated);
result.setParameter("summary", summary);
}
} catch (Exception ex) {
context.abort();
throw new RuntimeException(ex);
}
return result;
}
/**
* Restore a version
*
* @param versionID id of the version to restore
* @param context The DSpace context
* @return A result object
*/
// Versioning
public static FlowResult processRestoreVersion(Context context, int versionID, String summary) throws SQLException, AuthorizeException, IOException {
FlowResult result = new FlowResult();
try {
result.setContinue(false);
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
versioningService.restoreVersion(context, versionID, summary);
context.commit();
result.setOutcome(true);
result.setContinue(true);
result.setMessage(T_version_restored);
} catch (Exception ex) {
context.abort();
throw new RuntimeException(ex);
}
return result;
}
/**
* Delete version(s)
*
* @param context The DSpace context
* @param versionIDs list of versionIDs to delete
* @return A result object
*/
// Versioning
public static FlowResult processDeleteVersions(Context context, int itemId, String[] versionIDs) throws SQLException, AuthorizeException, IOException, UIException {
FlowResult result = new FlowResult();
try {
result.setContinue(false);
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
VersionHistory versionHistory = versioningService.findVersionHistory(context, itemId);
for (String id : versionIDs) {
versioningService.removeVersion(context, Integer.parseInt(id));
}
context.commit();
//Retrieve the latest version of our history (IF any is even present)
Version latestVersion = versionHistory.getLatestVersion();
if(latestVersion == null){
result.setParameter("itemID", null);
}else{
result.setParameter("itemID", latestVersion.getItemID());
}
result.setContinue(true);
result.setOutcome(true);
result.setMessage(T_version_delete);
} catch (Exception ex) {
context.abort();
throw new RuntimeException(ex);
}
return result;
}
}

View File

@@ -0,0 +1,125 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.apache.cocoon.ProcessingException;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.utils.HandleUtil;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.Body;
import org.dspace.app.xmlui.wing.element.Division;
import org.dspace.app.xmlui.wing.element.Para;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.handle.HandleManager;
import org.dspace.utils.DSpace;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.VersioningService;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* Adds a notice to item page in the following conditions
* A new version of an item is available
* If the person is an admin an message will also be shown if the item has a new version in the workflow
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*
*/
public class VersionNoticeTransformer extends AbstractDSpaceTransformer {
private static final Message T_new_version_head = message("xmlui.aspect.versioning.VersionNoticeTransformer.notice.new_version_head");
private static final Message T_new_version_help = message("xmlui.aspect.versioning.VersionNoticeTransformer.notice.new_version_help");
private static final Message T_workflow_version_head = message("xmlui.aspect.versioning.VersionNoticeTransformer.notice.workflow_version_head");
private static final Message T_workflow_version_help = message("xmlui.aspect.versioning.VersionNoticeTransformer.notice.workflow_version_help");
@Override
public void addBody(Body body) throws SAXException, WingException, SQLException, IOException, AuthorizeException, ProcessingException {
DSpaceObject dso = HandleUtil.obtainHandle(objectModel);
if (!(dso instanceof Item))
{
return;
}
Item item = (Item) dso;
if(item.isWithdrawn())
{
return;
}
//Always add a placeholder in which the item information can be added !
Division mainDivision = body.addDivision("item-view","primary");
String title = item.getName();
if(title != null)
{
mainDivision.setHead(title);
}else{
mainDivision.setHead(item.getHandle());
}
//Check if we have a history for the item
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
VersionHistory history = versioningService.findVersionHistory(context, item.getID());
if(history != null){
Version latestVersion = retrieveLatestVersion(history, item);
if(latestVersion != null && latestVersion.getItemID() != item.getID())
{
//We have a newer version
Item latestVersionItem = latestVersion.getItem();
if(latestVersionItem.isArchived())
{
//Available, add a link for the user alerting him that a new version is available
addVersionNotice(mainDivision, latestVersionItem, T_new_version_head, T_new_version_help, true);
}else{
//We might be dealing with a workflow/workspace item
addVersionNotice(mainDivision, latestVersionItem, T_workflow_version_head, T_workflow_version_help, false);
}
}
}
}
private Version retrieveLatestVersion(VersionHistory history, Item item) throws SQLException {
//Attempt to retrieve the latest version
List<Version> allVersions = history.getVersions();
for (Version version : allVersions) {
if (version.getItem().isArchived() || AuthorizeManager.isAdmin(context, item.getOwningCollection()))
{
return version;
}
}
return null;
}
protected void addVersionNotice(Division division, Item item, Message head, Message content, boolean addItemUrl) throws WingException, SQLException
{
Division noticeDiv = division.addDivision("general-message", "version-notice notice neutral");
noticeDiv.setHead(head);
Para para = noticeDiv.addPara();
para.addContent(content);
if(addItemUrl)
{
String url = HandleManager.resolveToURL(context, item.getHandle());
para.addXref(url, url);
}
}
}

View File

@@ -0,0 +1,80 @@
/**
* 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.app.xmlui.aspect.versioning;
import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer;
import org.dspace.app.xmlui.wing.Message;
import org.dspace.app.xmlui.wing.WingException;
import org.dspace.app.xmlui.wing.element.*;
import org.dspace.content.Item;
import org.dspace.utils.DSpace;
import org.dspace.versioning.VersioningService;
import java.sql.SQLException;
/**
*
*
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
// Versioning
public class VersionUpdateForm extends AbstractDSpaceTransformer {
/** Language strings */
private static final Message T_dspace_home = message("xmlui.general.dspace_home");
private static final Message T_submit_cancel = message("xmlui.general.cancel");
private static final Message T_title = message("xmlui.aspect.versioning.VersionUpdateForm.title");
private static final Message T_trail = message("xmlui.aspect.versioning.VersionUpdateForm.trail");
private static final Message T_head1 = message("xmlui.aspect.versioning.VersionUpdateForm.head1");
private static final Message T_submit_version= message("xmlui.aspect.versioning.VersionUpdateForm.submit_version");
private static final Message T_submit_update_version= message("xmlui.aspect.versioning.VersionUpdateForm.submit_update_version");
private static final Message T_summary = message("xmlui.aspect.versioning.VersionUpdateForm.summary");
public void addPageMeta(PageMeta pageMeta) throws WingException {
pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
pageMeta.addTrail().addContent(T_trail);
}
public void addBody(Body body) throws WingException, SQLException{
int versionID = parameters.getParameterAsInteger("versionID",-1);
org.dspace.versioning.Version version = getVersion(versionID);
Item item = version.getItem();
// DIVISION: Main
Division main = body.addInteractiveDivision("version-item", contextPath+"/item/versionhistory", Division.METHOD_POST, "primary administrative version");
main.setHead(T_head1.parameterize(item.getHandle()));
// Fields
List fields = main.addList("fields", List.TYPE_FORM);
Composite addComposite = fields.addItem().addComposite("summary");
addComposite.setLabel(T_summary);
TextArea addValue = addComposite.addTextArea("summary");
addValue.setValue(version.getSummary());
// Buttons
Para actions = main.addPara();
actions.addButton("submit_update").setValue(T_submit_update_version);
actions.addButton("submit_cancel").setValue(T_submit_cancel);
main.addHidden("versioning-continue").setValue(knot.getId());
}
private org.dspace.versioning.Version getVersion(int versionID){
VersioningService versioningService = new DSpace().getSingletonService(VersioningService.class);
return versioningService.getVersion(context, versionID);
}
}

View File

@@ -0,0 +1,216 @@
/**
* 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.springmvc;
import org.dspace.content.DCValue;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class BibTexView implements View {
private static final Logger LOGGER = LoggerFactory.getLogger(BibTexView.class);
private static final String EOL = "\r\n";
private String resourceIdentifier=null;
public String getContentType() {
return "text/plain;charset=utf-8";
}
public BibTexView(String resourceIdentifier)
{
this.resourceIdentifier = resourceIdentifier;
}
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
DSpaceObject item = (DSpaceObject)request.getAttribute(ResourceIdentifierController.DSPACE_OBJECT);
Context context = new Context();
context.turnOffAuthorisationSystem();
String fileName = getFileName(item);
write(response, getBibTex((Item) item, resourceIdentifier), fileName);
OutputStream aOutputStream = response.getOutputStream();
aOutputStream.close();
}
private void write(HttpServletResponse aResponse, String aContent, String aFileName) throws IOException {
aResponse.setContentType("text/plain;charset=utf-8");
aResponse.setContentLength(aContent.length());
aResponse.setHeader("Content-Disposition", "attachment; filename=\""
+ aFileName + "\"");
// It's all over but the writing...
PrintWriter writer = aResponse.getWriter();
writer.print(aContent);
writer.close();
}
private String getFileName(DSpaceObject item)
{
String fileName = resourceIdentifier;
if(resourceIdentifier.lastIndexOf("/") !=-1)
{
fileName = resourceIdentifier.replaceAll("/", "_") + ".bib";
}
return fileName;
}
private String getBibTex(Item item, String resourceIdentifier) {
// No standardized format for data so using 'misc' for now
StringBuilder builder = new StringBuilder("@misc{");
String[] authors = getAuthors(item);
String year = getYear(item);
String title = getMetadataValue(item, "dc.title");
builder.append(resourceIdentifier).append(',').append(EOL);
if (title != null) {
builder.append(" title = {").append(title).append("},");
builder.append(EOL);
}
if (authors.length > 0) {
builder.append(" author = {");
// Bibtex needs the comma... do we want full names here?
for (int index = 0; index < authors.length; index++) {
if (index + 1 >= authors.length) { // last one
builder.append(authors[index].replace(" ", ", "));
}
else if (index + 1 < authors.length) { // not last one
builder.append(authors[index].replace(" ", ", "));
builder.append(" and ");
}
}
builder.append("},").append(EOL);
}
if (year != null) {
builder.append(" year = {").append(year).append("},").append(EOL);
}
return builder.append("}").append(EOL).toString();
}
private String getMetadataValue(Item item, String metadatafield)
{
for (DCValue value : item.getMetadata(metadatafield))
{
return value.value;
}
return null;
}
private String[] getAuthors(Item aItem)
{
ArrayList<String> authors = new ArrayList<String>();
authors.addAll(getAuthors(aItem.getMetadata("dc.contributor.author")));
authors.addAll(getAuthors(aItem.getMetadata("dc.creator")));
authors.addAll(getAuthors(aItem.getMetadata("dc.contributor")));
return authors.toArray(new String[authors.size()]);
}
private String getYear(Item aItem)
{
for (DCValue date : aItem.getMetadata("dc.date.issued"))
{
return date.value.substring(0, 4);
}
return null;
}
private List<String> getAuthors(DCValue[] aMetadata)
{
ArrayList<String> authors = new ArrayList<String>();
StringTokenizer tokenizer;
for (DCValue metadata : aMetadata)
{
StringBuilder builder = new StringBuilder();
if (metadata.value.indexOf(",") != -1)
{
String[] parts = metadata.value.split(",");
if (parts.length > 1)
{
tokenizer = new StringTokenizer(parts[1], ". ");
builder.append(parts[0]).append(" ");
while (tokenizer.hasMoreTokens())
{
builder.append(tokenizer.nextToken().charAt(0));
}
}
else
{
builder.append(metadata.value);
}
authors.add(builder.toString());
}
// Now the minority case (as we've cleaned up data and input method)
else
{
String[] parts = metadata.value.split("\\s+|\\.");
String name = parts[parts.length - 1].replace("\\s+|\\.", "");
builder.append(name).append(" ");
for (int index = 0; index < parts.length - 1; index++)
{
if (parts[index].length() > 0)
{
name = parts[index].replace("\\s+|\\.", "");
builder.append(name.charAt(0));
}
}
}
}
return authors;
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.springmvc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.*;
/**
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
@Controller(value = "cocoonForwardController")
public class CocoonForwardController {
private static final Logger log = LoggerFactory.getLogger(CocoonForwardController.class);
@RequestMapping
public ModelAndView forwardRequest(HttpServletRequest request, HttpServletResponse response) throws SQLException {
log.debug("CocoonForwardController!!!!!");
return new ModelAndView(new CocoonView());
}
}

View File

@@ -0,0 +1,136 @@
/**
* 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.springmvc;
import org.apache.cocoon.servletservice.DynamicProxyRequestHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.View;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class CocoonView implements View
{
/**
* The startup date of the Spring application context used to setup the {@link #blockServletCollector}.
*/
long applicationContextStartDate;
/**
* The servlet collector bean
*/
Map blockServletCollector;
public CocoonView()
{
}
void getInterfaces(Set interfaces, Class clazz)
{
Class[] clazzInterfaces = clazz.getInterfaces();
for (int i = 0; i < clazzInterfaces.length; i++)
{
//add all interfaces extended by this interface or directly
//implemented by this class
getInterfaces(interfaces, clazzInterfaces[i]);
}
// the superclazz is null if class is instanceof Object, is
// an interface, a primitive type or void
Class superclazz = clazz.getSuperclass();
if (superclazz != null)
{
//add all interfaces of the superclass to the list
getInterfaces(interfaces, superclazz);
}
interfaces.addAll(Arrays.asList(clazzInterfaces));
}
public String getContentType()
{
return null;
}
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception
{
final Map mountableServlets = getBlockServletMap(request);
String path = request.getPathInfo();
if (path == null)
{
path = "";
}
// find the servlet which mount path is the longest prefix of the path info
int index = path.length();
Servlet servlet = null;
while (servlet == null && index != -1)
{
path = path.substring(0, index);
servlet = (Servlet) mountableServlets.get(path);
index = path.lastIndexOf('/');
}
//case when servlet is mounted at "/" must be handled separately
servlet = servlet == null ? (Servlet) mountableServlets.get("/") : servlet;
if (servlet == null)
{
String message = "No block for " + request.getPathInfo();
response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
return;
}
// Create a dynamic proxy class that overwrites the getServletPath and
// getPathInfo methods to provide reasonable values in the called servlet
// the dynamic proxy implements all interfaces of the original request
HttpServletRequest prequest = (HttpServletRequest) Proxy.newProxyInstance(
request.getClass().getClassLoader(),
getInterfaces(request.getClass()),
new DynamicProxyRequestHandler(request, path));
servlet.service(prequest, response);
}
Class[] getInterfaces(final Class clazz)
{
Set interfaces = new LinkedHashSet();
getInterfaces(interfaces, clazz);
return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
}
public Map getBlockServletMap(HttpServletRequest request)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession(true).getServletContext());
if (this.blockServletCollector == null || applicationContext.getStartupDate() != this.applicationContextStartDate)
{
this.applicationContextStartDate = applicationContext.getStartupDate();
this.blockServletCollector = (Map) applicationContext.getBean("org.apache.cocoon.servletservice.spring.BlockServletMap");
}
return blockServletCollector;
}
}

View File

@@ -0,0 +1,219 @@
/**
* 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.springmvc;
import org.dspace.app.xmlui.utils.ContextUtil;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.identifier.IdentifierNotFoundException;
import org.dspace.identifier.IdentifierNotResolvableException;
import org.dspace.identifier.IdentifierService;
import org.dspace.utils.DSpace;
import org.springframework.stereotype.Controller;
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.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
/**
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
@Controller
@RequestMapping(value={"/handle","/resource"})
public class ResourceIdentifierController {
public static final String DSPACE_OBJECT = "dspace.object";
private static final String RESOURCE = "/resource";
private static final String METS = "mets";
private static final String DRI = "DRI";
private static final int STATUS_OK=200;
private static final int STATUS_FORBIDDEN=400;
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD},value={"/{prefix:.*}"})
public String processHandle(HttpServletRequest request, @PathVariable String prefix) {
//String resourceIdentifier=null;
try {
//String requestUri = request.getRequestURI().toString();
//resourceIdentifier = requestUri.substring(requestUri.indexOf(RESOURCE) + RESOURCE.length() + 1);
Context context = ContextUtil.obtainContext(request);
IdentifierService dis = new DSpace().getSingletonService(IdentifierService.class);
if (dis == null)
throw new RuntimeException("Cannot instantiate IdentifierService. Problem with spring configuration!");
DSpaceObject dso = dis.resolve(context, prefix);
if (dso == null) throw new RuntimeException("Cannot find Item!");
request.setAttribute(DSPACE_OBJECT, dso);
/** TODO: This is a temporary solution until we can adjust cocoon to not have a /handle URI */
return "forward:/handle/" + dso.getHandle();
} catch (SQLException e) {
return "forward:/error";
} catch (IdentifierNotResolvableException e) {
return "forward:/tombstone";
} catch (IdentifierNotFoundException e) {
request.setAttribute("identifier", prefix);
return "forward:/identifier-not-found";
}
}
@RequestMapping("/**/mets.xml")
public String processMETSHandle(HttpServletRequest request) {
try {
String requestUri = request.getRequestURI().toString();
String resourceIdentifier = requestUri.substring(requestUri.indexOf(RESOURCE) + RESOURCE.length() + 1);
resourceIdentifier = resourceIdentifier.substring(0, resourceIdentifier.indexOf(METS) - 1);
Context context = ContextUtil.obtainContext(request);
IdentifierService dis = new DSpace().getSingletonService(IdentifierService.class);
DSpaceObject dso = dis.resolve(context, resourceIdentifier);
if (dso == null) return null;
request.setAttribute(DSPACE_OBJECT, dso);
return "forward:/metadata/handle/" + dso.getHandle() + "/mets.xml";
} catch (SQLException e) {
return "forward:/error";
} catch (IdentifierNotResolvableException e) {
return "forward:/tombstone";
} catch (IdentifierNotFoundException e) {
return "forward:/identifier-not-found";
}
}
@RequestMapping("/**/DRI")
public String processDRIHandle(HttpServletRequest request) {
try {
String requestUri = request.getRequestURI().toString();
String resourceIdentifier = requestUri.substring(requestUri.indexOf(RESOURCE) + RESOURCE.length() + 1);
resourceIdentifier = resourceIdentifier.substring(0, resourceIdentifier.indexOf(DRI) - 1);
Context context = ContextUtil.obtainContext(request);
IdentifierService dis = new DSpace().getSingletonService(IdentifierService.class);
DSpaceObject dso = dis.resolve(context, resourceIdentifier);
if (dso == null) return null;
request.setAttribute(DSPACE_OBJECT, dso);
return "forward:/DRI/handle/" + dso.getHandle();
} catch (SQLException e) {
return "forward:/error";
} catch (IdentifierNotResolvableException e) {
return "forward:/tombstone";
} catch (IdentifierNotFoundException e) {
return "forward:/identifier-not-found";
}
}
@RequestMapping("/{prefix}/{suffix}/citation/ris")
public ModelAndView genRisRepresentation(@PathVariable String prefix, @PathVariable String suffix, HttpServletRequest request, HttpServletResponse response) {
String resourceIdentifier = prefix + "/" + suffix;
request.setAttribute(DSPACE_OBJECT, getDSO(request, resourceIdentifier));
return new ModelAndView(new RisView(resourceIdentifier));
}
@RequestMapping("/{prefix}/{suffix}/citation/bib")
public ModelAndView genBibTexRepresentation(@PathVariable String prefix, @PathVariable String suffix, HttpServletRequest request, HttpServletResponse response) {
String resourceIdentifier = prefix + "/" + suffix;
request.setAttribute(DSPACE_OBJECT, getDSO(request, resourceIdentifier) );
return new ModelAndView(new BibTexView(resourceIdentifier));
}
private DSpaceObject getDSO(HttpServletRequest request, String resourceIdentifier) {
DSpaceObject dso=null;
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
Context context =null;
try {
context = new Context();
context.turnOffAuthorisationSystem();
dso = identifierService.resolve(context, resourceIdentifier);
if(dso==null) throw new RuntimeException("Invalid DOI! " + resourceIdentifier);
return dso;
}catch (IdentifierNotFoundException e) {
throw new RuntimeException(e);
} catch (IdentifierNotResolvableException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private int validate(String resourceID, HttpServletRequest request){
String token = request.getParameter("token");
if(token==null || "".equals(token)) return STATUS_FORBIDDEN;
if(resourceID==null || "".equals(resourceID)) return STATUS_FORBIDDEN;
// try to resolve DOI
DSpaceObject dso=null;
IdentifierService identifierService = new DSpace().getSingletonService(IdentifierService.class);
Context context =null;
try {
context = new Context();
context.turnOffAuthorisationSystem();
dso = identifierService.resolve(context, resourceID);
request.setAttribute(DSPACE_OBJECT, dso);
if(!(dso instanceof Item)) return STATUS_FORBIDDEN;
return STATUS_OK;
}catch (SQLException e) {
return STATUS_FORBIDDEN;
}catch (IdentifierNotFoundException e) {
return STATUS_FORBIDDEN;
} catch (IdentifierNotResolvableException e) {
return STATUS_FORBIDDEN;
}
}
}

View File

@@ -0,0 +1,272 @@
/**
* 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.springmvc;
import org.dspace.content.DCValue;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.identifier.IdentifierService;
import org.dspace.utils.DSpace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Fabio Bolognesi (fabio at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class RisView implements View {
private static final Logger LOGGER = LoggerFactory.getLogger(RisView.class);
private static final String EOL = "\r\n";
private String resourceIdentifier=null;
public RisView(String resourceIdentifier)
{
this.resourceIdentifier = resourceIdentifier;
}
public String getContentType()
{
return "text/plain;charset=utf-8";
}
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
DSpaceObject item = (DSpaceObject)request.getAttribute(ResourceIdentifierController.DSPACE_OBJECT);
Context context = new Context();
context.turnOffAuthorisationSystem();
String fileName = getFileName(item);
write(response, getRIS((Item) item, resourceIdentifier), fileName);
OutputStream aOutputStream = response.getOutputStream();
aOutputStream.close();
}
private String getFileName(DSpaceObject item)
{
String fileName = resourceIdentifier;
if(resourceIdentifier.indexOf("/") !=-1)
{
fileName = resourceIdentifier.replaceAll("/", "_") + ".ris";
}
return fileName;
}
private void write(HttpServletResponse aResponse, String aContent, String aFileName) throws IOException
{
aResponse.setContentType("text/plain;charset=utf-8");
aResponse.setContentLength(aContent.length());
aResponse.setHeader("Content-Disposition", "attachment; filename=\"" + aFileName + "\"");
// It's all over but the writing...
PrintWriter writer = aResponse.getWriter();
writer.print(aContent);
writer.close();
}
private String getRIS(Item item, String resourceIdentifier)
{
StringBuilder builder = new StringBuilder("TY - DATA").append(EOL);
String[] dateParts = getDate(item);
String title = getMetadataValue(item, "dc.title");
String description = getMetadataValue(item, "dc.description");
String[] keywords = getKeywords(item);
if (resourceIdentifier != null)
{
builder.append("ID - ").append(resourceIdentifier).append(EOL);
}
if (title != null)
{
builder.append("T1 - ").append(title).append(EOL);
}
for (String author : getAuthors(item)) {
builder.append("AU - ").append(author).append(EOL);
}
// Date for data package
if (dateParts.length > 0)
{
int count = 3;
builder.append("Y1 - ");
if (dateParts.length < 3)
{
count = dateParts.length;
}
for (int index = 0; index < count; index++) {
builder.append(dateParts[index]).append("/");
}
for (; count < 3; count++)
{
builder.append('/');
}
builder.append(EOL);
}
if (description != null)
{
builder.append("N2 - ").append(description).append(EOL);
}
for (String keyword : keywords)
{
builder.append("KW - ").append(keyword).append(EOL);
}
return builder.append("ER - ").append(EOL).toString();
}
private String[] getAuthors(Item aItem)
{
ArrayList<String> authors = new ArrayList<String>();
authors.addAll(getAuthors(aItem.getMetadata("dc.contributor.author")));
authors.addAll(getAuthors(aItem.getMetadata("dc.creator")));
authors.addAll(getAuthors(aItem.getMetadata("dc.contributor")));
return authors.toArray(new String[authors.size()]);
}
private String[] getKeywords(Item aItem)
{
ArrayList<String> keywordList = new ArrayList<String>();
for (DCValue keyword : aItem.getMetadata("dc.subject"))
{
if (keyword.value.length() < 255)
{
keywordList.add(keyword.value);
}
}
for (DCValue keyword : aItem.getMetadata("dwc.ScientificName"))
{
if (keyword.value.length() < 255)
{
keywordList.add(keyword.value);
}
}
return keywordList.toArray(new String[keywordList.size()]);
}
private String[] getDate(Item item)
{
StringTokenizer tokenizer;
for (DCValue date : item.getMetadata("dc.date.issued"))
{
tokenizer = new StringTokenizer(date.value, "-/ T");
String[] dateParts = new String[tokenizer.countTokens()];
for (int index = 0; index < dateParts.length; index++)
{
dateParts[index] = tokenizer.nextToken();
}
return dateParts;
}
return new String[0];
}
private String getMetadataValue(Item item, String metadatafield)
{
for (DCValue value : item.getMetadata(metadatafield))
{
return value.value;
}
return null;
}
private List<String> getAuthors(DCValue[] aMetadata)
{
ArrayList<String> authors = new ArrayList<String>();
StringTokenizer tokenizer;
for (DCValue metadata : aMetadata)
{
StringBuilder builder = new StringBuilder();
if (metadata.value.indexOf(",") != -1)
{
String[] parts = metadata.value.split(",");
if (parts.length > 1)
{
tokenizer = new StringTokenizer(parts[1], ". ");
builder.append(parts[0]).append(" ");
while (tokenizer.hasMoreTokens())
{
builder.append(tokenizer.nextToken().charAt(0));
}
}
else
{
builder.append(metadata.value);
}
authors.add(builder.toString());
}
// Now the minority case (as we've cleaned up data and input method)
else
{
String[] parts = metadata.value.split("\\s+|\\.");
String name = parts[parts.length - 1].replace("\\s+|\\.", "");
builder.append(name).append(" ");
for (int index = 0; index < parts.length - 1; index++)
{
if (parts[index].length() > 0)
{
name = parts[index].replace("\\s+|\\.", "");
builder.append(name.charAt(0));
}
}
}
}
return authors;
}
}

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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/
-->
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:components>
<map:transformers>
<map:transformer name="VersionHistoryForm" src="org.dspace.app.xmlui.aspect.versioning.VersionHistoryForm"/>
<map:transformer name="VersionItemForm" src="org.dspace.app.xmlui.aspect.versioning.VersionItemForm"/>
<map:transformer name="DeleteVersionsConfirm"
src="org.dspace.app.xmlui.aspect.versioning.DeleteVersionsConfirm"/>
<map:transformer name="RestoreVersionForm" src="org.dspace.app.xmlui.aspect.versioning.RestoreVersionForm"/>
<map:transformer name="Navigation"
src="org.dspace.app.xmlui.aspect.versioning.Navigation"/>
<map:transformer name="VersionUpdateForm" src="org.dspace.app.xmlui.aspect.versioning.VersionUpdateForm"/>
<map:transformer name="VersionNoticeTransformer" src="org.dspace.app.xmlui.aspect.versioning.VersionNoticeTransformer"/>
</map:transformers>
<map:matchers default="wildcard">
<map:matcher name="HandleTypeMatcher" src="org.dspace.app.xmlui.aspect.general.HandleTypeMatcher"/>
<map:matcher name="HandleAuthorizedMatcher" src="org.dspace.app.xmlui.aspect.general.HandleAuthorizedMatcher"/>
</map:matchers>
<map:selectors>
<map:selector name="AuthenticatedSelector" src="org.dspace.app.xmlui.aspect.general.AuthenticatedSelector"/>
</map:selectors>
</map:components>
<map:flow language="javascript">
<map:script src="versioning.js"/>
</map:flow>
<map:pipelines>
<map:pipeline>
<map:select type="AuthenticatedSelector">
<map:when test="eperson">
<map:match pattern="item/version">
<map:match type="request" pattern="versioning-continue">
<map:call continuation="{1}"/>
</map:match>
<map:match type="request" pattern="itemID">
<map:call function="startCreateNewVersionItem"/>
</map:match>
</map:match>
<map:match pattern="item/versionhistory">
<map:match type="request" pattern="versioning-continue">
<map:call continuation="{1}"/>
</map:match>
<map:match type="request" pattern="itemID">
<map:call function="startVersionHistoryItem"/>
</map:match>
</map:match>
</map:when>
<map:otherwise>
<map:match pattern="item/version">
<map:act type="StartAuthentication"/>
</map:match>
<map:match pattern="item/versionhistory">
<map:act type="StartAuthentication"/>
</map:match>
</map:otherwise>
</map:select>
<map:generate/>
<map:select type="AuthenticatedSelector">
<map:when test="eperson">
<map:transform type="Navigation"/>
<map:match pattern="item/version/create">
<map:transform type="VersionItemForm">
<map:parameter name="itemID" value="{flow-attribute:itemID}"/>
<map:parameter name="summary" value="{flow-attribute:summary}"/>
</map:transform>
</map:match>
<map:match pattern="item/versionhistory/show">
<map:transform type="VersionHistoryForm">
<map:parameter name="itemID" value="{flow-attribute:itemID}"/>
</map:transform>
</map:match>
<map:match pattern="item/versionhistory/delete">
<map:transform type="DeleteVersionsConfirm">
<map:parameter name="versionIDs" value="{flow-attribute:versionIDs}"/>
</map:transform>
</map:match>
<map:match pattern="item/versionhistory/restore">
<map:transform type="RestoreVersionForm">
<map:parameter name="itemID" value="{flow-attribute:itemID}"/>
<map:parameter name="versionID" value="{flow-attribute:versionID}"/>
</map:transform>
</map:match>
<map:match pattern="item/versionhistory/update">
<map:transform type="VersionUpdateForm">
<map:parameter name="itemID" value="{flow-attribute:itemID}"/>
<map:parameter name="versionID" value="{flow-attribute:versionID}"/>
</map:transform>
</map:match>
</map:when>
</map:select>
<map:match pattern="handle/*/*">
<map:match type="HandleAuthorizedMatcher" pattern="READ">
<map:match type="HandleTypeMatcher" pattern="item">
<map:transform type="VersionNoticeTransformer" />
<map:transform type="VersionHistoryForm"/>
</map:match>
</map:match>
</map:match>
<map:serialize type="xml"/>
</map:pipeline>
</map:pipelines>
</map:sitemap>

View File

@@ -0,0 +1,359 @@
/*
* 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/
*/
importClass(Packages.java.lang.Class);
importClass(Packages.java.lang.ClassLoader);
importClass(Packages.org.dspace.app.xmlui.utils.FlowscriptUtils);
importClass(Packages.org.dspace.app.xmlui.aspect.administrative.FlowResult);
importClass(Packages.org.apache.cocoon.environment.http.HttpEnvironment);
importClass(Packages.org.apache.cocoon.servlet.multipart.Part);
importClass(Packages.org.dspace.content.Item);
importClass(Packages.org.dspace.handle.HandleManager);
importClass(Packages.org.dspace.core.Constants);
importClass(Packages.org.dspace.authorize.AuthorizeManager);
importClass(Packages.org.dspace.license.CreativeCommons);
importClass(Packages.org.dspace.app.xmlui.utils.ContextUtil);
importClass(Packages.org.dspace.app.xmlui.cocoon.HttpServletRequestCocoonWrapper);
importClass(Packages.org.dspace.app.xmlui.aspect.versioning.VersionManager);
importClass(Packages.org.dspace.submit.AbstractProcessingStep);
/* Global variable which stores a comma-separated list of all fields
* which errored out during processing of the last step.
*/
var ERROR_FIELDS = null;
/**
* Simple access method to access the current cocoon object model.
*/
function getObjectModel()
{
return FlowscriptUtils.getObjectModel(cocoon);
}
/**
* Return the DSpace context for this request since each HTTP request generates
* a new context this object should never be stored and instead allways accessed
* through this method so you are ensured that it is the correct one.
*/
function getDSContext()
{
return ContextUtil.obtainContext(getObjectModel());
}
/**
* Return the HTTP Request object for this request
*/
function getHttpRequest()
{
//return getObjectModel().get(HttpEnvironment.HTTP_REQUEST_OBJECT)
// Cocoon's request object handles form encoding, thus if the users enters
// non-ascii characters such as those found in foriegn languages they will
// come through corruped if they are not obtained through the cocoon request
// object. However, since the dspace-api is built to accept only HttpServletRequest
// a wrapper class HttpServletRequestCocoonWrapper has bee built to translate
// the cocoon request back into a servlet request. This is not a fully complete
// translation as some methods are unimplemeted. But it is enough for our
// purposes here.
return new HttpServletRequestCocoonWrapper(getObjectModel());
}
/**
* Return the HTTP Response object for the response
* (used for compatibility with DSpace configurable submission system)
*/
function getHttpResponse()
{
return getObjectModel().get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
}
/**
* Start editing an individual item.
*/
function startCreateNewVersionItem(){
var itemID = cocoon.request.get("itemID");
assertEditItem(itemID);
var result= new FlowResult();
do{
result = doCreateNewVersion(itemID, result);
}while(result!=null);
var item = Item.find(getDSContext(),itemID);
//Send us back to the item page if we cancel !
cocoon.redirectTo(cocoon.request.getContextPath() + "/handle/" + item.getHandle(), true);
getDSContext().complete();
item = null;
cocoon.exit();
}
/*
* Move this item to another collection
*/
function doCreateNewVersion(itemID, result){
assertEditItem(itemID);
do {
sendPageAndWait("item/version/create",{"itemID":itemID, "summary":result.getParameter("summary")}, result);
if (cocoon.request.get("submit_cancel")){
return null;
}
else if (cocoon.request.get("submit_version")){
var summary = cocoon.request.get("summary");
assertEditItem(itemID);
result = VersionManager.processCreateNewVersion(getDSContext(),itemID, summary);
var wsid = result.getParameter("wsid");
cocoon.redirectTo(cocoon.request.getContextPath()+"/submit?workspaceID=" + wsid,true);
cocoon.exit();
}
else if (cocoon.request.get("submit_update_version")){
var summary = cocoon.request.get("summary");
assertEditItem(itemID);
result = VersionManager.processUpdateVersion(getDSContext(),itemID, summary);
}
} while (result == null || !result.getContinue());
return result;
}
/**
* Start editing an individual item.
*/
function startVersionHistoryItem(){
var itemID = cocoon.request.get("itemID");
assertEditItem(itemID);
var result= new FlowResult();
do{
result=doVersionHistoryItem(itemID, result);
}while(result!=null);
}
function doVersionHistoryItem(itemID, result){
//var result;
do {
sendPageAndWait("item/versionhistory/show",{"itemID":itemID},result);
assertEditItem(itemID);
result = null;
if (cocoon.request.get("submit_cancel")){
//Pressed the cancel button, redirect us to the item page
var item = Item.find(getDSContext(),itemID);
cocoon.redirectTo(cocoon.request.getContextPath()+"/handle/"+item.getHandle(),true);
getDSContext().complete();
item = null;
cocoon.exit();
}
else if (cocoon.request.get("submit_delete") && cocoon.request.get("remove")){
var versionIDs = cocoon.request.getParameterValues("remove");
result = doDeleteVersions(itemID, versionIDs);
if(result != null){
if(result.getParameter("itemID") == null){
// We have removed everything, redirect us to the home page !
cocoon.redirectTo(cocoon.request.getContextPath(), true);
getDSContext().complete();
cocoon.exit();
}else{
//Perhaps we have a new item (if we deleted the current version)
itemID = result.getParameter("itemID");
}
}
}
else if (cocoon.request.get("submit_restore") && cocoon.request.get("versionID")){
var versionID = cocoon.request.get("versionID");
itemID = cocoon.request.get("itemID");
result = doRestoreVersion(itemID, versionID);
}
else if (cocoon.request.get("submit_update") && cocoon.request.get("versionID")){
var versionID = cocoon.request.get("versionID");
itemID = cocoon.request.get("itemID");
result = doUpdateVersion(itemID, versionID);
}
} while (true)
}
/**
* Confirm and delete the given version(s)
*/
function doDeleteVersions(itemID, versionIDs){
sendPageAndWait("item/versionhistory/delete",{"itemID":itemID,"versionIDs":versionIDs.join(',')});
if (cocoon.request.get("submit_cancel")){
return null;
}
else if (cocoon.request.get("submit_confirm")){
return VersionManager.processDeleteVersions(getDSContext(), itemID, versionIDs);
}
return null;
}
/**
* Restore the given version
*/
function doRestoreVersion(itemID, versionID){
var result;
do {
sendPageAndWait("item/versionhistory/restore", {"itemID":itemID,"versionID":versionID}, result);
result = null;
if (cocoon.request.get("submit_cancel"))
return null;
else if (cocoon.request.get("submit_restore")){
var summary = cocoon.request.get("summary");
result = VersionManager.processRestoreVersion(getDSContext(),versionID, summary);
}
} while (result == null || ! result.getContinue())
return result;
}
/**
* Update the given version
*/
function doUpdateVersion(itemID, versionID){
var result;
do {
sendPageAndWait("item/versionhistory/update", {"itemID":itemID,"versionID":versionID}, result);
result = null;
if (cocoon.request.get("submit_cancel")){
return null;
}
else if (cocoon.request.get("submit_update")){
var summary = cocoon.request.get("summary");
result = VersionManager.processUpdateVersion(getDSContext(),itemID, summary);
}
} while (result == null || ! result.getContinue())
return result;
}
/**
* Assert that the currently authenticated eperson can edit this item, if they can
* not then this method will never return.
*/
function assertEditItem(itemID) {
if ( ! canEditItem(itemID)) {
sendPage("admin/not-authorized");
cocoon.exit();
}
}
/**
* Return weather the currently authenticated eperson can edit the identified item.
*/
function canEditItem(itemID)
{
// Navigation already deals with loading the right operation. return always true.
return true;
}
function sendPageAndWait(uri,bizData,result)
{
if (bizData == null)
bizData = {};
if (result != null)
{
var outcome = result.getOutcome();
var header = result.getHeader();
var message = result.getMessage();
var characters = result.getCharacters();
if (message != null || characters != null)
{
bizData["notice"] = "true";
bizData["outcome"] = outcome;
bizData["header"] = header;
bizData["message"] = message;
bizData["characters"] = characters;
}
var errors = result.getErrorString();
if (errors != null)
{
bizData["errors"] = errors;
}
}
// just to remember where we came from.
bizData["flow"] = "true";
cocoon.sendPageAndWait(uri,bizData);
}
/**
* Send the given page and DO NOT wait for the flow to be continued. Excution will
* proccede as normal. This method will preform two usefull actions: set the flow
* parameter & add result information.
*
* The flow parameter is used by the sitemap to seperate requests comming from a
* flow script from just normal urls.
*
* The result object could potentialy contain a notice message and a list of
* errors. If either of these are present then they are added to the sitemap's
* parameters.
*/
function sendPage(uri,bizData,result)
{
if (bizData == null)
bizData = {};
if (result != null)
{
var outcome = result.getOutcome();
var header = result.getHeader();
var message = result.getMessage();
var characters = result.getCharacters();
if (message != null || characters != null)
{
bizData["notice"] = "true";
bizData["outcome"] = outcome;
bizData["header"] = header;
bizData["message"] = message;
bizData["characters"] = characters;
}
var errors = result.getErrorString();
if (errors != null)
{
bizData["errors"] = errors;
}
}
// just to remember where we came from.
bizData["flow"] = "true";
cocoon.sendPage(uri,bizData);
}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="windows-1252"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- Use @Component annotations for bean definitions -->
<context:component-scan base-package="org.dspace.springmvc"/>
<!-- Use @Controller annotations for MVC controller definitions -->
<mvc:annotation-driven />
<!-- Add JPA support -->
<!--bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
</bean-->
<!-- Add Transaction support -->
<!--bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean-->
<!-- Use @Transaction annotations for managing transactions -->
<!--tx:annotation-driven transaction-manager="myTxManager" /-->
<!-- View resolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
</bean>
-->
<!--
Map standard URLs to Spring WebMVC Controllers.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath">
<value>true</value>
</property>
</bean>
-->
<!-- Default to DefaultSpringController -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="defaultHandler" ref="cocoonForwardController"/>
</bean>
</beans>

View File

@@ -127,17 +127,17 @@
-->
<filter-mapping>
<filter-name>CocoonMultipartFilter</filter-name>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>DSpaceCocoonServletFilter</filter-name>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>SetCharacterEncoding</filter-name>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
</filter-mapping>
<!--
@@ -188,15 +188,23 @@
Servlet Configuration ==========================================
-->
<servlet>
<!--servlet>
<description>Cocoon</description>
<display-name>Cocoon</display-name>
<servlet-name>Cocoon</servlet-name>
<servlet-class>org.apache.cocoon.servletservice.DispatcherServlet</servlet-class>
<servlet-class>org.apache.cocoon.servletservice.DispatcherServlet</servlet-class-->
<!--
This parameter allows you to startup Cocoon2 immediately after startup
of your servlet engine.
-->
<!--load-on-startup>1</load-on-startup>
</servlet-->
<!-- Spring Servlet -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
@@ -209,7 +217,7 @@
to change this parameter.
-->
<servlet-mapping>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
@@ -218,7 +226,7 @@
by '/' mapping, but must be overriden explicitly.
-->
<servlet-mapping>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<!--
@@ -226,7 +234,7 @@
by '/' mapping, but must be overriden explicitly.
-->
<servlet-mapping>
<servlet-name>Cocoon</servlet-name>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

View File

@@ -2163,4 +2163,68 @@
<message key="xmlui.mobile.items_in_google_scholar">Items in Google Scholar</message>
<message key="xmlui.mobile.download">Download</message>
<!-- Versioning -->
<message key="xmlui.aspect.versioning.VersioningNavigation.context_create_version">Create version of this item</message>
<message key="xmlui.aspect.versioning.VersioningNavigation.context_show_version_history">Show version history</message>
<message key="xmlui.aspect.versioning.VersionItemForm.title">Create Version</message>
<message key="xmlui.aspect.versioning.VersionItemForm.trail">Version</message>
<message key="xmlui.aspect.versioning.VersionItemForm.head1">Create new version of item: {0}</message>
<message key="xmlui.aspect.versioning.VersionItemForm.submit_version">Version</message>
<message key="xmlui.aspect.versioning.VersionItemForm.submit_update_version">Update Version</message>
<message key="xmlui.aspect.versioning.VersionItemForm.summary">Reason for creating new version</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.title">Update Version</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.trail">Version</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.head1">Update version of item: {0}</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.submit_version">Version</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.submit_update_version">Update Version</message>
<message key="xmlui.aspect.versioning.VersionUpdateForm.summary">Summary</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.title">Confirm Deletion</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.trail">Confirm deletion</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.head1">Confirm Deletion(s)</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.para1">Are you sure you want to delete these versions. </message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.para2">PLEASE NOTE: That by deleting these versions, the associated items will no longer be accessible.</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.column1">Version</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.column2">Item</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.column3">Editor</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.column4">Date</message>
<message key="xmlui.aspect.versioning.DeleteVersionsConfirm.column5">Summary</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.title">Restore Version</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.trail">Restore Version</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.head1">Restore Version</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.para1">Are you sure you want to restore this version:</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.column1">Version</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.column2">Editor</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.column3">Date</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.column4">Summary</message>
<message key="xmlui.aspect.versioning.RestoreVersionForm.restore">Restore</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.title">Version History</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.trail">Version history</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.head2">Version History</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column1">Version</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column2">Item</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column3">Editor</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column4">Date</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column5">Summary</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.column6">Actions</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.restore">Restore</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.update">Update</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.legend">*Selected version</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.delete">Delete Versions</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.return">Return</message>
<message key="xmlui.aspect.versioning.VersionHistoryForm.collection_admins_only">(collection administrators only)</message>
<message key="xmlui.aspect.versioning.VersionNoticeTransformer.notice.new_version_head">Notice</message>
<message key="xmlui.aspect.versioning.VersionNoticeTransformer.notice.new_version_help">This is not the latest version of this item. The latest version can be found at: </message>
<message key="xmlui.aspect.versioning.VersionNoticeTransformer.notice.workflow_version_head">Notice</message>
<message key="xmlui.aspect.versioning.VersionNoticeTransformer.notice.workflow_version_help">A more recent version of this item is in the Workflow.</message>
<!-- End Versioning -->
</catalogue>

View File

@@ -631,9 +631,9 @@ event.dispatcher.default.class = org.dspace.event.BasicDispatcher
#
# uncomment below and comment out original property to enable discovery indexing
# event.dispatcher.default.consumers = search, browse, discovery, eperson, harvester
# event.dispatcher.default.consumers = versioning, search, browse, discovery, eperson, harvester
#
event.dispatcher.default.consumers = search, browse, eperson, harvester
event.dispatcher.default.consumers = versioning, search, browse, eperson, harvester
# The noindex dispatcher will not create search or browse indexes (useful for batch item imports)
event.dispatcher.noindex.class = org.dspace.event.BasicDispatcher
@@ -663,6 +663,10 @@ event.consumer.harvester.filters = Item+Delete
#event.consumer.test.class = org.dspace.event.TestConsumer
#event.consumer.test.filters = All+All
# consumer to maintain versions
event.consumer.versioning.class = org.dspace.versioning.VersioningConsumer
event.consumer.versioning.filters = Item+Install
# ...set to true to enable testConsumer messages to standard output
#testConsumer.verbose = true

View File

@@ -0,0 +1,10 @@
#---------------------------------------------------#
#------------ VERSIONING CONFIGURATIONS ------------#
#---------------------------------------------------#
# These configs are used by the versioning system #
#---------------------------------------------------#
# Control if the history overview of an item should only be shown to administrators
# If enabled only the administrators for the item will be able to view the versioning history
# If disabled anyone with READ permissions on the item will be able to view the versioning history
item.history.view.admin=false

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2002-2010, DuraSpace. All rights reserved
Licensed under the DuraSpace License.
A copy of the DuraSpace License has been included in this
distribution and is available at: http://www.dspace.org/license
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- Identifier Service Application Interface. Will be autowired with
any Identifier Providers present in Spring context.
-->
<bean id="org.dspace.identifier.IdentifierService"
class="org.dspace.identifier.IdentifierServiceImpl"
autowire="byType"
scope="singleton"/>
<!-- provider for using the versioned handle identifier instead of the default one. -->
<!--<bean id="org.dspace.identifier.HandleIdentifierProvider" class="org.dspace.identifier.VersionedHandleIdentifierProvider"-->
<!--scope="singleton">-->
<!--<property name="configurationService" ref="org.dspace.services.ConfigurationService"/>-->
<!--</bean>-->
</beans>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2002-2010, DuraSpace. All rights reserved
Licensed under the DuraSpace License.
A copy of the DuraSpace License has been included in this
distribution and is available at: http://www.dspace.org/license
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- Versioning Service Application Interface for DSpace Will be autowired with
a Versioning Provider present in Spring context.
Default Item Versioning Provider, defines behavior for replicating
Item, Metadata, BUndles and Bitstreams. Autowired at this time.
<bean id="org.dspace.versioning.VersioningService"
class="org.dspace.versioning.VersioningServiceImpl"
autowire="byType"
scope="singleton">
<property name="versionDAO">
<bean class="org.dspace.versioning.VersionDAO"/>
</property>
<property name="versionHistoryDAO">
<bean class="org.dspace.versioning.VersionHistoryDAO"/>
</property>
<property name="provider">
<bean class="org.dspace.versioning.DefaultItemVersionProvider"/>
</property>
</bean>
-->
</beans>

View File

@@ -64,6 +64,7 @@
BrowseArtifacts, SearchArtifacts
<aspect name="Artifact Browser" path="resource://aspects/ArtifactBrowser/" />
-->
<aspect name="Versioning Aspect" path="resource://aspects/Versioning/" />
<aspect name="Displaying Artifacts" path="resource://aspects/ViewArtifacts/" />
<aspect name="Browsing Artifacts" path="resource://aspects/BrowseArtifacts/" />
<aspect name="Searching Artifacts" path="resource://aspects/SearchArtifacts/" />

View File

@@ -788,6 +788,25 @@ CREATE TABLE harvested_item
CREATE INDEX harvested_item_fk_idx ON harvested_item(item_id);
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;

View File

@@ -70,6 +70,8 @@ CREATE SEQUENCE group2group_seq;
CREATE SEQUENCE group2groupcache_seq;
CREATE SEQUENCE harvested_collection_seq;
CREATE SEQUENCE harvested_item_seq;
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;
-------------------------------------------------------
-- BitstreamFormatRegistry table
@@ -739,3 +741,19 @@ CREATE TABLE harvested_item
);
CREATE INDEX harvested_item_fk_idx ON harvested_item(item_id);
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR2(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);

View File

@@ -30,3 +30,22 @@ ALTER TABLE resourcepolicy
ALTER TABLE item ADD discoverable NUMBER(1);
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR2(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;

View File

@@ -107,6 +107,8 @@ CREATE SEQUENCE group2group_seq;
CREATE SEQUENCE group2groupcache_seq;
CREATE SEQUENCE harvested_collection_seq;
CREATE SEQUENCE harvested_item_seq;
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;
-------------------------------------------------------
-- BitstreamFormatRegistry table
@@ -780,6 +782,24 @@ CREATE INDEX harvested_item_fk_idx ON harvested_item(item_id);
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);

View File

@@ -31,8 +31,32 @@ ALTER TABLE resourcepolicy ADD rpdescription VARCHAR(100);
ALTER TABLE item ADD discoverable BOOLEAN;
update item set discoverable=true;
-------------------------------------------
-- Item Level Versioning Tables
-------------------------------------------
CREATE TABLE versionhistory
(
versionhistory_id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE versionitem
(
versionitem_id INTEGER NOT NULL PRIMARY KEY,
item_id INTEGER REFERENCES Item(item_id),
version_number INTEGER,
eperson_id INTEGER REFERENCES EPerson(eperson_id),
version_date TIMESTAMP,
version_summary VARCHAR(255),
versionhistory_id INTEGER REFERENCES VersionHistory(versionhistory_id)
);
CREATE SEQUENCE versionitem_seq;
CREATE SEQUENCE versionhistory_seq;
-------------------------------------------
-- New columns and longer hash for salted password hashing DS-861 --