Re-assemble pull request

This commit is contained in:
Terry Brady
2016-01-13 13:15:53 -08:00
parent 9f6cf4e7c7
commit d76f5696de
39 changed files with 5249 additions and 0 deletions

View File

@@ -937,6 +937,13 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
}
}
@Override
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList, List<String> query_op, List<String> query_val, List<UUID> collectionUuids, String regexClause, int offset, int limit)
throws SQLException, AuthorizeException, IOException
{
return itemDAO.findByMetadataQuery(context, listFieldList, query_op, query_val, collectionUuids, regexClause, offset, limit);
}
@Override
public DSpaceObject getAdminObject(Context context, Item item, int action) throws SQLException {
DSpaceObject adminObject = null;

View File

@@ -87,6 +87,11 @@ public class MetadataFieldServiceImpl implements MetadataFieldService {
return metadataFieldDAO.findByElement(context, metadataSchemaName, element, qualifier);
}
@Override
public List<MetadataField> findFieldsByElementNameUnqualified(Context context, String metadataSchemaName, String element) throws SQLException {
return metadataFieldDAO.findFieldsByElementNameUnqualified(context, metadataSchemaName, element);
}
@Override
public List<MetadataField> findAll(Context context) throws SQLException
{

View File

@@ -16,6 +16,8 @@ import org.dspace.eperson.EPerson;
import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/**
* Database Access Object interface class for the Item object.
@@ -46,6 +48,8 @@ public interface ItemDAO extends DSpaceObjectLegacySupportDAO<Item>
public Iterator<Item> findByMetadataField(Context context, MetadataField metadataField, String value, boolean inArchive) throws SQLException;
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList, List<String> query_op, List<String> query_val, List<UUID> collectionUuids, String regexClause, int offset, int limit) throws SQLException;
public Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority, boolean inArchive) throws SQLException;
public Iterator<Item> findArchivedByCollection(Context context, Collection collection, Integer limit, Integer offset) throws SQLException;

View File

@@ -33,6 +33,9 @@ public interface MetadataFieldDAO extends GenericDAO<MetadataField> {
public MetadataField findByElement(Context context, String metadataSchema, String element, String qualifier)
throws SQLException;
public List<MetadataField> findFieldsByElementNameUnqualified(Context context, String metadataSchema, String element)
throws SQLException;
public List<MetadataField> findAllInSchema(Context context, MetadataSchema metadataSchema)
throws SQLException;

View File

@@ -7,19 +7,33 @@
*/
package org.dspace.content.dao.impl;
import org.apache.log4j.Logger;
import org.dspace.content.Collection;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataValue;
import org.dspace.content.dao.ItemDAO;
import org.dspace.core.Context;
import org.dspace.core.AbstractHibernateDSODAO;
import org.dspace.eperson.EPerson;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/**
* Hibernate implementation of the Database Access Object interface class for the Item object.
@@ -29,6 +43,7 @@ import java.util.Iterator;
* @author kevinvandevelde at atmire.com
*/
public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDAO {
private static final Logger log = Logger.getLogger(ItemDAOImpl.class);
@Override
public Iterator<Item> findAll(Context context, boolean archived) throws SQLException {
Query query = createQuery(context, "FROM Item WHERE inArchive= :in_archive");
@@ -113,6 +128,73 @@ public class ItemDAOImpl extends AbstractHibernateDSODAO<Item> implements ItemDA
return iterate(query);
}
enum OP {equals,not_equals,like,not_like,contains,doesnt_contain,exists,doesnt_exist,matches,doesnt_match;}
@Override
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList, List<String> query_op, List<String> query_val, List<UUID> collectionUuids, String regexClause, int offset, int limit) throws SQLException {
Criteria criteria = createCriteria(context, Item.class, "item");
criteria.setFirstResult(offset);
criteria.setMaxResults(limit);
if (!collectionUuids.isEmpty()){
DetachedCriteria dcollCriteria = DetachedCriteria.forClass(Collection.class, "coll");
dcollCriteria.setProjection(Projections.property("coll.id"));
dcollCriteria.add(Restrictions.eqProperty("coll.id", "item.owningCollection"));
dcollCriteria.add(Restrictions.in("coll.id", collectionUuids));
criteria.add(Subqueries.exists(dcollCriteria));
}
int index = Math.min(listFieldList.size(), Math.min(query_op.size(), query_val.size()));
StringBuilder sb = new StringBuilder();
for(int i=0; i<index; i++) {
OP op = OP.valueOf(query_op.get(i));
if (op == null) {
log.warn("Skipping Invalid Operator: " + query_op.get(i));
continue;
}
if (op == OP.matches || op == OP.doesnt_match) {
if (regexClause.isEmpty()) {
log.warn("Skipping Unsupported Regex Operator: " + query_op.get(i));
continue;
}
}
DetachedCriteria subcriteria = DetachedCriteria.forClass(MetadataValue.class,"mv");
subcriteria.add(Property.forName("mv.dSpaceObject").eqProperty("item.id"));
subcriteria.setProjection(Projections.property("mv.dSpaceObject"));
if (!listFieldList.get(i).isEmpty()) {
subcriteria.add(Restrictions.in("metadataField", listFieldList.get(i)));
}
sb.append(op.name() + " ");
if (op == OP.equals || op == OP.not_equals){
subcriteria.add(Property.forName("mv.value").eq(query_val.get(i)));
sb.append(query_val.get(i));
} else if (op == OP.like || op == OP.not_like){
subcriteria.add(Property.forName("mv.value").like(query_val.get(i)));
sb.append(query_val.get(i));
} else if (op == OP.contains || op == OP.doesnt_contain){
subcriteria.add(Property.forName("mv.value").like("%"+query_val.get(i)+"%"));
sb.append(query_val.get(i));
} else if (op == OP.matches || op == OP.doesnt_match) {
subcriteria.add(Restrictions.sqlRestriction(regexClause, query_val.get(i), StandardBasicTypes.STRING));
sb.append(query_val.get(i));
}
if (op == OP.exists || op == OP.equals || op == OP.like || op == OP.contains || op == OP.matches) {
criteria.add(Subqueries.exists(subcriteria));
} else {
criteria.add(Subqueries.notExists(subcriteria));
}
}
log.debug(String.format("Running custom query with %d filters", index));
return list(criteria).iterator();
}
@Override
public Iterator<Item> findByAuthorityValue(Context context, MetadataField metadataField, String authority, boolean inArchive) throws SQLException {
Query query = createQuery(context, "SELECT item FROM Item as item join item.metadata metadatavalue WHERE item.inArchive=:in_archive AND metadatavalue.metadataField = :metadata_field AND metadatavalue.authority = :authority");

View File

@@ -70,6 +70,20 @@ public class MetadataFieldDAOImpl extends AbstractHibernateDAO<MetadataField> im
return singleResult(criteria);
}
@Override
public List<MetadataField> findFieldsByElementNameUnqualified(Context context, String metadataSchema, String element) throws SQLException
{
Criteria criteria = createCriteria(context, MetadataField.class);
criteria.createAlias("metadataSchema", "s").add(
Restrictions.and(
Restrictions.eq("s.name", metadataSchema),
Restrictions.eq("element", element)
)
);
return list(criteria);
}
@Override
public List<MetadataField> findAllInSchema(Context context, MetadataSchema metadataSchema) throws SQLException {
// Get all the metadatafieldregistry rows

View File

@@ -20,6 +20,7 @@ import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/**
* Service interface class for the Item object.
@@ -403,6 +404,8 @@ public interface ItemService extends DSpaceObjectService<Item>, DSpaceObjectLega
String schema, String element, String qualifier, String value)
throws SQLException, AuthorizeException, IOException;
public Iterator<Item> findByMetadataQuery(Context context, List<List<MetadataField>> listFieldList, List<String> query_op, List<String> query_val, List<UUID> collectionUuids, String regexClause, int offset, int limit)
throws SQLException, AuthorizeException, IOException;
/**
* Find all the items in the archive with a given authority key value

View File

@@ -67,6 +67,9 @@ public interface MetadataFieldService {
public MetadataField findByElement(Context context, String metadataSchemaName, String element, String qualifier)
throws SQLException;
public List<MetadataField> findFieldsByElementNameUnqualified(Context context, String metadataSchema, String element)
throws SQLException;
/**
* Retrieve all metadata field types from the registry
*

View File

@@ -30,6 +30,18 @@
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
</plugin>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<!--Exclude license check for XMLUI files which don't need it-->
<excludes>
<exclude>**/static/reports/spin.js</exclude>
<exclude>**/static/reports/README.md</exclude>
<exclude>**/*.xsd</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -0,0 +1,193 @@
/**
* 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.rest;
import org.apache.log4j.Logger;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.core.ConfigurationManager;
import org.dspace.rest.common.FilteredCollection;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.usage.UsageEvent;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/*
* This class provides the items within a collection evaluated against a set of Item Filters.
*
* @author Terry Brady, Georgetown University
*/
@Path("/filtered-collections")
public class FilteredCollectionsResource extends Resource {
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
private static Logger log = Logger.getLogger(FilteredCollectionsResource.class);
/**
* Return array of all collections in DSpace. You can add more properties
* through expand parameter.
*
* @param expand
* String in which is what you want to add to returned instance
* of collection. Options are: "all", "parentCommunityList",
* "parentCommunity", "topCommunity", "items", "license" and "logo".
* If you want to use multiple options, it must be separated by commas.
* @param limit
* Limit value for items in list in collection. Default value is
* 100.
* @param offset
* Offset of start index in list of items of collection. Default
* value is 0.
* @param headers
* If you want to access to collections under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return array of collection, on which has logged user permission
* to view.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException).
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public org.dspace.rest.common.FilteredCollection[] getCollections(@QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("filters") @DefaultValue("is_item") String filters,
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading all filtered collections.(offset=" + offset + ",limit=" + limit + ")");
org.dspace.core.Context context = null;
List<FilteredCollection> collections = new ArrayList<FilteredCollection>();
try
{
context = createContext(getUser(headers));
if (ConfigurationManager.getBooleanProperty("rest", "rest-reporting-authenticate", true) == false) {
context.turnOffAuthorisationSystem();
}
if (!((limit != null) && (limit >= 0) && (offset != null) && (offset >= 0)))
{
log.warn("Paging was badly set.");
limit = 100;
offset = 0;
}
List<org.dspace.content.Collection> dspaceCollections = collectionService.findAll(context, limit, offset);
for(org.dspace.content.Collection dspaceCollection : dspaceCollections)
{
if (authorizeService.authorizeActionBoolean(context, dspaceCollection, org.dspace.core.Constants.READ))
{
FilteredCollection collection = new org.dspace.rest.common.FilteredCollection(dspaceCollection, filters, expand, context, limit,
offset);
collections.add(collection);
writeStats(dspaceCollection, UsageEvent.Action.VIEW, user_ip, user_agent,
xforwardedfor, headers, request, context);
}
}
context.complete();
}
catch (SQLException e)
{
processException("Something went wrong while reading collections from database. Message: " + e, context);
}
catch (ContextException e)
{
processException("Something went wrong while reading collections, ContextError. Message: " + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("All collections were successfully read.");
return collections.toArray(new org.dspace.rest.common.FilteredCollection[0]);
}
/**
* Return instance of collection with passed id. You can add more properties
* through expand parameter.
*
* @param collectionId
* Id of collection in DSpace.
* @param expand
* String in which is what you want to add to returned instance
* of collection. Options are: "all", "parentCommunityList",
* "parentCommunity", "topCommunity", "items", "license" and "logo".
* If you want to use multiple options, it must be separated by commas.
* @param limit
* Limit value for items in list in collection. Default value is
* 100.
* @param offset
* Offset of start index in list of items of collection. Default
* value is 0.
* @param filters
* Comma separated list of Item Filters to use to evaluate against
* the items in a collection
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of collection. It can also return status code
* NOT_FOUND(404) if id of collection is incorrect or status code
* UNATHORIZED(401) if user has no permission to read collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException). It is thrown by NOT_FOUND and
* UNATHORIZED status codes, too.
*/
@GET
@Path("/{collection_id}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public org.dspace.rest.common.FilteredCollection getCollection(@PathParam("collection_id") String collection_id, @QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("1000") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@QueryParam("filters") @DefaultValue("is_item") String filters,
@Context HttpHeaders headers, @Context HttpServletRequest request) {
org.dspace.core.Context context = null;
FilteredCollection retColl = new org.dspace.rest.common.FilteredCollection();
try {
context = createContext(getUser(headers));
if (!ConfigurationManager.getBooleanProperty("rest", "rest-reporting-authenticate", false)) {
context.turnOffAuthorisationSystem();
}
org.dspace.content.Collection collection = collectionService.findByIdOrLegacyId(context, collection_id);
if(authorizeService.authorizeActionBoolean(context, collection, org.dspace.core.Constants.READ)) {
writeStats(collection, UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor, headers, request, context);
retColl = new org.dspace.rest.common.FilteredCollection(collection, filters, expand, context, limit, offset);
} else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
context.complete();
} catch (SQLException e) {
processException(e.getMessage(), context);
} catch (ContextException e) {
processException(String.format("Could not read collection %d. %s", collection_id, e.getMessage()), context);
} finally {
processFinally(context);
}
return retColl;
}
}

View File

@@ -0,0 +1,202 @@
/**
* 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.rest;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.SiteService;
import org.dspace.core.ConfigurationManager;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.rest.common.ItemFilter;
import org.dspace.rest.common.ItemFilterQuery;
import org.dspace.rest.filter.ItemFilterSet;
import org.dspace.usage.UsageEvent;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.HttpHeaders;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/*
* This class retrieves items by a constructed metadata query evaluated against a set of Item Filters.
*
* @author Terry Brady, Georgetown University
*/
@Path("/filtered-items")
public class FilteredItemsResource extends Resource {
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance().getMetadataSchemaService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService();
private static Logger log = Logger.getLogger(FilteredItemsResource.class);
/**
* Return instance of collection with passed id. You can add more properties
* through expand parameter.
*
* @param expand
* String in which is what you want to add to returned instance
* of collection. Options are: "all", "parentCommunityList",
* "parentCommunity", "items", "license" and "logo". If you want
* to use multiple options, it must be separated by commas.
* @param limit
* Limit value for items in list in collection. Default value is
* 100.
* @param offset
* Offset of start index in list of items of collection. Default
* value is 0.
* @param filters
* Comma separated list of Item Filters to use to evaluate against
* the items in a collection
* @param query_field
* List of metadata fields to evaluate in a metadata query.
* Each list value is used in conjunction with a query_op and query_field.
* @param query_op
* List of metadata operators to use in a metadata query.
* Each list value is used in conjunction with a query_field and query_field.
* @param query_val
* List of metadata values to evaluate in a metadata query.
* Each list value is used in conjunction with a query_value and query_op.
* @param collSel
* List of collections to query.
* @param headers
* If you want to access to collection under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of collection. It can also return status code
* NOT_FOUND(404) if id of collection is incorrect or status code
* UNATHORIZED(401) if user has no permission to read collection.
* @throws WebApplicationException
* It is thrown when was problem with database reading
* (SQLException) or problem with creating
* context(ContextException). It is thrown by NOT_FOUND and
* UNATHORIZED status codes, too.
*/
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public org.dspace.rest.common.ItemFilter getItemQuery(@QueryParam("expand") String expand,
@QueryParam("limit") @DefaultValue("100") Integer limit, @QueryParam("offset") @DefaultValue("0") Integer offset,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent, @QueryParam("xforwarderfor") String xforwarderfor,
@QueryParam("filters") @DefaultValue("is_item,all_filters") String filters,
@QueryParam("query_field[]") @DefaultValue("dc.title") List<String> query_field,
@QueryParam("query_op[]") @DefaultValue("exists") List<String> query_op,
@QueryParam("query_val[]") @DefaultValue("") List<String> query_val,
@QueryParam("collSel[]") @DefaultValue("") List<String> collSel,
@Context HttpHeaders headers, @Context HttpServletRequest request) {
org.dspace.core.Context context = null;
ItemFilterSet itemFilterSet = new ItemFilterSet(filters, true);
ItemFilter result = itemFilterSet.getAllFiltersFilter();
try {
context = createContext(getUser(headers));
if (ConfigurationManager.getBooleanProperty("rest", "rest-reporting-authenticate", true) == false) {
context.turnOffAuthorisationSystem();
}
int index = Math.min(query_field.size(), Math.min(query_op.size(), query_val.size()));
List<ItemFilterQuery> itemFilterQueries = new ArrayList<ItemFilterQuery>();
for(int i=0; i<index; i++){
itemFilterQueries.add(new ItemFilterQuery(query_field.get(i), query_op.get(i), query_val.get(i)));
}
String regexClause = ConfigurationManager.getProperty("rest", "rest-regex-clause");
if (regexClause == null) {
regexClause = "";
}
List<UUID> uuids = getUuidsFromStrings(collSel);
List<List<MetadataField>> listFieldList = getMetadataFieldsList(context, query_field);
Iterator<org.dspace.content.Item> childItems = itemService.findByMetadataQuery(context, listFieldList, query_op, query_val, uuids, regexClause, offset, limit);
int count = itemFilterSet.processSaveItems(context, childItems, true, expand);
writeStats(siteService.findSite(context), UsageEvent.Action.VIEW, user_ip, user_agent, xforwarderfor, headers, request, context);
result.annotateQuery(query_field, query_op, query_val);
result.setUnfilteredItemCount(count);
context.complete();
} catch (IOException e) {
processException(e.getMessage(), context);
} catch (SQLException e) {
processException(e.getMessage(), context);
} catch (AuthorizeException e) {
processException(e.getMessage(), context);
} catch (ContextException e) {
processException("Unauthorized filtered item query. " + e.getMessage(), context);
} finally {
processFinally(context);
}
return result;
}
private List<List<MetadataField>> getMetadataFieldsList(org.dspace.core.Context context, List<String> query_field) throws SQLException {
List<List<MetadataField>> listFieldList = new ArrayList<List<MetadataField>>();
for(String s: query_field) {
ArrayList<MetadataField> fields = new ArrayList<MetadataField>();
listFieldList.add(fields);
if (s.equals("*")) {
continue;
}
String schema = "";
String element = "";
String qualifier = null;
String[] parts = s.split("\\.");
if (parts.length>0) {
schema = parts[0];
}
if (parts.length>1) {
element = parts[1];
}
if (parts.length>2) {
qualifier = parts[2];
}
if (Item.ANY.equals(qualifier)) {
for(MetadataField mf: metadataFieldService.findFieldsByElementNameUnqualified(context, schema, element)){
fields.add(mf);
}
} else {
MetadataField mf = metadataFieldService.findByElement(context, schema, element, qualifier);
if (mf != null) {
fields.add(mf);
}
}
}
return listFieldList;
}
private List<UUID> getUuidsFromStrings(List<String> collSel) {
List<UUID> uuids = new ArrayList<UUID>();
for(String s: collSel) {
try {
uuids.add(UUID.fromString(s));
} catch (IllegalArgumentException e) {
log.warn("Invalid collection UUID: " + s);
}
}
return uuids;
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.rest;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.apache.log4j.Logger;
import org.dspace.rest.common.ItemFilter;
/**
* Class which provides read methods over the metadata registry.
*
* @author Terry Brady, Georgetown University
*
*/
@Path("/filters")
public class FiltersResource
{
private static Logger log = Logger.getLogger(FiltersResource.class);
/**
* Return all Use Case Item Filters in DSpace.
*
* @return Return array of metadata schemas.
* @throws WebApplicationException
* It can be caused by creating context or while was problem
* with reading community from database(SQLException).
*/
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public ItemFilter[] getFilters(@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading all Item Filters.");
return ItemFilter.getItemFilters(ItemFilter.ALL, false).toArray(new ItemFilter[0]);
}
}

View File

@@ -0,0 +1,751 @@
/**
* 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.rest;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.NonUniqueMetadataException;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.SiteService;
import org.dspace.rest.common.MetadataSchema;
import org.dspace.rest.exceptions.ContextException;
import org.dspace.usage.UsageEvent;
import org.dspace.rest.common.MetadataField;
/**
* Class which provides read methods over the metadata registry.
*
* @author Terry Brady, Georgetown University
*
* GET /registries/schema - Return the list of schemas in the registry
* GET /registries/schema/{schema_prefix} - Returns the specified schema
* GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within a schema with an unqualified element name
* GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata field within a schema with a qualified element name
* POST /registries/schema/ - Add a schema to the schema registry
* POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified schema
* GET /registries/metadata-fields/{field_id} - Return the specified metadata field
* PUT /registries/metadata-fields/{field_id} - Update the specified metadata field
* DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the metadata field registry
* DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry
*
* Note: intentionally not providing since there is no date to update other than the namespace
* PUT /registries/schema/{schema_id}
*/
@Path("/registries")
public class MetadataRegistryResource extends Resource
{
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance().getMetadataSchemaService();
protected SiteService siteService = ContentServiceFactory.getInstance().getSiteService();
private static Logger log = Logger.getLogger(MetadataRegistryResource.class);
/**
* Return all metadata registry items in DSpace.
*
* @param expand
* String in which is what you want to add to returned instance
* of metadata schema. Options are: "all", "fields". Default value "fields".
* @return Return array of metadata schemas.
* @throws WebApplicationException
* It can be caused by creating context or while was problem
* with reading schema from database(SQLException).
*/
@GET
@Path("/schema")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataSchema[] getSchemas(@QueryParam("expand") @DefaultValue("fields") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading all metadata schemas.");
org.dspace.core.Context context = null;
ArrayList<MetadataSchema> metadataSchemas = null;
try
{
context = createContext(getUser(headers));
List<org.dspace.content.MetadataSchema> schemas = metadataSchemaService.findAll(context);
metadataSchemas = new ArrayList<MetadataSchema>();
for(org.dspace.content.MetadataSchema schema: schemas) {
metadataSchemas.add(new MetadataSchema(schema, expand, context));
}
context.complete();
}
catch (SQLException e)
{
processException("Could not read metadata schemas, SQLException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not read metadata schemas, ContextException. Message:" + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("All metadata schemas successfully read.");
return metadataSchemas.toArray(new MetadataSchema[0]);
}
/**
* Returns metadata schema with basic properties. If you want more, use expand
* parameter or method for metadata fields.
*
* @param schemaPrefix
* Prefix for schema in DSpace.
* @param expand
* String in which is what you want to add to returned instance
* of metadata schema. Options are: "all", "fields". Default value "fields".
* @param headers
* If you want to access to metadata schema under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of org.dspace.rest.common.MetadataSchema.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading. Also if id/prefix of schema is incorrect
* or logged user into context has no permission to read.
*/
@GET
@Path("/schema/{schema_prefix}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataSchema getSchema(@PathParam("schema_prefix") String schemaPrefix, @QueryParam("expand") @DefaultValue("fields") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading metadata schemas.");
org.dspace.core.Context context = null;
MetadataSchema metadataSchema = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
metadataSchema = new MetadataSchema(schema, expand, context);
if (schema == null) {
processException(String.format("Schema not found for index %s", schemaPrefix), context);
}
context.complete();
}
catch (SQLException e)
{
processException("Could not read metadata schema, SQLException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not read metadata schema, ContextException. Message:" + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("Metadata schemas successfully read.");
return metadataSchema;
}
/**
* Returns metadata field with basic properties.
*
* @param schemaPreix
* Prefix for schema in DSpace.
* @param element
* Unqualified element name for field in the metadata registry.
* @param expand
* String in which is what you want to add to returned instance
* of the metadata field. Options are: "all", "parentSchema". Default value "".
* @param headers
* If you want to access to community under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of org.dspace.rest.common.MetadataField.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading. Also if id of field is incorrect
* or logged user into context has no permission to read.
*/
@GET
@Path("/schema/{schema_prefix}/metadata-fields/{element}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataField getMetadataFieldUnqualified(@PathParam("schema_prefix") String schemaPrefix,
@PathParam("element") String element,
@QueryParam("expand") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
return getMetadataFieldQualified(schemaPrefix, element, "", expand, user_ip, user_agent, xforwarderfor, headers, request);
}
/**
* Returns metadata field with basic properties.
*
* @param schemaPreix
* Prefix for schema in DSpace.
* @param element
* Element name for field in the metadata registry.
* @param qualifier
* Element name qualifier for field in the metadata registry.
* @param expand
* String in which is what you want to add to returned instance
* of the metadata field. Options are: "all", "parentSchema". Default value "".
* @param headers
* If you want to access to community under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of org.dspace.rest.common.MetadataField.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading. Also if id of field is incorrect
* or logged user into context has no permission to read.
*/
@GET
@Path("/schema/{schema_prefix}/metadata-fields/{element}/{qualifier}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataField getMetadataFieldQualified(@PathParam("schema_prefix") String schemaPrefix,
@PathParam("element") String element,
@PathParam("qualifier") @DefaultValue("") String qualifier,
@QueryParam("expand") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading metadata field.");
org.dspace.core.Context context = null;
MetadataField metadataField = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
if (schema == null) {
log.error(String.format("Schema not found for prefix %s", schemaPrefix));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
org.dspace.content.MetadataField field = metadataFieldService.findByElement(context, schema, element, qualifier);
if (field == null) {
log.error(String.format("Field %s.%s.%s not found", schemaPrefix, element, qualifier));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
metadataField = new MetadataField(schema, field, expand, context);
context.complete();
}
catch (SQLException e)
{
processException("Could not read metadata field, SQLException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("Metadata field successfully read.");
return metadataField;
}
/**
* Returns metadata field with basic properties.
*
* @param fieldId
* Id of metadata field in DSpace.
* @param expand
* String in which is what you want to add to returned instance
* of the metadata field. Options are: "all", "parentSchema". Default value "parentSchema".
* @param headers
* If you want to access to community under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return instance of org.dspace.rest.common.MetadataField.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading. Also if id of field is incorrect
* or logged user into context has no permission to read.
*/
@GET
@Path("/metadata-fields/{field_id}")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataField getMetadataField(@PathParam("field_id") Integer fieldId,
@QueryParam("expand") @DefaultValue("parentSchema") String expand,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwarderfor") String xforwarderfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Reading metadata field.");
org.dspace.core.Context context = null;
MetadataField metadataField = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataField field = metadataFieldService.find(context, fieldId);
if (field == null) {
log.error(String.format("Metadata Field %d not found", fieldId));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
org.dspace.content.MetadataSchema schema = field.getMetadataSchema();
if (schema == null) {
log.error(String.format("Parent Schema not found for Metadata Field %d not found", fieldId));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
metadataField = new MetadataField(schema, field, expand, context);
context.complete();
}
catch (SQLException e)
{
processException("Could not read metadata field, SQLException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not read metadata field, ContextException. Message:" + e.getMessage(), context);
}
finally
{
processFinally(context);
}
log.trace("Metadata field successfully read.");
return metadataField;
}
/**
* Create schema in the schema registry. Creating a schema is restricted to admin users.
*
* @param schema
* Schema that will be added to the metadata registry.
* @param headers
* If you want to access to schema under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response 200 if was everything all right. Otherwise 400
* when id of community was incorrect or 401 if was problem with
* permission to write into collection.
* Returns the schema (schemaId), if was all ok.
* @throws WebApplicationException
* It can be thrown by SQLException, AuthorizeException and
* ContextException.
*/
@POST
@Path("/schema")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataSchema createSchema(MetadataSchema schema, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Creating a schema.");
org.dspace.core.Context context = null;
MetadataSchema retSchema = null;
try
{
context = createContext(getUser(headers));
if (!authorizeService.isAdmin(context))
{
context.abort();
String user = "anonymous";
if (context.getCurrentUser() != null)
{
user = context.getCurrentUser().getEmail();
}
log.error("User(" + user + ") does not have permission to create a metadata schema!");
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
log.debug(String.format("Admin user creating schema with namespace %s and prefix %s", schema.getNamespace(), schema.getPrefix()));
org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService.create(context, schema.getNamespace(), schema.getPrefix());
log.debug("Creating return object.");
retSchema = new MetadataSchema(dspaceSchema, "", context);
writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor,
headers, request, context);
context.complete();
log.info("Schema created" + retSchema.getPrefix());
}
catch (SQLException e)
{
processException("Could not create new metadata schema, SQLException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not create new metadata schema, ContextException. Message: " + e.getMessage(), context);
}
catch (AuthorizeException e)
{
processException("Could not create new metadata schema, AuthorizeException. Message: " + e.getMessage(), context);
}
catch (NonUniqueMetadataException e) {
processException("Could not create new metadata schema, NonUniqueMetadataException. Message: " + e.getMessage(), context);
}
catch (Exception e)
{
processException("Could not create new metadata schema, Exception. Class: " + e.getClass(), context);
}
finally
{
processFinally(context);
}
return retSchema;
}
/**
* Create a new metadata field within a schema.
* Creating a metadata field is restricted to admin users.
*
* @param field
* Field that will be added to the metadata registry for a schema.
* @param headers
* If you want to access to schema under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response 200 if was everything all right. Otherwise 400
* when id of community was incorrect or 401 if was problem with
* permission to write into collection.
* Returns the field (with fieldId), if was all ok.
* @throws WebApplicationException
* It can be thrown by SQLException, AuthorizeException and
* ContextException.
*/
@POST
@Path("/schema/{schema_prefix}/metadata-fields")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public MetadataField createMetadataField(@PathParam("schema_prefix") String schemaPrefix,
MetadataField field, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info(String.format("Creating metadataField within schema %s.", schemaPrefix));
org.dspace.core.Context context = null;
MetadataField retField = null;
try
{
context = createContext(getUser(headers));
if (!authorizeService.isAdmin(context))
{
context.abort();
String user = "anonymous";
if (context.getCurrentUser() != null)
{
user = context.getCurrentUser().getEmail();
}
log.error("User(" + user + ") does not have permission to create a metadata field!");
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
org.dspace.content.MetadataSchema schema = metadataSchemaService.find(context, schemaPrefix);
if (schema == null) {
log.error(String.format("Schema not found for prefix %s", schemaPrefix));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
org.dspace.content.MetadataField dspaceField = metadataFieldService.create(context, schema, field.getElement(), field.getQualifier(), field.getDescription());
writeStats(siteService.findSite(context), UsageEvent.Action.CREATE, user_ip, user_agent, xforwardedfor,
headers, request, context);
retField = new MetadataField(schema, dspaceField, "", context);
context.complete();
log.info("Metadata field created within schema" + retField.getName());
}
catch (SQLException e)
{
processException("Could not create new metadata field, SQLException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not create new metadata field, ContextException. Message: " + e.getMessage(), context);
}
catch (AuthorizeException e)
{
processException("Could not create new metadata field, AuthorizeException. Message: " + e.getMessage(), context);
}
catch (NonUniqueMetadataException e) {
processException("Could not create new metadata field, NonUniqueMetadataException. Message: " + e.getMessage(), context);
}
catch (Exception e)
{
processException("Could not create new metadata field, Exception. Message: " + e.getMessage(), context);
}
finally
{
processFinally(context);
}
return retField;
}
//@PUT
//@Path("/schema/{schema_prefix}")
//Assumption - there are no meaningful fields to update for a schema
/**
* Update metadata field. Replace all information about community except the id and the containing schema.
*
* @param fieldId
* Id of the field in the DSpace metdata registry.
* @param field
* Instance of the metadata field which will replace actual metadata field in
* DSpace.
* @param headers
* If you want to access to metadata field under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Response 200 if was all ok. Otherwise 400 if was id incorrect or
* 401 if logged user has no permission to update the metadata field.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading or writing. Or problem with writing to
* community caused by authorization.
*/
@PUT
@Path("/metadata-fields/{field_id}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response updateMetadataField(@PathParam("field_id") Integer fieldId, MetadataField field,
@QueryParam("userIP") String user_ip, @QueryParam("userAgent") String user_agent,
@QueryParam("xforwardedfor") String xforwardedfor, @Context HttpHeaders headers, @Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Updating metadata field(id=" + fieldId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId);
if (field == null) {
log.error(String.format("Metadata Field %d not found", fieldId));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
writeStats(siteService.findSite(context), UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor,
headers, request, context);
dspaceField.setElement(field.getElement());
dspaceField.setQualifier(field.getQualifier());
dspaceField.setScopeNote(field.getDescription());
metadataFieldService.update(context, dspaceField);
context.complete();
}
catch (SQLException e)
{
processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not update metadata field(id=" + fieldId + "), ContextException Message:" + e, context);
}
catch (AuthorizeException e)
{
processException("Could not update metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, context);
}
catch (NonUniqueMetadataException e) {
processException("Could not update metadata field(id=" + fieldId + "), NonUniqueMetadataException. Message:" + e, context);
}
catch (IOException e) {
processException("Could not update metadata field(id=" + fieldId + "), IOException. Message:" + e, context);
}
finally
{
processFinally(context);
}
log.info("Metadata Field(id=" + fieldId + ") has been successfully updated.");
return Response.ok().build();
}
/**
* Delete metadata field from the DSpace metadata registry
*
* @param fieldId
* Id of the metadata field in DSpace.
* @param headers
* If you want to access to metadata field under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response code OK(200) if was everything all right.
* Otherwise return NOT_FOUND(404) if was id of metadata field is incorrect.
* Or (UNAUTHORIZED)401 if was problem with permission to metadata field.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading or deleting. Or problem with deleting
* metadata field caused by IOException or authorization.
*/
@DELETE
@Path("/metadata-fields/{field_id}")
public Response deleteMetadataField(@PathParam("field_id") Integer fieldId, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Deleting metadata field(id=" + fieldId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataField dspaceField = metadataFieldService.find(context, fieldId);
if (dspaceField == null) {
log.error(String.format("Metadata Field %d not found", fieldId));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, headers,
request, context);
metadataFieldService.delete(context, dspaceField);
context.complete();
}
catch (SQLException e)
{
processException("Could not delete metadata field(id=" + fieldId + "), SQLException. Message:" + e, context);
}
catch (AuthorizeException e)
{
processException("Could not delete metadata field(id=" + fieldId + "), AuthorizeException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not delete metadata field(id=" + fieldId + "), ContextException. Message:" + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.info("Metadata field(id=" + fieldId + ") was successfully deleted.");
return Response.status(Response.Status.OK).build();
}
/**
* Delete metadata schema from the DSpace metadata registry
*
* @param schemaId
* Id of the metadata schema in DSpace.
* @param headers
* If you want to access to metadata schema under logged user into
* context. In headers must be set header "rest-dspace-token"
* with passed token from login method.
* @return Return response code OK(200) if was everything all right.
* Otherwise return NOT_FOUND(404) if was id of metadata schema is incorrect.
* Or (UNAUTHORIZED)401 if was problem with permission to metadata schema.
* @throws WebApplicationException
* It is throw when was problem with creating context or problem
* with database reading or deleting. Or problem with deleting
* metadata schema caused by IOException or authorization.
*/
@DELETE
@Path("/schema/{schema_id}")
public Response deleteSchema(@PathParam("schema_id") Integer schemaId, @QueryParam("userIP") String user_ip,
@QueryParam("userAgent") String user_agent, @QueryParam("xforwardedfor") String xforwardedfor,
@Context HttpHeaders headers, @Context HttpServletRequest request) throws WebApplicationException
{
log.info("Deleting metadata schema(id=" + schemaId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext(getUser(headers));
org.dspace.content.MetadataSchema dspaceSchema = metadataSchemaService.find(context, schemaId);
if (dspaceSchema == null) {
log.error(String.format("Metadata Schema %d not found", schemaId));
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
writeStats(siteService.findSite(context), UsageEvent.Action.DELETE, user_ip, user_agent, xforwardedfor, headers,
request, context);
metadataSchemaService.delete(context, dspaceSchema);
context.complete();
}
catch (SQLException e)
{
processException("Could not delete metadata schema(id=" + schemaId + "), SQLException. Message:" + e, context);
}
catch (AuthorizeException e)
{
processException("Could not delete metadata schema(id=" + schemaId + "), AuthorizeException. Message:" + e, context);
}
catch (ContextException e)
{
processException("Could not delete metadata schema(id=" + schemaId + "), ContextException. Message:" + e.getMessage(),
context);
}
finally
{
processFinally(context);
}
log.info("Metadata schema(id=" + schemaId + ") was successfully deleted.");
return Response.status(Response.Status.OK).build();
}
}

View File

@@ -121,6 +121,28 @@ public class RestIndex {
"<ul>" +
"<li>GET /hierarchy - Return hierarchy of communities and collections in tree form. Each object is minimally populated (name, handle, id) for efficient retrieval.</li>" +
"</ul>" +
"<h2>Metadata and Schema Registry</h2>" +
"<ul>" +
"<li>GET /registries/schema - Return the list of metadata schemas in the registry</li>" +
"<li>GET /registries/schema/{schema_prefix} - Returns the specified metadata schema</li>" +
"<li>GET /registries/schema/{schema_prefix}/metadata-fields/{element} - Returns the metadata field within a schema with an unqualified element name</li>" +
"<li>GET /registries/schema/{schema_prefix}/metadata-fields/{element}/{qualifier} - Returns the metadata field within a schema with a qualified element name</li>" +
"<li>POST /registries/schema/ - Add a schema to the schema registry</li>" +
"<li>POST /registries/schema/{schema_prefix}/metadata-fields - Add a metadata field to the specified schema</li>" +
"<li>GET /registries/metadata-fields/{field_id} - Return the specified metadata field</li>" +
"<li>PUT /registries/metadata-fields/{field_id} - Update the specified metadata field</li>" +
"<li>DELETE /registries/metadata-fields/{field_id} - Delete the specified metadata field from the metadata field registry</li>" +
"<li>DELETE /registries/schema/{schema_id} - Delete the specified schema from the schema registry</li>" +
"</ul>" +
"<h2>Query/Reporting Tools</h2>" +
"<ul>" +
"<li>GET /reports - Return a list of report tools built on the rest api</li>" +
"<li>GET /reports/{nickname} - Return a redirect to a specific report</li>" +
"<li>GET /filters - Return a list of use case filters available for quality control reporting</li>" +
"<li>GET /filtered-collections - Return collections and item counts based on pre-defined filters</li>" +
"<li>GET /filtered-collections/{collection_id} - Return items and item counts for a collection based on pre-defined filters</li>" +
"<li>GET /filtered-items - Retrieve a set of items based on a metadata query and a set of filters</li>" +
"</ul>" +
"</body></html> ";
}

View File

@@ -0,0 +1,90 @@
/**
* 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.rest;
import java.net.URI;
import java.util.ArrayList;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.dspace.rest.common.Report;
/**
* Root of RESTful api. It provides login and logout. Also have method for
* printing every method which is provides by RESTful api.
*
* @author Terry Brady, Georgetown University
*
*/
@Path("/reports")
public class RestReports {
private static Logger log = Logger.getLogger(RestReports.class);
@javax.ws.rs.core.Context public static ServletContext servletContext;
public static final String REST_RPT_URL = "rest-report-url.";
/**
* Return html page with information about REST api. It contains methods all
* methods provide by REST api.
*
* @return HTML page which has information about all methods of REST api.
*/
@GET
@Produces(MediaType.APPLICATION_XML)
public Report[] reportIndex()
throws WebApplicationException {
ArrayList<Report> reports = new ArrayList<Report>();
Properties props = ConfigurationManager.getProperties("rest");
for(Object prop: props.keySet()) {
String sprop = prop.toString();
if (sprop.startsWith(REST_RPT_URL)) {
String nickname = sprop.substring(REST_RPT_URL.length());
String url = props.getProperty(sprop);
reports.add(new Report(nickname, url));
}
}
return reports.toArray(new Report[0]);
}
@Path("/{report_nickname}")
@GET
public Response customReport(@PathParam("report_nickname") String report_nickname, @Context UriInfo uriInfo)
throws WebApplicationException {
URI uri = null;
if (!report_nickname.isEmpty()){
log.info(String.format("Seeking report %s", report_nickname));
String url = ConfigurationManager.getProperty("rest", REST_RPT_URL + report_nickname);
log.info(String.format("URL for report %s found: [%s]", report_nickname, url));
if (!url.isEmpty()) {
uri = uriInfo.getBaseUriBuilder().path(url).build("");
log.info(String.format("URI for report %s", uri));
}
}
if (uri != null) {
return Response.temporaryRedirect(uri).build();
}
return Response.noContent().build();
}
}

View File

@@ -0,0 +1,178 @@
/**
* 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.rest.common;
import org.apache.log4j.Logger;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.CommunityService;
import org.dspace.content.service.ItemService;
import org.dspace.core.Context;
import org.dspace.rest.filter.ItemFilterSet;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Retrieve items within a collection that match a specific set of Item Filters of interest
*
* @author Terry Brady, Georgetown University
*/
@XmlRootElement(name = "filtered-collection")
public class FilteredCollection extends DSpaceObject {
protected CommunityService communityService = ContentServiceFactory.getInstance().getCommunityService();
protected CollectionService collectionService = ContentServiceFactory.getInstance().getCollectionService();
protected ItemService itemService = ContentServiceFactory.getInstance().getItemService();
Logger log = Logger.getLogger(FilteredCollection.class);
//Relationships
private Community parentCommunity;
private Community topCommunity;
private List<Community> parentCommunityList = new ArrayList<Community>();
private List<Item> items = new ArrayList<Item>();
private List<ItemFilter> itemFilters = new ArrayList<ItemFilter>();
//Calculated
private Integer numberItems;
private Integer numberItemsProcessed;
public FilteredCollection(){}
/**
* Evaluate a collection against of set of Item Filters
* @param collection
* DSpace Collection to evaluate
* @param filters
* String representing a list of filters
* @param expand
* @param context
* @param limit
* @param offset
* @throws SQLException
* @throws WebApplicationException
*/
public FilteredCollection(org.dspace.content.Collection collection, String filters, String expand, Context context, Integer limit, Integer offset) throws SQLException, WebApplicationException{
super(collection);
setup(collection, expand, context, limit, offset, filters);
}
private void setup(org.dspace.content.Collection collection, String expand, Context context, Integer limit, Integer offset, String filters) throws SQLException{
List<String> expandFields = new ArrayList<String>();
if(expand != null) {
expandFields = Arrays.asList(expand.split(","));
}
if(expandFields.contains("parentCommunityList") || expandFields.contains("all")) {
List<org.dspace.content.Community> parentCommunities = collection.getCommunities();
List<Community> parentCommunityList = new ArrayList<Community>();
for(org.dspace.content.Community parentCommunity : parentCommunities) {
parentCommunityList.add(new Community(parentCommunity, null, context));
}
this.setParentCommunityList(parentCommunityList);
} else {
this.addExpand("parentCommunityList");
}
if(expandFields.contains("parentCommunity") | expandFields.contains("all")) {
org.dspace.content.Community parentCommunity = collection.getCommunities().get(0);
this.setParentCommunity(new Community(parentCommunity, null, context));
} else {
this.addExpand("parentCommunity");
}
if(expandFields.contains("topCommunity") | expandFields.contains("all")) {
List<org.dspace.content.Community> parentCommunities = collection.getCommunities();
if (parentCommunities.size() > 0) {
org.dspace.content.Community topCommunity = parentCommunities.get(parentCommunities.size()-1);
this.setTopCommunity(new Community(topCommunity, null, context));
}
} else {
this.addExpand("topCommunity");
}
boolean reportItems = expandFields.contains("items") || expandFields.contains("all");
ItemFilterSet itemFilterSet = new ItemFilterSet(filters, reportItems);
this.setItemFilters(itemFilterSet.getItemFilters());
this.setNumberItemsProcessed(0);
if (itemFilters.size() > 0) {
Iterator<org.dspace.content.Item> childItems = itemService.findByCollection(context, collection, limit, offset);
int numProc = itemFilterSet.processSaveItems(context, childItems, items, reportItems, expand);
this.setNumberItemsProcessed(numProc);
}
if(!expandFields.contains("all")) {
this.addExpand("all");
}
this.setNumberItems(itemService.countItems(context, collection));
}
public Integer getNumberItems() {
return numberItems;
}
public void setNumberItems(Integer numberItems) {
this.numberItems = numberItems;
}
public Integer getNumberItemsProcessed() {
return numberItemsProcessed;
}
public void setNumberItemsProcessed(Integer numberItemsProcessed) {
this.numberItemsProcessed = numberItemsProcessed;
}
public Community getParentCommunity() {
return parentCommunity;
}
public void setParentCommunity(Community parentCommunity) {
this.parentCommunity = parentCommunity;
}
public Community getTopCommunity() {
return topCommunity;
}
public void setTopCommunity(Community topCommunity) {
this.topCommunity = topCommunity;
}
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
public void setParentCommunityList(List<Community> parentCommunityList) {
this.parentCommunityList = parentCommunityList;
}
public List<Community> getParentCommunityList() {
return parentCommunityList;
}
public List<ItemFilter> getItemFilters() {
return itemFilters;
}
public void setItemFilters(List<ItemFilter> itemFilters) {
this.itemFilters = itemFilters;
}
}

View File

@@ -86,10 +86,14 @@ public class Item extends DSpaceObject {
this.setLastModified(item.getLastModified().toString());
if(expandFields.contains("parentCollection") || expandFields.contains("all")) {
if (item.getOwningCollection() != null) {
this.parentCollection = new Collection(item.getOwningCollection(), null, context, null, null);
} else {
this.addExpand("parentCollection");
}
} else {
this.addExpand("parentCollection");
}
if(expandFields.contains("parentCollectionList") || expandFields.contains("all")) {
this.parentCollectionList = new ArrayList<Collection>();

View File

@@ -0,0 +1,265 @@
/**
* 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.rest.common;
import org.apache.log4j.Logger;
import org.dspace.core.Context;
import org.dspace.core.PluginManager;
import org.dspace.rest.filter.ItemFilterDefs;
import org.dspace.rest.filter.ItemFilterList;
import org.dspace.rest.filter.ItemFilterTest;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Use Case Item Filters that match a specific set of criteria.
* @author Terry Brady, Georgetown University
*/
@XmlRootElement(name = "item-filter")
public class ItemFilter {
static Logger log = Logger.getLogger(ItemFilter.class);
private ItemFilterTest itemFilterTest = null;
private String filterName = "";
private String title;
private String description;
private String category;
private String queryAnnotation;
private List<org.dspace.rest.common.Item> items = new ArrayList<org.dspace.rest.common.Item>();
private List<ItemFilterQuery> itemFilterQueries = new ArrayList<ItemFilterQuery>();
private List<MetadataEntry> metadata = new ArrayList<MetadataEntry>();
private Integer itemCount;
private Integer unfilteredItemCount;
private boolean saveItems = false;
public ItemFilter(){}
public static final String ALL_FILTERS = "all_filters";
public static final String ALL = "all";
public static List<ItemFilter> getItemFilters(String filters, boolean saveItems) {
LinkedHashMap<String,ItemFilterTest> availableTests = new LinkedHashMap<String,ItemFilterTest>();
for(ItemFilterList plugobj: (ItemFilterList[])PluginManager.getPluginSequence("rest", ItemFilterList.class)) {
for(ItemFilterTest defFilter: plugobj.getFilters()) {
availableTests.put(defFilter.getName(), defFilter);
}
}
List<ItemFilter> itemFilters = new ArrayList<ItemFilter>();
ItemFilter allFilters = new ItemFilter(ItemFilter.ALL_FILTERS, "Matches all specified filters",
"This filter includes all items that matched ALL specified filters", ItemFilterDefs.CAT_ITEM, saveItems);
if (filters.equals(ALL)) {
for(ItemFilterTest itemFilterDef: availableTests.values()) {
itemFilters.add(new ItemFilter(itemFilterDef, saveItems));
}
itemFilters.add(allFilters);
} else {
for(String filter: Arrays.asList(filters.split(","))) {
if (filter.equals(ItemFilter.ALL_FILTERS)) {
continue;
}
ItemFilterTest itemFilterDef;
itemFilterDef = availableTests.get(filter);
if (itemFilterDef == null) {
continue;
}
itemFilters.add(new ItemFilter(itemFilterDef, saveItems));
}
itemFilters.add(allFilters);
}
return itemFilters;
}
public static ItemFilter getAllFiltersFilter(List<ItemFilter> itemFilters) {
for(ItemFilter itemFilter: itemFilters) {
if (itemFilter.getFilterName().equals(ALL_FILTERS)) {
itemFilter.initCount();
return itemFilter;
}
}
return null;
}
public ItemFilter(ItemFilterTest itemFilterTest, boolean saveItems) throws WebApplicationException{
this.itemFilterTest = itemFilterTest;
this.saveItems = saveItems;
setup(itemFilterTest.getName(), itemFilterTest.getTitle(), itemFilterTest.getDescription(), itemFilterTest.getCategory());
}
public ItemFilter(String name, String title, String description, String category, boolean saveItems) throws WebApplicationException{
this.saveItems = saveItems;
setup(name, title, description, category);
}
private void setup(String name, String title, String description, String category) {
this.setFilterName(name);
this.setTitle(title);
this.setDescription(description);
this.setCategory(category);
}
private void initCount() {
if (itemCount == null) {
itemCount = 0;
}
if (unfilteredItemCount == null) {
unfilteredItemCount = 0;
}
}
public boolean hasItemTest() {
return itemFilterTest != null;
}
public void addItem(org.dspace.rest.common.Item restItem) {
initCount();
if (saveItems){
items.add(restItem);
}
itemCount++;
}
public boolean testItem(Context context, org.dspace.content.Item item, org.dspace.rest.common.Item restItem) {
initCount();
if (itemFilterTest == null) {
return false;
}
if (itemFilterTest.testItem(context, item)) {
addItem(restItem);
return true;
}
return false;
}
@XmlAttribute(name="filter-name")
public String getFilterName() {
return filterName;
}
public void setFilterName(String name) {
this.filterName = name;
}
@XmlAttribute(name="title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@XmlAttribute(name="category")
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
@XmlAttribute(name="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@XmlAttribute(name="query-annotation")
public String getQueryAnnotation() {
return queryAnnotation;
}
public void annotateQuery(List<String> query_field, List<String> query_op, List<String> query_val) throws SQLException {
int index = Math.min(query_field.size(), Math.min(query_op.size(), query_val.size()));
StringBuilder sb = new StringBuilder();
for(int i=0; i<index; i++) {
if (!sb.toString().isEmpty()) {
sb.append(" and ");
}
sb.append("(");
sb.append(query_field.get(i));
sb.append(" ");
sb.append(query_op.get(i));
sb.append(" ");
sb.append(query_val.get(i));
sb.append(")");
}
setQueryAnnotation(sb.toString());
}
public void setQueryAnnotation(String queryAnnotation) {
this.queryAnnotation = queryAnnotation;
}
@XmlAttribute(name="item-count")
public Integer getItemCount() {
return itemCount;
}
public void setItemCount(Integer itemCount) {
this.itemCount = itemCount;
}
@XmlAttribute(name="unfiltered-item-count")
public Integer getUnfilteredItemCount() {
return unfilteredItemCount;
}
public void setUnfilteredItemCount(Integer unfilteredItemCount) {
this.unfilteredItemCount = unfilteredItemCount;
}
public List<org.dspace.rest.common.Item> getItems() {
return items;
}
public void setItems(List<org.dspace.rest.common.Item> items) {
this.items = items;
}
public List<ItemFilterQuery> getItemFilterQueries() {
return itemFilterQueries;
}
public void setItemFilterQueries(List<ItemFilterQuery> itemFilterQueries) {
this.itemFilterQueries = itemFilterQueries;
}
public void initMetadataList(List<String> show_fields) {
if (show_fields != null) {
List<MetadataEntry> returnFields = new ArrayList<MetadataEntry>();
for(String field: show_fields) {
returnFields.add(new MetadataEntry(field, null, null));
}
setMetadata(returnFields);
}
}
public List<MetadataEntry> getMetadata() {
return metadata;
}
@XmlElement(required = true)
public void setMetadata(List<MetadataEntry> metadata) {
this.metadata = metadata;
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.rest.common;
import org.apache.log4j.Logger;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Metadata Query for DSpace Items using the REST API
* @author Terry Brady, Georgetown University
*/
@XmlRootElement(name = "item-filter-query")
public class ItemFilterQuery {
Logger log = Logger.getLogger(ItemFilterQuery.class);
private String field = "";
private String operation = "";
private String value = "";
public ItemFilterQuery(){}
/**
* Construct a metadata query for DSpace items
* @param field
* Name of the metadata field to query
* @param operation
* Operation to perform on a metadata field
* @param value
* @throws WebApplicationException
*/
public ItemFilterQuery(String field, String operation, String value) throws WebApplicationException{
setup(field, operation, value);
}
private void setup(String field, String operation, String value) {
this.setField(field);
this.setOperation(operation);
this.setValue(value);
}
@XmlAttribute(name="field")
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
@XmlAttribute(name="operation")
public String getOperation() {
return operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
@XmlAttribute(name="value")
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,120 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.common;
import org.dspace.core.Context;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Metadata field representation
* @author Terry Brady, Georgetown University.
*/
@XmlRootElement(name = "field")
public class MetadataField {
private int fieldId;
private String name;
private String element;
private String qualifier;
private String description;
private MetadataSchema parentSchema;
@XmlElement(required = true)
private ArrayList<String> expand = new ArrayList<String>();
public MetadataField(){}
public MetadataField(org.dspace.content.MetadataSchema schema, org.dspace.content.MetadataField field, String expand, Context context) throws SQLException, WebApplicationException{
setup(schema, field, expand, context);
}
private void setup(org.dspace.content.MetadataSchema schema, org.dspace.content.MetadataField field, String expand, Context context) throws SQLException{
List<String> expandFields = new ArrayList<String>();
if(expand != null) {
expandFields = Arrays.asList(expand.split(","));
}
StringBuilder sb = new StringBuilder();
sb.append(schema.getName());
sb.append(".");
sb.append(field.getElement());
if (field.getQualifier()!=null) {
sb.append(".");
sb.append(field.getQualifier());
}
this.setName(sb.toString());
this.setFieldId(field.getFieldID());
this.setElement(field.getElement());
this.setQualifier(field.getQualifier());
this.setDescription(field.getScopeNote());
if (expandFields.contains("parentSchema") || expandFields.contains("all")) {
this.addExpand("parentSchema");
parentSchema = new MetadataSchema(schema, "", context);
}
}
public void setParentSchema(MetadataSchema schema) {
this.parentSchema = schema;
}
public MetadataSchema getParentSchema() {
return this.parentSchema;
}
public void setFieldId(int fieldId) {
this.fieldId = fieldId;
}
public void setName(String name) {
this.name = name;
}
public void setElement(String element) {
this.element = element;
}
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
public void setDescription(String description) {
this.description = description;
}
public int getFieldId() {
return fieldId;
}
public String getName() {
return name;
}
public String getQualifier() {
return qualifier;
}
public String getElement() {
return element;
}
public String getDescription() {
return description;
}
public List<String> getExpand() {
return expand;
}
public void setExpand(ArrayList<String> expand) {
this.expand = expand;
}
public void addExpand(String expandableAttribute) {
this.expand.add(expandableAttribute);
}
}

View File

@@ -0,0 +1,99 @@
/**
* 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.rest.common;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.core.Context;
import javax.ws.rs.WebApplicationException;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Metadata schema representation
* @author Terry Brady, Georgetown University.
*/
@XmlRootElement(name = "schema")
public class MetadataSchema {
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
private int schemaID;
private String prefix;
private String namespace;
@XmlElement(required = true)
private ArrayList<String> expand = new ArrayList<String>();
@XmlElement(name = "fields", required = true)
private List<MetadataField> fields = new ArrayList<MetadataField>();
public MetadataSchema(){}
public MetadataSchema(org.dspace.content.MetadataSchema schema, String expand, Context context) throws SQLException, WebApplicationException{
setup(schema, expand, context);
}
private void setup(org.dspace.content.MetadataSchema schema, String expand, Context context) throws SQLException{
List<String> expandFields = new ArrayList<String>();
if(expand != null) {
expandFields = Arrays.asList(expand.split(","));
}
this.setSchemaID(schema.getSchemaID());
this.setPrefix(schema.getName());
this.setNamespace(schema.getNamespace());
if (expandFields.contains("fields") || expandFields.contains("all")) {
List<org.dspace.content.MetadataField> fields = metadataFieldService.findAllInSchema(context, schema);
this.addExpand("fields");
for(org.dspace.content.MetadataField field: fields) {
this.fields.add(new MetadataField(schema, field, "", context));
}
}
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getPrefix() {
return prefix;
}
public String getNamespace() {
return namespace;
}
public int getSchemaID()
{
return this.schemaID;
}
public void setSchemaID(int schemaID)
{
this.schemaID = schemaID;
}
public List<MetadataField> getMetadataFields() {
return fields;
}
public List<String> getExpand() {
return expand;
}
public void setExpand(ArrayList<String> expand) {
this.expand = expand;
}
public void addExpand(String expandableAttribute) {
this.expand.add(expandableAttribute);
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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.rest.common;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Used to handle/determine status of REST API.
* Mainly to know your authentication status
*
*/
@XmlRootElement(name = "report")
public class Report
{
private String nickname;
private String url;
public Report() {
setNickname("na");
setUrl("");
}
public Report(String nickname, String url) {
setNickname(nickname);
setUrl(url);
}
public String getUrl()
{
return this.url;
}
public String getNickname()
{
return this.nickname;
}
public void setUrl(String url)
{
this.url = url;
}
public void setNickname(String nickname)
{
this.nickname = nickname;
}
}

View File

@@ -0,0 +1,144 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.rest.filter;
import org.dspace.content.Item;
import org.dspace.core.Context;
/**
* Define the set of use cases for filtering items of interest through the REST API.
* @author Terry Brady, Georgetown University
*
*/
public class ItemFilterDefs implements ItemFilterList {
public static final String CAT_ITEM = "Item Property Filters";
public static final String CAT_BASIC = "Basic Bitstream Filters";
public static final String CAT_MIME = "Bitstream Filters by MIME Type";
private enum EnumItemFilterDefs implements ItemFilterTest {
is_item("Is Item - always true", null, CAT_ITEM) {
public boolean testItem(Context context, Item item) {
return true;
}
},
is_not_withdrawn("Withdrawn Items", null, CAT_ITEM) {
public boolean testItem(Context context, Item item) {
return item.isWithdrawn();
}
},
is_withdrawn("Available Items - Not Withdrawn", null, CAT_ITEM) {
public boolean testItem(Context context, Item item) {
return !item.isWithdrawn();
}
},
is_discoverable("Discoverable Items - Not Private", null, CAT_ITEM) {
public boolean testItem(Context context, Item item) {
return item.isDiscoverable();
}
},
is_not_discoverable("Not Discoverable - Private Item", null, CAT_ITEM) {
public boolean testItem(Context context, Item item) {
return !item.isDiscoverable();
}
},
has_multiple_originals("Item has Multiple Original Bitstreams", null, CAT_BASIC) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstream(item) > 1;
}
},
has_no_originals("Item has No Original Bitstreams", null, CAT_BASIC) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstream(item) == 0;
}
},
has_one_original("Item has One Original Bitstream", null, CAT_BASIC) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstream(item) == 1;
}
},
has_doc_original("Item has a Doc Original Bitstream (PDF, Office, Text, HTML, XML, etc)", null, CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes()) > 0;
}
},
has_image_original("Item has an Image Original Bitstream", null, CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image") > 0;
}
},
has_unsupp_type("Has Other Bitstream Types (not Doc or Image)", null, ItemFilterDefs.CAT_MIME) {
public boolean testItem(Context context, Item item) {
int bitCount = ItemFilterUtil.countOriginalBitstream(item);
if (bitCount == 0) {
return false;
}
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
int imgCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image");
return (bitCount - docCount - imgCount) > 0;
}
},
has_mixed_original("Item has multiple types of Original Bitstreams (Doc, Image, Other)", null, CAT_MIME) {
public boolean testItem(Context context, Item item) {
int countBit = ItemFilterUtil.countOriginalBitstream(item);
if (countBit <= 1) return false;
int countDoc = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
if (countDoc > 0) {
return countDoc != countBit;
}
int countImg = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image");
if (countImg > 0) {
return countImg != countBit;
}
return false;
}
},
has_pdf_original("Item has a PDF Original Bitstream", null, CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstreamMime(context, item, "application/pdf") > 0;
}
},
has_jpg_original("Item has JPG Original Bitstream", null, CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countOriginalBitstreamMime(context, item, "image/jpeg") > 0;
}
},
;
private String title = null;
private String description = null;
private EnumItemFilterDefs(String title, String description, String category) {
this.title = title;
this.description = description;
this.category = category;
}
private EnumItemFilterDefs() {
this(null, null, null);
}
public String getName() {
return name();
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
private String category = null;
public String getCategory() {
return category;
}
}
public ItemFilterDefs() {
}
public ItemFilterTest[] getFilters() {
return EnumItemFilterDefs.values();
}
}

View File

@@ -0,0 +1,162 @@
/**
* 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.rest.filter;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.dspace.content.Item;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
/**
* Define the set of use cases for filtering items of interest through the REST API.
* @author Terry Brady, Georgetown University
*
*/
public class ItemFilterDefsMeta implements ItemFilterList {
protected static ItemService itemService = ContentServiceFactory.getInstance().getItemService();
static Logger log = Logger.getLogger(ItemFilterDefsMeta.class);
public static final String CAT_META_GEN = "General Metadata Filters";
public static final String CAT_META_SPEC = "Specific Metadata Filters";
public static final String CAT_MOD = "Recently Modified";
private enum EnumItemFilterDefs implements ItemFilterTest {
has_no_title("Has no dc.title", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
return itemService.getMetadataByMetadataString(item, "dc.title").size() == 0;
}
},
has_no_uri("Has no dc.identifier.uri", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
return itemService.getMetadataByMetadataString(item, "dc.identifier.uri").size() == 0;
}
},
has_mult_uri("Has multiple dc.identifier.uri", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
return itemService.getMetadataByMetadataString(item, "dc.identifier.uri").size() > 1;
}
},
has_compound_subject("Has compound subject", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-compound-subject");
return ItemFilterUtil.hasMetadataMatch(item, "dc.subject.*", Pattern.compile(regex));
}
},
has_compound_author("Has compound author", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-compound-author");
return ItemFilterUtil.hasMetadataMatch(item, "dc.creator,dc.contributor.author", Pattern.compile(regex));
}
},
has_empty_metadata("Has empty metadata", null, CAT_META_GEN) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile("^\\s*$"));
}
},
has_unbreaking_metadata("Has unbreaking metadata", null, CAT_META_GEN) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-unbreaking");
return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex));
}
},
has_long_metadata("Has long metadata field", null, CAT_META_GEN) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-long");
return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex));
}
},
has_xml_entity("Has XML entity in metadata", null, CAT_META_GEN) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-xml-entity");
return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex));
}
},
has_non_ascii("Has non-ascii in metadata", null, CAT_META_GEN) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-non-ascii");
return ItemFilterUtil.hasMetadataMatch(item, "*", Pattern.compile(regex));
}
},
has_desc_url("Has url in description", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-url");
return ItemFilterUtil.hasMetadataMatch(item, "dc.description.*", Pattern.compile(regex));
}
},
has_fulltext_provenance("Has fulltext in provenance", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-fulltext");
return ItemFilterUtil.hasMetadataMatch(item, "dc.description.provenance", Pattern.compile(regex));
}
},
no_fulltext_provenance("Doesn't have fulltext in provenance", null, CAT_META_SPEC) {
public boolean testItem(Context context, Item item) {
String regex = ConfigurationManager.getProperty("rest","rest-report-regex-fulltext");
return !ItemFilterUtil.hasMetadataMatch(item, "dc.description.provenance", Pattern.compile(regex));
}
},
mod_last_day("Modified in last 1 day", null, CAT_MOD) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.recentlyModified(item, 1);
}
},
mod_last_7_days("Modified in last 7 days", null, CAT_MOD) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.recentlyModified(item, 7);
}
},
mod_last_30_days("Modified in last 30 days", null, CAT_MOD) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.recentlyModified(item, 30);
}
},
mod_last_90_days("Modified in last 60 days", null, CAT_MOD) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.recentlyModified(item, 60);
}
},
;
private String title = null;
private String description = null;
private EnumItemFilterDefs(String title, String description, String category) {
this.title = title;
this.description = description;
this.category = category;
}
private EnumItemFilterDefs() {
this(null, null, null);
}
public String getName() {
return name();
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
private String category = null;
public String getCategory() {
return category;
}
}
public ItemFilterDefsMeta() {
}
public ItemFilterTest[] getFilters() {
return EnumItemFilterDefs.values();
}
}

View File

@@ -0,0 +1,185 @@
/**
* 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.rest.filter;
import java.util.List;
import org.dspace.content.Item;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.rest.filter.ItemFilterUtil.BundleName;
/**
* Define the set of use cases for filtering items of interest through the REST API.
* @author Terry Brady, Georgetown University
*
*/
public class ItemFilterDefsMisc implements ItemFilterList {
public static final String CAT_MISC = "Bitstream Bundle Filters";
public static final String CAT_MIME_SUPP = "Supported MIME Type Filters";
private enum EnumItemFilterDefs implements ItemFilterTest {
has_only_supp_image_type("Item Image Bitstreams are Supported", null, CAT_MIME_SUPP) {
public boolean testItem(Context context, Item item) {
int imageCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image/");
if (imageCount == 0) {
return false;
}
int suppImageCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getSupportedImageMimeTypes());
return (imageCount == suppImageCount);
}
},
has_unsupp_image_type("Item has Image Bitstream that is Unsupported", null, CAT_MIME_SUPP) {
public boolean testItem(Context context, Item item) {
int imageCount = ItemFilterUtil.countOriginalBitstreamMimeStartsWith(context, item, "image/");
if (imageCount == 0) {
return false;
}
int suppImageCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getSupportedImageMimeTypes());
return (imageCount - suppImageCount) > 0;
}
},
has_only_supp_doc_type("Item Document Bitstreams are Supported", null, CAT_MIME_SUPP) {
public boolean testItem(Context context, Item item) {
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
if (docCount == 0) {
return false;
}
int suppDocCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getSupportedDocumentMimeTypes());
return docCount == suppDocCount;
}
},
has_unsupp_doc_type("Item has Document Bitstream that is Unsupported", null, CAT_MIME_SUPP) {
public boolean testItem(Context context, Item item) {
int docCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
if (docCount == 0) {
return false;
}
int suppDocCount = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getSupportedDocumentMimeTypes());
return (docCount - suppDocCount) > 0;
}
},
has_small_pdf("Has unusually small PDF", null, ItemFilterDefs.CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countBitstreamSmallerThanMinSize(context, BundleName.ORIGINAL, item, "application/pdf", "rest-report-pdf-min-size") > 0;
}
},
has_large_pdf("Has unusually large PDF", null, ItemFilterDefs.CAT_MIME) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countBitstreamLargerThanMaxSize(context, BundleName.ORIGINAL, item, "application/pdf", "rest-report-pdf-max-size") > 0;
}
},
has_unsupported_bundle("Has bitstream in an unsuppored bundle", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
String bundleList = ConfigurationManager.getProperty("rest","rest-report-supp-bundles");
return ItemFilterUtil.hasUnsupportedBundle(item, bundleList);
}
},
has_small_thumbnail("Has unusually small thumbnail", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countBitstreamSmallerThanMinSize(context, BundleName.THUMBNAIL, item, "image/jpeg", "rest-report-thumbnail-min-size") > 0;
}
},
has_doc_without_text("Has document bitstream without TEXT item", null, ItemFilterDefs.CAT_MIME) {
public boolean testItem(Context context, Item item) {
int countDoc = ItemFilterUtil.countOriginalBitstreamMime(context, item, ItemFilterUtil.getDocumentMimeTypes());
if (countDoc == 0) {
return false;
}
int countText = ItemFilterUtil.countBitstream(BundleName.TEXT, item);
return countDoc > countText;
}
},
has_original_without_thumbnail("Has original bitstream without thumbnail", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
int countBit = ItemFilterUtil.countOriginalBitstream(item);
if (countBit == 0) {
return false;
}
int countThumb = ItemFilterUtil.countBitstream(BundleName.THUMBNAIL, item);
return countBit > countThumb;
}
},
has_invalid_thumbnail_name("Has invalid thumbnail name (assumes one thumbnail for each original)", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
List<String> originalNames = ItemFilterUtil.getBitstreamNames(BundleName.ORIGINAL, item);
List<String> thumbNames = ItemFilterUtil.getBitstreamNames(BundleName.THUMBNAIL, item);
if (thumbNames.size() != originalNames.size()) {
return false;
}
for(String name: originalNames) {
if (!thumbNames.contains(name+".jpg")) {
return true;
}
}
return false;
}
},
has_non_generated_thumb("Has non generated thumbnail", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
String generatedThumbDesc = ConfigurationManager.getProperty("rest","rest-report-gen-thumbnail-desc");
int countThumb = ItemFilterUtil.countBitstream(BundleName.THUMBNAIL, item);
if (countThumb == 0) {
return false;
}
int countGen = ItemFilterUtil.countBitstreamByDesc(BundleName.THUMBNAIL, item, generatedThumbDesc);
return (countThumb > countGen);
}
},
no_license("Doesn't have a license", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
return ItemFilterUtil.countBitstream(BundleName.LICENSE, item) == 0;
}
},
has_license_documentation("Has documentation in the license bundle", null, CAT_MISC) {
public boolean testItem(Context context, Item item) {
List<String> names = ItemFilterUtil.getBitstreamNames(BundleName.LICENSE, item);
for(String name: names) {
if (!name.equals("license.txt")) {
return true;
}
}
return false;
}
},
;
private String title = null;
private String description = null;
private EnumItemFilterDefs(String title, String description, String category) {
this.title = title;
this.description = description;
this.category = category;
}
private EnumItemFilterDefs() {
this(null, null, null);
}
public String getName() {
return name();
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
private String category = null;
public String getCategory() {
return category;
}
}
public ItemFilterDefsMisc() {
}
public ItemFilterTest[] getFilters() {
return EnumItemFilterDefs.values();
}
}

View File

@@ -0,0 +1,126 @@
/**
* 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.rest.filter;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.rest.filter.ItemFilterUtil.BundleName;
/**
* Define the set of use cases for filtering items of interest through the REST API.
* @author Terry Brady, Georgetown University
*
*/
public class ItemFilterDefsPerm implements ItemFilterList {
protected static AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
public static final String CAT_PERM = "Perimission Filters";
private static Logger log = Logger.getLogger(ItemFilterDefsPerm.class);
public ItemFilterDefsPerm(){
}
public enum EnumItemFilterPermissionDefs implements ItemFilterTest {
has_restricted_original("Item has Restricted Original Bitstream",
"Item has at least one original bitstream that is not accessible to Anonymous user", CAT_PERM) {
public boolean testItem(Context context, Item item) {
try {
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(BundleName.ORIGINAL)) {
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
if (!authorizeService.authorizeActionBoolean(getAnonContext(), bit, org.dspace.core.Constants.READ)) {
return true;
}
}
}
} catch (SQLException e) {
ItemFilterDefsPerm.log.warn("SQL Exception testing original bitstream access " + e.getMessage(), e);
}
return false;
}
},
has_restricted_thumbnail("Item has Restricted Thumbnail",
"Item has at least one thumbnail that is not accessible to Anonymous user", CAT_PERM) {
public boolean testItem(Context context, Item item) {
try {
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(BundleName.THUMBNAIL)) {
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
if (!authorizeService.authorizeActionBoolean(getAnonContext(), bit, org.dspace.core.Constants.READ)) {
return true;
}
}
}
} catch (SQLException e) {
ItemFilterDefsPerm.log.warn("SQL Exception testing thumbnail bitstream access " + e.getMessage(), e);
}
return false;
}
},
has_restricted_metadata("Item has Restricted Metadata",
"Item has metadata that is not accessible to Anonymous user", CAT_PERM) {
public boolean testItem(Context context, Item item) {
try {
return !authorizeService.authorizeActionBoolean(getAnonContext(), item, org.dspace.core.Constants.READ);
} catch (SQLException e) {
ItemFilterDefsPerm.log.warn("SQL Exception testing item metadata access " + e.getMessage(), e);
return false;
}
}
},
;
private static Context anonContext;
private static Context getAnonContext() {
if (anonContext == null) {
anonContext = new Context();
}
return anonContext;
}
private String title = null;
private String description = null;
private EnumItemFilterPermissionDefs(String title, String description, String category) {
this.title = title;
this.description = description;
this.category = category;
}
private EnumItemFilterPermissionDefs() {
this(null, null, null);
}
public String getName() {
return name();
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
private String category = null;
public String getCategory() {
return category;
}
}
@Override
public ItemFilterTest[] getFilters() {
return EnumItemFilterPermissionDefs.values();
}
}

View File

@@ -0,0 +1,12 @@
/**
* 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.rest.filter;
public interface ItemFilterList {
public ItemFilterTest[] getFilters();
}

View File

@@ -0,0 +1,148 @@
/**
* 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.rest.filter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import org.apache.log4j.Logger;
import org.dspace.authorize.factory.AuthorizeServiceFactory;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.core.Context;
import org.dspace.rest.common.Item;
import org.dspace.rest.common.ItemFilter;
/**
* The set of Item Filter Use Cases to apply to a collection of items.
*
* @author Terry Brady, Georgetown University
*
*/
public class ItemFilterSet {
protected AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService();
static Logger log = Logger.getLogger(ItemFilterSet.class);
private List<ItemFilter> itemFilters;
private ItemFilter allFiltersFilter;
/**
* Construct a set of Item Filters identified by a list string.
*
* @param filterList
* Comma separated list of filter names to include
* @see ItemFilter.ALL to retrieve all filters
* @param reportItems
* If true, return item details. If false, return only counts of items.
*
* @author Terry Brady, Georgetown University
*
*/
public ItemFilterSet(String filterList, boolean reportItems) {
log.debug(String.format("Create ItemFilterSet: %s", filterList));
itemFilters = ItemFilter.getItemFilters(filterList, reportItems);
allFiltersFilter = ItemFilter.getAllFiltersFilter(itemFilters);
}
/**
* Get the special filter that represents the intersection of all items in the Item Filter Set.
* @return the special Item Filter that contains items that satisfied every other Item Filter in the Item Filter Set
*/
public ItemFilter getAllFiltersFilter() {
return allFiltersFilter;
}
/**
* Evaluate an item against the use cases in the Item Filter Set.
*
* If an item satisfies all items in the Item Filter Set, it should also ve added to the special all items filter.
*
* @param context
* Active DSpace Context
* @param item
* DSpace Object to evaluate
* @param restItem
* REST representation of the DSpace Object being evaluated
*/
public void testItem(Context context, org.dspace.content.Item item, Item restItem) {
boolean bAllTrue = true;
for(ItemFilter itemFilter: itemFilters) {
if (itemFilter.hasItemTest()) {
bAllTrue &= itemFilter.testItem(context, item, restItem);
}
}
if (bAllTrue && allFiltersFilter != null) {
allFiltersFilter.addItem(restItem);
}
}
/**
* Get all of the Item Filters initialized into the Item Filter Sest
* @return a list of Item Filters initialized into the Item Filter Set
*/
public List<ItemFilter> getItemFilters() {
return itemFilters;
}
/**
* Evaluate a set of Items against the Item Filters in the Item Filter Set
* Current DSpace Context
* @param childItems
* Collection of Items to Evaluate
* @param save
* If true, save the details of each item that is evaluated
* @param expand
* List of item details to include in the results
* @return
* The number of items evaluated
* @throws WebApplicationException
* @throws SQLException
*/
public int processSaveItems(Context context, Iterator<org.dspace.content.Item> childItems, boolean save, String expand) throws WebApplicationException, SQLException {
return processSaveItems(context, childItems, new ArrayList<Item>(), save, expand);
}
/**
* Evaluate a set of Items against the Item Filters in the Item Filter Set
* Current DSpace Context
* @param childItems
* Collection of Items to Evaluate
* @param items
* List of items to contain saved results
* @param save
* If true, save the details of each item that is evaluated
* @param expand
* List of item details to include in the results
* @return
* The number of items evaluated
* @throws WebApplicationException
* @throws SQLException
*/
public int processSaveItems(Context context, Iterator<org.dspace.content.Item> childItems, List<Item> items, boolean save, String expand) throws WebApplicationException, SQLException {
int count = 0;
while(childItems.hasNext()) {
count++;
org.dspace.content.Item item = childItems.next();
log.debug(item.getHandle() + " evaluate.");
if(authorizeService.authorizeActionBoolean(context, item, org.dspace.core.Constants.READ)) {
Item restItem = new Item(item, expand, context);
if(save) {
items.add(restItem);
}
testItem(context, item, restItem);
} else {
log.debug(item.getHandle() + " not authorized - not included in result set.");
}
}
return count;
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.rest.filter;
import org.dspace.content.Item;
import org.dspace.core.Context;
/**
* Item Filter Use Case Interface.
* Items will be evaluated against a set of filters.
*
* @author Terry Brady, Georgetown University
*
*/
public interface ItemFilterTest {
public String getName();
public String getTitle();
public String getDescription();
public String getCategory();
public boolean testItem(Context context, Item i);
}

View File

@@ -0,0 +1,257 @@
/**
* 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.rest.filter;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import com.ibm.icu.util.Calendar;
public class ItemFilterUtil {
protected static ItemService itemService = ContentServiceFactory.getInstance().getItemService();
static Logger log = Logger.getLogger(ItemFilterUtil.class);
public enum BundleName{ORIGINAL,TEXT,LICENSE,THUMBNAIL;}
static String getDocumentMimeTypes() {
return ConfigurationManager.getProperty("rest", "rest-report-mime-document");
}
static String getSupportedDocumentMimeTypes() {
return ConfigurationManager.getProperty("rest", "rest-report-mime-document-supported");
}
static String getSupportedImageMimeTypes() {
return ConfigurationManager.getProperty("rest", "rest-report-mime-document-image");
}
static int countOriginalBitstream(Item item) {
return countBitstream(BundleName.ORIGINAL, item);
}
static int countBitstream(BundleName bundleName, Item item) {
int count = 0;
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
count += bundle.getBitstreams().size();
}
return count;
}
static List<String> getBitstreamNames(BundleName bundleName, Item item) {
ArrayList<String> names = new ArrayList<String>();
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
names.add(bit.getName());
}
}
return names;
}
static int countOriginalBitstreamMime(Context context, Item item, String mimeList) {
return countBitstreamMime(context, BundleName.ORIGINAL, item, mimeList);
}
static int countBitstreamMime(Context context, BundleName bundleName, Item item, String mimeList) {
int count = 0;
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
for(String mime: mimeList.split(",")) {
try {
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
count++;
}
} catch (SQLException e) {
log.error("Get format error for bitstream " + bit.getName());
}
}
}
}
return count;
}
static int countBitstreamByDesc(BundleName bundleName, Item item, String descList) {
int count = 0;
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
for(String desc: descList.split(",")) {
String bitDesc = bit.getDescription();
if (bitDesc == null) {
continue;
}
if (bitDesc.equals(desc.trim())) {
count++;
}
}
}
}
return count;
}
static int countBitstreamSmallerThanMinSize(Context context, BundleName bundleName, Item item, String mimeList, String prop) {
long size = ConfigurationManager.getLongProperty("rest", prop);
int count = 0;
try {
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
for(String mime: mimeList.split(",")) {
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
if (bit.getSize() < size) {
count++;
}
}
}
}
}
} catch (SQLException e) {
}
return count;
}
static int countBitstreamLargerThanMaxSize(Context context, BundleName bundleName, Item item, String mimeList, String prop) {
long size = ConfigurationManager.getLongProperty("rest", prop);
int count = 0;
try {
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
for(String mime: mimeList.split(",")) {
if (bit.getFormat(context).getMIMEType().equals(mime.trim())) {
if (bit.getSize() > size) {
count++;
}
}
}
}
}
} catch (SQLException e) {
}
return count;
}
static int countOriginalBitstreamMimeStartsWith(Context context, Item item, String prefix) {
return countBitstreamMimeStartsWith(context, BundleName.ORIGINAL, item, prefix);
}
static int countBitstreamMimeStartsWith(Context context, BundleName bundleName, Item item, String prefix) {
int count = 0;
try {
for(Bundle bundle: item.getBundles()){
if (!bundle.getName().equals(bundleName.name())){
continue;
}
for(Bitstream bit: bundle.getBitstreams()) {
if (bit.getFormat(context).getMIMEType().startsWith(prefix)) {
count++;
}
}
}
} catch (SQLException e) {
}
return count;
}
static boolean hasUnsupportedBundle(Item item, String bundleList) {
if (bundleList == null) {
return false;
}
ArrayList<String> bundles = new ArrayList<String>();
for(String bundleName: bundleList.split(",")) {
bundles.add(bundleName.trim());
}
for(Bundle bundle: item.getBundles()) {
if (!bundles.contains(bundle.getName())) {
return true;
}
}
return false;
}
static boolean hasOriginalBitstreamMime(Context context, Item item, String mimeList) {
return hasBitstreamMime(context, BundleName.ORIGINAL, item, mimeList);
}
static boolean hasBitstreamMime(Context context, BundleName bundleName, Item item, String mimeList) {
return countBitstreamMime(context, bundleName, item, mimeList) > 0;
}
static boolean hasMetadataMatch(Item item, String fieldList, Pattern regex) {
if (fieldList.equals("*")) {
for(MetadataValue md: itemService.getMetadata(item, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY)){
if (regex.matcher(md.getValue()).matches()) {
return true;
}
}
} else {
for(String field: fieldList.split(",")) {
for(MetadataValue md: itemService.getMetadataByMetadataString(item, field.trim())){
if (regex.matcher(md.getValue()).matches()) {
return true;
}
}
}
}
return false;
}
static boolean hasOnlyMetadataMatch(Item item, String fieldList, Pattern regex) {
boolean matches = false;
if (fieldList.equals("*")) {
for(MetadataValue md: itemService.getMetadata(item, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY, org.dspace.content.Item.ANY)){
if (regex.matcher(md.getValue()).matches()) {
matches = true;
} else {
return false;
}
}
} else {
for(String field: fieldList.split(",")) {
for(MetadataValue md: itemService.getMetadataByMetadataString(item, field.trim())){
if (regex.matcher(md.getValue()).matches()) {
matches = true;
} else {
return false;
}
}
}
}
return matches;
}
static boolean recentlyModified(Item item, int days) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -days);
return cal.getTime().before(item.getLastModified());
}
}

View File

@@ -42,6 +42,11 @@
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
<!-- Security settings and mapping -->
<security-constraint>
<web-resource-collection>

View File

@@ -0,0 +1,66 @@
<!--
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/
-->
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<!-- sortable.js can be obtained at http://www.kryogenix.org/code/browser/sorttable/ -->
<!-- <script src="sorttable.js"></script> -->
<script src="restReport.js"></script>
<script src="restCollReport.js"></script>
<script src="spin.js"></script>
<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css"></link>
<link rel="stylesheet" type="text/css" href="restClient.css"></link>
<title>DSpace REST QC Client</title>
</head>
<body>
<a href="query.html">Query Tool</a>
<hr/>
<h1>DSpace REST QC Client</h1>
<div id='metadatadiv'>
<h3>Filters</h3>
<div>
<div>
<button disabled="disabled" class="activate showCollections">Show Collections</button>
</div>
<div id='filterdiv'>
</div>
<div>
<button disabled="disabled" class="activate showCollections">Show Collections</button>
</div>
</div>
<h3>Collection Report</h3>
<div>
<div><a class="this-search" href="#">URL to current search</a></div>
<div id="report">..</div>
</div>
<h3>Item Results</h3>
<div id="itemResults">
<h3>Additional data to return</h3>
<div id="queries-aux">
<button id="refresh-fields">Refresh Items</button>
<div id="show-fields"></div>
</div>
<h3>Results</h3>
<div id='itemdiv'>
<h3></h3>
<div><a class="this-search" href="#">URL to current search</a></div>
<div><button id="prev">Prev Page</button><button id="next">Next Page</button><button id="export">Export for Metadata Update</button></div>
<input type="hidden" id="ifilter"/>
<input type="hidden" id="icollection"/>
<table id="itemtable" class="sortable"></table>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,94 @@
<!--
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/
-->
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<!-- sortable.js can be obtained at http://www.kryogenix.org/code/browser/sorttable/ -->
<!-- <script src="sorttable.js"></script> -->
<script src="restReport.js"></script>
<script src="restQueryReport.js"></script>
<script src="spin.js"></script>
<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.css"></link>
<link rel="stylesheet" type="text/css" href="restClient.css"></link>
<title>DSpace REST Query Client</title>
</head>
<body>
<a href="index.html">Collection Filter</a>
<hr/>
<h1>DSpace REST Query Client</h1>
<div id="querydiv">
<div id='metadatadiv'>
<h3>Collection Selector</h3>
<div id="collSelector">
</div>
<h3>Metadata Field Queries</h3>
<div>
<fieldset id="predefqueries">
<label>Pre-defined Queries</label>
<select id="predefselect">
</select>
</fieldset>
<div id="queries">
</div>
<div>
<button class="query-button" disabled="disabled" class="activate">Run Item Query</button>
</div>
</div>
<h3>Limit/Paginate Queries</h3>
<div id="queries-page">
<div>
<label for="limit">Limit:</label>
<select id="limit" name="limit">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100" selected>100</option>
<option value="250">250</option>
<option value="1000">1000</option>
</select>
<label for="offset"> Offset:</label><input id="offset" name="offset" value="0"/>
</div>
<div>
<button class="query-button" disabled="disabled" class="activate">Run Item Query</button>
</div>
</div>
<h3>Filters</h3>
<div>
<div>
<button class="query-button" disabled="disabled" class="activate">Run Item Query</button>
</div>
<div id='filterdiv'>
</div>
<div>
<button class="query-button" disabled="disabled" class="activate">Run Item Query</button>
</div>
</div>
<h3>Additional data to return</h3>
<div id="queries-aux">
<div id="show-fields"></div>
<div>
<button class="query-button" disabled="disabled" class="activate">Run Item Query</button>
</div>
</div>
<h3>Item Results</h3>
<div id='itemdiv'>
<h3></h3>
<div><a class="this-search" href="#">URL to current search</a></div>
<div><button id="prev">Prev Page</button><button id="next">Next Page</button><button id="export">Export for Metadata Update</button></div>
<table id="itemtable" class="sortable">
</table>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,82 @@
/**
* 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/
*/
table {border-collapse: collapse;border-right:solid thin black;}
table td, table th {border: thin solid black; padding: 4px;}
tr.header {background-color: #EEEEEE;}
tr:hover td, tr:hover th {background-color: #DDDDDD;}
tr.even td {border-bottom: thin dotted black;}
tr.odd td {border-top: thin dotted black;}
td.even {background-color: #EEFFEE;}
td.head {background-color: #EEEEFF;}
td.num {text-align: right;}
td.link {text-decoration: underline; color: blue;}
td, th {width: 100px;}
td.error {color: red;background-color: yellow;}
td.title, th.title {width: 400px;}
td.mod, th.mod {width: 200px;}
#itemtable {width: 100%;}
#itemdiv {display: none;}
td.ititle, th.ititle {width: 600px;}
button:disabled {
background-color:gray;
}
input:read-only {
background-color: gray;
}
div.metadata {
padding: 2px;
width: 880px;
}
#metadatadiv select, #metadatadiv input {
padding: 2px;
margin: 4px;
}
#metadatadiv fieldset {
margin: 6px 15px;
width: 850px;
}
#metadatadiv label {
font-weight: bold;
}
#itemtable td div:not(:first-child) {
border-top: thin solid gray;
}
body {
min-height: 700px;
min-width: 700px;
}
tr.header th {
vertical-align: bottom;
}
a.partial::after {
content:" ?";
}
fieldset.catdiv {
border: thin solid black;
margin-bottom: 8px;
}
fieldset.catdiv div {
width: 380px;
float: left;
}
#collSel {
width: 90%;
}
#filterdiv label {
font-weight: normal;
}

View File

@@ -0,0 +1,344 @@
/*
* 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/
*/
var CollReport = function() {
Report.call(this);
//If sortable.js is included, uncomment the following
//this.hasSorttable = function(){return true;}
this.COLL_LIMIT = 25;
this.loadId = 0;
this.THREADS =11;
this.THREADSP = 11;
this.ACCIDX_COLL = 1;
this.ACCIDX_ITEM = 2;
this.getDefaultParameters = function(){
return {
"show_fields[]" : [],
filters : "",
limit : this.COUNT_LIMIT,
offset : 0,
icollection : "",
ifilter : "",
};
}
this.getCurrentParameters = function(){
return {
"show_fields[]" : this.myMetadataFields.getShowFields(),
filters : this.myFilters.getFilterList(),
limit : this.myReportParameters.getLimit(),
offset : this.myReportParameters.getOffset(),
icollection : $("#icollection").val(),
ifilter : $("#ifilter").val(),
};
}
var self = this;
this.init = function() {
this.baseInit();
$("#icollection").val(self.myReportParameters.params.icollection);
$("#ifilter").val(self.myReportParameters.params.ifilter);
$("#itemResults").accordion({
heightStyle: "content",
collapsible: true,
active: 1
});
}
this.myAuth.callback = function(data) {
self.createCollectionTable();
$(".showCollections").bind("click", function(){
self.loadData();
});
$("#refresh-fields").bind("click", function(){
self.drawItemTable($("#icollection").val(), $("#ifilter").val(), 0);
});
}
this.createCollectionTable = function() {
var self = this;
var tbl = $("<table/>");
tbl.attr("id","table");
$("#report").replaceWith(tbl);
var thead = $("<thead/>");
tbl.append(thead);
var tbody = $("<tbody/>");
tbl.append(tbody);
var tr = self.myHtmlUtil.addTr(thead).addClass("header");
self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric");
self.myHtmlUtil.addTh(tr, "Community").addClass("title");
self.myHtmlUtil.addTh(tr, "Collection").addClass("title");
var thn = self.myHtmlUtil.addTh(tr, "Num Items").addClass("sorttable_numeric");
self.myHtmlUtil.makeTotalCol(thn);
self.addCollectionRows(tbl, 0);
}
this.addCollectionRows = function(tbl, offset) {
var self = this;
$.ajax({
url: "/rest/filtered-collections",
data: {
limit : self.COLL_LIMIT,
expand : "topCommunity",
offset : offset,
},
dataType: "json",
headers: self.myAuth.getHeaders(),
success: function(data){
$.each(data, function(index, coll){
var tr = self.myHtmlUtil.addTr($("#table tbody"));
tr.attr("cid", self.getId(coll)).attr("index",index + offset).addClass(index % 2 == 0 ? "odd data" : "even data");
self.myHtmlUtil.addTd(tr, index + offset + 1).addClass("num");
var parval = "";
if ("topCommunity" in coll) {
var par = coll.topCommunity;
parval = par ? self.myHtmlUtil.getAnchor(par.name, "/handle/" + par.handle) : "";
} else if ("parCommunityList" in coll) {
var par = coll.parentCommunityList[coll.parentCommunityList.length-1];
parval = par ? self.myHtmlUtil.getAnchor(par.name, "/handle/" + par.handle) : "";
}
self.myHtmlUtil.addTd(tr, parval).addClass("title comm");
self.myHtmlUtil.addTdAnchor(tr, coll.name, "/handle/" + coll.handle).addClass("title");
var td = self.myHtmlUtil.addTd(tr, coll.numberItems).addClass("num").addClass("link");
td.on("click", function(){
self.drawItemTable(self.getId(coll),'',0);
$("#icollection").val(self.getId(coll));
$("#ifilter").val("");
});
});
if (data.length == self.COLL_LIMIT) {
self.addCollectionRows(tbl, offset + self.COLL_LIMIT);
return;
}
self.myHtmlUtil.totalCol(3);
$("#table").addClass("sortable");
if (self.hasSorttable()) {
sorttable.makeSortable($("#table")[0]);
}
if (self.myFilters.getFilterList() != "") {
self.loadData();
if ($("#icollection").val() != "") {
self.drawItemTable($("#icollection").val(), $("#ifilter").val(), 0);
}
}
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/filtered-collections "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
self.spinner.stop();
$(".showCollections").attr("disabled", false);
}
});
}
this.loadData = function() {
self.spinner.spin($("h1")[0]);
$(".showCollections").attr("disabled", true);
$("#metadatadiv").accordion("option", "active", self.ACCIDX_COLL);
self.loadId++;
$("td.datacol,th.datacol").remove();
$("#table tr.data").addClass("processing");
self.myFilters.filterString = self.myFilters.getFilterList();
self.doRow(0, self.THREADS, self.loadId);
}
this.doRow = function(row, threads, curLoadId) {
if (self.loadId != curLoadId) return;
var tr = $("tr[index="+row+"]");
if (!tr.is("*")){
return;
}
var cid = tr.attr("cid");
$.ajax({
url: "/rest/filtered-collections/"+cid,
data: {
limit : self.COUNT_LIMIT,
filters : self.myFilters.filterString,
},
dataType: "json",
headers: self.myAuth.getHeaders(),
success: function(data) {
var numItems = data.numberItems;
var numItemsProcessed = data.numberItemsProcessed;
$.each(data.itemFilters, function(index, itemFilter){
if (self.loadId != curLoadId) {
return;
}
var trh = $("#table tr.header");
var filterName = itemFilter["filter-name"];
var filterTitle = itemFilter.title == null ? filterName : itemFilter.title;
var icount = itemFilter["item-count"];
if (!trh.find("th."+filterName).is("*")) {
var th = self.myHtmlUtil.addTh(trh, filterTitle);
th.addClass(filterName).addClass("datacol").addClass("sorttable_numeric");
self.myHtmlUtil.makeTotalCol(th);
if (itemFilter.description != null) {
th.attr("title", itemFilter.description);
}
$("tr.data").each(function(){
var td = self.myHtmlUtil.addTd($(this), "");
td.addClass(filterName).addClass("num").addClass("datacol");
});
}
var td = tr.find("td."+filterName);
if (icount == null) {
icount = "0";
}
td.text(icount);
if (icount != "0") {
td.addClass("link");
td.on("click", function(){
self.drawItemTable(cid,filterName,0);
$("#icollection").val(cid);
$("#ifilter").val(filterName);
});
if (numItems != numItemsProcessed) {
td.addClass("partial");
td.attr("title", "Collection partially processed, item counts are incomplete");
}
}
});
tr.removeClass("processing");
if (!$("#table tr.processing").is("*")) {
if (self.hasSorttable()) {
$("#table").removeClass("sortable");
$("#table").addClass("sortable");
sorttable.makeSortable($("#table")[0]);
}
var colcount = $("#table tr th").length;
for(var i=4; i<colcount; i++) {
self.myHtmlUtil.totalCol(i);
}
self.spinner.stop();
$(".showCollections").attr("disabled", false);
return;
}
if (row % threads == 0 || threads == 1) {
for(var i=1; i<=threads; i++) {
self.doRow(row+i, threads, curLoadId);
}
}
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/filtered-collections "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
self.spinner.stop();
$(".showCollections").attr("disabled", false);
}
});
}
this.drawItemTable = function(cid, filter, offset) {
self = this;
self.spinner.spin($("h1")[0]);
$("#itemtable").replaceWith($('<table id="itemtable" class="sortable"></table>'));
var itbl = $("#itemtable");
//itbl.find("tr").remove("*");
var tr = self.myHtmlUtil.addTr(itbl).addClass("header");
self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric");
self.myHtmlUtil.addTh(tr, "id");
self.myHtmlUtil.addTh(tr, "Handle");
self.myHtmlUtil.addTh(tr, "dc.title" + self.getLangSuffix()).addClass("title");
var fields = $("#show-fields select").val();
if (fields != null) {
$.each(fields, function(index, field){
self.myHtmlUtil.addTh(tr, field + self.getLangSuffix());
});
}
var params = {
expand: fields == null ? "items" : "items,metadata",
limit: self.ITEM_LIMIT,
filters: filter,
offset: offset,
"show_fields[]" : fields,
}
$.ajax({
url: "/rest/filtered-collections/"+cid,
data: params,
dataType: "json",
headers: self.myAuth.getHeaders(),
success: function(data){
var source = filter == "" ? data.items : data.itemFilters[0].items;
$.each(source, function(index, item){
var tr = self.myHtmlUtil.addTr(itbl);
tr.addClass(index % 2 == 0 ? "odd data" : "even data");
self.myHtmlUtil.addTd(tr, offset+index+1).addClass("num");
self.myHtmlUtil.addTd(tr, self.getId(item));
self.myHtmlUtil.addTdAnchor(tr, item.handle, "/handle/" + item.handle);
self.myHtmlUtil.addTd(tr, item.name).addClass("ititle");
if (fields != null) {
$.each(fields, function(index, field){
var text = "";
$.each(item.metadata, function(mindex,mv){
if (mv.key == field) {
if (text != "") {
text += "<hr/>";
}
text += mv.value;
}
});
self.myHtmlUtil.addTd(tr, text);
});
}
});
self.displayItems(filter + " Items in " + data.name,
offset,
self.ITEM_LIMIT,
data.numberItems,
function(){self.drawItemTable(cid, filter, (offset - self.ITEM_LIMIT < 0) ? 0 : offset - self.ITEM_LIMIT);},
function(){self.drawItemTable(cid, filter, offset + self.ITEM_LIMIT);}
);
if (self.hasSorttable()){
sorttable.makeSortable(itbl[0]);
}
$("#metadatadiv").accordion("option", "active", self.ACCIDX_ITEM);
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/filtered-collections "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
self.spinner.stop();
$(".showCollections").attr("disabled", false);
}
});
}
//Ignore the first column containing a row number and the item handle
this.exportCol = function(colnum, col) {
var data = "";
if (colnum == 0) return "";
if (colnum == 2) return "";
data += (colnum == 1) ? "" : ",";
data += self.exportCell(col);
return data;
}
}
CollReport.prototype = Object.create(Report.prototype);
$(document).ready(function(){
var myReport=new CollReport();
myReport.init();
});

View File

@@ -0,0 +1,320 @@
/*
* 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/
*/
var QueryReport = function() {
Report.call(this);
//If sortable.js is included, uncomment the following
//this.hasSorttable = function(){return true;}
this.getDefaultParameters = function(){
return {
"collSel[]" : [],
"query_field[]" : [],
"query_op[]" : [],
"query_val[]" : [],
"show_fields[]" : [],
"filters" : "",
"limit" : this.ITEM_LIMIT,
"offset" : 0,
};
}
this.getCurrentParameters = function(){
var params = {
"query_field[]" : [],
"query_op[]" : [],
"query_val[]" : [],
"collSel[]" : ($("#collSel").val() == null) ? [""] : $("#collSel").val(),
limit : this.myReportParameters.getLimit(),
offset : this.myReportParameters.getOffset(),
"expand" : "parentCollection,metadata",
filters : this.myFilters.getFilterList(),
"show_fields[]" : this.myMetadataFields.getShowFields(),
};
$("select.query-tool,input.query-tool").each(function() {
var paramArr = params[$(this).attr("name")];
paramArr[paramArr.length] = $(this).val();
});
return params;
}
var self = this;
this.init = function() {
this.baseInit();
var communitySelector = new CommunitySelector(this, $("#collSelector"), this.myReportParameters.params["collSel[]"]);
}
this.initMetadataFields = function() {
this.myMetadataFields = new QueryableMetadataFields(self);
this.myMetadataFields.load();
}
this.myAuth.callback = function(data) {
$(".query-button").click(function(){self.runQuery();})
}
this.runQuery = function() {
this.spinner.spin($("body")[0]);
$("button").attr("disabled", true);
$.ajax({
url: "/rest/filtered-items",
data: this.getCurrentParameters(),
dataType: "json",
headers: self.myAuth.getHeaders(),
success: function(data){
data.metadata = $("#show-fields select").val();
self.drawItemFilterTable(data);
self.spinner.stop();
$("button").not("#next,#prev").attr("disabled", false);
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/filtered-items "+ status+ " " + errorThrown);
},
complete: function(xhr, status, errorThrown) {
self.spinner.stop();
$("button").not("#next,#prev").attr("disabled", false);
}
});
}
this.drawItemFilterTable = function(data) {
$("#itemtable").replaceWith($('<table id="itemtable" class="sortable"></table>'));
var itbl = $("#itemtable");
var tr = self.myHtmlUtil.addTr(itbl).addClass("header");
self.myHtmlUtil.addTh(tr, "Num").addClass("num").addClass("sorttable_numeric");
self.myHtmlUtil.addTh(tr, "id");
self.myHtmlUtil.addTh(tr, "collection");
self.myHtmlUtil.addTh(tr, "Item Handle");
self.myHtmlUtil.addTh(tr, "dc.title" + self.getLangSuffix());
var mdCols = [];
if (data.metadata) {
$.each(data.metadata, function(index, field) {
if (field != "") {
self.myHtmlUtil.addTh(tr,field + self.getLangSuffix()).addClass("returnFields");
mdCols[mdCols.length] = field;
}
});
}
$.each(data.items, function(index, item){
var tr = self.myHtmlUtil.addTr(itbl);
tr.addClass(index % 2 == 0 ? "odd data" : "even data");
self.myHtmlUtil.addTd(tr, self.myReportParameters.getOffset()+index+1).addClass("num");
self.myHtmlUtil.addTd(tr, self.getId(item));
if (item.parentCollection == null) {
self.myHtmlUtil.addTd(tr, "--");
} else {
self.myHtmlUtil.addTdAnchor(tr, item.parentCollection.name, "/handle/" + item.parentCollection.handle);
}
self.myHtmlUtil.addTdAnchor(tr, item.handle, "/handle/" + item.handle);
self.myHtmlUtil.addTd(tr, item.name);
for(var i=0; i<mdCols.length; i++) {
var key = mdCols[i];
var td = self.myHtmlUtil.addTd(tr, "");
$.each(item.metadata, function(colindex, metadata) {
if (metadata.key == key) {
if (metadata.value != null) {
var div = $("<div>"+metadata.value+"</div>");
td.append(div);
}
}
});
}
});
this.displayItems(data["query-annotation"],
this.myReportParameters.getOffset(),
this.myReportParameters.getLimit(),
data["unfiltered-item-count"],
function(){
self.myReportParameters.updateOffset(false);
self.runQuery();
},
function(){
self.myReportParameters.updateOffset(true);
self.runQuery();
}
);
if (this.hasSorttable()) {
sorttable.makeSortable(itbl[0]);
}
$("#metadatadiv").accordion("option", "active", $("#metadatadiv > h3").length - 1);
}
//Ignore the first column containing a row number and the item handle, get handle for the collection
this.exportCol = function(colnum, col) {
var data = "";
if (colnum == 0) return "";
if (colnum == 3) return "";
data += (colnum == 1) ? "" : ",";
if (colnum == 2) {
var anchor = $(col).find("a");
var href = anchor.is("a") ? anchor.attr("href").replace(/\/handle\//,"") : $(col).text();
data += "\"" + href + "\"";
} else {
data += self.exportCell(col); }
return data;
}
}
QueryReport.prototype = Object.create(Report.prototype);
$(document).ready(function(){
var myReport=new QueryReport();
myReport.init();
});
var QueryableMetadataFields = function(report) {
MetadataFields.call(this, report);
var self = this;
this.initFields = function(data, report) {
self.metadataSchemas = data;
var params = report.myReportParameters.params;
var fields = params["query_field[]"];
var ops = params["query_op[]"];
var vals = params["query_val[]"];
if (fields && ops && vals) {
if (fields.length == 0) {
self.drawFilterQuery("*","exists","");
} else {
for(var i=0; i<fields.length; i++) {
var op = ops.length > i ? ops[i] : "";
var val = vals.length > i ? vals[i] : "";
self.drawFilterQuery(fields[i],op,val);
}
}
}
self.drawShowFields(params["show_fields[]"]);
self.initQueries();
report.spinner.stop();
$(".query-button").attr("disabled", false);
}
this.initQueries = function() {
$("#predefselect")
.append($("<option value='new'>New Query</option>"))
.append($("<option value='q1'>Has No Title</option>"))
.append($("<option value='q2'>Has No dc.identifier.uri</option>"))
.append($("<option value='q3'>Has compound subject</option>"))
.append($("<option value='q4'>Has compound dc.contributor.author</option>"))
.append($("<option value='q5'>Has compound dc.creator</option>"))
.append($("<option value='q6'>Has URL in dc.description</option>"))
.append($("<option value='q7'>Has full text in dc.description.provenance</option>"))
.append($("<option value='q8'>Has non-full text in dc.description.provenance</option>"))
.append($("<option value='q9'>Has empty metadata</option>"))
.append($("<option value='q10'>Has unbreaking metadata in description</option>"))
.append($("<option value='q12'>Has XML entity in metadata</option>"))
.append($("<option value='q13'>Has non-ascii character in metadata</option>"))
.on("change",function(){
$("div.metadata").remove();
var val = $("#predefselect").val();
if (val == 'new') {
self.drawFilterQuery("","","");
} else if (val == 'q1') {
self.drawFilterQuery("dc.title","doesnt_exist","");
} else if (val == 'q2') {
self.drawFilterQuery("dc.identifier.uri","doesnt_exist","");
} else if (val == 'q3') {
self.drawFilterQuery("dc.subject.*","like","%;%");
} else if (val == 'q4') {
self.drawFilterQuery("dc.contributor.author","like","% and %");
} else if (val == 'q5') {
self.drawFilterQuery("dc.creator","like","% and %");
} else if (val == 'q6') {
self.drawFilterQuery("dc.description","matches","^.*(http://|https://|mailto:).*$");
} else if (val == 'q7') {
self.drawFilterQuery("dc.description.provenance","matches","^.*No\\. of bitstreams(.|\\r|\\n|\\r\\n)*\\.(PDF|pdf|DOC|doc|PPT|ppt|DOCX|docx|PPTX|pptx).*$");
} else if (val == 'q8') {
self.drawFilterQuery("dc.description.provenance","doesnt_match","^.*No\\. of bitstreams(.|\\r|\\n|\\r\\n)*\\.(PDF|pdf|DOC|doc|PPT|ppt|DOCX|docx|PPTX|pptx).*$");
} else if (val == 'q9') {
self.drawFilterQuery("*","matches","^\\s*$");
} else if (val == 'q10') {
self.drawFilterQuery("dc.description.*","matches","^.*[^\\s]{50,}.*$");
} else if (val == 'q12') {
self.drawFilterQuery("*","matches","^.*&#.*$");
} else if (val == 'q13') {
self.drawFilterQuery("*","matches","^.*[^[:ascii:]].*$");
}
});
}
this.drawFilterQuery = function(pField, pOp, pVal) {
var div = $("<div class='metadata'/>").appendTo("#queries");
var sel = $("<select class='query-tool' name='query_field[]'/>");
var opt = $("<option value='*'>Any Field</option>");
sel.append(opt);
$.each(self.metadataSchemas, function(index, schema){
$.each(schema.fields, function(findex, field) {
var name = field.name;
var parts = name.match(/^([^\.]+)\.([^\.]+)\.([^\.]+)$/);
if (parts == null) {
var wildname = name + ".*";
var opt = $("<option/>");
opt.attr("value",wildname).text(wildname);
sel.append(opt);
}
var opt = $("<option/>");
opt.attr("value",name).text(name);
sel.append(opt);
});
});
sel.val(pField);
div.append(sel);
var opsel = $("<select class='query-tool' name='query_op[]'/>");
$("<option>exists</option>").val("exists").appendTo(opsel);
$("<option>does not exist</option>").val("doesnt_exist").appendTo(opsel);
$("<option selected>equals</option>").val("equals").appendTo(opsel);
$("<option>does not equal</option>").val("not_equals").appendTo(opsel);
$("<option>like</option>").val("like").appendTo(opsel);
$("<option>not like</option>").val("not_like").appendTo(opsel);
$("<option>contains</option>").val("contains").appendTo(opsel);
$("<option>does not contain</option>").val("doesnt_contain").appendTo(opsel);
$("<option>matches</option>").val("matches").appendTo(opsel);
$("<option>does not match</option>").val("doesnt_match").appendTo(opsel);
opsel.val(pOp);
opsel.change(function(){
self.valField($(this));
});
div.append(opsel);
var input = $("<input class='query-tool' name='query_val[]'/>");
div.append(input);
input.val(pVal);
self.valField(opsel);
$("<button class='field_plus'>+</button>").appendTo(div).click(function(){
self.drawFilterQuery();
self.queryButtons();
});
$("<button class='field_minus'>-</button>").appendTo(div).click(function(){
$(this).parent("div.metadata").remove();
self.queryButtons();
});
self.queryButtons();
}
this.valField = function(valop) {
var val = valop.val();
var disableval = (val == "exists" || val == "not_exists");
var valinput = valop.parent("div.metadata").find("input[name='query_val[]']");
valinput.attr("readonly",disableval);
if (disableval) {
valinput.val("");
}
}
this.queryButtons = function() {
$("button.field_plus").attr("disabled",true);
$("button.field_plus:last").attr("disabled",false);
$("button.field_minus").attr("disabled",false);
if ($("button.field_minus").length == 1) {
$("button.field_minus").attr("disabled",true);
}
}
}
QueryableMetadataFields.prototype = Object.create(MetadataFields.prototype);

View File

@@ -0,0 +1,548 @@
/*
* 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/
*/
var Report = function() {
var self = this;
this.COLL_LIMIT = 500;
this.COUNT_LIMIT = 500;
this.ITEM_LIMIT = 100;
//Override this to return obj.id for DSpace 5 versions
this.getId = function(obj) {
return obj.uuid;
}
//Override this method is sortable.js has been included
this.hasSorttable = function() {
return false;
}
this.getDefaultParameters = function(){
return {};
}
this.getCurrentParameters = function(){
return {};
}
this.saveUrl = function() {
this.myReportParameters.saveAsUrl(this.getCurrentParameters());
}
this.getLoginPayload = function() {
//Example of how to pass credentials to the service
//return {"email" : "Email@my.edu","password" : "my-password"};
return undefined;
}
this.getLangSuffix = function(){
return "";
}
this.myAuth = new Auth(this.getLoginPayload());
this.myAuth.callback = function(data) {
self.spinner.stop();
}
this.myHtmlUtil = new HtmlUtil();
this.spinner = new Spinner({
lines: 13, // The number of lines to draw
length: 20, // The length of each line
width: 10, // The line thickness
radius: 30, // The radius of the inner circle
corners: 1, // Corner roundness (0..1)
rotate: 0, // The rotation offset
direction: 1, // 1: clockwise, -1: counterclockwise
color: '#000', // #rgb or #rrggbb or array of colors
speed: 1, // Rounds per second
trail: 60, // Afterglow percentage
shadow: false, // Whether to render a shadow
hwaccel: false, // Whether to use hardware acceleration
className: 'spinner', // The CSS class to assign to the spinner
zIndex: 2e9, // The z-index (defaults to 2000000000)
top: '400px', // Top position relative to parent
left: '600px' // Left position relative to parent
});
this.displayItems = function(itemsTitle, offset, limit, total, funcdec, funcinc) {
var count = $("#itemtable tr.data").length;
var last = offset + limit;
var suff = "";
if (total == null) {
last = offset + count;
suff = (count == limit) ? " of " + last + "+ " : " of " + last;
} else if (limit == total) {
//total may only be accurate to page size
suff = " of " + total + "+ ";
} else {
last = (last > total) ? total : last;
suff = " of " + total;
}
suff += " unfiltered; displaying " + count + " filtered" ;
itemsTitle += " (" + (offset+1) + " - " + last + suff + ")";
$("#prev,#next").attr("disabled",true);
$("#itemdiv h3").text(itemsTitle);
if (offset > 0) $("#prev").attr("disabled", false);
$("#prev").off("click").on("click", funcdec);
//in case of filters, always allow next
if (total == null) {
$("#next").attr("disabled", false);
} else if (offset + limit < total) {
$("#next").attr("disabled", false);
} else if (limit == total) {
//total may only be accurate to one page
$("#next").attr("disabled", false);
}
$("#next").off("click").on("click", funcinc);
}
this.myReportParameters = undefined;
this.myFilters = undefined;
this.myMetadataFields = undefined;
this.initMetadataFields = function() {
this.myMetadataFields = new MetadataFields(self);
this.myMetadataFields.load();
}
this.baseInit = function() {
this.myReportParameters = new ReportParameters(
this.getDefaultParameters(),
window.location.search.substr(1)
);
this.spinner.spin($("h1")[0]);
this.myFilters = new Filters(this.myReportParameters.params["filters"]);
this.initMetadataFields();
$("#metadatadiv").accordion({
heightStyle: "content",
collapsible: true,
active: $("#metadatadiv > h3").length - 2
});
$("#export").click(function(){
self.export($("#itemtable tr"));
});
$("a.this-search").on("click",function(){
self.saveUrl();
});
this.myFilters.createFilterTable(this.myReportParameters.params.filters);
this.myAuth.init();
}
this.export = function(rows) {
var itemdata = "data:text/csv;charset=utf-8,";
rows.each(function(rownum, row){
itemdata += (rownum == 0) ? "" : "\r\n";
$(row).find("td,th").each(function(colnum, col){
itemdata += self.exportCol(colnum, col);
});
});
var encodedUri = encodeURI(itemdata);
window.open(encodedUri);
}
//this is meant to be overridden for each report
this.exportCol = function(colnum, col) {
var data = "";
data += (colnum == 0) ? "" : ",";
data += self.exportCell(col);
return data;
}
this.exportCell = function(col) {
data = "\"";
$(col).contents().each(function(i, node){
if ($(node).is("hr")) {
data += "||";
} else {
data += $(node).text().replace(/\n/g," ").replace(/"/g,"\"\"");
if ($(node).is("div:not(:last-child)")) {
data += "||";
}
}
});
data += "\"";
return data;
}
this.init = function() {
this.baseInit();
}
}
var Auth = function(loginPayload) {
this.TOKEN = undefined;
this.callback = function(data) {
alert("auth complete " + this.TOKEN);
};
this.saveToken = function(data) {
this.TOKEN = data;
}
this.init = function() {
if (loginPayload == undefined) {
this.callback();
return;
}
var self = this;
$.ajax({
url : "/rest/login",
contentType : "application/json",
accepts : "application/json",
type : "POST",
data : JSON.stringify(loginPayload),
success : function(data){
self.saveToken(data);
self.callback();
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/login "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
self.callback();
}
});
}
this.getHeaders = function() {
var HEADERS = {};
if (this.TOKEN != null) {
HEADERS['rest-dspace-token'] = this.TOKEN;
}
return HEADERS;
}
}
var ReportParameters = function(defaultParams, prmstr) {
this.params = defaultParams;
if (prmstr == null) prmstr = "";
var prmarr = prmstr.split("&");
for ( var i = 0; i < prmarr.length; i++) {
var tmparr = prmarr[i].split("=");
var field = tmparr[0];
var val = decodeURIComponent(tmparr[1]);
var pval = this.params[field];
if ($.isArray(pval)) {
pval[pval.length] = val;
} else {
this.params[field] = val;
}
}
$("#limit").val(this.params.limit);
$("#offset").val(this.params.offset);
this.limit = this.params.limit;
this.offset = this.params.offset;
this.getOffset = function() {
var offset = $("#offset").val();
return $.isNumeric(offset) ? Number(offset) : this.offset;
}
this.getNextOffset = function() {
return this.getOffset() + this.getLimit();
}
this.getPrevOffset = function() {
var v = this.getOffset() - this.getLimit();
return v < 0 ? 0 : v;
}
this.getLimit = function() {
var limit = $("#limit").val();
return $.isNumeric(limit) ? Number(limit) : this.limit;
}
this.updateOffset = function(increment) {
var val = $("#offset").val();
var lim = $("#limit").val();
if ($.isNumeric(val) && $.isNumeric(lim)) {
if (increment) {
$("#offset").val(this.getNextOffset());
} else {
$("#offset").val(this.getPrevOffset());
}
}
}
this.saveAsUrl = function(params) {
var pstr = $.param(params).replace(/%5B%5D/g,"[]");
window.location.search = pstr;
}
}
var Filters = function() {
this.createFilterTable = function(filterList) {
self = this;
var paramFilterSel = filterList == null ? new Array() : filterList.split(",");
var categories = new Array();
self.addFilter("", categories, "General", "None", "De-select all filters", "none").click(
function(){
$("input.filter,input.all").attr("checked",false);
$("#filter-reload").attr("disabled", false);
}
);
self.addFilter("all", categories, "General", "All", "Show all filters", "all").click(
function(){
$("input.filter,input.none").attr("checked",false);
$("#filter-reload").attr("disabled", false);
}
);
$.getJSON(
"/rest/filters",
function(data){
$.each(data, function(index, filter){
var checkbox = self.addFilter(filter["filter-name"], categories, filter.category, filter.title, filter.description, "filter").click(
function(){
$("input.none,input.all").attr("checked",false);
$("#filter-reload").attr("disabled", false);
}
);
$.each(paramFilterSel, function(index, filtername){
if (filtername == filter["filter-name"]) {
checkbox.attr("checked", true);
}
});
});
}
);
}
this.addFilter = function(val, categories, category, title, description, cname) {
var catdiv = null;
for(var i=0; i<categories.length; i++) {
if (categories[i].name == category) {
catdiv = categories[i].div;
break;
}
}
if (catdiv == null) {
catdiv = $("<fieldset class='catdiv'/>");
catdiv.append($("<legend>"+category+"</legend>"));
$("#filterdiv").append(catdiv);
categories[categories.length] = {name: category, div: catdiv};
}
var div = $("<div/>");
var input = $("<input name='filters[]' type='checkbox'/>");
input.attr("id",val);
input.val(val);
input.addClass(cname);
div.append(input);
var ftitle = (title == null) ? val : title;
var label = $("<label>" + ftitle + "</label>");
label.attr("title", description);
div.append(label);
catdiv.append(div);
return input;
}
this.getFilterList = function() {
var list="";
$("input:checked[name='filters[]']").each(
function(){
if (list != "") {
list += ",";
}
list += $(this).val();
}
);
if (list == "") {
list = "none";
}
return list;
}
}
var MetadataFields = function(report) {
this.metadataSchemas = undefined;
var self = this;
this.load = function(){
$.ajax({
url: "/rest/registries/schema",
dataType: "json",
success: function(data){
self.initFields(data, report);
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/registries/schema "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
}
});
}
this.initFields = function(data, report) {
var params = report.myReportParameters.params;
self.metadataSchemas = data;
self.drawShowFields(params["show_fields[]"]);
}
this.getShowFields = function(){
var val = $("#show-fields select").val();
return val == null ? Array() : val;
}
this.drawShowFields = function(pfields) {
var self = this;
var sel = $("<select name='show_fields'/>").attr("multiple","true").attr("size","8").appendTo("#show-fields");
$.each(this.metadataSchemas, function(index, schema){
if (schema.prefix == 'eperson') {
return;
}
$.each(schema.fields, function(findex, field) {
var name = field.name;
var opt = $("<option/>");
opt.attr("value",name).text(name);
for(var i=0; i<pfields.length; i++) {
if (pfields[i] == name) {
opt.attr("selected", true);
}
}
sel.append(opt);
});
});
}
this.initQueries = function(){};
}
var HtmlUtil = function() {
this.addTr = function(tbl) {
var tr = $("<tr/>");
tbl.append(tr);
return tr;
}
this.addTd = function(tr, val) {
var td = $("<td/>");
if (val != null) {
td.append(val);
}
tr.append(td);
return td;
}
this.addTh = function(tr, val) {
var th = $("<th/>");
if (val != null) {
th.append(val);
}
tr.append(th);
return th;
}
this.addTdAnchor = function(tr, val, href) {
return this.addTd(tr, this.getAnchor(val, href));
}
this.getAnchor = function(val, href) {
var a = $("<a/>");
a.append(val);
a.attr("href", href);
a.attr("target", "_blank");
return a;
}
this.createOpt = function(name, val) {
var opt = $("<option/>");
opt.attr("value", val).text(name);
return opt;
}
this.addOpt = function(sel, name, val) {
var opt = this.createOpt(name, val);
sel.append(opt);
return opt;
}
this.addDisabledOpt = function(sel, name, val) {
var opt = this.createOpt(name, val).attr("disabled",true);
sel.append(opt);
return opt;
}
this.makeTotalCol = function(th) {
th.append($("<hr><span class='num'>-</span>"));
}
this.totalCol = function(index){
var total = 0;
$("#table tr.data").each(function(){
var val = $($(this).find("td")[index]).text();
if ($.isNumeric(val)) {
total += Number(val);
}
});
$($("#table tr.header th")[index]).find("span.num").text(total);
}
}
var CommunitySelector = function(report, parent, paramCollSel) {
var self = this;
var collSel = $("<select/>").attr("id","collSel").attr("name","collSel").attr("multiple", true).attr("size",15);
parent.append(collSel);
report.myHtmlUtil.addOpt(collSel, "Whole Repository", "");
$.ajax({
url: "/rest/communities",
data: {
limit : report.COLL_LIMIT,
expand : "subCommunities,collections"
},
dataType: "json",
headers: report.myAuth.getHeaders(),
success: function(data){
var collSel = $("#collSel");
var COMMS = {};
var TOPCOMMS = {};
$.each(data, function(index, comm){
COMMS["comm"+report.getId(comm)] = comm;
TOPCOMMS["comm"+report.getId(comm)] = comm;
});
$.each(data, function(index, comm){
$.each(comm.subcommunities, function(index, scomm){
delete TOPCOMMS["comm"+report.getId(scomm)];
});
});
for(var commindex in TOPCOMMS) {
self.addCommLabel(collSel, COMMS, COMMS[commindex], 0, paramCollSel);
};
},
error: function(xhr, status, errorThrown) {
alert("Error in /rest/communities "+ status+ " " + errorThrown);
},
complete: function(xhr, status) {
}
});
this.addCommLabel = function(collSel, COMMS, comm, indent, paramCollSel) {
var prefix = "";
for(var i=0; i<indent; i++) {
prefix += "--";
}
report.myHtmlUtil.addDisabledOpt(collSel, prefix + comm.name, "comm" + report.getId(comm));
if (comm.collections != null) {
$.each(comm.collections, function(index, coll) {
var opt = report.myHtmlUtil.addOpt(collSel, prefix + "--" + coll.name, report.getId(coll));
$.each(paramCollSel, function(index, collid){
if (collid == report.getId(coll)) {
opt.attr("selected", true);
}
});
});
}
if (comm.subcommunities != null) {
$.each(comm.subcommunities, function(index, scomm) {
self.addCommLabel(collSel, COMMS, COMMS["comm"+report.getId(scomm)], indent + 1, paramCollSel);
});
}
}
}

View File

@@ -0,0 +1,361 @@
/**
* Copyright (c) 2011-2014 Felix Gnass
* Licensed under the MIT license
*/
/*
* The MIT License
Copyright (c) 2011-2014 Felix Gnass [fgnass at neteye dot de]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
(function(root, factory) {
/* CommonJS */
if (typeof exports == 'object') module.exports = factory()
/* AMD module */
else if (typeof define == 'function' && define.amd) define(factory)
/* Browser global */
else root.Spinner = factory()
}
(this, function() {
"use strict";
var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
, animations = {} /* Animation rules keyed by their name */
, useCssAnimations /* Whether to use CSS animations or setTimeout */
/**
* Utility function to create elements. If no tag name is given,
* a DIV is created. Optionally properties can be passed.
*/
function createEl(tag, prop) {
var el = document.createElement(tag || 'div')
, n
for(n in prop) el[n] = prop[n]
return el
}
/**
* Appends children and returns the parent.
*/
function ins(parent /* child1, child2, ...*/) {
for (var i=1, n=arguments.length; i<n; i++)
parent.appendChild(arguments[i])
return parent
}
/**
* Insert a new stylesheet to hold the @keyframe or VML rules.
*/
var sheet = (function() {
var el = createEl('style', {type : 'text/css'})
ins(document.getElementsByTagName('head')[0], el)
return el.sheet || el.styleSheet
}())
/**
* Creates an opacity keyframe animation rule and returns its name.
* Since most mobile Webkits have timing issues with animation-delay,
* we create separate rules for each line/segment.
*/
function addAnimation(alpha, trail, i, lines) {
var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
, start = 0.01 + i/lines * 100
, z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
, prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
, pre = prefix && '-' + prefix + '-' || ''
if (!animations[name]) {
sheet.insertRule(
'@' + pre + 'keyframes ' + name + '{' +
'0%{opacity:' + z + '}' +
start + '%{opacity:' + alpha + '}' +
(start+0.01) + '%{opacity:1}' +
(start+trail) % 100 + '%{opacity:' + alpha + '}' +
'100%{opacity:' + z + '}' +
'}', sheet.cssRules.length)
animations[name] = 1
}
return name
}
/**
* Tries various vendor prefixes and returns the first supported property.
*/
function vendor(el, prop) {
var s = el.style
, pp
, i
prop = prop.charAt(0).toUpperCase() + prop.slice(1)
for(i=0; i<prefixes.length; i++) {
pp = prefixes[i]+prop
if(s[pp] !== undefined) return pp
}
if(s[prop] !== undefined) return prop
}
/**
* Sets multiple style properties at once.
*/
function css(el, prop) {
for (var n in prop)
el.style[vendor(el, n)||n] = prop[n]
return el
}
/**
* Fills in default values.
*/
function merge(obj) {
for (var i=1; i < arguments.length; i++) {
var def = arguments[i]
for (var n in def)
if (obj[n] === undefined) obj[n] = def[n]
}
return obj
}
/**
* Returns the line color from the given string or array.
*/
function getColor(color, idx) {
return typeof color == 'string' ? color : color[idx % color.length]
}
// Built-in defaults
var defaults = {
lines: 12, // The number of lines to draw
length: 7, // The length of each line
width: 5, // The line thickness
radius: 10, // The radius of the inner circle
rotate: 0, // Rotation offset
corners: 1, // Roundness (0..1)
color: '#000', // #rgb or #rrggbb
direction: 1, // 1: clockwise, -1: counterclockwise
speed: 1, // Rounds per second
trail: 100, // Afterglow percentage
opacity: 1/4, // Opacity of the lines
fps: 20, // Frames per second when using setTimeout()
zIndex: 2e9, // Use a high z-index by default
className: 'spinner', // CSS class to assign to the element
top: '50%', // center vertically
left: '50%', // center horizontally
position: 'absolute' // element position
}
/** The constructor */
function Spinner(o) {
this.opts = merge(o || {}, Spinner.defaults, defaults)
}
// Global defaults that override the built-ins:
Spinner.defaults = {}
merge(Spinner.prototype, {
/**
* Adds the spinner to the given target element. If this instance is already
* spinning, it is automatically removed from its previous target b calling
* stop() internally.
*/
spin: function(target) {
this.stop()
var self = this
, o = self.opts
, el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
css(el, {
left: o.left,
top: o.top
})
if (target) {
target.insertBefore(el, target.firstChild||null)
}
el.setAttribute('role', 'progressbar')
self.lines(el, self.opts)
if (!useCssAnimations) {
// No CSS animation support, use setTimeout() instead
var i = 0
, start = (o.lines - 1) * (1 - o.direction) / 2
, alpha
, fps = o.fps
, f = fps/o.speed
, ostep = (1-o.opacity) / (f*o.trail / 100)
, astep = f/o.lines
;(function anim() {
i++;
for (var j = 0; j < o.lines; j++) {
alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
self.opacity(el, j * o.direction + start, alpha, o)
}
self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
})()
}
return self
},
/**
* Stops and removes the Spinner.
*/
stop: function() {
var el = this.el
if (el) {
clearTimeout(this.timeout)
if (el.parentNode) el.parentNode.removeChild(el)
this.el = undefined
}
return this
},
/**
* Internal method that draws the individual lines. Will be overwritten
* in VML fallback mode below.
*/
lines: function(el, o) {
var i = 0
, start = (o.lines - 1) * (1 - o.direction) / 2
, seg
function fill(color, shadow) {
return css(createEl(), {
position: 'absolute',
width: (o.length+o.width) + 'px',
height: o.width + 'px',
background: color,
boxShadow: shadow,
transformOrigin: 'left',
transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
borderRadius: (o.corners * o.width>>1) + 'px'
})
}
for (; i < o.lines; i++) {
seg = css(createEl(), {
position: 'absolute',
top: 1+~(o.width/2) + 'px',
transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
opacity: o.opacity,
animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
})
if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)')))
}
return el
},
/**
* Internal method that adjusts the opacity of a single line.
* Will be overwritten in VML fallback mode below.
*/
opacity: function(el, i, val) {
if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
}
})
function initVML() {
/* Utility function to create a VML tag */
function vml(tag, attr) {
return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
}
// No CSS transforms but VML support, add a CSS rule for VML elements:
sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
Spinner.prototype.lines = function(el, o) {
var r = o.length+o.width
, s = 2*r
function grp() {
return css(
vml('group', {
coordsize: s + ' ' + s,
coordorigin: -r + ' ' + -r
}),
{ width: s, height: s }
)
}
var margin = -(o.width+o.length)*2 + 'px'
, g = css(grp(), {position: 'absolute', top: margin, left: margin})
, i
function seg(i, dx, filter) {
ins(g,
ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
ins(css(vml('roundrect', {arcsize: o.corners}), {
width: r,
height: o.width,
left: o.radius,
top: -o.width>>1,
filter: filter
}),
vml('fill', {color: getColor(o.color, i), opacity: o.opacity}),
vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
)
)
)
}
if (o.shadow)
for (i = 1; i <= o.lines; i++)
seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
for (i = 1; i <= o.lines; i++) seg(i)
return ins(el, g)
}
Spinner.prototype.opacity = function(el, i, val, o) {
var c = el.firstChild
o = o.shadow && o.lines || 0
if (c && i+o < c.childNodes.length) {
c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
if (c) c.opacity = val
}
}
}
var probe = css(createEl('group'), {behavior: 'url(#default#VML)'})
if (!vendor(probe, 'transform') && probe.adj) initVML()
else useCssAnimations = vendor(probe, 'animation')
return Spinner
}));

View File

@@ -11,3 +11,135 @@ stats = true
# By default, the DSpace REST API will only return communities/collections/items that are accessible to a particular user.
# Set the rest-hierarchy-authenticate option to false to bypass authorization
# rest-hierarchy-authenticate = false
#------------------------------------------------------------------#
# REST API Reporting Tools #
#------------------------------------------------------------------#
# This project is intended as an optional add-on to DSpace to provide
# Quality Control Reporting for Collection Managers.
#
# See https://github.com/DSpace-Labs/DSpace-REST-Reports
#
# These reports utilize the DSpace REST API to provide a Collection
# Manager with
# - an overview of their collections
# - a tool to query metadata for consistency
#
# When deploying the DSpace REST API, and institution may choose to
# make the API publicly accessible or to restrict access to the API.
# If these reports are deployed in a protected manner, the reporting
# tools can be configured to bypass DSpace authorization when
# reporting on collections and items.
##### Enable/disable authorization for the reporting tools. #####
# By default, the DSpace REST API will only return communities/collections/items that are accessible to a particular user.
# If the REST API has been deployed in a protected manner, the reporting tools can be configured to bypass authorization checks.
# This will allow all items/collections/communities to be returned to the report user.
# Set the rest-reporting-authenticate option to false to bypass authorization
# rest-reporting-authenticate = false
##### Configure the report pages that can be requested by name #####
# Create a map of named reports that are available to a report tool user
# Each map entry should be prefixed with rest-report-url
# The map key is a name for a report
# The map value is a URL to a report page
# A list of available reports will be available with the call /rest/reports.
# If a request is sent to /rest/reports/[report key], the request will be re-directed to the specified URL
#
# This project currently contains 2 sample reports. Eventually, additional reports could be introduced through this mechanism.
rest-report-url.collections = static/reports/index.html
rest-report-url.item-query = static/reports/query.html
#rest-report-url.custom =
##### database specific way to format a regex SQL clause #####
# The REST Report Tools may pass a regular expression test to the database.
# The following configuration setting will construct a SQL regular expression test appropriate to your database engine
rest-regex-clause = text_value ~ ?
##### Configure REST Report Filters #####
# A filter contains a set of tests that will be applied to an item to determine its inclusion in a particular report.
# Private items and withdrawn items are frequently excluded from DSpace reports.
# Additional filters can be configured to examine other item properties.
# For instance, items containing an image bitstream often have different requirements from a item containing a PDF.
# The DSpace REST reports come with a variety of filters that examine item properties, item bitstream properties,
# and item authorization policies. The existing filters can be used as an example to construct institution specific filters
# that will test conformity to a set of institutional policies.
# plugin.sequence.org.dspace.rest.filter points to a list of classes that contain available filters.
# Each class must implement the ItemFilterList interface.
# ItemFilterDefs: Filters that examine simple item and bitstream type properties
# ItemFilterDefsMisc: Filters that examine bitstream mime types and dependencies between bitstreams
# ItemFilterDefsMeta: Filters that examine metadata properties
# ItemFilterDefsPerm: Filters that examine item and bitstream authorization policies
plugin.sequence.org.dspace.rest.filter.ItemFilterList = \
org.dspace.rest.filter.ItemFilterDefs,\
org.dspace.rest.filter.ItemFilterDefsMisc,\
org.dspace.rest.filter.ItemFilterDefsPerm
# org.dspace.rest.filter.ItemFilterDefsMeta,\
##### Configuration Settings used by REST Report Filters #####
# Define the set of supported bitstream bundle names for your repository as a comma separated list
rest-report-supp-bundles = ORIGINAL,THUMBNAIL,TEXT,LICENSE
# Define the bitstream mime types that will be interpreted as a "document".
# Generally, a "document" should be expected to be searchable and to have a TEXT bitstream. An institution may expect document types to
# have a thumbnail.
rest-report-mime-document = text/plain,application/pdf,text/html,application/msword,text/xml,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
# Define the standard/preferred bitstream mime types for document items for your repository
# This setting allows the reporting tools to report on "supported" and "unsupported" document types.
rest-report-mime-document-supported = application/pdf
# Define the standard/preferred bitstream mime types for image items for your repository
# This setting allows the reporting tools to report on "supported" and "unsupported" image types.
rest-report-mime-document-image = image/jpeg,image/jp2
# Minimum size for a supported PDF in the repository
# PDF bitstreams smaller than this size will be highlighted in a report.
# PDF files smaller than this size are potentially corrupt.
rest-report-pdf-min-size = 20000
# Maximum size for a typical PDF in the repository
# PDF bitstreams larger than this size will be highlighted in a report.
# PDF files larger than this size may be slow to retrieve.
rest-report-pdf-max-size = 25000000
# Minimum size for a thumbnail - could indicate a corrupted original
# Thumbnail bitstreams smaller than this size will be highlighted in a report.
# Thumbnail files smaller than this size are potentially corrupt.
rest-report-thumbnail-min-size = 400
# Bitstream descriptor to identify generated thumbnails
# The ImageMagick Thumbnail generator tags the thumbnails it has created with a standard description.
# This description identifies thumbnails that can safely be re-generated.
rest-report-gen-thumbnail-desc = Generated Thumbnail
#### Metadata Filtering by Regular Expression #####
# Used by org.dspace.rest.filter.ItemFilterDefsMeta
# This class filters items based on metadata properties.
# These filters are useful for filtering a small set of items. These filters will be inefficient as a query tool.
# regex to detect compound subjects - detect subject terms that should be split into individual terms
rest-report-regex-compound-subject = .*;.*
# regex to detect compound authors - detect author/creator names taht should be split into individual fields
rest-report-regex-compound-author = .* and .*
# regex to detect unbreaking metadata - detect long unbreaking text that may not render properly on a page
rest-report-regex-unbreaking = ^.*[^ ]{50,50}.*$
# regex to detect url in description - detect description fields that contain URL's
rest-report-regex-url = ^.*(http://|https://|mailto:).*$
# regex to identify full text content from the provenance field
# This test has been used to identfiy "full-text" content when harvesting DSpace content by Summon
rest-report-regex-fulltext = ^.*No\\. of bitstreams(.|\\r|\\n|\\r\\n)*\\.(PDF|pdf|DOC|doc|PPT|ppt|DOCX|docx|PPTX|pptx).*$
# regex to identify very long metadata fields that may be slow to render
rest-report-regex-long = ^[\\s\\S]{6000,}$
# regex to identify partial XML entities within a description field (a frequent problem found in ProQuest ETD's)
rest-report-regex-xml-entity = ^.*&#.*$
# regex to identify non ascii characters in metadata
rest-report-regex-non-ascii = ^.*[^\\p{ASCII}].*$