Merge branch 'main' of https://github.com/DSpace/DSpace into openaire-oai-bugfix-language

This commit is contained in:
Paulo Graça
2021-10-11 13:19:39 +01:00
31 changed files with 1052 additions and 127 deletions

View File

@@ -743,7 +743,7 @@
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.5.5</version>
<version>6.5.7</version>
</dependency>
<!-- Google Analytics -->

View File

@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.RelationshipUtils;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.factory.AuthorityServiceFactory;
import org.dspace.authority.service.AuthorityValueService;
@@ -1793,36 +1794,7 @@ public class MetadataImport extends DSpaceRunnable<MetadataImportScriptConfigura
*/
private RelationshipType matchRelationshipType(List<RelationshipType> relTypes,
String targetType, String originType, String originTypeName) {
RelationshipType foundRelationshipType = null;
if (originTypeName.split("\\.").length > 1) {
originTypeName = originTypeName.split("\\.")[1];
}
for (RelationshipType relationshipType : relTypes) {
// Is origin type leftward or righward
boolean isLeft = false;
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType)) {
isLeft = true;
}
if (isLeft) {
// Validate typeName reference
if (!relationshipType.getLeftwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType) &&
relationshipType.getRightType().getLabel().equalsIgnoreCase(targetType)) {
foundRelationshipType = relationshipType;
}
} else {
if (!relationshipType.getRightwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(targetType) &&
relationshipType.getRightType().getLabel().equalsIgnoreCase(originType)) {
foundRelationshipType = relationshipType;
}
}
}
return foundRelationshipType;
return RelationshipUtils.matchRelationshipType(relTypes, targetType, originType, originTypeName);
}
}

View File

@@ -296,29 +296,36 @@ public class ItemImportCLITool {
// validate each collection arg to see if it's a real collection
for (int i = 0; i < collections.length; i++) {
// is the ID a handle?
if (collections[i].indexOf('/') != -1) {
// string has a / so it must be a handle - try and resolve
// it
mycollections.add((Collection) handleService
.resolveToObject(c, collections[i]));
// resolved, now make sure it's a collection
if ((mycollections.get(i) == null)
|| (mycollections.get(i).getType() != Constants.COLLECTION)) {
mycollections.set(i, null);
Collection resolved = null;
if (collections[i] != null) {
// is the ID a handle?
if (collections[i].indexOf('/') != -1) {
// string has a / so it must be a handle - try and resolve
// it
resolved = ((Collection) handleService
.resolveToObject(c, collections[i]));
} else {
// not a handle, try and treat it as an integer collection database ID
resolved = collectionService.find(c, UUID.fromString(collections[i]));
}
} else if (collections[i] != null) {
// not a handle, try and treat it as an integer collection database ID
mycollections.set(i, collectionService.find(c, UUID.fromString(collections[i])));
}
// was the collection valid?
if (mycollections.get(i) == null) {
if ((resolved == null)
|| (resolved.getType() != Constants.COLLECTION)) {
throw new IllegalArgumentException("Cannot resolve "
+ collections[i] + " to collection");
}
// add resolved collection to list
mycollections.add(resolved);
// print progress info
String owningPrefix = "";
@@ -327,7 +334,7 @@ public class ItemImportCLITool {
}
System.out.println(owningPrefix + " Collection: "
+ mycollections.get(i).getName());
+ resolved.getName());
}
} // end of validating collections

View File

@@ -55,6 +55,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.dspace.app.itemimport.service.ItemImportService;
import org.dspace.app.util.LocalSchemaFilenameFilter;
import org.dspace.app.util.RelationshipUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
@@ -68,6 +69,9 @@ import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataSchemaEnum;
import org.dspace.content.MetadataValue;
import org.dspace.content.Relationship;
import org.dspace.content.RelationshipType;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
@@ -77,6 +81,9 @@ import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.MetadataValueService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.RelationshipTypeService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
@@ -151,6 +158,12 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
protected WorkflowService workflowService;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected RelationshipService relationshipService;
@Autowired(required = true)
protected RelationshipTypeService relationshipTypeService;
@Autowired(required = true)
protected MetadataValueService metadataValueService;
protected String tempWorkDir;
@@ -160,6 +173,9 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
protected boolean useWorkflowSendEmail = false;
protected boolean isQuiet = false;
//remember which folder item was imported from
Map<String, Item> itemFolderMap = null;
@Override
public void afterPropertiesSet() throws Exception {
tempWorkDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
@@ -211,10 +227,13 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
// create the mapfile
File outFile = null;
PrintWriter mapOut = null;
try {
Map<String, String> skipItems = new HashMap<>(); // set of items to skip if in 'resume'
// mode
itemFolderMap = new HashMap<>();
System.out.println("Adding items from directory: " + sourceDir);
log.debug("Adding items from directory: " + sourceDir);
System.out.println("Generating mapfile: " + mapFile);
@@ -255,6 +274,12 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
for (int i = 0; i < dircontents.length; i++) {
if (skipItems.containsKey(dircontents[i])) {
System.out.println("Skipping import of " + dircontents[i]);
//we still need the item in the map for relationship linking
String skippedHandle = skipItems.get(dircontents[i]);
Item skippedItem = (Item) handleService.resolveToObject(c, skippedHandle);
itemFolderMap.put(dircontents[i], skippedItem);
} else {
List<Collection> clist;
if (directoryFileCollections) {
@@ -274,12 +299,19 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
} else {
clist = mycollections;
}
Item item = addItem(c, clist, sourceDir, dircontents[i], mapOut, template);
itemFolderMap.put(dircontents[i], item);
c.uncacheEntity(item);
System.out.println(i + " " + dircontents[i]);
}
}
//now that all items are imported, iterate again to link relationships
addRelationships(c, sourceDir);
} finally {
if (mapOut != null) {
mapOut.flush();
@@ -288,6 +320,276 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
}
}
/**
* Add relationships from a 'relationships' manifest file.
*
* @param c Context
* @param sourceDir The parent import source directory
* @throws Exception
*/
protected void addRelationships(Context c, String sourceDir) throws Exception {
for (Map.Entry<String, Item> itemEntry : itemFolderMap.entrySet()) {
String folderName = itemEntry.getKey();
String path = sourceDir + File.separatorChar + folderName;
Item item = itemEntry.getValue();
//look for a 'relationship' manifest
Map<String, List<String>> relationships = processRelationshipFile(path, "relationships");
if (!relationships.isEmpty()) {
for (Map.Entry<String, List<String>> relEntry : relationships.entrySet()) {
String relationshipType = relEntry.getKey();
List<String> identifierList = relEntry.getValue();
for (String itemIdentifier : identifierList) {
if (isTest) {
System.out.println("\tAdding relationship (type: " + relationshipType +
") from " + folderName + " to " + itemIdentifier);
continue;
}
//find referenced item
Item relationItem = resolveRelatedItem(c, itemIdentifier);
if (null == relationItem) {
throw new Exception("Could not find item for " + itemIdentifier);
}
//get entity type of entity and item
String itemEntityType = getEntityType(item);
String relatedEntityType = getEntityType(relationItem);
//find matching relationship type
List<RelationshipType> relTypes = relationshipTypeService.findByLeftwardOrRightwardTypeName(
c, relationshipType);
RelationshipType foundRelationshipType = RelationshipUtils.matchRelationshipType(
relTypes, relatedEntityType, itemEntityType, relationshipType);
if (foundRelationshipType == null) {
throw new Exception("No Relationship type found for:\n" +
"Target type: " + relatedEntityType + "\n" +
"Origin referer type: " + itemEntityType + "\n" +
"with typeName: " + relationshipType
);
}
boolean left = false;
if (foundRelationshipType.getLeftwardType().equalsIgnoreCase(relationshipType)) {
left = true;
}
// Placeholder items for relation placing
Item leftItem = null;
Item rightItem = null;
if (left) {
leftItem = item;
rightItem = relationItem;
} else {
leftItem = relationItem;
rightItem = item;
}
// Create the relationship
int leftPlace = relationshipService.findNextLeftPlaceByLeftItem(c, leftItem);
int rightPlace = relationshipService.findNextRightPlaceByRightItem(c, rightItem);
Relationship persistedRelationship = relationshipService.create(
c, leftItem, rightItem, foundRelationshipType, leftPlace, rightPlace);
// relationshipService.update(c, persistedRelationship);
System.out.println("\tAdded relationship (type: " + relationshipType + ") from " +
leftItem.getHandle() + " to " + rightItem.getHandle());
}
}
}
}
}
/**
* Get the item's entity type from meta.
*
* @param item
* @return
*/
protected String getEntityType(Item item) throws Exception {
return itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY).get(0).getValue();
}
/**
* Read the relationship manifest file.
*
* Each line in the file contains a relationship type id and an item identifier in the following format:
*
* relation.<relation_key> <handle|uuid|folderName:import_item_folder|schema.element[.qualifier]:value>
*
* The input_item_folder should refer the folder name of another item in this import batch.
*
* @param path The main import folder path.
* @param filename The name of the manifest file to check ('relationships')
* @return Map of found relationships
* @throws Exception
*/
protected Map<String, List<String>> processRelationshipFile(String path, String filename) throws Exception {
File file = new File(path + File.separatorChar + filename);
Map<String, List<String>> result = new HashMap<>();
if (file.exists()) {
System.out.println("\tProcessing relationships file: " + filename);
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
String line = null;
while ((line = br.readLine()) != null) {
line = line.trim();
if ("".equals(line)) {
continue;
}
String relationshipType = null;
String itemIdentifier = null;
StringTokenizer st = new StringTokenizer(line);
if (st.hasMoreTokens()) {
relationshipType = st.nextToken();
if (relationshipType.split("\\.").length > 1) {
relationshipType = relationshipType.split("\\.")[1];
}
} else {
throw new Exception("Bad mapfile line:\n" + line);
}
if (st.hasMoreTokens()) {
itemIdentifier = st.nextToken("").trim();
} else {
throw new Exception("Bad mapfile line:\n" + line);
}
if (!result.containsKey(relationshipType)) {
result.put(relationshipType, new ArrayList<>());
}
result.get(relationshipType).add(itemIdentifier);
}
} catch (FileNotFoundException e) {
System.out.println("\tNo relationships file found.");
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.out.println("Non-critical problem releasing resources.");
}
}
}
}
return result;
}
/**
* Resolve an item identifier referred to in the relationships manifest file.
*
* The import item map will be checked first to see if the identifier refers to an item folder
* that was just imported. Next it will try to find the item by handle or UUID, or by a unique
* meta value.
*
* @param c Context
* @param itemIdentifier The identifier string found in the import manifest (handle, uuid, or import subfolder)
* @return Item if found, or null.
* @throws Exception
*/
protected Item resolveRelatedItem(Context c, String itemIdentifier) throws Exception {
if (itemIdentifier.contains(":")) {
if (itemIdentifier.startsWith("folderName:") || itemIdentifier.startsWith("rowName:")) {
//identifier refers to a folder name in this import
int i = itemIdentifier.indexOf(":");
String folderName = itemIdentifier.substring(i + 1);
if (itemFolderMap.containsKey(folderName)) {
return itemFolderMap.get(folderName);
}
} else {
//lookup by meta value
int i = itemIdentifier.indexOf(":");
String metaKey = itemIdentifier.substring(0, i);
String metaValue = itemIdentifier.substring(i + 1);
return findItemByMetaValue(c, metaKey, metaValue);
}
} else if (itemIdentifier.indexOf('/') != -1) {
//resolve by handle
return (Item) handleService.resolveToObject(c, itemIdentifier);
} else {
//try to resolve by UUID
return itemService.findByIdOrLegacyId(c, itemIdentifier);
}
return null;
}
/**
* Lookup an item by a (unique) meta value.
*
* @param metaKey
* @param metaValue
* @return Item
* @throws Exception if single item not found.
*/
protected Item findItemByMetaValue(Context c, String metaKey, String metaValue) throws Exception {
Item item = null;
String mf[] = metaKey.split("\\.");
if (mf.length < 2) {
throw new Exception("Bad metadata field in reference: '" + metaKey +
"' (expected syntax is schema.element[.qualifier])");
}
String schema = mf[0];
String element = mf[1];
String qualifier = mf.length == 2 ? null : mf[2];
try {
MetadataField mfo = metadataFieldService.findByElement(c, schema, element, qualifier);
Iterator<MetadataValue> mdv = metadataValueService.findByFieldAndValue(c, mfo, metaValue);
if (mdv.hasNext()) {
MetadataValue mdvVal = mdv.next();
UUID uuid = mdvVal.getDSpaceObject().getID();
if (mdv.hasNext()) {
throw new Exception("Ambiguous reference; multiple matches in db: " + metaKey);
}
item = itemService.find(c, uuid);
}
} catch (SQLException e) {
throw new Exception("Error looking up item by metadata reference: " + metaKey, e);
}
if (item == null) {
throw new Exception("Item not found by metadata reference: " + metaKey);
}
return item;
}
@Override
public void replaceItems(Context c, List<Collection> mycollections,
String sourceDir, String mapFile, boolean template) throws Exception {
@@ -1823,4 +2125,5 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
public void setQuiet(boolean isQuiet) {
this.isQuiet = isQuiet;
}
}

View File

@@ -0,0 +1,69 @@
/**
* 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.util;
import java.util.List;
import org.dspace.content.RelationshipType;
public class RelationshipUtils {
private RelationshipUtils() {
}
/**
* Matches two Entity types to a Relationship Type from a set of Relationship Types.
*
* Given a list of Relationship Types, this method will find a Relationship Type that
* is configured between the originType and the targetType, with the matching originTypeName.
* It will match a relationship between these two entities in either direction (eg leftward
* or rightward).
*
* Example: originType = Author, targetType = Publication, originTypeName = isAuthorOfPublication.
*
* @param relTypes set of Relationship Types in which to find a match.
* @param targetType entity type of target (eg. Publication).
* @param originType entity type of origin referer (eg. Author).
* @param originTypeName the name of the relationship (eg. isAuthorOfPublication)
* @return null or matched Relationship Type.
*/
public static RelationshipType matchRelationshipType(List<RelationshipType> relTypes, String targetType,
String originType, String originTypeName) {
RelationshipType foundRelationshipType = null;
if (originTypeName.split("\\.").length > 1) {
originTypeName = originTypeName.split("\\.")[1];
}
for (RelationshipType relationshipType : relTypes) {
// Is origin type leftward or righward
boolean isLeft = false;
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType)) {
isLeft = true;
}
if (isLeft) {
// Validate typeName reference
if (!relationshipType.getLeftwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType)
&& relationshipType.getRightType().getLabel().equalsIgnoreCase(targetType)) {
foundRelationshipType = relationshipType;
}
} else {
if (!relationshipType.getRightwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(targetType)
&& relationshipType.getRightType().getLabel().equalsIgnoreCase(originType)) {
foundRelationshipType = relationshipType;
}
}
}
return foundRelationshipType;
}
}

View File

@@ -14,6 +14,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
@@ -104,6 +105,16 @@ public class Collection extends DSpaceObject implements DSpaceObjectLegacySuppor
}
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Collection(UUID uuid) {
this.predefinedUUID = uuid;
}
@Override
public String getName() {
String value = getCollectionService()

View File

@@ -129,12 +129,23 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
@Override
public Collection create(Context context, Community community, String handle)
throws SQLException, AuthorizeException {
throws SQLException, AuthorizeException {
return create(context, community, handle, null);
}
@Override
public Collection create(Context context, Community community,
String handle, UUID uuid) throws SQLException, AuthorizeException {
if (community == null) {
throw new IllegalArgumentException("Community cannot be null when creating a new collection.");
}
Collection newCollection = collectionDAO.create(context, new Collection());
Collection newCollection;
if (uuid != null) {
newCollection = collectionDAO.create(context, new Collection(uuid));
} else {
newCollection = collectionDAO.create(context, new Collection());
}
//Add our newly created collection to our community, authorization checks occur in THIS method
communityService.addCollection(context, community, newCollection);
@@ -146,9 +157,10 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
authorizeService.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.READ, null);
// now create the default policies for submitted items
authorizeService
.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_ITEM_READ, null);
.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_ITEM_READ, null);
authorizeService
.createResourcePolicy(context, newCollection, anonymousGroup, null, Constants.DEFAULT_BITSTREAM_READ, null);
.createResourcePolicy(context, newCollection, anonymousGroup, null,
Constants.DEFAULT_BITSTREAM_READ, null);
collectionDAO.save(context, newCollection);
@@ -164,12 +176,12 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
}
context.addEvent(new Event(Event.CREATE, Constants.COLLECTION,
newCollection.getID(), newCollection.getHandle(),
getIdentifiers(context, newCollection)));
newCollection.getID(), newCollection.getHandle(),
getIdentifiers(context, newCollection)));
log.info(LogHelper.getHeader(context, "create_collection",
"collection_id=" + newCollection.getID())
+ ",handle=" + newCollection.getHandle());
"collection_id=" + newCollection.getID())
+ ",handle=" + newCollection.getHandle());
return newCollection;
}
@@ -951,7 +963,7 @@ public class CollectionServiceImpl extends DSpaceObjectServiceImpl<Collection> i
* Finds all Indexed Collections where the current user has submit rights. If the user is an Admin,
* this is all Indexed Collections. Otherwise, it includes those collections where
* an indexed "submit" policy lists either the eperson or one of the eperson's groups
*
*
* @param context DSpace context
* @param discoverQuery
* @param community parent community, could be null

View File

@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -89,6 +90,16 @@ public class Community extends DSpaceObject implements DSpaceObjectLegacySupport
}
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Community(UUID uuid) {
this.predefinedUUID = uuid;
}
void addSubCommunity(Community subCommunity) {
subCommunities.add(subCommunity);
setModified();

View File

@@ -86,13 +86,24 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
@Override
public Community create(Community parent, Context context, String handle) throws SQLException, AuthorizeException {
return create(parent, context, handle, null);
}
@Override
public Community create(Community parent, Context context, String handle,
UUID uuid) throws SQLException, AuthorizeException {
if (!(authorizeService.isAdmin(context) ||
(parent != null && authorizeService.authorizeActionBoolean(context, parent, Constants.ADD)))) {
(parent != null && authorizeService.authorizeActionBoolean(context, parent, Constants.ADD)))) {
throw new AuthorizeException(
"Only administrators can create communities");
"Only administrators can create communities");
}
Community newCommunity = communityDAO.create(context, new Community());
Community newCommunity;
if (uuid != null) {
newCommunity = communityDAO.create(context, new Community(uuid));
} else {
newCommunity = communityDAO.create(context, new Community());
}
if (parent != null) {
parent.addSubCommunity(newCommunity);
@@ -129,8 +140,8 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
}
log.info(LogHelper.getHeader(context, "create_community",
"community_id=" + newCommunity.getID())
+ ",handle=" + newCommunity.getHandle());
"community_id=" + newCommunity.getID())
+ ",handle=" + newCommunity.getHandle());
return newCommunity;
}
@@ -383,17 +394,26 @@ public class CommunityServiceImpl extends DSpaceObjectServiceImpl<Community> imp
@Override
public Community createSubcommunity(Context context, Community parentCommunity)
throws SQLException, AuthorizeException {
throws SQLException, AuthorizeException {
return createSubcommunity(context, parentCommunity, null);
}
@Override
public Community createSubcommunity(Context context, Community parentCommunity, String handle)
throws SQLException, AuthorizeException {
throws SQLException, AuthorizeException {
return createSubcommunity(context, parentCommunity, handle, null);
}
@Override
public Community createSubcommunity(Context context, Community parentCommunity, String handle,
UUID uuid) throws SQLException, AuthorizeException {
// Check authorisation
authorizeService.authorizeAction(context, parentCommunity, Constants.ADD);
Community c = create(parentCommunity, context, handle);
Community c;
c = create(parentCommunity, context, handle, uuid);
addSubcommunity(context, parentCommunity, c);
return c;

View File

@@ -38,8 +38,8 @@ import org.hibernate.annotations.GenericGenerator;
@Table(name = "dspaceobject")
public abstract class DSpaceObject implements Serializable, ReloadableEntity<java.util.UUID> {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid2")
@GeneratedValue(generator = "predefined-uuid")
@GenericGenerator(name = "predefined-uuid", strategy = "org.dspace.content.PredefinedUUIDGenerator")
@Column(name = "uuid", unique = true, nullable = false, insertable = true, updatable = false)
protected java.util.UUID id;
@@ -76,6 +76,15 @@ public abstract class DSpaceObject implements Serializable, ReloadableEntity<jav
@Transient
private boolean modified = false;
/**
* This will read our predefinedUUID property to pass it along to the UUID generator
*/
@Transient
protected UUID predefinedUUID;
public UUID getPredefinedUUID() {
return predefinedUUID;
}
protected DSpaceObject() {
}

View File

@@ -13,6 +13,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -122,6 +123,16 @@ public class Item extends DSpaceObject implements DSpaceObjectLegacySupport {
}
/**
* Takes a pre-determined UUID to be passed to the object to allow for the
* restoration of previously defined UUID's.
*
* @param uuid Takes a uuid to be passed to the Pre-Defined UUID Generator
*/
protected Item(UUID uuid) {
this.predefinedUUID = uuid;
}
/**
* Find out if the item is part of the main archive
*

View File

@@ -176,16 +176,29 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Override
public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException {
return create(context, workspaceItem, null);
}
@Override
public Item create(Context context, WorkspaceItem workspaceItem,
UUID uuid) throws SQLException, AuthorizeException {
Collection collection = workspaceItem.getCollection();
authorizeService.authorizeAction(context, collection, Constants.ADD);
if (workspaceItem.getItem() != null) {
throw new IllegalArgumentException(
"Attempting to create an item for a workspace item that already contains an item");
"Attempting to create an item for a workspace item that already contains an item");
}
Item item = null;
if (uuid != null) {
item = createItem(context, uuid);
} else {
item = createItem(context);
}
Item item = createItem(context);
workspaceItem.setItem(item);
log.info(LogHelper.getHeader(context, "create_item", "item_id="
+ item.getID()));
+ item.getID()));
return item;
}
@@ -418,6 +431,30 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
return bitstreamList;
}
protected Item createItem(Context context, UUID uuid) throws SQLException, AuthorizeException {
Item item;
if (uuid != null) {
item = itemDAO.create(context, new Item(uuid));
} else {
item = itemDAO.create(context, new Item());
}
// set discoverable to true (default)
item.setDiscoverable(true);
// Call update to give the item a last modified date. OK this isn't
// amazingly efficient but creates don't happen that often.
context.turnOffAuthorisationSystem();
update(context, item);
context.restoreAuthSystemState();
context.addEvent(new Event(Event.CREATE, Constants.ITEM, item.getID(),
null, getIdentifiers(context, item)));
log.info(LogHelper.getHeader(context, "create_item", "item_id=" + item.getID()));
return item;
}
protected Item createItem(Context context) throws SQLException, AuthorizeException {
Item item = itemDAO.create(context, new Item());
// set discoverable to true (default)

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.content;
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.UUIDGenerator;
/**
* Allows DSpaceObjects to provide a pre-determined UUID
*
* @author April Herron
*/
public class PredefinedUUIDGenerator extends UUIDGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
if (object instanceof DSpaceObject) {
UUID uuid = ((DSpaceObject) object).getPredefinedUUID();
if (uuid != null) {
return uuid;
}
}
return super.generate(session, object);
}
}

View File

@@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.DCInputsReaderException;
@@ -80,6 +81,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
@Override
public WorkspaceItem create(Context context, Collection collection, boolean template)
throws AuthorizeException, SQLException {
return create(context, collection, null, template);
}
@Override
public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template)
throws AuthorizeException, SQLException {
// Check the user has permission to ADD to the collection
authorizeService.authorizeAction(context, collection, Constants.ADD);
@@ -89,7 +96,12 @@ public class WorkspaceItemServiceImpl implements WorkspaceItemService {
// Create an item
Item item = itemService.create(context, workspaceItem);
Item item;
if (uuid != null) {
item = itemService.create(context, workspaceItem, uuid);
} else {
item = itemService.create(context, workspaceItem);
}
item.setSubmitter(context.getCurrentUser());
// Now create the policies for the submitter to modify item and contents

View File

@@ -32,6 +32,7 @@ import org.dspace.eperson.EPerson;
import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
@@ -54,14 +55,14 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findAll(Context context, boolean archived) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive");
Query query = createQuery(context, "FROM Item WHERE inArchive=:in_archive ORDER BY id");
query.setParameter("in_archive", archived);
return iterate(query);
}
@Override
public Iterator<Item> findAll(Context context, boolean archived, int limit, int offset) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive");
Query query = createQuery(context, "FROM Item WHERE inArchive=:in_archive ORDER BY id");
query.setParameter("in_archive", archived);
query.setFirstResult(offset);
query.setMaxResults(limit);
@@ -71,7 +72,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findAll(Context context, boolean archived, boolean withdrawn) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive or withdrawn = :withdrawn");
Query query = createQuery(context,
"FROM Item WHERE inArchive=:in_archive or withdrawn=:withdrawn ORDER BY id");
query.setParameter("in_archive", archived);
query.setParameter("withdrawn", withdrawn);
return iterate(query);
@@ -89,6 +91,7 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
if (lastModified != null) {
queryStr.append(" AND last_modified > :last_modified");
}
queryStr.append(" ORDER BY i.id");
Query query = createQuery(context, queryStr.toString());
query.setParameter("in_archive", archived);
@@ -102,7 +105,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findBySubmitter(Context context, EPerson eperson) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive and submitter= :submitter");
Query query = createQuery(context,
"FROM Item WHERE inArchive=:in_archive and submitter=:submitter ORDER BY id");
query.setParameter("in_archive", true);
query.setParameter("submitter", eperson);
return iterate(query);
@@ -114,7 +118,7 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
if (!retrieveAllItems) {
return findBySubmitter(context, eperson);
}
Query query = createQuery(context, "FROM Item WHERE submitter= :submitter");
Query query = createQuery(context, "FROM Item WHERE submitter=:submitter ORDER BY id");
query.setParameter("submitter", eperson);
return iterate(query);
}
@@ -146,7 +150,7 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
if (value != null) {
hqlQueryString += " AND STR(metadatavalue.value) = :text_value";
}
Query query = createQuery(context, hqlQueryString);
Query query = createQuery(context, hqlQueryString + " ORDER BY item.id");
query.setParameter("in_archive", inArchive);
query.setParameter("metadata_field", metadataField);
@@ -262,6 +266,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
criteria.add(Subqueries.notExists(subcriteria));
}
}
criteria.addOrder(Order.asc("item.id"));
log.debug(String.format("Running custom query with %d filters", index));
return ((List<Item>) criteria.list()).iterator();
@@ -274,7 +280,7 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
Query query = createQuery(context,
"SELECT item FROM Item as item join item.metadata metadatavalue " +
"WHERE item.inArchive=:in_archive AND metadatavalue.metadataField = :metadata_field AND " +
"metadatavalue.authority = :authority");
"metadatavalue.authority = :authority ORDER BY item.id");
query.setParameter("in_archive", inArchive);
query.setParameter("metadata_field", metadataField);
query.setParameter("authority", authority);
@@ -286,7 +292,7 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
Integer offset) throws SQLException {
Query query = createQuery(context,
"select i from Item i join i.collections c " +
"WHERE :collection IN c AND i.inArchive=:in_archive");
"WHERE :collection IN c AND i.inArchive=:in_archive ORDER BY i.id");
query.setParameter("collection", collection);
query.setParameter("in_archive", true);
if (offset != null) {
@@ -309,6 +315,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
criteriaBuilder.notEqual(itemRoot.get(Item_.owningCollection), collection),
criteriaBuilder.isMember(collection, itemRoot.get(Item_.collections)),
criteriaBuilder.isTrue(itemRoot.get(Item_.inArchive))));
criteriaQuery.orderBy(criteriaBuilder.asc(itemRoot.get(Item_.id)));
criteriaQuery.groupBy(itemRoot.get(Item_.id));
return list(context, criteriaQuery, false, Item.class, limit, offset).iterator();
}
@@ -327,7 +335,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findAllByCollection(Context context, Collection collection) throws SQLException {
Query query = createQuery(context, "select i from Item i join i.collections c WHERE :collection IN c");
Query query = createQuery(context,
"select i from Item i join i.collections c WHERE :collection IN c ORDER BY i.id");
query.setParameter("collection", collection);
return iterate(query);
@@ -336,7 +345,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
throws SQLException {
Query query = createQuery(context, "select i from Item i join i.collections c WHERE :collection IN c");
Query query = createQuery(context,
"select i from Item i join i.collections c WHERE :collection IN c ORDER BY i.id");
query.setParameter("collection", collection);
if (offset != null) {
@@ -381,7 +391,8 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
@Override
public Iterator<Item> findByLastModifiedSince(Context context, Date since)
throws SQLException {
Query query = createQuery(context, "SELECT i FROM item i WHERE last_modified > :last_modified");
Query query = createQuery(context,
"SELECT i FROM item i WHERE last_modified > :last_modified ORDER BY id");
query.setParameter("last_modified", since, TemporalType.TIMESTAMP);
return iterate(query);
}

View File

@@ -777,9 +777,6 @@ public abstract class AbstractMETSDisseminator
Mets mets = new Mets();
String identifier = "DB-ID-" + dso.getID();
if (dso.getHandle() != null) {
identifier = dso.getHandle().replace('/', '-');
}
// this ID should be globally unique (format: DSpace_[objType]_[handle with slash replaced with a dash])
mets.setID("DSpace_" + Constants.typeText[dso.getType()] + "_" + identifier);

View File

@@ -16,6 +16,7 @@ import java.net.URLConnection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -409,6 +410,7 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
// get handle from manifest
handle = getObjectHandle(manifest);
}
UUID uuid = getObjectID(manifest);
// -- Step 2 --
// Create our DSpace Object based on info parsed from manifest, and
@@ -416,7 +418,7 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
DSpaceObject dso;
try {
dso = PackageUtils.createDSpaceObject(context, parent,
type, handle, params);
type, handle, uuid, params);
} catch (SQLException sqle) {
throw new PackageValidationException("Exception while ingesting "
+ pkgFile.getPath(), sqle);
@@ -727,7 +729,6 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
// retrieve path/name of file in manifest
String path = METSManifest.getFileName(mfile);
// extract the file input stream from package (or retrieve
// externally, if it is an externally referenced file)
InputStream fileStream = getFileInputStream(pkgFile, params, path);
@@ -1506,4 +1507,22 @@ public abstract class AbstractMETSIngester extends AbstractPackageIngester {
*/
public abstract String getConfigurationName();
public UUID getObjectID(METSManifest manifest)
throws PackageValidationException {
Element mets = manifest.getMets();
String idStr = mets.getAttributeValue("ID");
if (idStr == null || idStr.length() == 0) {
throw new PackageValidationException("Manifest is missing the required mets@ID attribute.");
}
if (idStr.contains("DB-ID-")) {
idStr = idStr.substring(idStr.lastIndexOf("DB-ID-") + 6, idStr.length());
}
try {
return UUID.fromString(idStr);
} catch (IllegalArgumentException ignored) {
//do nothing
}
return null;
}
}

View File

@@ -17,6 +17,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -447,6 +448,7 @@ public class PackageUtils {
* @param parent Parent Object
* @param type Type of new Object
* @param handle Handle of new Object (may be null)
* @param uuid
* @param params Properties-style list of options (interpreted by each packager).
* @return newly created DSpace Object (or null)
* @throws AuthorizeException if authorization error
@@ -454,29 +456,55 @@ public class PackageUtils {
* @throws IOException if IO error
*/
public static DSpaceObject createDSpaceObject(Context context, DSpaceObject parent, int type, String handle,
PackageParameters params)
UUID uuid, PackageParameters params)
throws AuthorizeException, SQLException, IOException {
DSpaceObject dso = null;
switch (type) {
case Constants.COLLECTION:
dso = collectionService.create(context, (Community) parent, handle);
Collection collection = collectionService.find(context, uuid);
if (collection != null) {
dso = collectionService.create(context, (Community) parent, handle);
} else {
dso = collectionService.create(context, (Community) parent, handle, uuid);
}
return dso;
case Constants.COMMUNITY:
// top-level community?
if (parent == null || parent.getType() == Constants.SITE) {
dso = communityService.create(null, context, handle);
Community community = communityService.find(context, uuid);
if (community != null) {
dso = communityService.create(null, context, handle);
} else {
dso = communityService.create(null, context, handle, uuid);
}
} else {
dso = communityService.createSubcommunity(context, ((Community) parent), handle);
Community community = communityService.find(context, uuid);
if (community != null) {
dso = communityService.createSubcommunity(context, ((Community) parent), handle);
} else {
dso = communityService.createSubcommunity(context, ((Community) parent), handle, uuid);
}
}
return dso;
case Constants.ITEM:
//Initialize a WorkspaceItem
//(Note: Handle is not set until item is finished)
WorkspaceItem wsi = workspaceItemService
.create(context, (Collection) parent, params.useCollectionTemplate());
Item item = itemService.find(context, uuid);
if (item != null) {
return item;
}
WorkspaceItem wsi = null;
if (!params.replaceModeEnabled()) {
wsi = workspaceItemService.create(context, (Collection)parent, params.useCollectionTemplate());
} else {
wsi = workspaceItemService.create(context, (Collection)parent,
uuid, params.useCollectionTemplate());
}
// Please note that we are returning an Item which is *NOT* yet in the Archive,
// and doesn't yet have a handle assigned.

View File

@@ -12,6 +12,7 @@ import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
@@ -60,6 +61,21 @@ public interface CollectionService
public Collection create(Context context, Community community, String handle) throws SQLException,
AuthorizeException;
/**
* Create a new collection with the supplied handle and ID.
* Once created the collection is added to the given community
*
* @param context DSpace context object
* @param community DSpace Community (parent)
* @param handle the pre-determined Handle to assign to the new collection
* @param uuid the pre-determined UUID to assign to the new collection
* @return the newly created collection
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Collection create(Context context, Community community, String handle, UUID uuid) throws SQLException,
AuthorizeException;
/**
* Get all collections in the system. These are alphabetically sorted by
* collection name.
@@ -308,7 +324,7 @@ public interface CollectionService
throws java.sql.SQLException;
/**
*
*
* @param context DSpace Context
* @param group EPerson Group
* @return the collection, if any, that has the specified group as administrators or submitters
@@ -349,7 +365,7 @@ public interface CollectionService
* NOTE: for better performance, this method retrieves its results from an
* index (cache) and does not query the database directly.
* This means that results may be stale or outdated until DS-4524 is resolved"
*
*
* @param q limit the returned collection to those with metadata values matching the query terms.
* The terms are used to make also a prefix query on SOLR so it can be used to implement
* an autosuggest feature over the collection name
@@ -369,7 +385,7 @@ public interface CollectionService
* NOTE: for better performance, this method retrieves its results from an index (cache)
* and does not query the database directly.
* This means that results may be stale or outdated until DS-4524 is resolved."
*
*
* @param q limit the returned collection to those with metadata values matching the query terms.
* The terms are used to make also a prefix query on SOLR so it can be used to implement
* an autosuggest feature over the collection name

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
@@ -53,6 +54,20 @@ public interface CommunityService extends DSpaceObjectService<Community>, DSpace
public Community create(Community parent, Context context, String handle)
throws SQLException, AuthorizeException;
/**
* Create a new top-level community, with a new ID.
*
* @param parent parent community
* @param context DSpace context object
* @param handle the pre-determined Handle to assign to the new community
* @param uuid the pre-determined uuid to assign to the new community
* @return the newly created community
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Community create(Community parent, Context context,
String handle, UUID uuid) throws SQLException, AuthorizeException;
/**
* Get a list of all communities in the system. These are alphabetically
@@ -202,6 +217,20 @@ public interface CommunityService extends DSpaceObjectService<Community>, DSpace
public Community createSubcommunity(Context context, Community parentCommunity, String handle)
throws SQLException, AuthorizeException;
/**
* Create a new sub-community within this community.
*
* @param context context
* @param handle the pre-determined Handle to assign to the new community
* @param parentCommunity parent community
* @param uuid the pre-determined UUID to assign to the new community
* @return the new community
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Community createSubcommunity(Context context, Community parentCommunity, String handle, UUID uuid)
throws SQLException, AuthorizeException;
/**
* Add an existing community as a subcommunity to the community
*

View File

@@ -43,9 +43,8 @@ public interface ItemService
public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException;
/**
* Create a new item, with a new internal ID. This method is not public,
* since items need to be created as workspace items. Authorisation is the
* responsibility of the caller.
* Create a new item, with a new internal ID. Authorization is done
* inside of this method.
*
* @param context DSpace context object
* @param workspaceItem in progress workspace item
@@ -55,6 +54,19 @@ public interface ItemService
*/
public Item create(Context context, WorkspaceItem workspaceItem) throws SQLException, AuthorizeException;
/**
* Create a new item, with a provided ID. Authorisation is done
* inside of this method.
*
* @param context DSpace context object
* @param workspaceItem in progress workspace item
* @param uuid the pre-determined UUID to assign to the new item
* @return the newly created item
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public Item create(Context context, WorkspaceItem workspaceItem, UUID uuid) throws SQLException, AuthorizeException;
/**
* Create an empty template item for this collection. If one already exists,
* no action is taken. Caution: Make sure you call <code>update</code> on

View File

@@ -11,6 +11,7 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Collection;
@@ -55,6 +56,22 @@ public interface WorkspaceItemService extends InProgressSubmissionService<Worksp
public WorkspaceItem create(Context context, Collection collection, boolean template)
throws AuthorizeException, SQLException;
/**
* Create a new workspace item, with a new ID. An Item is also created. The
* submitter is the current user in the context.
*
* @param context DSpace context object
* @param collection Collection being submitted to
* @param uuid the preferred uuid of the new item (used if restoring an item and retaining old uuid)
* @param template if <code>true</code>, the workspace item starts as a copy
* of the collection's template item
* @return the newly created workspace item
* @throws SQLException if database error
* @throws AuthorizeException if authorization error
*/
public WorkspaceItem create(Context context, Collection collection, UUID uuid, boolean template)
throws AuthorizeException, SQLException;
public WorkspaceItem create(Context c, WorkflowItem wfi) throws SQLException, AuthorizeException;

View File

@@ -0,0 +1,15 @@
--
-- 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/
--
-------------------------------------------------------------------------------------
---- ALTER table collection
-------------------------------------------------------------------------------------
ALTER TABLE collection DROP COLUMN workflow_step_1;
ALTER TABLE collection DROP COLUMN workflow_step_2;
ALTER TABLE collection DROP COLUMN workflow_step_3;

View File

@@ -0,0 +1,15 @@
--
-- 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/
--
-------------------------------------------------------------------------------------
---- ALTER table collection
-------------------------------------------------------------------------------------
ALTER TABLE collection DROP COLUMN workflow_step_1;
ALTER TABLE collection DROP COLUMN workflow_step_2;
ALTER TABLE collection DROP COLUMN workflow_step_3;

View File

@@ -0,0 +1,15 @@
--
-- 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/
--
-------------------------------------------------------------------------------------
---- ALTER table collection
-------------------------------------------------------------------------------------
ALTER TABLE collection DROP COLUMN workflow_step_1;
ALTER TABLE collection DROP COLUMN workflow_step_2;
ALTER TABLE collection DROP COLUMN workflow_step_3;

View File

@@ -0,0 +1,205 @@
/**
* 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.packager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.common.collect.Iterators;
import org.dspace.AbstractIntegrationTestWithDatabase;
import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.ItemBuilder;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.crosswalk.MetadataValidationException;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.packager.METSManifest;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.jdom.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Basic integration testing for the Packager restore feature
*
* @author Nathan Buckingham
*/
public class PackagerIT extends AbstractIntegrationTestWithDatabase {
private ItemService itemService = ContentServiceFactory.getInstance().getItemService();
private CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
private CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
private WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
protected static final InstallItemService installItemService = ContentServiceFactory.getInstance()
.getInstallItemService();
protected ConfigurationService configService = DSpaceServicesFactory.getInstance().getConfigurationService();
protected Community child1;
protected Collection col1;
protected Item article;
File tempFile;
@Before
public void setup() throws IOException {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
// Create a new Publication (which is an Article)
article = ItemBuilder.createItem(context, col1)
.withTitle("Article")
.withIssueDate("2017-10-17")
.withEntityType("Publication")
.build();
tempFile = File.createTempFile("packagerExportTest", ".zip");
context.restoreAuthSystemState();
}
@After
public void destroy() throws SQLException, IOException {
tempFile.delete();
}
@Test
public void packagerExportUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
performExportScript(article.getHandle(), tempFile);
assertTrue(tempFile.length() > 0);
String idStr = getID();
assertEquals(idStr, article.getID().toString());
}
@Test
public void packagerImportUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
//Item
performExportScript(article.getHandle(), tempFile);
String idStr = getID();
itemService.delete(context, article);
performImportScript(tempFile);
Item item = itemService.find(context, UUID.fromString(idStr));
assertNotNull(item);
}
@Test
public void packagerImportColUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
configService.setProperty("upload.temp.dir",tempFile.getParent());
performExportScript(col1.getHandle(), tempFile);
String idStr = getID();
collectionService.delete(context, col1);
performImportScript(tempFile);
Collection collection = collectionService.find(context, UUID.fromString(idStr));
assertNotNull(collection);
}
@Test
public void packagerImportComUUIDTest() throws Exception {
context.turnOffAuthorisationSystem();
configService.setProperty("upload.temp.dir",tempFile.getParent());
//Community
performExportScript(child1.getHandle(), tempFile);
String idStr = getID();
communityService.delete(context, child1);
performImportScript(tempFile);
Community community = communityService.find(context, UUID.fromString(idStr));
assertNotNull(community);
}
@Test
public void packagerUUIDAlreadyExistTest() throws Exception {
context.turnOffAuthorisationSystem();
//Item should be overwritten if UUID already Exists
performExportScript(article.getHandle(), tempFile);
performImportScript(tempFile);
Iterator<Item> items = itemService.findByCollection(context, col1);
assertEquals(1, Iterators.size(items));
}
@Test
public void packagerUUIDAlreadyExistWithoutForceTest() throws Exception {
context.turnOffAuthorisationSystem();
//should fail to restore the item because the uuid already exists.
performExportScript(article.getHandle(), tempFile);
UUID id = article.getID();
itemService.delete(context, article);
WorkspaceItem workspaceItem = workspaceItemService.create(context, col1, id, false);
installItemService.installItem(context, workspaceItem, "123456789/0100");
performImportNoForceScript(tempFile);
Iterator<Item> items = itemService.findByCollection(context, col1);
Item testItem = items.next();
assertFalse(items.hasNext()); //check to make sure there is only 1 item
assertEquals("123456789/0100", testItem.getHandle()); //check to make sure the item wasn't overwritten as
// it would have the old handle.
itemService.delete(context, testItem);
}
private String getID() throws IOException, MetadataValidationException {
//this method gets the UUID from the mets file thats stored in the attribute element
METSManifest manifest = null;
ZipFile zip = new ZipFile(tempFile);
ZipEntry manifestEntry = zip.getEntry(METSManifest.MANIFEST_FILE);
if (manifestEntry != null) {
// parse the manifest and sanity-check it.
manifest = METSManifest.create(zip.getInputStream(manifestEntry),
false, "AIP");
}
Element mets = manifest.getMets();
String idStr = mets.getAttributeValue("ID");
if (idStr.contains("DB-ID-")) {
idStr = idStr.substring(idStr.lastIndexOf("DB-ID-") + 6, idStr.length());
}
return idStr;
}
private void performExportScript(String handle, File outputFile) throws Exception {
runDSpaceScript("packager", "-d", "-e", "admin@email.com", "-i", handle, "-t",
"AIP", outputFile.getPath());
}
private void performImportNoForceScript(File outputFile) throws Exception {
runDSpaceScript("packager", "-r", "-u", "-e", "admin@email.com", "-t",
"AIP", outputFile.getPath());
}
private void performImportScript(File outputFile) throws Exception {
runDSpaceScript("packager", "-r", "-f", "-u", "-e", "admin@email.com", "-t",
"AIP", outputFile.getPath());
}
}

View File

@@ -14,6 +14,8 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
@@ -33,12 +35,15 @@ import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
/**
@@ -46,6 +51,7 @@ import org.springframework.test.util.ReflectionTestUtils;
*
* @author pvillega
*/
@RunWith(MockitoJUnitRunner.class)
public class WorkspaceItemTest extends AbstractUnitTest {
/**
@@ -98,6 +104,7 @@ public class WorkspaceItemTest extends AbstractUnitTest {
// "Wire" our spy to be used by the current loaded object services
// (To ensure these services use the spy instead of the real service)
ReflectionTestUtils.setField(workspaceItemService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(itemService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(collectionService, "authorizeService", authorizeServiceSpy);
ReflectionTestUtils.setField(communityService, "authorizeService", authorizeServiceSpy);
} catch (AuthorizeException ex) {
@@ -158,7 +165,8 @@ public class WorkspaceItemTest extends AbstractUnitTest {
@Test
public void testCreateAuth() throws Exception {
// Allow Collection ADD perms
doNothing().when(authorizeServiceSpy).authorizeAction(context, collection, Constants.ADD);
doNothing().when(authorizeServiceSpy).authorizeAction(any(Context.class),
any(Collection.class), eq(Constants.ADD));
boolean template;
WorkspaceItem created;

View File

@@ -29,6 +29,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -2414,16 +2415,34 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.withName("Mapped Collection")
.build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
Item item0 = ItemBuilder.createItem(context, collection).withTitle("Item 0").build();
items.add(item0);
Item item1 = ItemBuilder.createItem(context, collection).withTitle("Item 1").build();
items.add(item1);
Item item2 = ItemBuilder.createItem(context, collection).withTitle("Item 2").build();
items.add(item2);
Item item3 = ItemBuilder.createItem(context, collection).withTitle("Item 3").build();
items.add(item3);
Item item4 = ItemBuilder.createItem(context, collection).withTitle("Item 4").build();
items.add(item4);
Item item5 = ItemBuilder.createItem(context, collection).withTitle("Item 5").build();
items.add(item5);
Item item6 = ItemBuilder.createItem(context, collection).withTitle("Item 6").build();
items.add(item6);
Item item7 = ItemBuilder.createItem(context, collection).withTitle("Item 7").build();
items.add(item7);
Item item8 = ItemBuilder.createItem(context, collection).withTitle("Item 8").build();
items.add(item8);
Item item9 = ItemBuilder.createItem(context, collection).withTitle("Item 9").build();
items.add(item9);
// sort items list by UUID (as Items will come back ordered by UUID)
items.sort(compareByUUID);
collectionService.addItem(context, mappedCollection, item0);
collectionService.addItem(context, mappedCollection, item1);
@@ -2445,12 +2464,13 @@ public class CollectionRestRepositoryIT extends AbstractControllerIntegrationTes
.param("embed.size", "mappedItems=5"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", CollectionMatcher.matchCollection(mappedCollection)))
.andExpect(jsonPath("$._embedded.mappedItems._embedded.mappedItems", Matchers.containsInAnyOrder(
ItemMatcher.matchItemProperties(item0),
ItemMatcher.matchItemProperties(item1),
ItemMatcher.matchItemProperties(item2),
ItemMatcher.matchItemProperties(item3),
ItemMatcher.matchItemProperties(item4)
.andExpect(jsonPath("$._embedded.mappedItems._embedded.mappedItems",
Matchers.containsInRelativeOrder(
ItemMatcher.matchItemProperties(items.get(0)),
ItemMatcher.matchItemProperties(items.get(1)),
ItemMatcher.matchItemProperties(items.get(2)),
ItemMatcher.matchItemProperties(items.get(3)),
ItemMatcher.matchItemProperties(items.get(4))
)))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/collections/" + mappedCollection.getID())))

View File

@@ -27,6 +27,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -111,6 +112,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
Collection col2 = CollectionBuilder.createCollection(context, child1).withName("Collection 2").build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
//2. Three public items that are readable by Anonymous with different subjects
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
@@ -118,6 +124,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
items.add(publicItem1);
Item publicItem2 = ItemBuilder.createItem(context, col2)
.withTitle("Public item 2")
@@ -125,6 +132,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withAuthor("Smith, Maria").withAuthor("Doe, Jane")
.withSubject("TestingForMore").withSubject("ExtraEntry")
.build();
items.add(publicItem2);
Item publicItem3 = ItemBuilder.createItem(context, col2)
.withTitle("Public item 3")
@@ -133,19 +141,19 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withSubject("AnotherTest").withSubject("TestingForMore")
.withSubject("ExtraEntry")
.build();
items.add(publicItem3);
// sort items list by UUID (as Items will come back ordered by UUID)
items.sort(compareByUUID);
context.restoreAuthSystemState();
String token = getAuthToken(admin.getEmail(), password);
getClient(token).perform(get("/api/core/items"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.items", Matchers.containsInAnyOrder(
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem1,
"Public item 1", "2017-10-17"),
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem2,
"Public item 2", "2016-02-13"),
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem3,
"Public item 3", "2016-02-13")
.andExpect(jsonPath("$._embedded.items", Matchers.containsInRelativeOrder(
ItemMatcher.matchItemProperties(items.get(0)),
ItemMatcher.matchItemProperties(items.get(1)),
ItemMatcher.matchItemProperties(items.get(2))
)))
.andExpect(jsonPath("$._links.self.href",
Matchers.containsString("/api/core/items")))
@@ -185,6 +193,11 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withTemplateItem()
.build();
List<Item> items = new ArrayList();
// This comparator is used to sort our test Items by java.util.UUID (which sorts them based on the RFC
// and not based on String comparison, see also https://stackoverflow.com/a/51031298/3750035 )
Comparator<Item> compareByUUID = Comparator.comparing(i -> i.getID());
//2. Three public items that are readable by Anonymous with different subjects
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
@@ -192,6 +205,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.withSubject("ExtraEntry")
.build();
items.add(publicItem1);
Item publicItem2 = ItemBuilder.createItem(context, col2)
.withTitle("Public item 2")
@@ -199,6 +213,7 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withAuthor("Smith, Maria").withAuthor("Doe, Jane")
.withSubject("TestingForMore").withSubject("ExtraEntry")
.build();
items.add(publicItem2);
Item publicItem3 = ItemBuilder.createItem(context, col2)
.withTitle("Public item 3")
@@ -207,6 +222,9 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.withSubject("AnotherTest").withSubject("TestingForMore")
.withSubject("ExtraEntry")
.build();
items.add(publicItem3);
// sort items list by UUID (as Items will come back ordered by UUID)
items.sort(compareByUUID);
// Create a Workspace Item (which in turn creates an Item with "in_archive=false")
// This is only created to prove that WorkspaceItems are NOT counted/listed in this endpoint
@@ -232,16 +250,13 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
getClient(token).perform(get("/api/core/items")
.param("size", "2"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.items", Matchers.containsInAnyOrder(
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem1,
"Public item 1", "2017-10-17"),
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem2,
"Public item 2", "2016-02-13")
.andExpect(jsonPath("$._embedded.items", Matchers.containsInRelativeOrder(
ItemMatcher.matchItemProperties(items.get(0)),
ItemMatcher.matchItemProperties(items.get(1))
)))
.andExpect(jsonPath("$._embedded.items", Matchers.not(
Matchers.contains(
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem3,
"Public item 3", "2016-02-13"),
ItemMatcher.matchItemProperties(items.get(2)),
ItemMatcher.matchItemWithTitleAndDateIssued(itemInWorkspace,
"In Progress Item", "2018-02-05"),
ItemMatcher.matchItemWithTitleAndDateIssued(itemInWorkflow,
@@ -271,15 +286,12 @@ public class ItemRestRepositoryIT extends AbstractControllerIntegrationTest {
.param("page", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.items", Matchers.contains(
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem3,
"Public item 3", "2016-02-13")
ItemMatcher.matchItemProperties(items.get(2))
)))
.andExpect(jsonPath("$._embedded.items", Matchers.not(
Matchers.contains(
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem1,
"Public item 1", "2017-10-17"),
ItemMatcher.matchItemWithTitleAndDateIssued(publicItem2,
"Public item 2", "2016-02-13"),
ItemMatcher.matchItemProperties(items.get(0)),
ItemMatcher.matchItemProperties(items.get(1)),
ItemMatcher.matchItemWithTitleAndDateIssued(itemInWorkspace,
"In Progress Item", "2018-02-05"),
ItemMatcher.matchItemWithTitleAndDateIssued(itemInWorkflow,

View File

@@ -161,8 +161,7 @@
<repeatable>true</repeatable>
<label>Type</label>
<input-type value-pairs-name="common_types">dropdown</input-type>
<hint>Select the type(s) of content of the item. To select more than one value in the list, you may
have to hold down the "CTRL" or "Shift" key.
<hint>Select the type of content of the item.
</hint>
<required></required>
</field>

View File

@@ -24,7 +24,7 @@
<spring-security.version>5.2.2.RELEASE</spring-security.version> <!-- sync with version used by spring-boot-->
<hibernate.version>5.4.10.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>
<postgresql.driver.version>42.2.9</postgresql.driver.version>
<postgresql.driver.version>42.2.24</postgresql.driver.version>
<solr.client.version>8.8.1</solr.client.version>
<axiom.version>1.2.22</axiom.version>