
org.dspace.content.ItemServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* 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.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.requestitem.RequestItem;
import org.dspace.app.requestitem.service.RequestItemService;
import org.dspace.app.util.AuthorizeUtil;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.authority.Choices;
import org.dspace.content.dao.ItemDAO;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.EntityTypeService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.content.virtual.VirtualMetadataPopulator;
import org.dspace.contentreport.QueryPredicate;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogHelper;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.indexobject.IndexableItem;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.GroupService;
import org.dspace.eperson.service.SubscribeService;
import org.dspace.event.Event;
import org.dspace.harvest.HarvestedItem;
import org.dspace.harvest.service.HarvestedItemService;
import org.dspace.identifier.DOI;
import org.dspace.identifier.DOIIdentifierProvider;
import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.service.DOIService;
import org.dspace.identifier.service.IdentifierService;
import org.dspace.orcid.OrcidHistory;
import org.dspace.orcid.OrcidQueue;
import org.dspace.orcid.OrcidToken;
import org.dspace.orcid.model.OrcidEntityType;
import org.dspace.orcid.service.OrcidHistoryService;
import org.dspace.orcid.service.OrcidQueueService;
import org.dspace.orcid.service.OrcidSynchronizationService;
import org.dspace.orcid.service.OrcidTokenService;
import org.dspace.profile.service.ResearcherProfileService;
import org.dspace.qaevent.dao.QAEventsDAO;
import org.dspace.services.ConfigurationService;
import org.dspace.versioning.Version;
import org.dspace.versioning.VersionHistory;
import org.dspace.versioning.service.VersionHistoryService;
import org.dspace.versioning.service.VersioningService;
import org.dspace.workflow.WorkflowItemService;
import org.dspace.workflow.factory.WorkflowServiceFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Service implementation for the Item object.
* This class is responsible for all business logic calls for the Item object and is autowired by spring.
* This class should never be accessed directly.
*
* @author kevinvandevelde at atmire.com
*/
public class ItemServiceImpl extends DSpaceObjectServiceImpl- implements ItemService {
/**
* log4j category
*/
private static final Logger log = org.apache.logging.log4j.LogManager.getLogger();
@Autowired(required = true)
protected ItemDAO itemDAO;
@Autowired(required = true)
protected CommunityService communityService;
@Autowired(required = true)
protected GroupService groupService;
@Autowired(required = true)
protected AuthorizeService authorizeService;
@Autowired(required = true)
protected BundleService bundleService;
@Autowired(required = true)
protected BitstreamFormatService bitstreamFormatService;
@Autowired(required = true)
protected MetadataSchemaService metadataSchemaService;
@Autowired(required = true)
protected BitstreamService bitstreamService;
@Autowired(required = true)
protected InstallItemService installItemService;
@Autowired(required = true)
protected SearchService searchService;
@Autowired(required = true)
protected ResourcePolicyService resourcePolicyService;
@Autowired(required = true)
protected CollectionService collectionService;
@Autowired(required = true)
protected IdentifierService identifierService;
@Autowired(required = true)
protected DOIService doiService;
@Autowired(required = true)
protected VersioningService versioningService;
@Autowired(required = true)
protected HarvestedItemService harvestedItemService;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected WorkspaceItemService workspaceItemService;
@Autowired(required = true)
protected WorkflowItemService workflowItemService;
@Autowired(required = true)
protected RelationshipService relationshipService;
@Autowired(required = true)
protected VirtualMetadataPopulator virtualMetadataPopulator;
@Autowired(required = true)
private RelationshipMetadataService relationshipMetadataService;
@Autowired(required = true)
private EntityTypeService entityTypeService;
@Autowired
private OrcidTokenService orcidTokenService;
@Autowired(required = true)
private OrcidHistoryService orcidHistoryService;
@Autowired(required = true)
private OrcidQueueService orcidQueueService;
@Autowired(required = true)
private OrcidSynchronizationService orcidSynchronizationService;
@Autowired(required = true)
private ResearcherProfileService researcherProfileService;
@Autowired(required = true)
private RequestItemService requestItemService;
@Autowired(required = true)
protected SubscribeService subscribeService;
@Autowired
private QAEventsDAO qaEventsDao;
@Autowired
private VersionHistoryService versionHistoryService;
protected ItemServiceImpl() {
}
@Override
public Thumbnail getThumbnail(Context context, Item item, boolean requireOriginal) throws SQLException {
Bitstream thumbBitstream;
List
originalBundles = getBundles(item, "ORIGINAL");
Bitstream primaryBitstream = null;
if (CollectionUtils.isNotEmpty(originalBundles)) {
primaryBitstream = originalBundles.get(0).getPrimaryBitstream();
}
if (primaryBitstream != null) {
if (primaryBitstream.getFormat(context).getMIMEType().equals("text/html")) {
return null;
}
thumbBitstream = bitstreamService
.getBitstreamByName(item, "THUMBNAIL", primaryBitstream.getName() + ".jpg");
} else {
if (requireOriginal) {
primaryBitstream = bitstreamService.getFirstBitstream(item, "ORIGINAL");
}
thumbBitstream = bitstreamService.getFirstBitstream(item, "THUMBNAIL");
}
if (thumbBitstream != null) {
return new Thumbnail(thumbBitstream, primaryBitstream);
}
return null;
}
@Override
public Item find(Context context, UUID id) throws SQLException {
Item item = itemDAO.findByID(context, Item.class, id);
if (item == null) {
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_item",
"not_found,item_id=" + id));
}
return null;
}
// not null, return item
if (log.isDebugEnabled()) {
log.debug(LogHelper.getHeader(context, "find_item", "item_id="
+ id));
}
return item;
}
@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");
}
Item item = null;
if (uuid != null) {
item = createItem(context, uuid);
} else {
item = createItem(context);
}
workspaceItem.setItem(item);
log.info(LogHelper.getHeader(context, "create_item", "item_id="
+ item.getID()));
return item;
}
@Override
public Item createTemplateItem(Context context, Collection collection) throws SQLException, AuthorizeException {
if (collection == null || collection.getTemplateItem() != null) {
throw new IllegalArgumentException("Collection is null or already contains template item.");
}
AuthorizeUtil.authorizeManageTemplateItem(context, collection);
if (collection.getTemplateItem() == null) {
Item template = createItem(context);
collection.setTemplateItem(template);
template.setTemplateItemOf(collection);
log.info(LogHelper.getHeader(context, "create_template_item",
"collection_id=" + collection.getID() + ",template_item_id="
+ template.getID()));
return template;
}
return collection.getTemplateItem();
}
@Override
public void populateWithTemplateItemMetadata(Context context, Collection collection, boolean template, Item item)
throws SQLException {
Item templateItem = collection.getTemplateItem();
Optional colEntityType = getDSpaceEntityType(collection);
Optional templateItemEntityType = getDSpaceEntityType(templateItem);
if (template && colEntityType.isPresent() && templateItemEntityType.isPresent() &&
!StringUtils.equals(colEntityType.get().getValue(), templateItemEntityType.get().getValue())) {
throw new IllegalStateException("The template item has entity type : (" +
templateItemEntityType.get().getValue() + ") different than collection entity type : " +
colEntityType.get().getValue());
}
if (template && colEntityType.isPresent() && templateItemEntityType.isEmpty()) {
MetadataValue original = colEntityType.get();
MetadataField metadataField = original.getMetadataField();
MetadataSchema metadataSchema = metadataField.getMetadataSchema();
// NOTE: dspace.entity.type = does not make sense
// the collection entity type is by default blank when a collection is first created
if (StringUtils.isNotBlank(original.getValue())) {
addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(),
metadataField.getQualifier(), original.getLanguage(), original.getValue());
}
}
if (template && (templateItem != null)) {
List md = getMetadata(templateItem, Item.ANY, Item.ANY, Item.ANY, Item.ANY);
for (MetadataValue aMd : md) {
MetadataField metadataField = aMd.getMetadataField();
MetadataSchema metadataSchema = metadataField.getMetadataSchema();
addMetadata(context, item, metadataSchema.getName(), metadataField.getElement(),
metadataField.getQualifier(), aMd.getLanguage(), aMd.getValue());
}
}
}
private Optional getDSpaceEntityType(DSpaceObject dSpaceObject) {
return Objects.nonNull(dSpaceObject) ? dSpaceObject.getMetadata()
.stream()
.filter(x -> x.getMetadataField().toString('.')
.equalsIgnoreCase("dspace.entity.type"))
.findFirst()
: Optional.empty();
}
@Override
public Iterator- findAll(Context context) throws SQLException {
return itemDAO.findAll(context, true);
}
@Override
public Iterator
- findAll(Context context, Integer limit, Integer offset) throws SQLException {
return itemDAO.findAll(context, true, limit, offset);
}
@Override
public Iterator
- findAllUnfiltered(Context context) throws SQLException {
return itemDAO.findAll(context, true, true);
}
@Override
public Iterator
- findAllRegularItems(Context context) throws SQLException {
return itemDAO.findAllRegularItems(context);
}
@Override
public Iterator
- findBySubmitter(Context context, EPerson eperson) throws SQLException {
return itemDAO.findBySubmitter(context, eperson);
}
@Override
public Iterator
- findBySubmitter(Context context, EPerson eperson, boolean retrieveAllItems)
throws SQLException {
return itemDAO.findBySubmitter(context, eperson, retrieveAllItems);
}
@Override
public Iterator
- findBySubmitterDateSorted(Context context, EPerson eperson, Integer limit)
throws SQLException {
MetadataField metadataField = metadataFieldService
.findByElement(context, MetadataSchemaEnum.DC.getName(), "date", "accessioned");
if (metadataField == null) {
throw new IllegalArgumentException(
"Required metadata field '" + MetadataSchemaEnum.DC.getName() + ".date.accessioned' doesn't exist!");
}
return itemDAO.findBySubmitter(context, eperson, metadataField, limit);
}
@Override
public Iterator
- findByCollection(Context context, Collection collection) throws SQLException {
return findByCollection(context, collection, null, null);
}
@Override
public Iterator
- findByCollection(Context context, Collection collection, Integer limit, Integer offset)
throws SQLException {
return itemDAO.findArchivedByCollection(context, collection, limit, offset);
}
@Override
public Iterator
- findByCollectionMapping(Context context, Collection collection, Integer limit, Integer offset)
throws SQLException {
return itemDAO.findArchivedByCollectionExcludingOwning(context, collection, limit, offset);
}
@Override
public int countByCollectionMapping(Context context, Collection collection) throws SQLException {
return itemDAO.countArchivedByCollectionExcludingOwning(context, collection);
}
@Override
public Iterator
- findAllByCollection(Context context, Collection collection) throws SQLException {
return itemDAO.findAllByCollection(context, collection);
}
@Override
public Iterator
- findAllByCollection(Context context, Collection collection, Integer limit, Integer offset)
throws SQLException {
return itemDAO.findAllByCollection(context, collection, limit, offset);
}
@Override
public Iterator
- findInArchiveOrWithdrawnDiscoverableModifiedSince(Context context, Instant since)
throws SQLException {
return itemDAO.findAll(context, true, true, true, since);
}
@Override
public Iterator
- findInArchiveOrWithdrawnNonDiscoverableModifiedSince(Context context, Instant since)
throws SQLException {
return itemDAO.findAll(context, true, true, false, since);
}
@Override
public void updateLastModified(Context context, Item item) throws SQLException, AuthorizeException {
item.setLastModified(Instant.now());
update(context, item);
//Also fire a modified event since the item HAS been modified
context.addEvent(new Event(Event.MODIFY, Constants.ITEM, item.getID(), null, getIdentifiers(context, item)));
}
@Override
public boolean isIn(Item item, Collection collection) throws SQLException {
List
collections = item.getCollections();
return collections != null && collections.contains(collection);
}
@Override
public List getCommunities(Context context, Item item) throws SQLException {
List result = new ArrayList<>();
List collections = item.getCollections();
for (Collection collection : collections) {
result.addAll(communityService.getAllParents(context, collection));
}
return result;
}
@Override
public List getBundles(Item item, String name) throws SQLException {
List matchingBundles = new ArrayList<>();
// now only keep bundles with matching names
List bunds = item.getBundles();
for (Bundle bund : bunds) {
if (name.equals(bund.getName())) {
matchingBundles.add(bund);
}
}
return matchingBundles;
}
@Override
public void addBundle(Context context, Item item, Bundle bundle) throws SQLException, AuthorizeException {
// Check authorisation
authorizeService.authorizeAction(context, item, Constants.ADD);
log.info(LogHelper.getHeader(context, "add_bundle", "item_id="
+ item.getID() + ",bundle_id=" + bundle.getID()));
// Check it's not already there
if (item.getBundles().contains(bundle)) {
// Bundle is already there; no change
return;
}
// now add authorization policies from owning item
// hmm, not very "multiple-inclusion" friendly
authorizeService.inheritPolicies(context, item, bundle);
// Add the bundle to in-memory list
item.addBundle(bundle);
bundle.addItem(item);
context.addEvent(new Event(Event.ADD, Constants.ITEM, item.getID(),
Constants.BUNDLE, bundle.getID(), bundle.getName(),
getIdentifiers(context, item)));
}
@Override
public void removeBundle(Context context, Item item, Bundle bundle)
throws SQLException, AuthorizeException, IOException {
// Check authorisation
authorizeService.authorizeAction(context, item, Constants.REMOVE);
log.info(LogHelper.getHeader(context, "remove_bundle", "item_id="
+ item.getID() + ",bundle_id=" + bundle.getID()));
context.addEvent(new Event(Event.REMOVE, Constants.ITEM, item.getID(),
Constants.BUNDLE, bundle.getID(), bundle.getName(), getIdentifiers(context, item)));
bundleService.delete(context, bundle);
}
@Override
public Bitstream createSingleBitstream(Context context, InputStream is, Item item, String name)
throws AuthorizeException, IOException, SQLException {
// Authorisation is checked by methods below
// Create a bundle
Bundle bnd = bundleService.create(context, item, name);
Bitstream bitstream = bitstreamService.create(context, bnd, is);
addBundle(context, item, bnd);
// FIXME: Create permissions for new bundle + bitstream
return bitstream;
}
@Override
public Bitstream createSingleBitstream(Context context, InputStream is, Item item)
throws AuthorizeException, IOException, SQLException {
return createSingleBitstream(context, is, item, "ORIGINAL");
}
@Override
public List getNonInternalBitstreams(Context context, Item item) throws SQLException {
List bitstreamList = new ArrayList<>();
// Go through the bundles and bitstreams picking out ones which aren't
// of internal formats
List bunds = item.getBundles();
for (Bundle bund : bunds) {
List bitstreams = bund.getBitstreams();
for (Bitstream bitstream : bitstreams) {
if (!bitstream.getFormat(context).isInternal()) {
// Bitstream is not of an internal format
bitstreamList.add(bitstream);
}
}
}
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)
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;
}
@Override
public void removeDSpaceLicense(Context context, Item item) throws SQLException, AuthorizeException, IOException {
// get all bundles with name "LICENSE" (these are the DSpace license
// bundles)
List bunds = getBundles(item, "LICENSE");
for (Bundle bund : bunds) {
// FIXME: probably serious troubles with Authorizations
// fix by telling system not to check authorization?
removeBundle(context, item, bund);
}
}
@Override
public void removeLicenses(Context context, Item item) throws SQLException, AuthorizeException, IOException {
// Find the License format
BitstreamFormat bf = bitstreamFormatService.findByShortDescription(context, "License");
int licensetype = bf.getID();
// search through bundles, looking for bitstream type license
List bunds = item.getBundles();
for (Bundle bund : bunds) {
boolean removethisbundle = false;
List bits = bund.getBitstreams();
for (Bitstream bit : bits) {
BitstreamFormat bft = bit.getFormat(context);
if (bft.getID() == licensetype) {
removethisbundle = true;
}
}
// probably serious troubles with Authorizations
// fix by telling system not to check authorization?
if (removethisbundle) {
removeBundle(context, item, bund);
}
}
}
@Override
public void update(Context context, Item item) throws SQLException, AuthorizeException {
// Check authorisation
// only do write authorization if user is not an editor
if (!canEdit(context, item)) {
authorizeService.authorizeAction(context, item, Constants.WRITE);
}
log.info(LogHelper.getHeader(context, "update_item", "item_id="
+ item.getID()));
super.update(context, item);
// Set sequence IDs for bitstreams in Item. To guarantee uniqueness,
// sequence IDs are assigned in sequential order (starting with 1)
int sequence = 0;
List bunds = item.getBundles();
// find the highest current sequence number
for (Bundle bund : bunds) {
List streams = bund.getBitstreams();
for (Bitstream bitstream : streams) {
if (bitstream.getSequenceID() > sequence) {
sequence = bitstream.getSequenceID();
}
}
}
// start sequencing bitstreams without sequence IDs
sequence++;
for (Bundle bund : bunds) {
List streams = bund.getBitstreams();
for (Bitstream stream : streams) {
if (stream.getSequenceID() < 0) {
stream.setSequenceID(sequence);
sequence++;
bitstreamService.update(context, stream);
// modified = true;
}
}
}
if (item.isMetadataModified() || item.isModified()) {
// Set the last modified date
item.setLastModified(Instant.now());
itemDAO.save(context, item);
if (item.isMetadataModified()) {
context.addEvent(new Event(Event.MODIFY_METADATA, item.getType(), item.getID(), item.getDetails(),
getIdentifiers(context, item)));
}
context.addEvent(new Event(Event.MODIFY, Constants.ITEM, item.getID(),
null, getIdentifiers(context, item)));
item.clearModified();
item.clearDetails();
}
}
@Override
public void withdraw(Context context, Item item) throws SQLException, AuthorizeException {
// Check permission. User either has to have REMOVE on owning collection
// or be COLLECTION_EDITOR of owning collection
AuthorizeUtil.authorizeWithdrawItem(context, item);
String timestamp = DCDate.getCurrent().toString();
// Add suitable provenance - includes user, date, collections +
// bitstream checksums
EPerson e = context.getCurrentUser();
// Build some provenance data while we're at it.
StringBuilder prov = new StringBuilder();
prov.append("Item withdrawn by ").append(e.getFullName()).append(" (")
.append(e.getEmail()).append(") on ").append(timestamp).append("\n")
.append("Item was in collections:\n");
List colls = item.getCollections();
for (Collection coll : colls) {
prov.append(coll.getName()).append(" (ID: ").append(coll.getID()).append(")\n");
}
// Set withdrawn flag. timestamp will be set; last_modified in update()
item.setWithdrawn(true);
// in_archive flag is now false
item.setArchived(false);
prov.append(installItemService.getBitstreamProvenanceMessage(context, item));
addMetadata(context, item, MetadataSchemaEnum.DC.getName(), "description", "provenance", "en", prov.toString());
// Update item in DB
update(context, item);
context.addEvent(new Event(Event.MODIFY, Constants.ITEM, item.getID(),
"WITHDRAW", getIdentifiers(context, item)));
// switch all READ authorization policies to WITHDRAWN_READ
authorizeService.switchPoliciesAction(context, item, Constants.READ, Constants.WITHDRAWN_READ);
for (Bundle bnd : item.getBundles()) {
authorizeService.switchPoliciesAction(context, bnd, Constants.READ, Constants.WITHDRAWN_READ);
for (Bitstream bs : bnd.getBitstreams()) {
authorizeService.switchPoliciesAction(context, bs, Constants.READ, Constants.WITHDRAWN_READ);
}
}
// Write log
log.info(LogHelper.getHeader(context, "withdraw_item", "user="
+ e.getEmail() + ",item_id=" + item.getID()));
}
@Override
public void reinstate(Context context, Item item) throws SQLException, AuthorizeException {
// check authorization
AuthorizeUtil.authorizeReinstateItem(context, item);
String timestamp = DCDate.getCurrent().toString();
// Check permission. User must have ADD on all collections.
// Build some provenance data while we're at it.
List colls = item.getCollections();
// Add suitable provenance - includes user, date, collections +
// bitstream checksums
EPerson e = context.getCurrentUser();
StringBuilder prov = new StringBuilder();
prov.append("Item reinstated by ").append(e.getFullName()).append(" (")
.append(e.getEmail()).append(") on ").append(timestamp).append("\n")
.append("Item was in collections:\n");
for (Collection coll : colls) {
prov.append(coll.getName()).append(" (ID: ").append(coll.getID()).append(")\n");
}
// Clear withdrawn flag
item.setWithdrawn(false);
// in_archive flag is now true
item.setArchived(true);
// Add suitable provenance - includes user, date, collections +
// bitstream checksums
prov.append(installItemService.getBitstreamProvenanceMessage(context, item));
addMetadata(context, item, MetadataSchemaEnum.DC.getName(), "description", "provenance", "en", prov.toString());
// Update item in DB
update(context, item);
context.addEvent(new Event(Event.MODIFY, Constants.ITEM, item.getID(),
"REINSTATE", getIdentifiers(context, item)));
// restore all WITHDRAWN_READ authorization policies back to READ
for (Bundle bnd : item.getBundles()) {
authorizeService.switchPoliciesAction(context, bnd, Constants.WITHDRAWN_READ, Constants.READ);
for (Bitstream bs : bnd.getBitstreams()) {
authorizeService.switchPoliciesAction(context, bs, Constants.WITHDRAWN_READ, Constants.READ);
}
}
// check if the item was withdrawn before the fix DS-3097
if (authorizeService.getPoliciesActionFilter(context, item, Constants.WITHDRAWN_READ).size() != 0) {
authorizeService.switchPoliciesAction(context, item, Constants.WITHDRAWN_READ, Constants.READ);
} else {
// authorization policies
if (colls.size() > 0) {
// remove the item's policies and replace them with
// the defaults from the collection
adjustItemPolicies(context, item, item.getOwningCollection());
}
}
// Write log
log.info(LogHelper.getHeader(context, "reinstate_item", "user="
+ e.getEmail() + ",item_id=" + item.getID()));
}
@Override
public void delete(Context context, Item item) throws SQLException, AuthorizeException, IOException {
authorizeService.authorizeAction(context, item, Constants.DELETE);
rawDelete(context, item);
}
@Override
public int getSupportsTypeConstant() {
return Constants.ITEM;
}
protected void rawDelete(Context context, Item item) throws AuthorizeException, SQLException, IOException {
authorizeService.authorizeAction(context, item, Constants.REMOVE);
context.addEvent(new Event(Event.DELETE, Constants.ITEM, item.getID(),
item.getHandle(), getIdentifiers(context, item)));
log.info(LogHelper.getHeader(context, "delete_item", "item_id="
+ item.getID()));
//remove subscription related with it
subscribeService.deleteByDspaceObject(context, item);
// Remove relationships
for (Relationship relationship : relationshipService.findByItem(context, item, -1, -1, false, false)) {
relationshipService.forceDelete(context, relationship, false, false);
}
// Remove bundles
removeAllBundles(context, item);
// Remove any Handle
handleService.unbindHandle(context, item);
// Delete a DOI if linked to the item.
// If no DOI consumer or provider is configured, but a DOI remains linked to this item's uuid,
// hibernate will throw a foreign constraint exception.
// Here we use the DOI service directly as it is able to manage DOIs even without any configured
// consumer or provider.
DOI doi = doiService.findDOIByDSpaceObject(context, item);
if (doi != null) {
doi.setDSpaceObject(null);
doi.setStatus(DOIIdentifierProvider.TO_BE_DELETED);
}
// remove version attached to the item
removeVersion(context, item);
removeRequest(context, item);
removeOrcidSynchronizationStuff(context, item);
// Also delete the item if it appears in a harvested collection.
HarvestedItem hi = harvestedItemService.find(context, item);
if (hi != null) {
harvestedItemService.delete(context, hi);
}
OrcidToken orcidToken = orcidTokenService.findByProfileItem(context, item);
if (orcidToken != null) {
orcidToken.setProfileItem(null);
}
List qaEvents = qaEventsDao.findByItem(context, item);
for (QAEventProcessed qaEvent : qaEvents) {
qaEventsDao.delete(context, qaEvent);
}
//Only clear collections after we have removed everything else from the item
item.clearCollections();
item.setOwningCollection(null);
// Finally remove item row
itemDAO.delete(context, item);
}
protected void removeRequest(Context context, Item item) throws SQLException {
Iterator requestItems = requestItemService.findByItem(context, item);
while (requestItems.hasNext()) {
RequestItem requestItem = requestItems.next();
requestItemService.delete(context, requestItem);
}
}
@Override
public void removeAllBundles(Context context, Item item) throws AuthorizeException, SQLException, IOException {
Iterator bundles = item.getBundles().iterator();
while (bundles.hasNext()) {
Bundle bundle = bundles.next();
bundles.remove();
deleteBundle(context, item, bundle);
}
}
protected void deleteBundle(Context context, Item item, Bundle b)
throws AuthorizeException, SQLException, IOException {
// Check authorisation
authorizeService.authorizeAction(context, item, Constants.REMOVE);
bundleService.delete(context, b);
log.info(LogHelper.getHeader(context, "remove_bundle", "item_id="
+ item.getID() + ",bundle_id=" + b.getID()));
context
.addEvent(new Event(Event.REMOVE, Constants.ITEM, item.getID(), Constants.BUNDLE, b.getID(), b.getName()));
}
protected void removeVersion(Context context, Item item) throws AuthorizeException, SQLException {
if (versioningService.getVersion(context, item) != null) {
versioningService.removeVersion(context, item);
} else {
try {
identifierService.delete(context, item);
} catch (IdentifierException e) {
throw new RuntimeException(e);
}
}
}
@Override
public boolean isOwningCollection(Item item, Collection collection) {
Collection owningCollection = item.getOwningCollection();
return owningCollection != null && collection.getID().equals(owningCollection.getID());
}
@Override
public void replaceAllItemPolicies(Context context, Item item, List newpolicies)
throws SQLException, AuthorizeException {
// remove all our policies, add new ones
authorizeService.removeAllPolicies(context, item);
authorizeService.addPolicies(context, newpolicies, item);
}
@Override
public void replaceAllBitstreamPolicies(Context context, Item item, List newpolicies)
throws SQLException, AuthorizeException {
// remove all policies from bundles, add new ones
List bunds = item.getBundles();
for (Bundle mybundle : bunds) {
bundleService.replaceAllBitstreamPolicies(context, mybundle, newpolicies);
}
}
@Override
public void removeGroupPolicies(Context context, Item item, Group group) throws SQLException, AuthorizeException {
// remove Group's policies from Item
authorizeService.removeGroupPolicies(context, item, group);
// remove all policies from bundles
List bunds = item.getBundles();
for (Bundle mybundle : bunds) {
List bs = mybundle.getBitstreams();
for (Bitstream bitstream : bs) {
// remove bitstream policies
authorizeService.removeGroupPolicies(context, bitstream, group);
}
// change bundle policies
authorizeService.removeGroupPolicies(context, mybundle, group);
}
}
@Override
public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection)
throws SQLException, AuthorizeException {
inheritCollectionDefaultPolicies(context, item, collection, true);
}
@Override
public void inheritCollectionDefaultPolicies(Context context, Item item, Collection collection,
boolean replaceReadRPWithCollectionRP)
throws SQLException, AuthorizeException {
adjustItemPolicies(context, item, collection, replaceReadRPWithCollectionRP);
adjustBundleBitstreamPolicies(context, item, collection, replaceReadRPWithCollectionRP);
log.debug(LogHelper.getHeader(context, "item_inheritCollectionDefaultPolicies",
"item_id=" + item.getID()));
}
@Override
public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection)
throws SQLException, AuthorizeException {
adjustBundleBitstreamPolicies(context, item, collection, true);
}
@Override
public void adjustBundleBitstreamPolicies(Context context, Item item, Collection collection,
boolean replaceReadRPWithCollectionRP)
throws SQLException, AuthorizeException {
// Bundles should inherit from DEFAULT_ITEM_READ so that if the item is readable, the files
// can be listed (even if they are themselves not readable as per DEFAULT_BITSTREAM_READ or other
// policies or embargoes applied
List defaultCollectionBundlePolicies = authorizeService
.getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ);
// Bitstreams should inherit from DEFAULT_BITSTREAM_READ
List defaultCollectionBitstreamPolicies = authorizeService
.getPoliciesActionFilter(context, collection, Constants.DEFAULT_BITSTREAM_READ);
List defaultItemPolicies = authorizeService.findPoliciesByDSOAndType(context, item,
ResourcePolicy.TYPE_CUSTOM);
if (defaultCollectionBitstreamPolicies.size() < 1) {
throw new SQLException("Collection " + collection.getID()
+ " (" + collection.getHandle() + ")"
+ " has no default bitstream READ policies");
}
// TODO: should we also throw an exception if no DEFAULT_ITEM_READ?
boolean removeCurrentReadRPBitstream =
replaceReadRPWithCollectionRP && defaultCollectionBitstreamPolicies.size() > 0;
boolean removeCurrentReadRPBundle =
replaceReadRPWithCollectionRP && defaultCollectionBundlePolicies.size() > 0;
// remove all policies from bundles, add new ones
// Remove bundles
List bunds = item.getBundles();
for (Bundle mybundle : bunds) {
// If collection has default READ policies, remove the bundle's READ policies.
if (removeCurrentReadRPBundle) {
authorizeService.removePoliciesActionFilter(context, mybundle, Constants.READ);
}
// if come from InstallItem: remove all submission/workflow policies
authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_SUBMISSION);
authorizeService.removeAllPoliciesByDSOAndType(context, mybundle, ResourcePolicy.TYPE_WORKFLOW);
addCustomPoliciesNotInPlace(context, mybundle, defaultItemPolicies);
addDefaultPoliciesNotInPlace(context, mybundle, defaultCollectionBundlePolicies);
for (Bitstream bitstream : mybundle.getBitstreams()) {
// If collection has default READ policies, remove the bundle's READ policies.
if (removeCurrentReadRPBitstream) {
authorizeService.removePoliciesActionFilter(context, bitstream, Constants.READ);
}
// if come from InstallItem: remove all submission/workflow policies
removeAllPoliciesAndAddDefault(context, bitstream, defaultItemPolicies,
defaultCollectionBitstreamPolicies);
}
}
}
@Override
public void adjustBitstreamPolicies(Context context, Item item, Collection collection, Bitstream bitstream)
throws SQLException, AuthorizeException {
adjustBitstreamPolicies(context, item, collection, bitstream, true);
}
@Override
public void adjustBitstreamPolicies(Context context, Item item, Collection collection , Bitstream bitstream,
boolean replaceReadRPWithCollectionRP)
throws SQLException, AuthorizeException {
List defaultCollectionPolicies = authorizeService
.getPoliciesActionFilter(context, collection, Constants.DEFAULT_BITSTREAM_READ);
List defaultItemPolicies = authorizeService.findPoliciesByDSOAndType(context, item,
ResourcePolicy.TYPE_CUSTOM);
if (defaultCollectionPolicies.size() < 1) {
throw new SQLException("Collection " + collection.getID()
+ " (" + collection.getHandle() + ")"
+ " has no default bitstream READ policies");
}
// remove all policies from bitstream, add new ones
removeAllPoliciesAndAddDefault(context, bitstream, defaultItemPolicies, defaultCollectionPolicies);
}
private void removeAllPoliciesAndAddDefault(Context context, Bitstream bitstream,
List defaultItemPolicies,
List defaultCollectionPolicies)
throws SQLException, AuthorizeException {
authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_SUBMISSION);
authorizeService.removeAllPoliciesByDSOAndType(context, bitstream, ResourcePolicy.TYPE_WORKFLOW);
addCustomPoliciesNotInPlace(context, bitstream, defaultItemPolicies);
addDefaultPoliciesNotInPlace(context, bitstream, defaultCollectionPolicies);
}
@Override
public void adjustItemPolicies(Context context, Item item, Collection collection)
throws SQLException, AuthorizeException {
adjustItemPolicies(context, item, collection, true);
}
@Override
public void adjustItemPolicies(Context context, Item item, Collection collection,
boolean replaceReadRPWithCollectionRP)
throws SQLException, AuthorizeException {
// read collection's default READ policies
List defaultCollectionPolicies = authorizeService
.getPoliciesActionFilter(context, collection, Constants.DEFAULT_ITEM_READ);
// If collection has defaultREAD policies, remove the item's READ policies.
if (replaceReadRPWithCollectionRP && defaultCollectionPolicies.size() > 0) {
authorizeService.removePoliciesActionFilter(context, item, Constants.READ);
}
// MUST have default policies
if (defaultCollectionPolicies.size() < 1) {
throw new SQLException("Collection " + collection.getID()
+ " (" + collection.getHandle() + ")"
+ " has no default item READ policies");
}
try {
//ignore the authorizations for now.
context.turnOffAuthorisationSystem();
// if come from InstallItem: remove all submission/workflow policies
authorizeService.removeAllPoliciesByDSOAndType(context, item, ResourcePolicy.TYPE_SUBMISSION);
authorizeService.removeAllPoliciesByDSOAndType(context, item, ResourcePolicy.TYPE_WORKFLOW);
// add default policies only if not already in place
addDefaultPoliciesNotInPlace(context, item, defaultCollectionPolicies);
} finally {
context.restoreAuthSystemState();
}
}
@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);
}
@Override
public void move(Context context, Item item, Collection from, Collection to, boolean inheritDefaultPolicies)
throws SQLException, AuthorizeException, IOException {
// Check authorisation on the item before that the move occur
// otherwise we will need edit permission on the "target collection" to archive our goal
// only do write authorization if user is not an editor
if (!canEdit(context, item)) {
authorizeService.authorizeAction(context, item, Constants.WRITE);
}
// Move the Item from one Collection to the other
collectionService.addItem(context, to, item);
collectionService.removeItem(context, from, item);
// If we are moving from the owning collection, update that too
if (isOwningCollection(item, from)) {
// Update the owning collection
log.info(LogHelper.getHeader(context, "move_item",
"item_id=" + item.getID() + ", from " +
"collection_id=" + from.getID() + " to " +
"collection_id=" + to.getID()));
item.setOwningCollection(to);
// If applicable, update the item policies
if (inheritDefaultPolicies) {
log.info(LogHelper.getHeader(context, "move_item",
"Updating item with inherited policies"));
inheritCollectionDefaultPolicies(context, item, to);
}
// Update the item
context.turnOffAuthorisationSystem();
update(context, item);
context.restoreAuthSystemState();
} else {
// Although we haven't actually updated anything within the item
// we'll tell the event system that it has, so that any consumers that
// care about the structure of the repository can take account of the move
// Note that updating the owning collection above will have the same effect,
// so we only do this here if the owning collection hasn't changed.
context.addEvent(new Event(Event.MODIFY, Constants.ITEM, item.getID(),
null, getIdentifiers(context, item)));
}
}
@Override
public boolean hasUploadedFiles(Item item) throws SQLException {
List bundles = getBundles(item, "ORIGINAL");
for (Bundle bundle : bundles) {
if (CollectionUtils.isNotEmpty(bundle.getBitstreams())) {
return true;
}
}
return false;
}
@Override
public List getCollectionsNotLinked(Context context, Item item) throws SQLException {
List allCollections = collectionService.findAll(context);
List linkedCollections = item.getCollections();
List notLinkedCollections = new ArrayList<>(allCollections.size() - linkedCollections.size());
if (allCollections.size() - linkedCollections.size() == 0) {
return notLinkedCollections;
}
for (Collection collection : allCollections) {
boolean alreadyLinked = false;
for (Collection linkedCommunity : linkedCollections) {
if (collection.getID().equals(linkedCommunity.getID())) {
alreadyLinked = true;
break;
}
}
if (!alreadyLinked) {
notLinkedCollections.add(collection);
}
}
return notLinkedCollections;
}
@Override
public boolean canEdit(Context context, Item item) throws SQLException {
// can this person write to the item?
if (authorizeService.authorizeActionBoolean(context, item,
Constants.WRITE)) {
return true;
}
// is this collection not yet created, and an item template is created
if (item.getOwningCollection() == null) {
if (!isInProgressSubmission(context, item)) {
return true;
}
return false;
}
return collectionService.canEditBoolean(context, item.getOwningCollection(), false);
}
/**
* Finds all Indexed Items where the current user has edit rights. If the user is an Admin,
* this is all Indexed Items. Otherwise, it includes those Items where
* an indexed "edit" policy lists either the eperson or one of the eperson's groups
*
* @param context DSpace context
* @param discoverQuery
* @return discovery search result objects
* @throws SQLException if something goes wrong
* @throws SearchServiceException if search error
*/
private DiscoverResult retrieveItemsWithEdit(Context context, DiscoverQuery discoverQuery)
throws SQLException, SearchServiceException {
EPerson currentUser = context.getCurrentUser();
if (!authorizeService.isAdmin(context)) {
String userId = currentUser != null ? "e" + currentUser.getID().toString() : "e";
Stream groupIds = groupService.allMemberGroupsSet(context, currentUser).stream()
.map(group -> "g" + group.getID());
String query = Stream.concat(Stream.of(userId), groupIds)
.collect(Collectors.joining(" OR ", "edit:(", ")"));
discoverQuery.addFilterQueries(query);
}
return searchService.search(context, discoverQuery);
}
@Override
public List- findItemsWithEdit(Context context, int offset, int limit)
throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
discoverQuery.setStart(offset);
discoverQuery.setMaxResults(limit);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return resp.getIndexableObjects().stream()
.map(solrItems -> ((IndexableItem) solrItems).getIndexedObject())
.collect(Collectors.toList());
}
@Override
public int countItemsWithEdit(Context context) throws SQLException, SearchServiceException {
DiscoverQuery discoverQuery = new DiscoverQuery();
discoverQuery.setMaxResults(0);
discoverQuery.setDSpaceObjectFilter(IndexableItem.TYPE);
DiscoverResult resp = retrieveItemsWithEdit(context, discoverQuery);
return (int) resp.getTotalSearchResults();
}
/**
* Check if the item is an inprogress submission
*
* @param context The relevant DSpace Context.
* @param item item to check
* @return
true
if the item is an inprogress submission, i.e. a WorkspaceItem or WorkflowItem
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
@Override
public boolean isInProgressSubmission(Context context, Item item) throws SQLException {
return workspaceItemService.findByItem(context, item) != null
|| workflowItemService.findByItem(context, item) != null;
}
/*
With every finished submission a bunch of resource policy entries which have null value for the dspace_object
column are generated in the database.
prevent the generation of resource policy entry values with null dspace_object as value
*/
/**
* Add the default policies, which have not been already added to the given DSpace object
*
* @param context The relevant DSpace Context.
* @param dso The DSpace Object to add policies to
* @param defaultCollectionPolicies list of policies
* @throws SQLException An exception that provides information on a database access error or other errors.
* @throws AuthorizeException Exception indicating the current user of the context does not have permission
* to perform a particular action.
*/
protected void addDefaultPoliciesNotInPlace(Context context, DSpaceObject dso,
List defaultCollectionPolicies) throws SQLException, AuthorizeException {
boolean appendMode = configurationService
.getBooleanProperty("core.authorization.installitem.inheritance-read.append-mode", false);
for (ResourcePolicy defaultPolicy : defaultCollectionPolicies) {
if (!authorizeService
.isAnIdenticalPolicyAlreadyInPlace(context, dso, defaultPolicy.getGroup(), Constants.READ,
defaultPolicy.getID()) &&
(!appendMode && isNotAlreadyACustomRPOfThisTypeOnDSO(context, dso) ||
appendMode && shouldBeAppended(context, dso, defaultPolicy))) {
ResourcePolicy newPolicy = resourcePolicyService.clone(context, defaultPolicy);
newPolicy.setdSpaceObject(dso);
newPolicy.setAction(Constants.READ);
newPolicy.setRpType(ResourcePolicy.TYPE_INHERITED);
resourcePolicyService.update(context, newPolicy);
}
}
}
private void addCustomPoliciesNotInPlace(Context context, DSpaceObject dso, List customPolicies)
throws SQLException, AuthorizeException {
boolean customPoliciesAlreadyInPlace = authorizeService
.findPoliciesByDSOAndType(context, dso, ResourcePolicy.TYPE_CUSTOM).size() > 0;
if (!customPoliciesAlreadyInPlace) {
authorizeService.addPolicies(context, customPolicies, dso);
}
}
/**
* Check whether or not there is already an RP on the given dso, which has actionId={@link Constants.READ} and
* resourceTypeId={@link ResourcePolicy.TYPE_CUSTOM}
*
* @param context DSpace context
* @param dso DSpace object to check for custom read RP
* @return True if there is no RP on the item with custom read RP, otherwise false
* @throws SQLException If something goes wrong retrieving the RP on the DSO
*/
private boolean isNotAlreadyACustomRPOfThisTypeOnDSO(Context context, DSpaceObject dso) throws SQLException {
List readRPs = resourcePolicyService.find(context, dso, Constants.READ);
for (ResourcePolicy readRP : readRPs) {
if (readRP.getRpType() != null && readRP.getRpType().equals(ResourcePolicy.TYPE_CUSTOM)) {
return false;
}
}
return true;
}
/**
* Check if the provided default policy should be appended or not to the final
* item. If an item has at least one custom READ policy any anonymous READ
* policy with empty start/end date should be skipped
*
* @param context DSpace context
* @param dso DSpace object to check for custom read RP
* @param defaultPolicy The policy to check
* @return
* @throws SQLException If something goes wrong retrieving the RP on the DSO
*/
private boolean shouldBeAppended(Context context, DSpaceObject dso, ResourcePolicy defaultPolicy)
throws SQLException {
boolean hasCustomPolicy = resourcePolicyService.find(context, dso, Constants.READ)
.stream()
.filter(rp -> (Objects.nonNull(rp.getRpType()) &&
Objects.equals(rp.getRpType(), ResourcePolicy.TYPE_CUSTOM)))
.findFirst()
.isPresent();
boolean isAnonimousGroup = Objects.nonNull(defaultPolicy.getGroup())
&& StringUtils.equals(defaultPolicy.getGroup().getName(), Group.ANONYMOUS);
boolean datesAreNull = Objects.isNull(defaultPolicy.getStartDate())
&& Objects.isNull(defaultPolicy.getEndDate());
return !(hasCustomPolicy && isAnonimousGroup && datesAreNull);
}
/**
* Returns an iterator of Items possessing the passed metadata field, or only
* those matching the passed value, if value is not Item.ANY
*
* @param context DSpace context object
* @param schema metadata field schema
* @param element metadata field element
* @param qualifier metadata field qualifier
* @param value field value or Item.ANY to match any value
* @return an iterator over the items matching that authority value
* @throws SQLException if database error
* An exception that provides information on a database access error or other errors.
* @throws AuthorizeException if authorization error
* Exception indicating the current user of the context does not have permission
* to perform a particular action.
*/
@Override
public Iterator- findArchivedByMetadataField(Context context,
String schema, String element, String qualifier, String value)
throws SQLException, AuthorizeException {
MetadataSchema mds = metadataSchemaService.find(context, schema);
if (mds == null) {
throw new IllegalArgumentException("No such metadata schema: " + schema);
}
MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier);
if (mdf == null) {
throw new IllegalArgumentException(
"No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier);
}
if (Item.ANY.equals(value)) {
return itemDAO.findByMetadataField(context, mdf, null, true);
}
return itemDAO.findByMetadataField(context, mdf, value, true);
}
@Override
public Iterator
- findArchivedByMetadataField(Context context, String metadataField, String value)
throws SQLException, AuthorizeException {
String[] mdValueByField = getMDValueByField(metadataField);
return findArchivedByMetadataField(context, mdValueByField[0], mdValueByField[1], mdValueByField[2], value);
}
/**
* Returns an iterator of Items possessing the passed metadata field, or only
* those matching the passed value, if value is not Item.ANY
*
* @param context DSpace context object
* @param schema metadata field schema
* @param element metadata field element
* @param qualifier metadata field qualifier
* @param value field value or Item.ANY to match any value
* @return an iterator over the items matching that authority value
* @throws SQLException if database error
* An exception that provides information on a database access error or other errors.
* @throws AuthorizeException if authorization error
* Exception indicating the current user of the context does not have permission
* to perform a particular action.
* @throws IOException if IO error
* A general class of exceptions produced by failed or interrupted I/O operations.
*/
@Override
public Iterator
- findByMetadataField(Context context,
String schema, String element, String qualifier, String value)
throws SQLException, AuthorizeException, IOException {
MetadataSchema mds = metadataSchemaService.find(context, schema);
if (mds == null) {
throw new IllegalArgumentException("No such metadata schema: " + schema);
}
MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier);
if (mdf == null) {
throw new IllegalArgumentException(
"No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier);
}
if (Item.ANY.equals(value)) {
return itemDAO.findByMetadataField(context, mdf, null, true);
}
return itemDAO.findByMetadataField(context, mdf, value, true);
}
@Override
public List
- findByMetadataQuery(Context context, List
queryPredicates,
List collectionUuids, long offset, int limit)
throws SQLException {
return itemDAO.findByMetadataQuery(context, queryPredicates, collectionUuids, "value ~ ?",
offset, limit);
}
@Override
public long countForMetadataQuery(Context context, List queryPredicates,
List collectionUuids)
throws SQLException {
return itemDAO.countForMetadataQuery(context, queryPredicates, collectionUuids, "value ~ ?");
}
@Override
public DSpaceObject getAdminObject(Context context, Item item, int action) throws SQLException {
DSpaceObject adminObject = null;
//Items are always owned by collections
Collection collection = (Collection) getParentObject(context, item);
Community community = null;
if (collection != null) {
if (CollectionUtils.isNotEmpty(collection.getCommunities())) {
community = collection.getCommunities().get(0);
}
}
switch (action) {
case Constants.ADD:
// ADD a cc license is less general than add a bitstream but we can't/won't
// add complex logic here to know if the ADD action on the item is required by a cc or
// a generic bitstream so simply we ignore it.. UI need to enforce the requirements.
if (AuthorizeConfiguration.canItemAdminPerformBitstreamCreation()) {
adminObject = item;
} else if (AuthorizeConfiguration.canCollectionAdminPerformBitstreamCreation()) {
adminObject = collection;
} else if (AuthorizeConfiguration.canCommunityAdminPerformBitstreamCreation()) {
adminObject = community;
}
break;
case Constants.REMOVE:
// see comments on ADD action, same things...
if (AuthorizeConfiguration.canItemAdminPerformBitstreamDeletion()) {
adminObject = item;
} else if (AuthorizeConfiguration.canCollectionAdminPerformBitstreamDeletion()) {
adminObject = collection;
} else if (AuthorizeConfiguration.canCommunityAdminPerformBitstreamDeletion()) {
adminObject = community;
}
break;
case Constants.DELETE:
adminObject = item;
break;
case Constants.WRITE:
// if it is a template item we need to check the
// collection/community admin configuration
if (item.getOwningCollection() == null) {
if (AuthorizeConfiguration.canCollectionAdminManageTemplateItem()) {
adminObject = collection;
} else if (AuthorizeConfiguration.canCommunityAdminManageCollectionTemplateItem()) {
adminObject = community;
}
} else {
adminObject = item;
}
break;
default:
adminObject = item;
break;
}
return adminObject;
}
@Override
public DSpaceObject getParentObject(Context context, Item item) throws SQLException {
Collection ownCollection = item.getOwningCollection();
if (ownCollection != null) {
return ownCollection;
}
InProgressSubmission inprogress = ContentServiceFactory.getInstance().getWorkspaceItemService()
.findByItem(context,
item);
if (inprogress == null) {
inprogress = WorkflowServiceFactory.getInstance().getWorkflowItemService().findByItem(context, item);
}
if (inprogress != null) {
return inprogress.getCollection();
}
// is a template item?
return item.getTemplateItemOf();
}
@Override
public Iterator- findByAuthorityValue(Context context, String schema, String element, String qualifier,
String value) throws SQLException, AuthorizeException {
MetadataSchema mds = metadataSchemaService.find(context, schema);
if (mds == null) {
throw new IllegalArgumentException("No such metadata schema: " + schema);
}
MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier);
if (mdf == null) {
throw new IllegalArgumentException(
"No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier);
}
return itemDAO.findByAuthorityValue(context, mdf, value, true);
}
@Override
public Iterator
- findByMetadataFieldAuthority(Context context, String mdString, String authority)
throws SQLException, AuthorizeException {
String[] elements = getElementsFilled(mdString);
String schema = elements[0];
String element = elements[1];
String qualifier = elements[2];
MetadataSchema mds = metadataSchemaService.find(context, schema);
if (mds == null) {
throw new IllegalArgumentException("No such metadata schema: " + schema);
}
MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier);
if (mdf == null) {
throw new IllegalArgumentException(
"No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier);
}
return findByAuthorityValue(context, mds.getName(), mdf.getElement(), mdf.getQualifier(), authority);
}
@Override
public boolean isItemListedForUser(Context context, Item item) {
try {
if (authorizeService.isAdmin(context)) {
return true;
}
if (authorizeService.authorizeActionBoolean(context, item, org.dspace.core.Constants.READ)) {
if (item.isDiscoverable()) {
return true;
}
}
log.debug("item(" + item.getID() + ") " + item.getName() + " is unlisted.");
return false;
} catch (SQLException e) {
log.error(e.getMessage());
return false;
}
}
@Override
public int countItems(Context context, Collection collection) throws SQLException {
return itemDAO.countItems(context, collection, true, false, true);
}
@Override
public int countAllItems(Context context, Collection collection) throws SQLException {
return itemDAO.countItems(context, collection, true, false, true) + itemDAO.countItems(context, collection,
false, true, true);
}
@Override
public int countItems(Context context, Community community) throws SQLException {
// First we need a list of all collections under this community in the hierarchy
List
collections = communityService.getAllCollections(context, community);
// Now, lets count unique items across that list of collections
return itemDAO.countItems(context, collections, true, false, true);
}
@Override
public int countAllItems(Context context, Community community) throws SQLException {
// First we need a list of all collections under this community in the hierarchy
List collections = communityService.getAllCollections(context, community);
// Now, lets count unique items across that list of collections
return itemDAO.countItems(context, collections, true, false, true) + itemDAO.countItems(context, collections,
false, false, true);
}
@Override
protected void getAuthoritiesAndConfidences(String fieldKey, Collection collection, List values,
List authorities, List confidences, int i) {
Choices c = choiceAuthorityService.getBestMatch(fieldKey, values.get(i), collection, null);
authorities.add(c.values.length > 0 && c.values[0] != null ? c.values[0].authority : null);
confidences.add(c.confidence);
}
@Override
public Item findByIdOrLegacyId(Context context, String id) throws SQLException {
try {
if (StringUtils.isNumeric(id)) {
return findByLegacyId(context, Integer.parseInt(id));
}
return find(context, UUID.fromString(id));
} catch (IllegalArgumentException e) {
// Not a valid legacy ID or valid UUID
return null;
}
}
@Override
public Item findByLegacyId(Context context, int id) throws SQLException {
return itemDAO.findByLegacyId(context, id, Item.class);
}
@Override
public Iterator- findByLastModifiedSince(Context context, Instant last)
throws SQLException {
return itemDAO.findByLastModifiedSince(context, last);
}
@Override
public int countTotal(Context context) throws SQLException {
return itemDAO.countRows(context);
}
@Override
public int countNotArchivedItems(Context context) throws SQLException {
// return count of items not in archive and also not withdrawn
return itemDAO.countItems(context, false, false, true);
}
@Override
public int countArchivedItems(Context context) throws SQLException {
// return count of items in archive and also not withdrawn
return itemDAO.countItems(context, true, false, true);
}
@Override
public int countWithdrawnItems(Context context) throws SQLException {
// return count of items that are not in archive and withdrawn
return itemDAO.countItems(context, false, true, true );
}
@Override
public boolean canCreateNewVersion(Context context, Item item) throws SQLException {
if (authorizeService.isAdmin(context, item)) {
return true;
}
if (context.getCurrentUser() != null
&& context.getCurrentUser().equals(item.getSubmitter())) {
return configurationService.getPropertyAsType(
"versioning.submitterCanCreateNewVersion", false);
}
return false;
}
/**
* This method will return a list of MetadataValue objects that contains all the regular
* metadata of the item passed along in the parameters as well as all the virtual metadata
* which will be generated and processed together with the {@link VirtualMetadataPopulator}
* by processing the item's relationships
* @param item the Item to be processed
* @param schema the schema for the metadata field. Must match
* the
name
of an existing metadata schema.
* @param element the element name. DSpaceObject.ANY
matches any
* element. null
doesn't really make sense as all
* metadata must have an element.
* @param qualifier the qualifier. null
means unqualified, and
* DSpaceObject.ANY
means any qualifier (including
* unqualified.)
* @param lang the ISO639 language code, optionally followed by an underscore
* and the ISO3166 country code. null
means only
* values with no language are returned, and
* DSpaceObject.ANY
means values with any country code or
* no country code are returned.
* @return
*/
@Override
public List getMetadata(Item item, String schema, String element, String qualifier, String lang) {
return this.getMetadata(item, schema, element, qualifier, lang, true);
}
@Override
public List getMetadata(Item item, String schema, String element, String qualifier, String lang,
boolean enableVirtualMetadata) {
if (!enableVirtualMetadata) {
log.debug("Called getMetadata for " + item.getID() + " without enableVirtualMetadata");
return super.getMetadata(item, schema, element, qualifier, lang);
}
if (item.isModifiedMetadataCache()) {
log.debug("Called getMetadata for " + item.getID() + " with invalid cache");
//rebuild cache
List dbMetadataValues = item.getMetadata();
List fullMetadataValueList = new LinkedList<>();
fullMetadataValueList.addAll(relationshipMetadataService.getRelationshipMetadata(item, true));
fullMetadataValueList.addAll(dbMetadataValues);
item.setCachedMetadata(MetadataValueComparators.sort(fullMetadataValueList));
}
log.debug("Called getMetadata for " + item.getID() + " based on cache");
// Build up list of matching values based on the cache
List values = new ArrayList<>();
for (MetadataValue dcv : item.getCachedMetadata()) {
if (match(schema, element, qualifier, lang, dcv)) {
values.add(dcv);
}
}
// Create an array of matching values
return values;
}
/**
* Supports moving metadata by adding the metadata value or updating the place of the relationship
*/
@Override
protected void moveSingleMetadataValue(Context context, Item dso, int place, MetadataValue rr) {
// If this is a (virtual) metadata value representing a relationship,
// then we must also update the corresponding Relationship with the new place
if (rr instanceof RelationshipMetadataValue) {
try {
//Retrieve the applicable relationship
Relationship rs = relationshipService.find(context,
((RelationshipMetadataValue) rr).getRelationshipId());
if (rs.getLeftItem().equals(dso)) {
rs.setLeftPlace(place);
} else {
rs.setRightPlace(place);
}
relationshipService.update(context, rs);
} catch (Exception e) {
//should not occur, otherwise metadata can't be updated either
log.error("An error occurred while moving " + rr.getAuthority() + " for item " + dso.getID(), e);
}
}
// Update the MetadataValue object with the new place setting
rr.setPlace(place);
}
@Override
public MetadataValue addMetadata(Context context, Item dso, String schema, String element, String qualifier,
String lang, String value, String authority, int confidence, int place) throws SQLException {
// We will not verify that they are valid entries in the registry
// until update() is called.
MetadataField metadataField = metadataFieldService.findByElement(context, schema, element, qualifier);
if (metadataField == null) {
throw new SQLException(
"bad_dublin_core schema=" + schema + "." + element + "." + qualifier + ". Metadata field does not " +
"exist!");
}
final Supplier placeSupplier = () -> place;
return addMetadata(context, dso, metadataField, lang, Arrays.asList(value),
Arrays.asList(authority), Arrays.asList(confidence), placeSupplier)
.stream().findFirst().orElse(null);
}
@Override
public String getEntityTypeLabel(Item item) {
List mdvs = getMetadata(item, "dspace", "entity", "type", Item.ANY, false);
if (mdvs.isEmpty()) {
return null;
}
if (mdvs.size() > 1) {
log.warn(
"Item with uuid {}, handle {} has {} entity types ({}), expected 1 entity type",
item.getID(), item.getHandle(), mdvs.size(),
mdvs.stream().map(MetadataValue::getValue).collect(Collectors.toList())
);
}
String entityType = mdvs.get(0).getValue();
if (StringUtils.isBlank(entityType)) {
return null;
}
return entityType;
}
@Override
public EntityType getEntityType(Context context, Item item) throws SQLException {
String entityTypeString = getEntityTypeLabel(item);
if (StringUtils.isBlank(entityTypeString)) {
return null;
}
return entityTypeService.findByEntityType(context, entityTypeString);
}
private void removeOrcidSynchronizationStuff(Context context, Item item) throws SQLException, AuthorizeException {
if (isNotProfileOrOrcidEntity(item)) {
return;
}
context.turnOffAuthorisationSystem();
try {
createOrcidQueueRecordsToDeleteOnOrcid(context, item);
deleteOrcidHistoryRecords(context, item);
deleteOrcidQueueRecords(context, item);
} finally {
context.restoreAuthSystemState();
}
}
private boolean isNotProfileOrOrcidEntity(Item item) {
String entityType = getEntityTypeLabel(item);
return !OrcidEntityType.isValidEntityType(entityType)
&& !researcherProfileService.getProfileType().equals(entityType);
}
private void createOrcidQueueRecordsToDeleteOnOrcid(Context context, Item entity) throws SQLException {
String entityType = getEntityTypeLabel(entity);
if (entityType == null || researcherProfileService.getProfileType().equals(entityType)) {
return;
}
Map- profileAndPutCodeMap = orcidHistoryService.findLastPutCodes(context, entity);
for (Item profile : profileAndPutCodeMap.keySet()) {
if (orcidSynchronizationService.isSynchronizationAllowed(profile, entity)) {
String putCode = profileAndPutCodeMap.get(profile);
String title = getMetadataFirstValue(entity, "dc", "title", null, Item.ANY);
orcidQueueService.createEntityDeletionRecord(context, profile, title, entityType, putCode);
}
}
}
private void deleteOrcidHistoryRecords(Context context, Item item) throws SQLException {
List
historyRecords = orcidHistoryService.findByProfileItemOrEntity(context, item);
for (OrcidHistory historyRecord : historyRecords) {
if (historyRecord.getProfileItem().equals(item)) {
orcidHistoryService.delete(context, historyRecord);
} else {
historyRecord.setEntity(null);
orcidHistoryService.update(context, historyRecord);
}
}
}
private void deleteOrcidQueueRecords(Context context, Item item) throws SQLException {
List orcidQueueRecords = orcidQueueService.findByProfileItemOrEntity(context, item);
for (OrcidQueue orcidQueueRecord : orcidQueueRecords) {
orcidQueueService.delete(context, orcidQueueRecord);
}
}
@Override
public boolean isLatestVersion(Context context, Item item) throws SQLException {
VersionHistory history = versionHistoryService.findByItem(context, item);
if (history == null) {
// not all items have a version history
// if an item does not have a version history, it is by definition the latest
// version
return true;
}
// start with the very latest version of the given item (may still be in
// workspace)
Version latestVersion = versionHistoryService.getLatestVersion(context, history);
// find the latest version of the given item that is archived
while (latestVersion != null && !latestVersion.getItem().isArchived()) {
latestVersion = versionHistoryService.getPrevious(context, history, latestVersion);
}
// could not find an archived version of the given item
if (latestVersion == null) {
// this scenario should never happen, but let's err on the side of showing too
// many items vs. to little
// (see discovery.xml, a lot of discovery configs filter out all items that are
// not the latest version)
return true;
}
// sanity check
assert latestVersion.getItem().isArchived();
return item.equals(latestVersion.getItem());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy