Merge remote-tracking branch 'dspace-origin/main' into issue-8140_create-citation-page-once_main

This commit is contained in:
Marie Verdonck
2022-03-07 12:54:44 +01:00
57 changed files with 469 additions and 176 deletions

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -20,7 +20,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -290,20 +289,13 @@ public class ShibAuthentication implements AuthenticationMethod {
try {
// User has not successfuly authenticated via shibboleth.
if (request == null ||
context.getCurrentUser() == null ||
request.getSession().getAttribute("shib.authenticated") == null) {
context.getCurrentUser() == null) {
return Collections.EMPTY_LIST;
}
// If we have already calculated the special groups then return them.
if (request.getSession().getAttribute("shib.specialgroup") != null) {
if (context.getSpecialGroups().size() > 0 ) {
log.debug("Returning cached special groups.");
List<UUID> sessionGroupIds = (List<UUID>) request.getSession().getAttribute("shib.specialgroup");
List<Group> result = new ArrayList<>();
for (UUID uuid : sessionGroupIds) {
result.add(groupService.find(context, uuid));
}
return result;
return context.getSpecialGroups();
}
log.debug("Starting to determine special groups");
@@ -396,16 +388,8 @@ public class ShibAuthentication implements AuthenticationMethod {
log.info("Added current EPerson to special groups: " + groups);
List<UUID> groupIds = new ArrayList<>();
for (Group group : groups) {
groupIds.add(group.getID());
}
// Cache the special groups, so we don't have to recalculate them again
// for this session.
request.setAttribute("shib.specialgroup", groupIds);
return new ArrayList<>(groups);
} catch (Throwable t) {
log.error("Unable to validate any sepcial groups this user may belong too because of an exception.", t);
return Collections.EMPTY_LIST;

View File

@@ -243,67 +243,64 @@ public abstract class DSpaceObjectServiceImpl<T extends DSpaceObject> implements
boolean authorityControlled = metadataAuthorityService.isAuthorityControlled(metadataField);
boolean authorityRequired = metadataAuthorityService.isAuthorityRequired(metadataField);
List<MetadataValue> newMetadata = new ArrayList<>(values.size());
List<MetadataValue> newMetadata = new ArrayList<>();
// We will not verify that they are valid entries in the registry
// until update() is called.
for (int i = 0; i < values.size(); i++) {
if (authorities != null && authorities.size() >= i) {
if (StringUtils.startsWith(authorities.get(i), Constants.VIRTUAL_AUTHORITY_PREFIX)) {
continue;
}
}
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
newMetadata.add(metadataValue);
metadataValue.setPlace(placeSupplier.get());
metadataValue.setLanguage(lang == null ? null : lang.trim());
// Logic to set Authority and Confidence:
// - normalize an empty string for authority to NULL.
// - if authority key is present, use given confidence or NOVALUE if not given
// - otherwise, preserve confidence if meaningful value was given since it may document a failed
// authority lookup
// - CF_UNSET signifies no authority nor meaningful confidence.
// - it's possible to have empty authority & CF_ACCEPTED if e.g. user deletes authority key
if (authorityControlled) {
if (authorities != null && authorities.get(i) != null && authorities.get(i).length() > 0) {
metadataValue.setAuthority(authorities.get(i));
metadataValue.setConfidence(confidences == null ? Choices.CF_NOVALUE : confidences.get(i));
} else {
metadataValue.setAuthority(null);
metadataValue.setConfidence(confidences == null ? Choices.CF_UNSET : confidences.get(i));
}
// authority sanity check: if authority is required, was it supplied?
// XXX FIXME? can't throw a "real" exception here without changing all the callers to expect it, so
// use a runtime exception
if (authorityRequired && (metadataValue.getAuthority() == null || metadataValue.getAuthority()
.length() == 0)) {
throw new IllegalArgumentException("The metadata field \"" + metadataField
.toString() + "\" requires an authority key but none was provided. Value=\"" + values
.get(i) + "\"");
}
}
if (values.get(i) != null) {
if (authorities != null && authorities.size() >= i) {
if (StringUtils.startsWith(authorities.get(i), Constants.VIRTUAL_AUTHORITY_PREFIX)) {
continue;
}
}
MetadataValue metadataValue = metadataValueService.create(context, dso, metadataField);
newMetadata.add(metadataValue);
metadataValue.setPlace(placeSupplier.get());
metadataValue.setLanguage(lang == null ? null : lang.trim());
// Logic to set Authority and Confidence:
// - normalize an empty string for authority to NULL.
// - if authority key is present, use given confidence or NOVALUE if not given
// - otherwise, preserve confidence if meaningful value was given since it may document a failed
// authority lookup
// - CF_UNSET signifies no authority nor meaningful confidence.
// - it's possible to have empty authority & CF_ACCEPTED if e.g. user deletes authority key
if (authorityControlled) {
if (authorities != null && authorities.get(i) != null && authorities.get(i).length() > 0) {
metadataValue.setAuthority(authorities.get(i));
metadataValue.setConfidence(confidences == null ? Choices.CF_NOVALUE : confidences.get(i));
} else {
metadataValue.setAuthority(null);
metadataValue.setConfidence(confidences == null ? Choices.CF_UNSET : confidences.get(i));
}
// authority sanity check: if authority is required, was it supplied?
// XXX FIXME? can't throw a "real" exception here without changing all the callers to expect it, so
// use a runtime exception
if (authorityRequired && (metadataValue.getAuthority() == null || metadataValue.getAuthority()
.length() == 0)) {
throw new IllegalArgumentException("The metadata field \"" + metadataField
.toString() + "\" requires an authority key but none was provided. Value=\"" + values
.get(i) + "\"");
}
}
// remove control unicode char
String temp = values.get(i).trim();
char[] dcvalue = temp.toCharArray();
for (int charPos = 0; charPos < dcvalue.length; charPos++) {
if (Character.isISOControl(dcvalue[charPos]) &&
!String.valueOf(dcvalue[charPos]).equals("\u0009") &&
!String.valueOf(dcvalue[charPos]).equals("\n") &&
!String.valueOf(dcvalue[charPos]).equals("\r")) {
!String.valueOf(dcvalue[charPos]).equals("\u0009") &&
!String.valueOf(dcvalue[charPos]).equals("\n") &&
!String.valueOf(dcvalue[charPos]).equals("\r")) {
dcvalue[charPos] = ' ';
}
}
metadataValue.setValue(String.valueOf(dcvalue));
} else {
metadataValue.setValue(null);
}
//An update here isn't needed, this is persited upon the merge of the owning object
//An update here isn't needed, this is persited upon the merge of the owning object
// metadataValueService.update(context, metadataValue);
dso.addDetails(metadataField.toString());
dso.addDetails(metadataField.toString());
}
}
setMetadataModified(dso);
return newMetadata;

View File

@@ -911,6 +911,12 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
@Override
public void move(Context context, Item item, Collection from, Collection to)
throws SQLException, AuthorizeException, IOException {
// If the two collections are the same, do nothing.
if (from.equals(to)) {
return;
}
// Use the normal move method, and default to not inherit permissions
this.move(context, item, from, to, false);
}

View File

@@ -201,7 +201,7 @@ public class Group extends DSpaceObject implements DSpaceObjectLegacySupport {
void setName(String name) throws SQLException {
if (!StringUtils.equals(this.name, name) && !isPermanent()) {
this.name = name;
groupsChanged = true;
setMetadataModified();
}
}

View File

@@ -34,8 +34,6 @@ public class DOI
implements Identifier, ReloadableEntity<Integer> {
public static final String SCHEME = "doi:";
public static final String RESOLVER = "http://dx.doi.org";
@Id
@Column(name = "doi_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "doi_seq")

View File

@@ -1028,7 +1028,7 @@ public class DOIIdentifierProvider extends FilteredIdentifierProvider {
Item item = (Item) dso;
List<MetadataValue> metadata = itemService.getMetadata(item, MD_SCHEMA, DOI_ELEMENT, DOI_QUALIFIER, null);
String leftPart = DOI.RESOLVER + SLASH + getPrefix() + SLASH + getNamespaceSeparator();
String leftPart = doiService.getResolver() + SLASH + getPrefix() + SLASH + getNamespaceSeparator();
for (MetadataValue id : metadata) {
if (id.getValue().startsWith(leftPart)) {
return doiService.DOIFromExternalFormat(id.getValue());

View File

@@ -17,11 +17,13 @@ import org.dspace.core.Context;
import org.dspace.identifier.dao.DOIDAO;
import org.dspace.identifier.doi.DOIIdentifierException;
import org.dspace.identifier.service.DOIService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Service implementation for the DOI object.
* This class is responsible for all business logic calls for the DOI object and is autowired by spring.
* Service implementation for the {@link DOI} object.
* This class is responsible for all business logic calls for the DOI object
* and is autowired by Spring.
* This class should never be accessed directly.
*
* @author kevinvandevelde at atmire.com
@@ -31,6 +33,16 @@ public class DOIServiceImpl implements DOIService {
@Autowired(required = true)
protected DOIDAO doiDAO;
@Autowired(required = true)
protected ConfigurationService configurationService;
private static final Pattern DOI_URL_PATTERN
= Pattern.compile("http(s)?://([a-z0-9-.]+)?doi.org(?<path>/.*)",
Pattern.CASE_INSENSITIVE);
private static final String DOI_URL_PATTERN_PATH_GROUP = "path";
private static final String RESOLVER_DEFAULT = "https://doi.org";
protected DOIServiceImpl() {
}
@@ -66,25 +78,46 @@ public class DOIServiceImpl implements DOIService {
if (null == identifier) {
throw new IllegalArgumentException("Identifier is null.", new NullPointerException());
}
if (identifier.isEmpty()) {
throw new IllegalArgumentException("Cannot format an empty identifier.");
}
if (identifier.startsWith(DOI.SCHEME)) {
return DOI.RESOLVER + "/" + identifier.substring(DOI.SCHEME.length());
String resolver = getResolver();
if (identifier.startsWith(DOI.SCHEME)) { // doi:something
StringBuilder result = new StringBuilder(resolver);
if (!resolver.endsWith("/")) {
result.append('/');
}
result.append(identifier.substring(DOI.SCHEME.length()));
return result.toString();
}
if (identifier.startsWith("10.") && identifier.contains("/")) {
return DOI.RESOLVER + "/" + identifier;
if (identifier.startsWith("10.") && identifier.contains("/")) { // 10.something
StringBuilder result = new StringBuilder(resolver);
if (!resolver.endsWith("/")) {
result.append('/');
}
result.append(identifier);
return result.toString();
}
if (identifier.startsWith(DOI.RESOLVER + "/10.")) {
if (identifier.startsWith(resolver + "/10.")) { // https://doi.org/10.something
return identifier;
}
Matcher matcher = DOI_URL_PATTERN.matcher(identifier);
if (matcher.matches()) { // various old URL forms
return resolver + matcher.group(DOI_URL_PATTERN_PATH_GROUP);
}
throw new IdentifierException(identifier + "does not seem to be a DOI.");
}
@Override
public String DOIFromExternalFormat(String identifier) throws DOIIdentifierException {
Pattern pattern = Pattern.compile("^" + DOI.RESOLVER + "/+(10\\..*)$");
Pattern pattern = Pattern.compile("^" + getResolver() + "/+(10\\..*)$");
Matcher matcher = pattern.matcher(identifier);
if (matcher.find()) {
return DOI.SCHEME + matcher.group(1);
@@ -99,18 +132,29 @@ public class DOIServiceImpl implements DOIService {
if (null == identifier) {
throw new IllegalArgumentException("Identifier is null.", new NullPointerException());
}
if (identifier.startsWith(DOI.SCHEME)) {
return identifier;
}
if (identifier.isEmpty()) {
throw new IllegalArgumentException("Cannot format an empty identifier.");
}
if (identifier.startsWith("10.") && identifier.contains("/")) {
if (identifier.startsWith(DOI.SCHEME)) { // doi:something
return identifier;
}
if (identifier.startsWith("10.") && identifier.contains("/")) { // 10.something
return DOI.SCHEME + identifier;
}
if (identifier.startsWith(DOI.RESOLVER + "/10.")) {
return DOI.SCHEME + identifier.substring(18);
String resolver = getResolver();
if (identifier.startsWith(resolver + "/10.")) { //https://doi.org/10.something
return DOI.SCHEME + identifier.substring(resolver.length());
}
Matcher matcher = DOI_URL_PATTERN.matcher(identifier);
if (matcher.matches()) { // various old URL forms
return DOI.SCHEME + matcher.group(DOI_URL_PATTERN_PATH_GROUP).substring(1);
}
throw new DOIIdentifierException(identifier + "does not seem to be a DOI.",
DOIIdentifierException.UNRECOGNIZED);
}
@@ -126,4 +170,14 @@ public class DOIServiceImpl implements DOIService {
throws SQLException {
return doiDAO.findSimilarNotInState(context, doiPattern, statuses, dsoIsNotNull);
}
@Override
public String getResolver() {
String resolver = configurationService.getProperty("identifier.doi.resolver",
RESOLVER_DEFAULT);
if (resolver.endsWith("/")) {
resolver = resolver.substring(0, resolver.length() - 1);
}
return resolver;
}
}

View File

@@ -17,26 +17,65 @@ import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.doi.DOIIdentifierException;
/**
* Service interface class for the DOI object.
* The implementation of this class is responsible for all business logic calls for the DOI object and is autowired
* by spring
* Service interface class for the {@link DOI} object.
* The implementation of this class is responsible for all business logic calls
* for the {@link DOI} object and is autowired by Spring.
*
* @author kevinvandevelde at atmire.com
*/
public interface DOIService {
/**
* Update a DOI in storage.
*
* @param context current DSpace session.
* @param doi the DOI to persist.
* @throws SQLException passed through.
*/
public void update(Context context, DOI doi) throws SQLException;
/**
* Create a new DOI in storage.
*
* @param context current DSpace session.
* @return the new DOI.
* @throws SQLException passed through.
*/
public DOI create(Context context) throws SQLException;
/**
* Find a specific DOI in storage.
*
* @param context current DSpace session.
* @param doi string representation of the DOI.
* @return the DOI object found.
* @throws SQLException passed through, can mean none found.
*/
public DOI findByDoi(Context context, String doi) throws SQLException;
/**
* Find the DOI assigned to a given DSpace Object.
*
* @param context current DSpace session.
* @param dso The DSpace Object.
* @return the DSO's DOI.
* @throws SQLException passed through.
*/
public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso) throws SQLException;
/**
* Find the DOI assigned to a given DSpace Object, unless it has one of a
* given set of statuses.
*
* @param context current DSpace context.
* @param dso the DSpace Object.
* @param statusToExclude uninteresting statuses.
* @return the DSO's DOI.
* @throws SQLException passed through.
*/
public DOI findDOIByDSpaceObject(Context context, DSpaceObject dso, List<Integer> statusToExclude)
throws SQLException;
/**
* This method helps to convert a DOI into a URL. It takes DOIs in one of
* the following formats and returns it as URL (f.e.
@@ -49,12 +88,18 @@ public interface DOIService {
*
* @param identifier A DOI that should be returned in external form.
* @return A String containing a URL to the official DOI resolver.
* @throws IllegalArgumentException If identifier is null or an empty String.
* @throws org.dspace.identifier.IdentifierException If identifier could not be recognized as valid DOI.
* @throws IllegalArgumentException If identifier is null or an empty String.
* @throws IdentifierException If identifier could not be recognized as valid DOI.
*/
public String DOIToExternalForm(String identifier)
throws IdentifierException;
/**
* Convert an HTTP DOI URL (https://doi.org/10.something) to a "doi:" URI.
* @param identifier HTTP URL
* @return DOI URI
* @throws DOIIdentifierException if {@link identifier} is not recognizable.
*/
public String DOIFromExternalFormat(String identifier)
throws DOIIdentifierException;
@@ -64,16 +109,24 @@ public interface DOIService {
* @param identifier Identifier to format, following format are accepted:
* f.e. 10.123/456, doi:10.123/456, http://dx.doi.org/10.123/456.
* @return Given Identifier with DOI-Scheme, f.e. doi:10.123/456.
* @throws IllegalArgumentException If identifier is empty or null.
* @throws org.dspace.identifier.doi.DOIIdentifierException If DOI could not be recognized.
* @throws IllegalArgumentException If identifier is empty or null.
* @throws DOIIdentifierException If DOI could not be recognized.
*/
public String formatIdentifier(String identifier)
throws DOIIdentifierException;
/**
* Find all DOIs that have one of a given set of statuses.
* @param context current DSpace session.
* @param statuses desired statuses.
* @return all DOIs having any of the given statuses.
* @throws SQLException passed through.
*/
public List<DOI> getDOIsByStatus(Context context, List<Integer> statuses) throws SQLException;
/**
* Find all DOIs that are similar to the specified pattern ant not in the specified states.
* Find all DOIs that are similar to the specified pattern and not in the
* specified states.
*
* @param context DSpace context
* @param doiPattern The pattern, e.g. "10.5072/123.%"
@@ -85,4 +138,11 @@ public interface DOIService {
public List<DOI> getSimilarDOIsNotInState(Context context, String doiPattern, List<Integer> statuses,
boolean dsoIsNotNull)
throws SQLException;
/**
* Get the URL stem of the DOI resolver, e.g. "https://doi.org/".
*
* @return URL to the DOI resolver.
*/
public String getResolver();
}

View File

@@ -286,51 +286,54 @@ public class RDFConsumer implements Consumer {
@Override
public void end(Context ctx) throws Exception {
log.debug("Started processing of queued events.");
// create a new context, to be sure to work as anonymous user
// we don't want to store private data in a triplestore with public
// SPARQL endpoint.
ctx = new Context(Context.Mode.READ_ONLY);
if (toDelete == null) {
log.debug("Deletion queue does not exists, creating empty queue.");
this.toDelete = new LinkedList<>();
}
if (toConvert != null) {
log.debug("Starting conversion of DSpaceObjects.");
// store the context mode, set context read only for performance reasons, and restore the old mode
Context.Mode oldMode = ctx.getCurrentMode();
try {
ctx.setMode(Context.Mode.READ_ONLY);
if (toDelete == null) {
log.debug("Deletion queue does not exists, creating empty queue.");
this.toDelete = new LinkedList<>();
}
if (toConvert != null) {
log.debug("Starting conversion of DSpaceObjects.");
while (true) {
DSOIdentifier id;
try {
id = toConvert.removeFirst();
} catch (NoSuchElementException ex) {
break;
}
if (toDelete.contains(id)) {
log.debug("Skipping " + Constants.typeText[id.type] + " "
+ id.id.toString() + " as it is marked for "
+ "deletion as well.");
continue;
}
log.debug("Converting " + Constants.typeText[id.type] + " "
+ id.id.toString() + ".");
convert(ctx, id);
}
log.debug("Conversion ended.");
}
log.debug("Starting to delete data from the triple store...");
while (true) {
DSOIdentifier id;
try {
id = toConvert.removeFirst();
id = toDelete.removeFirst();
} catch (NoSuchElementException ex) {
break;
}
if (toDelete.contains(id)) {
log.debug("Skipping " + Constants.typeText[id.type] + " "
+ id.id.toString() + " as it is marked for "
+ "deletion as well.");
continue;
}
log.debug("Converting " + Constants.typeText[id.type] + " "
log.debug("Going to delete data from " +
Constants.typeText[id.type] + " "
+ id.id.toString() + ".");
convert(ctx, id);
delete(ctx, id);
}
log.debug("Conversion ended.");
} finally {
// restore context mode
ctx.setMode(oldMode);
}
log.debug("Starting to delete data from the triple store...");
while (true) {
DSOIdentifier id;
try {
id = toDelete.removeFirst();
} catch (NoSuchElementException ex) {
break;
}
log.debug("Going to delete data from " +
Constants.typeText[id.type] + " "
+ id.id.toString() + ".");
delete(ctx, id);
}
ctx.abort();
log.debug("Deletion finished.");
}

View File

@@ -206,6 +206,7 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
bitstream = c.reloadEntity(bitstream);

View File

@@ -34,6 +34,7 @@ public class BitstreamFormatBuilder extends AbstractCRUDBuilder<BitstreamFormat>
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
bitstreamFormat = c.reloadEntity(bitstreamFormat);

View File

@@ -55,6 +55,7 @@ public class BundleBuilder extends AbstractDSpaceObjectBuilder<Bundle> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
bundle = c.reloadEntity(bundle);

View File

@@ -124,6 +124,7 @@ public class ClaimedTaskBuilder extends AbstractBuilder<ClaimedTask, ClaimedTask
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
workspaceItem = c.reloadEntity(workspaceItem);

View File

@@ -253,6 +253,7 @@ public class CollectionBuilder extends AbstractDSpaceObjectBuilder<Collection> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
collection = c.reloadEntity(collection);

View File

@@ -116,6 +116,7 @@ public class CommunityBuilder extends AbstractDSpaceObjectBuilder<Community> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
community = c.reloadEntity(community);

View File

@@ -32,6 +32,7 @@ public class EPersonBuilder extends AbstractDSpaceObjectBuilder<EPerson> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
ePerson = c.reloadEntity(ePerson);

View File

@@ -36,6 +36,7 @@ public class EntityTypeBuilder extends AbstractBuilder<EntityType, EntityTypeSer
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
entityType = c.reloadEntity(entityType);

View File

@@ -35,6 +35,7 @@ public class GroupBuilder extends AbstractDSpaceObjectBuilder<Group> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
group = c.reloadEntity(group);

View File

@@ -209,6 +209,7 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
item = c.reloadEntity(item);

View File

@@ -38,6 +38,7 @@ public class MetadataFieldBuilder extends AbstractBuilder<MetadataField, Metadat
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
metadataField = c.reloadEntity(metadataField);

View File

@@ -37,6 +37,7 @@ public class MetadataSchemaBuilder extends AbstractBuilder<MetadataSchema, Metad
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
metadataSchema = c.reloadEntity(metadataSchema);

View File

@@ -104,6 +104,7 @@ public class PoolTaskBuilder extends AbstractBuilder<PoolTask, PoolTaskService>
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
workspaceItem = c.reloadEntity(workspaceItem);

View File

@@ -60,6 +60,7 @@ public class ProcessBuilder extends AbstractBuilder<Process, ProcessService> {
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
process = c.reloadEntity(process);

View File

@@ -39,6 +39,7 @@ public class RelationshipBuilder extends AbstractBuilder<Relationship, Relations
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
relationship = c.reloadEntity(relationship);

View File

@@ -37,6 +37,7 @@ public class RelationshipTypeBuilder extends AbstractBuilder<RelationshipType, R
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
relationshipType = c.reloadEntity(relationshipType);

View File

@@ -117,6 +117,7 @@ public class RequestItemBuilder
throws Exception {
LOG.debug("cleanup()");
try ( Context ctx = new Context(); ) {
ctx.setDispatcher("noindex");
ctx.turnOffAuthorisationSystem();
requestItem = ctx.reloadEntity(requestItem);
if (null != requestItem) {

View File

@@ -41,6 +41,7 @@ public class ResourcePolicyBuilder extends AbstractBuilder<ResourcePolicy, Resou
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
resourcePolicy = c.reloadEntity(resourcePolicy);
@@ -81,6 +82,7 @@ public class ResourcePolicyBuilder extends AbstractBuilder<ResourcePolicy, Resou
public void delete(ResourcePolicy rp) throws Exception {
try (Context c = new Context()) {
c.turnOffAuthorisationSystem();
c.setDispatcher("noindex");
ResourcePolicy attachedDso = c.reloadEntity(rp);
if (attachedDso != null) {
getService().delete(c, attachedDso);

View File

@@ -85,6 +85,7 @@ public class VersionBuilder extends AbstractBuilder<Version, VersioningService>
public void delete(Version version) throws Exception {
try (Context context = new Context()) {
context.turnOffAuthorisationSystem();
context.setDispatcher("noindex");
Version attachedTab = context.reloadEntity(version);
if (attachedTab != null) {
getService().delete(context, attachedTab);

View File

@@ -116,6 +116,7 @@ public class WorkflowItemBuilder extends AbstractBuilder<XmlWorkflowItem, XmlWor
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
workspaceItem = c.reloadEntity(workspaceItem);

View File

@@ -106,6 +106,7 @@ public class WorkspaceItemBuilder extends AbstractBuilder<WorkspaceItem, Workspa
@Override
public void cleanup() throws Exception {
try (Context c = new Context()) {
c.setDispatcher("noindex");
c.turnOffAuthorisationSystem();
// Ensure object and any related objects are reloaded before checking to see what needs cleanup
workspaceItem = c.reloadEntity(workspaceItem);

View File

@@ -19,6 +19,8 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
@@ -41,6 +43,7 @@ import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.core.Constants;
@@ -1410,6 +1413,27 @@ public class ItemTest extends AbstractDSpaceObjectTest {
assertThat("testMove 1", it.getOwningCollection(), equalTo(to));
}
/**
* Test of move method, of class Item, where both Collections are the same.
*/
@Test
public void testMoveSameCollection() throws Exception {
context.turnOffAuthorisationSystem();
while (it.getCollections().size() > 1) {
it.removeCollection(it.getCollections().get(0));
}
Collection collection = it.getCollections().get(0);
it.setOwningCollection(collection);
ItemService itemServiceSpy = spy(itemService);
itemService.move(context, it, collection, collection);
context.restoreAuthSystemState();
assertThat("testMoveSameCollection 0", it.getOwningCollection(), notNullValue());
assertThat("testMoveSameCollection 1", it.getOwningCollection(), equalTo(collection));
verify(itemServiceSpy, times(0)).delete(context, it);
}
/**
* Test of hasUploadedFiles method, of class Item.
*/

View File

@@ -191,7 +191,7 @@ public class DOIIdentifierProviderTest
List<String> remainder = new ArrayList<>();
for (MetadataValue id : metadata) {
if (!id.getValue().startsWith(DOI.RESOLVER)) {
if (!id.getValue().startsWith(doiService.getResolver())) {
remainder.add(id.getValue());
}
}
@@ -278,11 +278,11 @@ public class DOIIdentifierProviderTest
PREFIX + "/" + NAMESPACE_SEPARATOR + "lkjljasd1234",
DOI.SCHEME + "10.5072/123abc-lkj/kljl",
"http://dx.doi.org/10.5072/123abc-lkj/kljl",
DOI.RESOLVER + "/10.5072/123abc-lkj/kljl"
doiService.getResolver() + "/10.5072/123abc-lkj/kljl"
};
for (String doi : validDOIs) {
assertTrue("DOI should be supported", provider.supports(doi));
assertTrue("DOI " + doi + " should be supported", provider.supports(doi));
}
}

View File

@@ -15,7 +15,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -8,7 +8,7 @@
<parent>
<artifactId>dspace-parent</artifactId>
<groupId>org.dspace</groupId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -3,7 +3,7 @@
<groupId>org.dspace</groupId>
<artifactId>dspace-rest</artifactId>
<packaging>war</packaging>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<name>DSpace (Deprecated) REST Webapp</name>
<description>DSpace RESTful Web Services API. NOTE: this REST API is DEPRECATED.
Please consider using the REST API in the dspace-server-webapp instead!</description>
@@ -12,7 +12,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -15,7 +15,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -43,7 +43,6 @@ public class ResourcePolicyEndDateAddOperation<R> extends PatchOperation<R> {
checkOperationValue(operation.getValue());
if (this.supports(resource, operation)) {
ResourcePolicy resourcePolicy = (ResourcePolicy) resource;
resourcePolicyUtils.checkResourcePolicyForExistingEndDateValue(resourcePolicy, operation);
resourcePolicyUtils.checkResourcePolicyForConsistentEndDateValue(resourcePolicy, operation);
this.add(resourcePolicy, operation);
return resource;

View File

@@ -1353,6 +1353,65 @@ public class ResourcePolicyRestRepositoryIT extends AbstractControllerIntegratio
hasJsonPath("$.startDate", is(formatDate.format(newDate))))));
}
@Test
public void patchAddEndDataTest() throws Exception {
context.turnOffAuthorisationSystem();
EPerson eperson1 = EPersonBuilder.createEPerson(context)
.withEmail("eperson1@mail.com")
.withPassword("qwerty01")
.build();
Community community = CommunityBuilder.createCommunity(context).build();
Collection collection = CollectionBuilder.createCollection(context, community)
.withAdminGroup(eperson1)
.build();
Item publicItem1 = ItemBuilder.createItem(context, collection)
.withTitle("Public item")
.build();
ResourcePolicy resourcePolicy = ResourcePolicyBuilder.createResourcePolicy(context)
.withAction(Constants.READ)
.withDspaceObject(publicItem1)
.withGroup(
EPersonServiceFactory.getInstance().getGroupService()
.findByName(context,
Group.ANONYMOUS))
.withPolicyType(ResourcePolicy.TYPE_CUSTOM)
.build();
context.restoreAuthSystemState();
Calendar newCalendar = Calendar.getInstance();
SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
Date newDate = new Date();
List<Operation> ops = new ArrayList<Operation>();
AddOperation addOperation = new AddOperation("/endDate", formatDate.format(newDate));
ops.add(addOperation);
String patchBody = getPatchContent(ops);
String authToken = getAuthToken(eperson1.getEmail(), "qwerty01");
getClient(authToken).perform(patch("/api/authz/resourcepolicies/" + resourcePolicy.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.name", is(resourcePolicy.getRpName())),
hasJsonPath("$.description", is(resourcePolicy.getRpDescription())),
hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])),
hasJsonPath("$.startDate", is(resourcePolicy.getStartDate())),
hasJsonPath("$.endDate", is(formatDate.format(newDate))))));
getClient(authToken).perform(get("/api/authz/resourcepolicies/" + resourcePolicy.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.allOf(
hasJsonPath("$.action", is(Constants.actionText[resourcePolicy.getAction()])),
hasJsonPath("$.endDate", is(formatDate.format(newDate))))));
}
@Test
public void patchRemoveStartDataTest() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -43,6 +43,7 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.core.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.matchers.JsonPathMatchers;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.dspace.app.rest.matcher.CollectionMatcher;
@@ -2515,6 +2516,76 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
;
}
@Test
/**
* Test the addition of metadata of a null value
*
* @throws Exception
*/
public void patchAddMetadataNullValueTest() throws Exception {
context.turnOffAuthorisationSystem();
//** GIVEN **
//1. A community-collection structure with one parent community with sub-community and two collections.
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity)
.withName("Sub Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, child1).withName("Collection 1").build();
String authToken = getAuthToken(eperson.getEmail(), password);
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
.withIssueDate("2017-10-17")
.withSubject("ExtraEntry")
.build();
//disable file upload mandatory
configurationService.setProperty("webui.submit.upload.required", false);
context.restoreAuthSystemState();
// try to add the title
List<Operation> operations = new ArrayList<Operation>();
// create a list of values to use in add operation
List<Map<String, String>> titelValues = new ArrayList<Map<String, String>>();
List<Map<String, String>> uriValues = new ArrayList<Map<String, String>>();
Map<String, String> value = new HashMap<String, String>();
Map<String, String> value2 = new HashMap<String, String>();
value.put("value", "New Title");
value2.put("value", null);
titelValues.add(value);
uriValues.add(value2);
operations.add(new AddOperation("/sections/traditionalpageone/dc.title", titelValues));
operations.add(new AddOperation("/sections/traditionalpageone/dc.identifier.uri", uriValues));
String patchBody = getPatchContent(operations);
getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID())
.content(patchBody)
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
// check if the new title if back and the other values untouched
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"New Title", "2017-10-17", "ExtraEntry"))))
.andExpect(jsonPath("$", JsonPathMatchers
.hasNoJsonPath("$.sections.traditionalpageone['dc.identifier.uri']")));
// verify that the patch changes have been persisted
getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.errors").doesNotExist())
.andExpect(jsonPath("$",
Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem,
"New Title", "2017-10-17", "ExtraEntry"))))
.andExpect(jsonPath("$", JsonPathMatchers
.hasNoJsonPath("$.sections.traditionalpageone['dc.identifier.uri']")))
;
}
@Test
public void patchAddMetadataUpdateExistValueTest() throws Exception {
context.turnOffAuthorisationSystem();

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -15,7 +15,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -56,7 +56,7 @@ or refer to the Web site http://dspace-dev.dsi.uminho.pt.
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="id" type="xs:ID" use="optional"/>
<xs:attribute name="id" type="xs:ID" use="required"/>
<xs:attribute name="label" type="xs:string" use="required"/>
<xs:attribute name="selectable" type="xs:boolean" default="true"/>
</xs:complexType>

View File

@@ -42,9 +42,14 @@ default.language = en_US
# Solr server/webapp.
# DSpace uses Solr for all search/browse capability (and for usage statistics).
# Since DSpace 7, SOLR must be installed as a stand-alone service
# Since DSpace 7, SOLR must be installed as a stand-alone service.
solr.server = http://localhost:8983/solr
# Solr core name prefix.
# If you connect multiple instances of DSpace to a single Solr instance, you
# can organize them with a common core name prefix.
solr.multicorePrefix =
# Solr connection pool.
# If you change these values, the changes are not effective until DSpace is
# restarted.
@@ -235,9 +240,14 @@ logging.server.max-payload-length = 10000
# Credentials used to authenticate against the registration agency:
identifier.doi.user = username
identifier.doi.password = password
# URL for the DOI resolver. This will be the stem for generated DOIs.
#identifier.doi.resolver = https://doi.org
# DOI prefix used to mint DOIs. All DOIs minted by DSpace will use this prefix.
# The Prefix will be assigned by the registration agency.
identifier.doi.prefix = 10.5072
# If you want to, you can further separate your namespace. Should all the
# suffixes of all DOIs minted by DSpace start with a special string to separate
# it from other services also minting DOIs under your prefix?
@@ -1349,11 +1359,11 @@ sherpa.romeo.apikey =
# org.dspace.content.authority.SampleAuthority = Sample, \
# org.dspace.content.authority.SHERPARoMEOPublisher = SRPublisher, \
# org.dspace.content.authority.SHERPARoMEOJournalTitle = SRJournalTitle, \
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
#Uncomment to enable ORCID authority control
#plugin.named.org.dspace.content.authority.ChoiceAuthority = \
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
# URL of ORCID API
# Defaults to using the Public API V3 (pub.orcid.org)
@@ -1403,8 +1413,9 @@ plugin.selfnamed.org.dspace.content.authority.ChoiceAuthority = \
## See manual or org.dspace.content.authority.Choices source for descriptions.
authority.minconfidence = ambiguous
# Configuration settings for ORCID based authority control, uncomment the lines below to enable configuration
#solr.authority.server=${solr.server}/authority
# Configuration settings for ORCID based authority control.
# Uncomment the lines below to enable configuration.
#solr.authority.server=${solr.server}/${solr.multicorePrefix}authority
#choices.plugin.dc.contributor.author = SolrAuthorAuthority
#choices.presentation.dc.contributor.author = authorLookup
#authority.controlled.dc.contributor.author = true

View File

@@ -59,6 +59,11 @@ dspace.name = DSpace at My University
# Since DSpace 7, SOLR must be installed as a stand-alone service
#solr.server = http://localhost:8983/solr
# Solr core name prefix.
# If you connect multiple instances of DSpace to a single Solr instance, you
# can organize them with a common core name prefix.
#solr.multicorePrefix =
##########################
# DATABASE CONFIGURATION #
##########################

View File

@@ -5,7 +5,7 @@
# faceted-search system. #
#---------------------------------------------------------------#
##### Search Indexing #####
discovery.search.server = ${solr.server}/search
discovery.search.server = ${solr.server}/${solr.multicorePrefix}search
#Enable the url validation of the search.server setting above.
#Defaults to true: validation is enabled

View File

@@ -10,7 +10,7 @@ iiif.image.server = http://localhost:8182/iiif/2/
# Base URL of the search service used to support the (experimental) IIIF Search
# capability. The actual path will depend on how search is being supported.
# This example uses the solr plugin https://dbmdz.github.io/solr-ocrhighlighting/
# iiif.search.url = ${solr.server}/word_highlighting
# iiif.search.url = ${solr.server}/${solr.multicorePrefix}word_highlighting
# The search plugin used to support (experimental) IIIF Search.
# This is the class used with https://dbmdz.github.io/solr-ocrhighlighting/

View File

@@ -24,7 +24,7 @@ oai.storage=solr
oai.url = ${dspace.server.url}/${oai.path}
# Base solr index
oai.solr.url=${solr.server}/oai
oai.solr.url=${solr.server}/${solr.multicorePrefix}oai
# OAI persistent identifier prefix
# This field is used for two purposes:

View File

@@ -10,7 +10,7 @@
# set this to be the port you run the dspace "solr" webapp
# on, by default, we are assuming a test configuration with
# tomcat still running on port 8080
solr-statistics.server = ${solr.server}/statistics
solr-statistics.server = ${solr.server}/${solr.multicorePrefix}statistics
# A comma-separated list that contains the bundles for which the bitstreams will be displayed
solr-statistics.query.filter.bundles=ORIGINAL

View File

@@ -17,7 +17,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>modules</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>modules</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -13,7 +13,7 @@ just adding new jar in the classloader</description>
<parent>
<artifactId>modules</artifactId>
<groupId>org.dspace</groupId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

View File

@@ -16,7 +16,7 @@
<parent>
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

36
pom.xml
View File

@@ -4,7 +4,7 @@
<groupId>org.dspace</groupId>
<artifactId>dspace-parent</artifactId>
<packaging>pom</packaging>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<name>DSpace Parent Project</name>
<description>
DSpace open source software is a turnkey institutional repository application.
@@ -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.25</postgresql.driver.version>
<postgresql.driver.version>42.3.3</postgresql.driver.version>
<solr.client.version>8.8.1</solr.client.version>
<axiom.version>1.2.22</axiom.version>
@@ -868,14 +868,14 @@
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-rest</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-rest</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<type>war</type>
</dependency>
</dependencies>
@@ -1026,69 +1026,69 @@
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
<type>test-jar</type>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dspace.modules</groupId>
<artifactId>additions</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-sword</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-swordv2</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-oai</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-services</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-server-webapp</artifactId>
<type>test-jar</type>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-rdf</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-iiif</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-server-webapp</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<type>jar</type>
<classifier>classes</classifier>
</dependency>
<dependency>
<groupId>org.dspace</groupId>
<artifactId>dspace-server-webapp</artifactId>
<version>7.2</version>
<version>7.3-SNAPSHOT</version>
<type>war</type>
</dependency>
<!-- DSpace API Localization Packages -->
@@ -1570,7 +1570,7 @@
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.0</version>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
@@ -1877,7 +1877,7 @@
<connection>scm:git:git@github.com:DSpace/DSpace.git</connection>
<developerConnection>scm:git:git@github.com:DSpace/DSpace.git</developerConnection>
<url>git@github.com:DSpace/DSpace.git</url>
<tag>dspace-7.2</tag>
<tag>HEAD</tag>
</scm>