Contribution by University of Missouri in collaboration with @mire under the ORCID Adoption and Integration program funded by the Alfred P. Sloan Foundation.

This commit is contained in:
KevinVdV
2014-08-20 13:00:33 +02:00
parent 9a777004fa
commit e0b7b4609e
90 changed files with 8622 additions and 58 deletions

View File

@@ -7,6 +7,11 @@
*/ */
package org.dspace.app.bulkedit; package org.dspace.app.bulkedit;
import org.dspace.authority.AuthorityValue;
import org.dspace.app.bulkedit.DSpaceCSVLine;
import org.dspace.app.bulkedit.MetadataImport;
import org.dspace.app.bulkedit.MetadataImportInvalidHeadingException;
import org.dspace.content.Collection;
import org.dspace.content.*; import org.dspace.content.*;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.core.ConfigurationManager; import org.dspace.core.ConfigurationManager;
@@ -129,6 +134,14 @@ public class DSpaceCSV implements Serializable
} }
else if (!"id".equals(element)) else if (!"id".equals(element))
{ {
String authorityPrefix = "";
AuthorityValue authorityValueType = MetadataImport.getAuthorityValueType(element);
if (authorityValueType != null) {
String authorityType = authorityValueType.getAuthorityType();
authorityPrefix = element.substring(0, authorityType.length() + 1);
element = element.substring(authorityPrefix.length());
}
// Verify that the heading is valid in the metadata registry // Verify that the heading is valid in the metadata registry
String[] clean = element.split("\\["); String[] clean = element.split("\\[");
String[] parts = clean[0].split("\\."); String[] parts = clean[0].split("\\.");
@@ -164,7 +177,7 @@ public class DSpaceCSV implements Serializable
} }
// Store the heading // Store the heading
headings.add(element); headings.add(authorityPrefix + element);
} }
} }

View File

@@ -7,7 +7,11 @@
*/ */
package org.dspace.app.bulkedit; package org.dspace.app.bulkedit;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.AuthorityValueFinder;
import org.dspace.authority.AuthorityValueGenerator;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.content.*; import org.dspace.content.*;
@@ -62,6 +66,8 @@ public class MetadataImport
/** Logger */ /** Logger */
private static final Logger log = Logger.getLogger(MetadataImport.class); private static final Logger log = Logger.getLogger(MetadataImport.class);
private AuthorityValueFinder authorityValueFinder = new AuthorityValueFinder();
/** /**
* Create an instance of the metadata importer. Requires a context and an array of CSV lines * Create an instance of the metadata importer. Requires a context and an array of CSV lines
* to examine. * to examine.
@@ -437,6 +443,11 @@ public class MetadataImport
language = bits[1].substring(0, bits[1].length() - 1); language = bits[1].substring(0, bits[1].length() - 1);
} }
AuthorityValue fromAuthority = getAuthorityValueType(md);
if (md.indexOf(':') > 0) {
md = md.substring(md.indexOf(':') + 1);
}
String[] bits = md.split("\\."); String[] bits = md.split("\\.");
String schema = bits[0]; String schema = bits[0];
String element = bits[1]; String element = bits[1];
@@ -484,34 +495,17 @@ public class MetadataImport
} }
// Compare from current->csv // Compare from current->csv
for (String value : fromCSV) for (int v = 0; v < fromCSV.length; v++) {
{ String value = fromCSV[v];
// Look to see if it should be added DCValue dcv = getDcValueFromCSV(language, schema, element, qualifier, value, fromAuthority);
DCValue dcv = new DCValue(); if (fromAuthority!=null) {
dcv.schema = schema; value = dcv.value + DSpaceCSV.authoritySeparator + dcv.authority + DSpaceCSV.authoritySeparator + dcv.confidence;
dcv.element = element; fromCSV[v] = value;
dcv.qualifier = qualifier;
dcv.language = language;
if (value == null || value.indexOf(DSpaceCSV.authoritySeparator) < 0)
{
dcv.value = value;
dcv.authority = null;
dcv.confidence = Choices.CF_UNSET;
}
else
{
String[] parts = value.split(DSpaceCSV.escapedAuthoritySeparator);
dcv.value = parts[0];
dcv.authority = parts[1];
dcv.confidence = (parts.length > 2 ? Integer.valueOf(parts[2]) : Choices.CF_ACCEPTED);
} }
if ((value != null) && (!"".equals(value)) && (!contains(value, dcvalues))) if ((value != null) && (!"".equals(value)) && (!contains(value, dcvalues))) {
{
changes.registerAdd(dcv); changes.registerAdd(dcv);
} } else {
else
{
// Keep it // Keep it
changes.registerConstant(dcv); changes.registerConstant(dcv);
} }
@@ -527,11 +521,7 @@ public class MetadataImport
dcv.qualifier = qualifier; dcv.qualifier = qualifier;
dcv.language = language; dcv.language = language;
if (value == null || value.indexOf(DSpaceCSV.authoritySeparator) < 0) if (value == null || value.indexOf(DSpaceCSV.authoritySeparator) < 0)
{ simplyCopyValue(value, dcv);
dcv.value = value;
dcv.authority = null;
dcv.confidence = Choices.CF_UNSET;
}
else else
{ {
String[] parts = value.split(DSpaceCSV.escapedAuthoritySeparator); String[] parts = value.split(DSpaceCSV.escapedAuthoritySeparator);
@@ -807,6 +797,11 @@ public class MetadataImport
String[] bits = md.split("\\["); String[] bits = md.split("\\[");
language = bits[1].substring(0, bits[1].length() - 1); language = bits[1].substring(0, bits[1].length() - 1);
} }
AuthorityValue fromAuthority = getAuthorityValueType(md);
if (md.indexOf(':') > 0) {
md = md.substring(md.indexOf(':')+1);
}
String[] bits = md.split("\\."); String[] bits = md.split("\\.");
String schema = bits[0]; String schema = bits[0];
String element = bits[1]; String element = bits[1];
@@ -830,24 +825,9 @@ public class MetadataImport
// Add all the values // Add all the values
for (String value : fromCSV) for (String value : fromCSV)
{ {
// Look to see if it should be removed DCValue dcv = getDcValueFromCSV(language, schema, element, qualifier, value, fromAuthority);
DCValue dcv = new DCValue(); if(fromAuthority!=null){
dcv.schema = schema; value = dcv.value + DSpaceCSV.authoritySeparator + dcv.authority + DSpaceCSV.authoritySeparator + dcv.confidence;
dcv.element = element;
dcv.qualifier = qualifier;
dcv.language = language;
if (value == null || value.indexOf(DSpaceCSV.authoritySeparator) < 0)
{
dcv.value = value;
dcv.authority = null;
dcv.confidence = Choices.CF_UNSET;
}
else
{
String[] parts = value.split(DSpaceCSV.escapedAuthoritySeparator);
dcv.value = parts[0];
dcv.authority = parts[1];
dcv.confidence = (parts.length > 2 ? Integer.valueOf(parts[2]) : Choices.CF_ACCEPTED);
} }
// Add it // Add it
@@ -858,6 +838,61 @@ public class MetadataImport
} }
} }
public static AuthorityValue getAuthorityValueType(String md) {
AuthorityValue fromAuthority = null;
List<AuthorityValue> types = AuthorityValue.getAuthorityTypes().getTypes();
for (AuthorityValue type : types) {
if (StringUtils.startsWithIgnoreCase(md,type.getAuthorityType())) {
fromAuthority = type;
}
}
return fromAuthority;
}
private DCValue getDcValueFromCSV(String language, String schema, String element, String qualifier, String value, AuthorityValue fromAuthority) {
// Look to see if it should be removed
DCValue dcv = new DCValue();
dcv.schema = schema;
dcv.element = element;
dcv.qualifier = qualifier;
dcv.language = language;
if (fromAuthority != null) {
if (value.indexOf(':') > 0) {
value = value.substring(0, value.indexOf(':'));
}
// look up the value and authority in solr
List<AuthorityValue> byValue = authorityValueFinder.findByValue(c, schema, element, qualifier, value);
AuthorityValue authorityValue = null;
if (byValue.isEmpty()) {
String toGenerate = fromAuthority.generateString() + value;
String field = schema + "_" + element + (StringUtils.isNotBlank(qualifier) ? "_" + qualifier : "");
authorityValue = AuthorityValueGenerator.generate(toGenerate, value, field);
dcv.authority = toGenerate;
} else {
authorityValue = byValue.get(0);
dcv.authority = authorityValue.getId();
}
dcv.value = authorityValue.getValue();
dcv.confidence = Choices.CF_ACCEPTED;
} else if (value == null || !value.contains(DSpaceCSV.authoritySeparator)) {
simplyCopyValue(value, dcv);
} else {
String[] parts = value.split(DSpaceCSV.escapedAuthoritySeparator);
dcv.value = parts[0];
dcv.authority = parts[1];
dcv.confidence = (parts.length > 2 ? Integer.valueOf(parts[2]) : Choices.CF_ACCEPTED);
}
return dcv;
}
private void simplyCopyValue(String value, DCValue dcv) {
dcv.value = value;
dcv.authority = null;
dcv.confidence = Choices.CF_UNSET;
}
/** /**
* Method to find if a String occurs in an array of Strings * Method to find if a String occurs in an array of Strings
* *

View File

@@ -0,0 +1,30 @@
/**
* 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.authority;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import java.net.MalformedURLException;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public interface AuthoritySearchService {
QueryResponse search(SolrQuery query) throws SolrServerException, MalformedURLException;
List<String> getAllIndexedMetadataFields() throws Exception;
}

View File

@@ -0,0 +1,138 @@
/**
* 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.authority;
import org.dspace.authority.indexer.AuthorityIndexingService;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.core.ConfigurationManager;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthoritySolrServiceImpl implements AuthorityIndexingService, AuthoritySearchService {
private static final Logger log = Logger.getLogger(AuthoritySolrServiceImpl.class);
/**
* Non-Static CommonsHttpSolrServer for processing indexing events.
*/
protected HttpSolrServer solr = null;
protected HttpSolrServer getSolr() throws MalformedURLException, SolrServerException {
if (solr == null) {
String solrService = ConfigurationManager.getProperty("solr.authority.server");
log.debug("Solr authority URL: " + solrService);
solr = new HttpSolrServer(solrService);
solr.setBaseURL(solrService);
SolrQuery solrQuery = new SolrQuery().setQuery("*:*");
solr.query(solrQuery);
}
return solr;
}
public void indexContent(AuthorityValue value, boolean force) {
SolrInputDocument doc = value.getSolrInputDocument();
try{
writeDocument(doc);
}catch (Exception e){
log.error("Error while writing authority value to the index: " + value.toString(), e);
}
}
public void cleanIndex() throws Exception {
try{
getSolr().deleteByQuery("*:*");
} catch (Exception e){
log.error("Error while cleaning authority solr server index", e);
throw new Exception(e);
}
}
public void commit() {
try {
getSolr().commit();
} catch (SolrServerException e) {
log.error("Error while committing authority solr server", e);
} catch (IOException e) {
log.error("Error while committing authority solr server", e);
}
}
/**
* Write the document to the solr index
* @param doc the solr document
* @throws java.io.IOException
*/
protected void writeDocument(SolrInputDocument doc) throws IOException {
try {
getSolr().add(doc);
} catch (Exception e) {
try {
log.error("An error occurred for document: " + doc.getField("id").getFirstValue() + ", source: " + doc.getField("source").getFirstValue() + ", field: " + doc.getField("field").getFirstValue() + ", full-text: " + doc.getField("full-text").getFirstValue(), e);
} catch (Exception e1) {
//shouldn't happen
}
log.error(e.getMessage(), e);
}
}
public QueryResponse search(SolrQuery query) throws SolrServerException, MalformedURLException {
return getSolr().query(query);
}
/**
* Retrieves all the metadata fields which are indexed in the authority control
* @return a list of metadata fields
*/
public List<String> getAllIndexedMetadataFields() throws Exception {
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery("*:*");
solrQuery.setFacet(true);
solrQuery.addFacetField("field");
QueryResponse response = getSolr().query(solrQuery);
List<String> results = new ArrayList<String>();
FacetField facetField = response.getFacetField("field");
if(facetField != null){
List<FacetField.Count> values = facetField.getValues();
if(values != null){
for (FacetField.Count facetValue : values) {
if (facetValue != null && facetValue.getName() != null) {
results.add(facetValue.getName());
}
}
}
}
return results;
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.authority;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class contains a list of active authority types.
* It can be used to created a new instance of a specific type.
* However if you need to make a new instance to store it in solr, you need to use AuthorityValueGenerator.
* To create an instance from a solr record, use AuthorityValue#fromSolr(SolrDocument).
*
* This class is instantiated in spring and accessed by a static method in AuthorityValue.
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityTypes {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(AuthorityTypes.class);
private List<AuthorityValue> types = new ArrayList<AuthorityValue>();
private Map<String, AuthorityValue> fieldDefaults = new HashMap<String, AuthorityValue>();
public List<AuthorityValue> getTypes() {
return types;
}
public void setTypes(List<AuthorityValue> types) {
this.types = types;
}
public Map<String, AuthorityValue> getFieldDefaults() {
return fieldDefaults;
}
public void setFieldDefaults(Map<String, AuthorityValue> fieldDefaults) {
this.fieldDefaults = fieldDefaults;
}
public AuthorityValue getEmptyAuthorityValue(String type) {
AuthorityValue result = null;
for (AuthorityValue authorityValue : types) {
if (authorityValue.getAuthorityType().equals(type)) {
try {
result = authorityValue.getClass().newInstance();
} catch (InstantiationException e) {
log.error("Error", e);
} catch (IllegalAccessException e) {
log.error("Error", e);
}
}
}
if (result == null) {
result = new AuthorityValue();
}
return result;
}
}

View File

@@ -0,0 +1,299 @@
/**
* 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.authority;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.content.DCValue;
import org.dspace.content.Item;
import org.dspace.utils.DSpace;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import java.util.*;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityValue {
/**
* The id of the record in solr
*/
private String id;
/**
* The metadata field that this authority value is for
*/
private String field;
/**
* The text value of this authority
*/
private String value;
/**
* When this authority record has been created
*/
private Date creationDate;
/**
* If this authority has been removed
*/
private boolean deleted;
/**
* represents the last time that DSpace got updated information from its external source
*/
private Date lastModified;
public AuthorityValue() {
}
public AuthorityValue(SolrDocument document) {
setValues(document);
}
public String getId() {
return id;
}
public String getField() {
return field;
}
public String getValue() {
return value;
}
public void setId(String id) {
this.id = id;
}
public void setField(String field) {
this.field = field;
}
public void setValue(String value) {
this.value = value;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public void setCreationDate(String creationDate) {
this.creationDate = stringToDate(creationDate);
}
public Date getLastModified() {
return lastModified;
}
public void setLastModified(String lastModified) {
this.lastModified = stringToDate(lastModified);
}
public void setLastModified(Date lastModified) {
this.lastModified = lastModified;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
protected void updateLastModifiedDate() {
this.lastModified = new Date();
}
public void update() {
updateLastModifiedDate();
}
public void delete() {
setDeleted(true);
updateLastModifiedDate();
}
/**
* Generate a solr record from this instance
*/
public SolrInputDocument getSolrInputDocument() {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", getId());
doc.addField("field", getField());
doc.addField("value", getValue());
doc.addField("deleted", isDeleted());
doc.addField("creation_date", getCreationDate());
doc.addField("last_modified_date", getLastModified());
doc.addField("authority_type", getAuthorityType());
return doc;
}
/**
* Initialize this instance based on a solr record
*/
public void setValues(SolrDocument document) {
this.id = String.valueOf(document.getFieldValue("id"));
this.field = String.valueOf(document.getFieldValue("field"));
this.value = String.valueOf(document.getFieldValue("value"));
this.deleted = (Boolean) document.getFieldValue("deleted");
this.creationDate = (Date) document.getFieldValue("creation_date");
this.lastModified = (Date) document.getFieldValue("last_modified_date");
}
/**
* Replace an item's DCValue with this authority
*/
public void updateItem(Item currentItem, DCValue value) {
DCValue newValue = value.copy();
newValue.value = getValue();
newValue.authority = getId();
currentItem.replaceMetadataValue(value,newValue);
}
/**
* Information that can be used the choice ui
*/
public Map<String, String> choiceSelectMap() {
return new HashMap<String, String>();
}
public List<DateTimeFormatter> getDateFormatters() {
List<DateTimeFormatter> list = new ArrayList<DateTimeFormatter>();
list.add(ISODateTimeFormat.dateTime());
list.add(ISODateTimeFormat.dateTimeNoMillis());
return list;
}
public Date stringToDate(String date) {
Date result = null;
if (StringUtils.isNotBlank(date)) {
List<DateTimeFormatter> dateFormatters = getDateFormatters();
boolean converted = false;
int formatter = 0;
while(!converted) {
try {
DateTimeFormatter dateTimeFormatter = dateFormatters.get(formatter);
DateTime dateTime = dateTimeFormatter.parseDateTime(date);
result = dateTime.toDate();
converted = true;
} catch (IllegalArgumentException e) {
formatter++;
if (formatter > dateFormatters.size()) {
converted = true;
}
log.error("Could not find a valid date format for: \""+date+"\"", e);
}
}
}
return result;
}
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(AuthorityValue.class);
@Override
public String toString() {
return "AuthorityValue{" +
"id='" + id + '\'' +
", field='" + field + '\'' +
", value='" + value + '\'' +
", creationDate=" + creationDate +
", deleted=" + deleted +
", lastModified=" + lastModified +
'}';
}
/**
* Provides a string that will be allow a this AuthorityType to be recognized and provides information to create a new instance to be created using public AuthorityValue newInstance(String info).
* See the implementation of com.atmire.org.dspace.authority.AuthorityValueGenerator#generateRaw(java.lang.String, java.lang.String) for more precisions.
*/
public String generateString() {
return AuthorityValueGenerator.GENERATE;
}
/**
* Makes an instance of the AuthorityValue with the given information.
*/
public AuthorityValue newInstance(String info) {
return new AuthorityValue();
}
public String getAuthorityType() {
return "internal";
}
private static AuthorityTypes authorityTypes;
public static AuthorityTypes getAuthorityTypes() {
if (authorityTypes == null) {
authorityTypes = new DSpace().getServiceManager().getServiceByName("AuthorityTypes", AuthorityTypes.class);
}
return authorityTypes;
}
public static AuthorityValue fromSolr(SolrDocument solrDocument) {
String type = (String) solrDocument.getFieldValue("authority_type");
AuthorityValue value = getAuthorityTypes().getEmptyAuthorityValue(type);
value.setValues(solrDocument);
return value;
}
/**
* The regular equals() only checks if both AuthorityValues describe the same authority.
* This method checks if the AuthorityValues have different information
* E.g. it is used to decide when lastModified should be updated.
*/
public boolean hasTheSameInformationAs(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AuthorityValue that = (AuthorityValue) o;
if (deleted != that.deleted) {
return false;
}
if (field != null ? !field.equals(that.field) : that.field != null) {
return false;
}
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
if (value != null ? !value.equals(that.value) : that.value != null) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,114 @@
/**
* 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.authority;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.dspace.content.authority.SolrAuthority;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityValueFinder {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(AuthorityValueFinder.class);
/**
* Item.ANY does not work here.
*/
public AuthorityValue findByUID(Context context, String authorityID) {
String queryString = "id:" + authorityID;
List<AuthorityValue> findings = find(context, queryString);
return findings.size() > 0 ? findings.get(0) : null;
}
public List<AuthorityValue> findByValue(Context context, String schema, String element, String qualifier, String value) {
String field = fieldParameter(schema, element, qualifier);
String queryString = "value:" + value + " AND field:" + field;
return find(context, queryString);
}
public AuthorityValue findByOrcidID(Context context, String orcid_id) {
String queryString = "orcid_id:" + orcid_id;
List<AuthorityValue> findings = find(context, queryString);
return findings.size() > 0 ? findings.get(0) : null;
}
public List<AuthorityValue> findByName(Context context, String schema, String element, String qualifier, String name) {
String field = fieldParameter(schema, element, qualifier);
String queryString = "first_name:" + name + " OR last_name:" + name + " OR name_variant:" + name + " AND field:" + field;
return find(context, queryString);
}
public List<AuthorityValue> findByAuthorityMetadata(Context context, String schema, String element, String qualifier, String value) {
String field = fieldParameter(schema, element, qualifier);
String queryString = "all_Labels:" + value + " AND field:" + field;
return find(context, queryString);
}
public List<AuthorityValue> findOrcidHolders(Context context) {
String queryString = "orcid_id:*";
return find(context, queryString);
}
public List<AuthorityValue> findAll(Context context) {
String queryString = "*:*";
return find(context, queryString);
}
private List<AuthorityValue> find(Context context, String queryString) {
List<AuthorityValue> findings = new ArrayList<AuthorityValue>();
try {
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery(filtered(queryString));
log.debug("AuthorityValueFinder makes the query: " + queryString);
QueryResponse queryResponse = SolrAuthority.getSearchService().search(solrQuery);
if (queryResponse != null && queryResponse.getResults() != null && 0 < queryResponse.getResults().getNumFound()) {
for (SolrDocument document : queryResponse.getResults()) {
AuthorityValue authorityValue = AuthorityValue.fromSolr(document);
findings.add(authorityValue);
log.debug("AuthorityValueFinder found: " + authorityValue.getValue());
}
}
} catch (Exception e) {
log.error(LogManager.getHeader(context, "Error while retrieving AuthorityValue from solr", "query: " + queryString),e);
}
return findings;
}
private String filtered(String queryString) throws InstantiationException, IllegalAccessException {
String instanceFilter = "-deleted:true";
if (StringUtils.isNotBlank(instanceFilter)) {
queryString += " AND " + instanceFilter;
}
return queryString;
}
private String fieldParameter(String schema, String element, String qualifier) {
return schema + "_" + element + ((qualifier != null) ? "_" + qualifier : "");
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.authority;
import org.apache.commons.lang.StringUtils;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
/**
* This class generates AuthorityValue that do not have a solr record yet.
* <p/>
* This class parses the AuthorityValue.generateString(),
* creates an AuthorityValue instance of the appropriate type
* and then generates another instance using AuthorityValue.newInstance(info).
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityValueGenerator {
public static final String SPLIT = "::";
public static final String GENERATE = "will be generated" + SPLIT;
public static AuthorityValue generate(String uid, String content, String field) {
AuthorityValue nextValue = null;
nextValue = generateRaw(uid, content, field);
if (nextValue != null) {
nextValue.setId(UUID.randomUUID().toString());
nextValue.updateLastModifiedDate();
nextValue.setCreationDate(new Date());
nextValue.setField(field);
}
return nextValue;
}
protected static AuthorityValue generateRaw(String uid, String content, String field) {
AuthorityValue nextValue;
if (uid != null && uid.startsWith(AuthorityValueGenerator.GENERATE)) {
String[] split = StringUtils.split(uid, SPLIT);
String type = null, info = null;
if (split.length > 0) {
type = split[1];
if (split.length > 1) {
info = split[2];
}
}
AuthorityValue authorityType = AuthorityValue.getAuthorityTypes().getEmptyAuthorityValue(type);
nextValue = authorityType.newInstance(info);
} else {
Map<String, AuthorityValue> fieldDefaults = AuthorityValue.getAuthorityTypes().getFieldDefaults();
nextValue = fieldDefaults.get(field).newInstance(null);
if (nextValue == null) {
nextValue = new AuthorityValue();
}
nextValue.setValue(content);
}
return nextValue;
}
public static AuthorityValue update(AuthorityValue value) {
AuthorityValue updated = generateRaw(value.generateString(), value.getValue(), value.getField());
if (updated != null) {
updated.setId(value.getId());
updated.setCreationDate(value.getCreationDate());
updated.setField(value.getField());
if (updated.hasTheSameInformationAs(value)) {
updated.setLastModified(value.getLastModified());
}else {
updated.updateLastModifiedDate();
}
}
return updated;
}
}

View File

@@ -0,0 +1,259 @@
/**
* 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.authority;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class PersonAuthorityValue extends AuthorityValue {
private String firstName;
private String lastName;
private List<String> nameVariants = new ArrayList<String>();
private String institution;
private List<String> emails = new ArrayList<String>();
public PersonAuthorityValue() {
}
public PersonAuthorityValue(SolrDocument document) {
super(document);
}
public String getName() {
String name = "";
if (StringUtils.isNotBlank(lastName)) {
name = lastName;
if (StringUtils.isNotBlank(firstName)) {
name += ", ";
}
}
if (StringUtils.isNotBlank(firstName)) {
name += firstName;
}
return name;
}
public void setName(String name) {
if (StringUtils.isNotBlank(name)) {
String[] split = name.split(",");
if (split.length > 0) {
setLastName(split[0].trim());
if (split.length > 1) {
setFirstName(split[1].trim());
}
}
}
if (!StringUtils.equals(getValue(), name)) {
setValue(name);
}
}
@Override
public void setValue(String value) {
super.setValue(value);
setName(value);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<String> getNameVariants() {
return nameVariants;
}
public void addNameVariant(String name) {
if (StringUtils.isNotBlank(name)) {
nameVariants.add(name);
}
}
public String getInstitution() {
return institution;
}
public void setInstitution(String institution) {
this.institution = institution;
}
public List<String> getEmails() {
return emails;
}
public void addEmail(String email) {
if (StringUtils.isNotBlank(email)) {
emails.add(email);
}
}
@Override
public SolrInputDocument getSolrInputDocument() {
SolrInputDocument doc = super.getSolrInputDocument();
if (StringUtils.isNotBlank(getFirstName())) {
doc.addField("first_name", getFirstName());
}
if (StringUtils.isNotBlank(getLastName())) {
doc.addField("last_name", getLastName());
}
for (String nameVariant : getNameVariants()) {
doc.addField("name_variant", nameVariant);
}
for (String email : emails) {
doc.addField("email", email);
}
doc.addField("institution", getInstitution());
return doc;
}
@Override
public void setValues(SolrDocument document) {
super.setValues(document);
this.firstName = ObjectUtils.toString(document.getFieldValue("first_name"));
this.lastName = ObjectUtils.toString(document.getFieldValue("last_name"));
nameVariants = new ArrayList<String>();
Collection<Object> document_name_variant = document.getFieldValues("name_variant");
if (document_name_variant != null) {
for (Object name_variants : document_name_variant) {
addNameVariant(String.valueOf(name_variants));
}
}
if (document.getFieldValue("institution") != null) {
this.institution = String.valueOf(document.getFieldValue("institution"));
}
Collection<Object> emails = document.getFieldValues("email");
if (emails != null) {
for (Object email : emails) {
addEmail(String.valueOf(email));
}
}
}
@Override
public Map<String, String> choiceSelectMap() {
Map<String, String> map = super.choiceSelectMap();
if (StringUtils.isNotBlank(getFirstName())) {
map.put("first-name", getFirstName());
} else {
map.put("first-name", "/");
}
if (StringUtils.isNotBlank(getLastName())) {
map.put("last-name", getLastName());
} else {
map.put("last-name", "/");
}
if (!getEmails().isEmpty()) {
boolean added = false;
for (String email : getEmails()) {
if (!added && StringUtils.isNotBlank(email)) {
map.put("email",email);
added = true;
}
}
}
if (StringUtils.isNotBlank(getInstitution())) {
map.put("institution", getInstitution());
}
return map;
}
@Override
public String getAuthorityType() {
return "person";
}
@Override
public String generateString() {
return AuthorityValueGenerator.GENERATE + getAuthorityType() + AuthorityValueGenerator.SPLIT + getName();
// the part after "AuthorityValueGenerator.GENERATE + getAuthorityType() + AuthorityValueGenerator.SPLIT" is the value of the "info" parameter in public AuthorityValue newInstance(String info)
}
@Override
public AuthorityValue newInstance(String info) {
PersonAuthorityValue authorityValue = new PersonAuthorityValue();
authorityValue.setValue(info);
return authorityValue;
}
@Override
public String toString() {
return "PersonAuthorityValue{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", nameVariants=" + nameVariants +
", institution='" + institution + '\'' +
", emails=" + emails +
"} " + super.toString();
}
public boolean hasTheSameInformationAs(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if(!super.hasTheSameInformationAs(o)){
return false;
}
PersonAuthorityValue that = (PersonAuthorityValue) o;
if (emails != null ? !emails.equals(that.emails) : that.emails != null) {
return false;
}
if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) {
return false;
}
if (institution != null ? !institution.equals(that.institution) : that.institution != null) {
return false;
}
if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) {
return false;
}
if (nameVariants != null ? !nameVariants.equals(that.nameVariants) : that.nameVariants != null) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,174 @@
/**
* 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.authority;
import org.apache.commons.cli.*;
import org.apache.log4j.Logger;
import org.dspace.content.DCValue;
import org.dspace.content.Item;
import org.dspace.content.ItemIterator;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class UpdateAuthorities {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(UpdateAuthorities.class);
protected PrintWriter print = null;
private Context context;
private List<String> selectedIDs;
public UpdateAuthorities(Context context) {
print = new PrintWriter(System.out);
this.context = context;
}
public static void main(String[] args) throws ParseException {
Context c = null;
try {
c = new Context();
UpdateAuthorities UpdateAuthorities = new UpdateAuthorities(c);
if (processArgs(args, UpdateAuthorities) == 0) {
System.exit(0);
}
UpdateAuthorities.run();
} catch (SQLException e) {
log.error("Error in UpdateAuthorities", e);
} finally {
if (c != null) {
c.abort();
}
}
}
protected static int processArgs(String[] args, UpdateAuthorities UpdateAuthorities) throws ParseException {
CommandLineParser parser = new PosixParser();
Options options = createCommandLineOptions();
CommandLine line = parser.parse(options, args);
// help
HelpFormatter helpFormatter = new HelpFormatter();
if (line.hasOption("h")) {
helpFormatter.printHelp("dsrun " + UpdateAuthorities.class.getCanonicalName(), options);
return 0;
}
// other arguments
if (line.hasOption("i")) {
UpdateAuthorities.setSelectedIDs(line.getOptionValue("i"));
}
// print to std out
UpdateAuthorities.setPrint(new PrintWriter(System.out, true));
return 1;
}
private void setSelectedIDs(String b) {
this.selectedIDs = new ArrayList<String>();
String[] orcids = b.split(",");
for (String orcid : orcids) {
this.selectedIDs.add(orcid.trim());
}
}
protected static Options createCommandLineOptions() {
Options options = new Options();
options.addOption("h", "help", false, "help");
options.addOption("i", "id", true, "Import and/or update specific solr records with the given ids (comma-separated)");
return options;
}
public void run() {
// This implementation could be very heavy on the REST service.
// Use with care or make it more efficient.
AuthorityValueFinder authorityValueFinder = new AuthorityValueFinder();
List<AuthorityValue> authorities;
if (selectedIDs != null && !selectedIDs.isEmpty()) {
authorities = new ArrayList<AuthorityValue>();
for (String selectedID : selectedIDs) {
AuthorityValue byUID = authorityValueFinder.findByUID(context, selectedID);
authorities.add(byUID);
}
} else {
authorities = authorityValueFinder.findAll(context);
}
if (authorities != null) {
print.println(authorities.size() + " authorities found.");
for (AuthorityValue authority : authorities) {
AuthorityValue updated = AuthorityValueGenerator.update(authority);
if (!updated.getLastModified().equals(authority.getLastModified())) {
followUp(updated);
}
}
}
}
protected void followUp(AuthorityValue authority) {
print.println("Updated: " + authority.getValue() + " - " + authority.getId());
boolean updateItems = ConfigurationManager.getBooleanProperty("solrauthority", "auto-update-items");
if (updateItems) {
updateItems(authority);
}
}
protected void updateItems(AuthorityValue authority) {
try {
ItemIterator itemIterator = Item.findByMetadataFieldAuthority(context, authority.getField(), authority.getId());
while (itemIterator.hasNext()) {
Item next = itemIterator.next();
List<DCValue> metadata = next.getMetadata(authority.getField(), authority.getId());
authority.updateItem(next, metadata.get(0)); //should be only one
List<DCValue> metadataAfter = next.getMetadata(authority.getField(), authority.getId());
if (!metadata.get(0).value.equals(metadataAfter.get(0).value)) {
print.println("Updated item with handle " + next.getHandle());
}
}
} catch (Exception e) {
log.error("Error updating item", e);
print.println("Error updating item. " + Arrays.toString(e.getStackTrace()));
}
}
public PrintWriter getPrint() {
return print;
}
public void setPrint(PrintWriter print) {
this.print = print;
}
}

View File

@@ -0,0 +1,93 @@
/**
* 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.authority.indexer;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import java.util.HashSet;
import java.util.Set;
/**
* Consumer that takes care of the indexing of authority controlled metadata fields for installed/updated items
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityConsumer implements Consumer {
private static final Logger log = Logger.getLogger(AuthorityConsumer.class);
/** A set of all item IDs installed which need their authority updated **/
private Set<Integer> itemsToUpdateAuthority = null;
/** A set of item IDs who's metadata needs to be reindexed **/
private Set<Integer> itemsToReindex = null;
public void initialize() throws Exception {
}
public void consume(Context ctx, Event event) throws Exception {
if(itemsToUpdateAuthority == null){
itemsToUpdateAuthority = new HashSet<Integer>();
itemsToReindex = new HashSet<Integer>();
}
DSpaceObject dso = event.getSubject(ctx);
if(dso instanceof Item){
Item item = (Item) dso;
if(item.isArchived()){
if(!itemsToReindex.contains(item.getID()))
itemsToReindex.add(item.getID());
}
if(("ARCHIVED: " + true).equals(event.getDetail())){
itemsToUpdateAuthority.add(item.getID());
}
}
}
public void end(Context ctx) throws Exception {
if(itemsToUpdateAuthority == null)
return;
try{
ctx.turnOffAuthorisationSystem();
for (Integer id : itemsToUpdateAuthority) {
Item item = Item.find(ctx, id);
AuthorityIndexClient.indexItem(ctx, item);
}
//Loop over our items which need to be re indexed
for (Integer id : itemsToReindex) {
Item item = Item.find(ctx, id);
AuthorityIndexClient.indexItem(ctx, item);
}
} catch (Exception e){
log.error("Error while consuming the authority consumer", e);
} finally {
itemsToUpdateAuthority = null;
itemsToReindex = null;
ctx.restoreAuthSystemState();
}
}
public void finish(Context ctx) throws Exception {
}
}

View File

@@ -0,0 +1,100 @@
/**
* 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.authority.indexer;
import org.dspace.authority.AuthorityValue;
import org.apache.log4j.Logger;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.kernel.ServiceManager;
import org.dspace.utils.DSpace;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class AuthorityIndexClient {
private static Logger log = Logger.getLogger(AuthorityIndexClient.class);
public static void main(String[] args) throws Exception {
//Populate our solr
Context context = new Context();
ServiceManager serviceManager = getServiceManager();
AuthorityIndexingService indexingService = serviceManager.getServiceByName(AuthorityIndexingService.class.getName(),AuthorityIndexingService.class);
List<AuthorityIndexerInterface> indexers = serviceManager.getServicesByType(AuthorityIndexerInterface.class);
log.info("Cleaning the old index");
System.out.println("Cleaning the old index");
indexingService.cleanIndex();
//Get all our values from the input forms
Map<String, AuthorityValue> toIndexValues = new HashMap<String, AuthorityValue>();
for (AuthorityIndexerInterface indexerInterface : indexers) {
log.info("Initialize " + indexerInterface.getClass().getName());
System.out.println("Initialize " + indexerInterface.getClass().getName());
indexerInterface.init(context);
while (indexerInterface.hasMore()) {
AuthorityValue authorityValue = indexerInterface.nextValue();
if(authorityValue != null){
toIndexValues.put(authorityValue.getId(), authorityValue);
}
}
//Close up
indexerInterface.close();
}
for(String id : toIndexValues.keySet()){
indexingService.indexContent(toIndexValues.get(id), true);
}
//In the end commit our server
indexingService.commit();
context.abort();
System.out.println("All done !");
}
public static void indexItem(Context context, Item item){
ServiceManager serviceManager = getServiceManager();
AuthorityIndexingService indexingService = serviceManager.getServiceByName(AuthorityIndexingService.class.getName(),AuthorityIndexingService.class);
List<AuthorityIndexerInterface> indexers = serviceManager.getServicesByType(AuthorityIndexerInterface.class);
for (AuthorityIndexerInterface indexerInterface : indexers) {
indexerInterface.init(context , item);
while (indexerInterface.hasMore()) {
AuthorityValue authorityValue = indexerInterface.nextValue();
if(authorityValue != null)
indexingService.indexContent(authorityValue, true);
}
//Close up
indexerInterface.close();
}
//Commit to our server
indexingService.commit();
}
private static ServiceManager getServiceManager() {
//Retrieve our service
DSpace dspace = new DSpace();
return dspace.getServiceManager();
}
}

View File

@@ -0,0 +1,33 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.indexer;
import org.dspace.authority.AuthorityValue;
import org.dspace.content.Item;
import org.dspace.core.Context;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public interface AuthorityIndexerInterface {
public void init(Context context, Item item);
public void init(Context context);
public AuthorityValue nextValue();
public boolean hasMore();
public void close();
}

View File

@@ -0,0 +1,29 @@
/**
* 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.authority.indexer;
import org.dspace.authority.AuthorityValue;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public interface AuthorityIndexingService {
public void indexContent(AuthorityValue value, boolean force);
public void cleanIndex() throws Exception;
public void commit();
}

View File

@@ -0,0 +1,189 @@
/**
* 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.authority.indexer;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.AuthorityValueFinder;
import org.dspace.authority.AuthorityValueGenerator;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.content.DCValue;
import org.dspace.content.Item;
import org.dspace.content.ItemIterator;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* DSpaceAuthorityIndexer is used in IndexClient, which is called by the AuthorityConsumer and the indexing-script.
* <p/>
* An instance of DSpaceAuthorityIndexer is bound to a list of items.
* This can be one item or all items too depending on the init() method.
* <p/>
* DSpaceAuthorityIndexer lets you iterate over each metadata value
* for each metadata field defined in dspace.cfg with 'authority.author.indexer.field'
* for each item in the list.
* <p/>
* <p/>
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class DSpaceAuthorityIndexer implements AuthorityIndexerInterface {
private static final Logger log = Logger.getLogger(DSpaceAuthorityIndexer.class);
private ItemIterator itemIterator;
private Item currentItem;
/**
* The list of metadata fields which are to be indexed *
*/
private List<String> metadataFields;
private int currentFieldIndex;
private int currentMetadataIndex;
private AuthorityValue nextValue;
private Context context;
private AuthorityValueFinder authorityValueFinder;
public void init(Context context, Item item) {
ArrayList<Integer> itemList = new ArrayList<Integer>();
itemList.add(item.getID());
this.itemIterator = new ItemIterator(context, itemList);
try {
currentItem = this.itemIterator.next();
} catch (SQLException e) {
log.error("Error while retrieving an item in the metadata indexer");
}
initialize(context);
}
public void init(Context context) {
try {
this.itemIterator = Item.findAll(context);
currentItem = this.itemIterator.next();
} catch (SQLException e) {
log.error("Error while retrieving all items in the metadata indexer");
}
initialize(context);
}
private void initialize(Context context) {
this.context = context;
this.authorityValueFinder = new AuthorityValueFinder();
currentFieldIndex = 0;
currentMetadataIndex = 0;
int counter = 1;
String field;
metadataFields = new ArrayList<String>();
while ((field = ConfigurationManager.getProperty("authority.author.indexer.field." + counter)) != null) {
metadataFields.add(field);
counter++;
}
}
public AuthorityValue nextValue() {
return nextValue;
}
public boolean hasMore() {
if (currentItem == null) {
return false;
}
// 1. iterate over the metadata values
String metadataField = metadataFields.get(currentFieldIndex);
DCValue[] values = currentItem.getMetadata(metadataField);
if (currentMetadataIndex < values.length) {
prepareNextValue(metadataField, values[currentMetadataIndex]);
currentMetadataIndex++;
return true;
} else {
// 2. iterate over the metadata fields
if ((currentFieldIndex + 1) < metadataFields.size()) {
currentFieldIndex++;
//Reset our current metadata index since we are moving to another field
currentMetadataIndex = 0;
return hasMore();
} else {
// 3. iterate over the items
try {
if (itemIterator.hasNext()) {
currentItem = itemIterator.next();
//Reset our current field index
currentFieldIndex = 0;
//Reset our current metadata index
currentMetadataIndex = 0;
} else {
currentItem = null;
}
return hasMore();
} catch (SQLException e) {
currentItem = null;
log.error("Error while retrieving next item in the author indexer",e);
return false;
}
}
}
}
/**
* This method looks at the authority of a metadata.
* If the authority can be found in solr, that value is reused.
* Otherwise a new authority value will be generated that will be indexed in solr.
* If the authority starts with AuthorityValueGenerator.GENERATE, a specific type of AuthorityValue will be generated.
* Depending on the type this may involve querying an external REST service
*
* @param metadataField Is one of the fields defined in dspace.cfg to be indexed.
* @param value Is one of the values of the given metadataField in one of the items being indexed.
*/
private void prepareNextValue(String metadataField, DCValue value) {
nextValue = null;
String content = value.value;
String uid = value.authority;
if (StringUtils.isNotBlank(uid) && !uid.startsWith(AuthorityValueGenerator.GENERATE)) {
// !uid.startsWith(AuthorityValueGenerator.GENERATE) is not strictly necessary here but it prevents exceptions in solr
nextValue = authorityValueFinder.findByUID(context, uid);
}
if (nextValue == null) {
nextValue = AuthorityValueGenerator.generate(uid, content, metadataField.replaceAll("\\.", "_"));
}
if (nextValue != null) {
nextValue.updateItem(currentItem, value);
}
try {
currentItem.update();
} catch (Exception e) {
log.error("Error creating a metadatavalue's authority", e);
}
}
public void close() {
itemIterator.close();
itemIterator = null;
}
}

View File

@@ -0,0 +1,86 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.orcid.model.Bio;
import org.dspace.authority.orcid.model.Work;
import org.dspace.authority.orcid.xml.XMLtoBio;
import org.dspace.authority.orcid.xml.XMLtoWork;
import org.dspace.authority.rest.RestSource;
import org.apache.log4j.Logger;
import org.dspace.utils.DSpace;
import org.w3c.dom.Document;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class Orcid extends RestSource {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(Orcid.class);
private static Orcid orcid;
public static Orcid getOrcid() {
if (orcid == null) {
orcid = new DSpace().getServiceManager().getServiceByName("OrcidSource", Orcid.class);
}
return orcid;
}
private Orcid(String url) {
super(url);
}
public Bio getBio(String id) {
Document bioDocument = restConnector.get(id + "/orcid-bio");
XMLtoBio converter = new XMLtoBio();
Bio bio = converter.convert(bioDocument).get(0);
bio.setOrcid(id);
return bio;
}
public List<Work> getWorks(String id) {
Document document = restConnector.get(id + "/orcid-works");
XMLtoWork converter = new XMLtoWork();
return converter.convert(document);
}
public List<Bio> queryBio(String name, int start, int rows) {
Document bioDocument = restConnector.get("search/orcid-bio?q=" + URLEncoder.encode("\"" + name + "\"") + "&start=" + start + "&rows=" + rows);
XMLtoBio converter = new XMLtoBio();
return converter.convert(bioDocument);
}
@Override
public List<AuthorityValue> queryAuthorities(String text, int max) {
List<Bio> bios = queryBio(text, 0, max);
List<AuthorityValue> authorities = new ArrayList<AuthorityValue>();
for (Bio bio : bios) {
authorities.add(OrcidAuthorityValue.create(bio));
}
return authorities;
}
@Override
public AuthorityValue queryAuthorityID(String id) {
Bio bio = getBio(id);
return OrcidAuthorityValue.create(bio);
}
}

View File

@@ -0,0 +1,316 @@
/**
* 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.authority.orcid;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.AuthorityValueGenerator;
import org.dspace.authority.PersonAuthorityValue;
import org.dspace.authority.orcid.model.Bio;
import org.dspace.authority.orcid.model.BioExternalIdentifier;
import org.dspace.authority.orcid.model.BioName;
import org.dspace.authority.orcid.model.BioResearcherUrl;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import java.util.*;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class OrcidAuthorityValue extends PersonAuthorityValue {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(OrcidAuthorityValue.class);
private String orcid_id;
private Map<String, List<String>> otherMetadata = new HashMap<String, List<String>>();
private boolean update; // used in setValues(Bio bio)
/**
* Creates an instance of OrcidAuthorityValue with only uninitialized fields.
* This is meant to be filled in with values from an existing record.
* To create a brand new OrcidAuthorityValue, use create()
*/
public OrcidAuthorityValue() {
}
public OrcidAuthorityValue(SolrDocument document) {
super(document);
}
public String getOrcid_id() {
return orcid_id;
}
public void setOrcid_id(String orcid_id) {
this.orcid_id = orcid_id;
}
public Map<String, List<String>> getOtherMetadata() {
return otherMetadata;
}
public void addOtherMetadata(String label, String data) {
List<String> strings = otherMetadata.get(label);
if (strings == null) {
strings = new ArrayList<String>();
}
strings.add(data);
otherMetadata.put(label, strings);
}
@Override
public SolrInputDocument getSolrInputDocument() {
SolrInputDocument doc = super.getSolrInputDocument();
if (StringUtils.isNotBlank(getOrcid_id())) {
doc.addField("orcid_id", getOrcid_id());
}
for (String t : otherMetadata.keySet()) {
List<String> data = otherMetadata.get(t);
for (String data_entry : data) {
doc.addField("label_" + t, data_entry);
}
}
return doc;
}
@Override
public void setValues(SolrDocument document) {
super.setValues(document);
this.orcid_id = String.valueOf(document.getFieldValue("orcid_id"));
otherMetadata = new HashMap<String, List<String>>();
for (String fieldName : document.getFieldNames()) {
String labelPrefix = "label_";
if (fieldName.startsWith(labelPrefix)) {
String label = fieldName.substring(labelPrefix.length());
List<String> list = new ArrayList<String>();
Collection<Object> fieldValues = document.getFieldValues(fieldName);
for (Object o : fieldValues) {
list.add(String.valueOf(o));
}
otherMetadata.put(label, list);
}
}
}
public static OrcidAuthorityValue create() {
OrcidAuthorityValue orcidAuthorityValue = new OrcidAuthorityValue();
orcidAuthorityValue.setId(UUID.randomUUID().toString());
orcidAuthorityValue.updateLastModifiedDate();
orcidAuthorityValue.setCreationDate(new Date());
return orcidAuthorityValue;
}
/**
* Create an authority based on a given orcid bio
*/
public static OrcidAuthorityValue create(Bio bio) {
OrcidAuthorityValue authority = OrcidAuthorityValue.create();
authority.setValues(bio);
return authority;
}
public boolean setValues(Bio bio) {
BioName name = bio.getName();
if (updateValue(bio.getOrcid(), getOrcid_id())) {
setOrcid_id(bio.getOrcid());
}
if (updateValue(name.getFamilyName(), getLastName())) {
setLastName(name.getFamilyName());
}
if (updateValue(name.getGivenNames(), getFirstName())) {
setFirstName(name.getGivenNames());
}
if (StringUtils.isNotBlank(name.getCreditName())) {
if (!getNameVariants().contains(name.getCreditName())) {
addNameVariant(name.getCreditName());
update = true;
}
}
for (String otherName : name.getOtherNames()) {
if (!getNameVariants().contains(otherName)) {
addNameVariant(otherName);
update = true;
}
}
if (updateOtherMetadata("country", bio.getCountry())) {
addOtherMetadata("country", bio.getCountry());
}
for (String keyword : bio.getKeywords()) {
if (updateOtherMetadata("keyword", keyword)) {
addOtherMetadata("keyword", keyword);
}
}
for (BioExternalIdentifier externalIdentifier : bio.getBioExternalIdentifiers()) {
if (updateOtherMetadata("external_identifier", externalIdentifier.toString())) {
addOtherMetadata("external_identifier", externalIdentifier.toString());
}
}
for (BioResearcherUrl researcherUrl : bio.getResearcherUrls()) {
if (updateOtherMetadata("researcher_url", researcherUrl.toString())) {
addOtherMetadata("researcher_url", researcherUrl.toString());
}
}
if (updateOtherMetadata("biography", bio.getBiography())) {
addOtherMetadata("biography", bio.getBiography());
}
setValue(getName());
if (update) {
update();
}
boolean result = update;
update = false;
return result;
}
private boolean updateOtherMetadata(String label, String data) {
List<String> strings = getOtherMetadata().get(label);
boolean update;
if (strings == null) {
update = StringUtils.isNotBlank(data);
} else {
update = !strings.contains(data);
}
if (update) {
this.update = true;
}
return update;
}
private boolean updateValue(String incoming, String resident) {
boolean update = StringUtils.isNotBlank(incoming) && !incoming.equals(resident);
if (update) {
this.update = true;
}
return update;
}
@Override
public Map<String, String> choiceSelectMap() {
Map<String, String> map = super.choiceSelectMap();
map.put("orcid", getOrcid_id());
return map;
}
public String getAuthorityType() {
return "orcid";
}
@Override
public String generateString() {
String generateString = AuthorityValueGenerator.GENERATE + getAuthorityType() + AuthorityValueGenerator.SPLIT;
if (StringUtils.isNotBlank(getOrcid_id())) {
generateString += getOrcid_id();
}
return generateString;
}
@Override
public AuthorityValue newInstance(String info) {
AuthorityValue authorityValue = null;
if (StringUtils.isNotBlank(info)) {
Orcid orcid = Orcid.getOrcid();
authorityValue = orcid.queryAuthorityID(info);
} else {
authorityValue = OrcidAuthorityValue.create();
}
return authorityValue;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OrcidAuthorityValue that = (OrcidAuthorityValue) o;
if (orcid_id != null ? !orcid_id.equals(that.orcid_id) : that.orcid_id != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return orcid_id != null ? orcid_id.hashCode() : 0;
}
public boolean hasTheSameInformationAs(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.hasTheSameInformationAs(o)) {
return false;
}
OrcidAuthorityValue that = (OrcidAuthorityValue) o;
if (orcid_id != null ? !orcid_id.equals(that.orcid_id) : that.orcid_id != null) {
return false;
}
for (String key : otherMetadata.keySet()) {
if(otherMetadata.get(key) != null){
List<String> metadata = otherMetadata.get(key);
List<String> otherMetadata = that.otherMetadata.get(key);
if (otherMetadata == null) {
return false;
} else {
HashSet<String> metadataSet = new HashSet<String>(metadata);
HashSet<String> otherMetadataSet = new HashSet<String>(otherMetadata);
if (!metadataSet.equals(otherMetadataSet)) {
return false;
}
}
}else{
if(that.otherMetadata.get(key) != null){
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,113 @@
/**
* 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.authority.orcid.model;
import java.util.LinkedHashSet;
import java.util.Set;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class Bio {
protected String orcid;
protected BioName name;
protected String country;
protected Set<String> keywords;
protected Set<BioExternalIdentifier> bioExternalIdentifiers;
protected Set<BioResearcherUrl> researcherUrls;
protected String biography;
public Bio() {
this.name = new BioName();
keywords = new LinkedHashSet<String>();
bioExternalIdentifiers = new LinkedHashSet<BioExternalIdentifier>();
researcherUrls = new LinkedHashSet<BioResearcherUrl>();
}
public String getOrcid() {
return orcid;
}
public void setOrcid(String orcid) {
this.orcid = orcid;
}
public BioName getName() {
return name;
}
public void setName(BioName name) {
this.name = name;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Set<String> getKeywords() {
return keywords;
}
public void addKeyword(String keyword) {
this.keywords.add(keyword);
}
public Set<BioExternalIdentifier> getBioExternalIdentifiers() {
return bioExternalIdentifiers;
}
public void addExternalIdentifier(BioExternalIdentifier externalReference) {
bioExternalIdentifiers.add(externalReference);
}
public Set<BioResearcherUrl> getResearcherUrls() {
return researcherUrls;
}
public void addResearcherUrl(BioResearcherUrl researcherUrl) {
researcherUrls.add(researcherUrl);
}
public String getBiography() {
return biography;
}
public void setBiography(String biography) {
this.biography = biography;
}
@Override
public String toString() {
return "Bio{" +
"orcid='" + orcid + '\'' +
", name=" + name +
", country='" + country + '\'' +
", keywords=" + keywords +
", bioExternalIdentifiers=" + bioExternalIdentifiers +
", researcherUrls=" + researcherUrls +
", biography='" + biography + '\'' +
'}';
}
}

View File

@@ -0,0 +1,109 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid.model;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class BioExternalIdentifier {
protected String id_orcid;
protected String id_common_name;
protected String id_reference;
protected String id_url;
public BioExternalIdentifier(String id_orcid, String id_common_name, String id_reference, String id_url) {
this.id_orcid = id_orcid;
this.id_common_name = id_common_name;
this.id_reference = id_reference;
this.id_url = id_url;
}
public String getId_orcid() {
return id_orcid;
}
public void setId_orcid(String id_orcid) {
this.id_orcid = id_orcid;
}
public String getId_common_name() {
return id_common_name;
}
public void setId_common_name(String id_common_name) {
this.id_common_name = id_common_name;
}
public String getId_reference() {
return id_reference;
}
public void setId_reference(String id_reference) {
this.id_reference = id_reference;
}
public String getId_url() {
return id_url;
}
public void setId_url(String id_url) {
this.id_url = id_url;
}
@Override
public String toString() {
return "BioExternalIdentifier{" +
"id_orcid='" + id_orcid + '\'' +
", id_common_name='" + id_common_name + '\'' +
", id_reference='" + id_reference + '\'' +
", id_url='" + id_url + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BioExternalIdentifier that = (BioExternalIdentifier) o;
if (id_common_name != null ? !id_common_name.equals(that.id_common_name) : that.id_common_name != null) {
return false;
}
if (id_orcid != null ? !id_orcid.equals(that.id_orcid) : that.id_orcid != null) {
return false;
}
if (id_reference != null ? !id_reference.equals(that.id_reference) : that.id_reference != null) {
return false;
}
if (id_url != null ? !id_url.equals(that.id_url) : that.id_url != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = id_orcid != null ? id_orcid.hashCode() : 0;
result = 31 * result + (id_common_name != null ? id_common_name.hashCode() : 0);
result = 31 * result + (id_reference != null ? id_reference.hashCode() : 0);
result = 31 * result + (id_url != null ? id_url.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,115 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid.model;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class BioName {
protected String givenNames;
protected String familyName;
protected String creditName;
protected List<String> otherNames;
BioName() {
otherNames = new ArrayList<String>();
}
BioName(String givenNames, String familyName, String creditName, List<String> otherNames) {
this.givenNames = givenNames;
this.familyName = familyName;
this.creditName = creditName;
this.otherNames = otherNames;
}
public String getGivenNames() {
return givenNames;
}
public void setGivenNames(String givenNames) {
this.givenNames = givenNames;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getCreditName() {
return creditName;
}
public void setCreditName(String creditName) {
this.creditName = creditName;
}
public List<String> getOtherNames() {
return otherNames;
}
public void setOtherNames(List<String> otherNames) {
this.otherNames = otherNames;
}
@Override
public String toString() {
return "BioName{" +
"givenNames='" + givenNames + '\'' +
", familyName='" + familyName + '\'' +
", creditName='" + creditName + '\'' +
", otherNames=" + otherNames +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BioName bioName = (BioName) o;
if (creditName != null ? !creditName.equals(bioName.creditName) : bioName.creditName != null) {
return false;
}
if (familyName != null ? !familyName.equals(bioName.familyName) : bioName.familyName != null) {
return false;
}
if (givenNames != null ? !givenNames.equals(bioName.givenNames) : bioName.givenNames != null) {
return false;
}
if (otherNames != null ? !otherNames.equals(bioName.otherNames) : bioName.otherNames != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = givenNames != null ? givenNames.hashCode() : 0;
result = 31 * result + (familyName != null ? familyName.hashCode() : 0);
result = 31 * result + (creditName != null ? creditName.hashCode() : 0);
result = 31 * result + (otherNames != null ? otherNames.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,78 @@
/**
* 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.authority.orcid.model;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class BioResearcherUrl {
protected String name;
protected String url;
public BioResearcherUrl(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "BioResearcherUrl{" +
"name='" + name + '\'' +
", url='" + url + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BioResearcherUrl that = (BioResearcherUrl) o;
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
if (url != null ? !url.equals(that.url) : that.url != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (url != null ? url.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,50 @@
/**
* 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.authority.orcid.model;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class Citation {
private CitationType type;
private String citation;
public Citation(CitationType type, String citation) {
this.type = type;
this.citation = citation;
}
public CitationType getType() {
return type;
}
public void setType(CitationType type) {
this.type = type;
}
public String getCitation() {
return citation;
}
public void setCitation(String citation) {
this.citation = citation;
}
@Override
public String toString() {
return "Citation{" +
"type=" + type +
", citation='" + citation + '\'' +
'}';
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.authority.orcid.model;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public enum CitationType {
FORMATTED_UNSPECIFIED,
BIBTEX,
FORMATTED_APA,
FORMATTED_HARVARD,
FORMATTED_IEEE,
FORMATTED_MLA,
FORMATTED_VANCOUVER,
FORMATTED_CHICAGO
}

View File

@@ -0,0 +1,111 @@
/**
* 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.authority.orcid.model;
import java.util.Set;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class Contributor {
private String orcid;
private String creditName;
private String email;
private Set<ContributorAttribute> contributorAttributes;
public Contributor(String orcid, String creditName, String email, Set<ContributorAttribute> contributorAttributes) {
this.orcid = orcid;
this.creditName = creditName;
this.email = email;
this.contributorAttributes = contributorAttributes;
}
public String getOrcid() {
return orcid;
}
public void setOrcid(String orcid) {
this.orcid = orcid;
}
public String getCreditName() {
return creditName;
}
public void setCreditName(String creditName) {
this.creditName = creditName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<ContributorAttribute> getContributorAttributes() {
return contributorAttributes;
}
public void setContributorAttributes(Set<ContributorAttribute> contributorAttributes) {
this.contributorAttributes = contributorAttributes;
}
@Override
public String toString() {
return "Contributor{" +
"orcid='" + orcid + '\'' +
", creditName='" + creditName + '\'' +
", email='" + email + '\'' +
", contributorAttributes=" + contributorAttributes +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Contributor that = (Contributor) o;
if (contributorAttributes != null ? !contributorAttributes.equals(that.contributorAttributes) : that.contributorAttributes != null) {
return false;
}
if (creditName != null ? !creditName.equals(that.creditName) : that.creditName != null) {
return false;
}
if (email != null ? !email.equals(that.email) : that.email != null) {
return false;
}
if (orcid != null ? !orcid.equals(that.orcid) : that.orcid != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = orcid != null ? orcid.hashCode() : 0;
result = 31 * result + (creditName != null ? creditName.hashCode() : 0);
result = 31 * result + (email != null ? email.hashCode() : 0);
result = 31 * result + (contributorAttributes != null ? contributorAttributes.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,79 @@
/**
* 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.authority.orcid.model;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class ContributorAttribute {
private ContributorAttributeRole role;
private ContributorAttributeSequence sequence;
public ContributorAttribute(ContributorAttributeRole role, ContributorAttributeSequence sequence) {
this.role = role;
this.sequence = sequence;
}
public ContributorAttributeRole getRole() {
return role;
}
public void setRole(ContributorAttributeRole role) {
this.role = role;
}
public ContributorAttributeSequence getSequence() {
return sequence;
}
public void setSequence(ContributorAttributeSequence sequence) {
this.sequence = sequence;
}
@Override
public String toString() {
return "ContributorAttribute{" +
"role=" + role +
", sequence=" + sequence +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ContributorAttribute that = (ContributorAttribute) o;
if (role != that.role) {
return false;
}
if (sequence != that.sequence) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = role != null ? role.hashCode() : 0;
result = 31 * result + (sequence != null ? sequence.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,32 @@
/**
* 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.authority.orcid.model;
/**
* http://support.orcid.org/knowledgebase/articles/118843-anatomy-of-a-contributor
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public enum ContributorAttributeRole {
AUTHOR,
ASSIGNEE,
EDITOR,
CHAIR_OR_TRANSLATOR,
CO_INVESTIGATOR,
CO_INVENTOR,
GRADUATE_STUDENT,
OTHER_INVENTOR,
PRINCIPAL_INVESTIGATOR,
POSTDOCTORAL_RESEARCHER,
SUPPORT_STAFF
}

View File

@@ -0,0 +1,23 @@
/**
* 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.authority.orcid.model;
/**
* http://support.orcid.org/knowledgebase/articles/118843-anatomy-of-a-contributor
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public enum ContributorAttributeSequence {
FIRST,
ADDITIONAL
}

View File

@@ -0,0 +1,117 @@
/**
* 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.authority.orcid.model;
import java.util.Set;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class Work {
private WorkTitle workTitle;
private String description;
private Citation citation;
private WorkType workType;
private String publicationDate;
private WorkExternalIdentifier workExternalIdentifier;
private String url;
private Set<Contributor> contributors;
private String workSource;
public WorkTitle getWorkTitle() {
return workTitle;
}
public void setWorkTitle(WorkTitle workTitle) {
this.workTitle = workTitle;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Citation getCitation() {
return citation;
}
public void setCitation(Citation citation) {
this.citation = citation;
}
public WorkType getWorkType() {
return workType;
}
public void setWorkType(WorkType workType) {
this.workType = workType;
}
public String getPublicationDate() {
return publicationDate;
}
public void setPublicationDate(String publicationDate) {
this.publicationDate = publicationDate;
}
public WorkExternalIdentifier getWorkExternalIdentifier() {
return workExternalIdentifier;
}
public void setWorkExternalIdentifier(WorkExternalIdentifier workExternalIdentifier) {
this.workExternalIdentifier = workExternalIdentifier;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Set<Contributor> getContributors() {
return contributors;
}
public void setContributors(Set<Contributor> contributors) {
this.contributors = contributors;
}
public String getWorkSource() {
return workSource;
}
public void setWorkSource(String workSource) {
this.workSource = workSource;
}
@Override
public String toString() {
return "Work{" +
"workTitle=" + workTitle +
", description='" + description + '\'' +
", citation=" + citation +
", workType=" + workType +
", publicationDate='" + publicationDate + '\'' +
", workExternalIdentifier=" + workExternalIdentifier +
", url='" + url + '\'' +
", contributors=" + contributors +
", workSource='" + workSource + '\'' +
'}';
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.authority.orcid.model;
/**
* http://support.orcid.org/knowledgebase/articles/118807
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class WorkExternalIdentifier {
private WorkExternalIdentifierType workExternalIdentifierType;
private String workExternalIdenfitierID;
public WorkExternalIdentifier(WorkExternalIdentifierType workExternalIdentifierType, String workExternalIdenfitierID) {
this.workExternalIdentifierType = workExternalIdentifierType;
this.workExternalIdenfitierID = workExternalIdenfitierID;
}
public WorkExternalIdentifierType getWorkExternalIdentifierType() {
return workExternalIdentifierType;
}
public void setWorkExternalIdentifierType(WorkExternalIdentifierType workExternalIdentifierType) {
this.workExternalIdentifierType = workExternalIdentifierType;
}
@Override
public String toString() {
return "WorkExternalIdentifier{" +
"workExternalIdentifierType=" + workExternalIdentifierType +
", workExternalIdenfitierID='" + workExternalIdenfitierID + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
WorkExternalIdentifier that = (WorkExternalIdentifier) o;
if (workExternalIdenfitierID != null ? !workExternalIdenfitierID.equals(that.workExternalIdenfitierID) : that.workExternalIdenfitierID != null) {
return false;
}
if (workExternalIdentifierType != that.workExternalIdentifierType) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = workExternalIdentifierType != null ? workExternalIdentifierType.hashCode() : 0;
result = 31 * result + (workExternalIdenfitierID != null ? workExternalIdenfitierID.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,42 @@
/**
* 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.authority.orcid.model;
/**
* http://support.orcid.org/knowledgebase/articles/118807
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public enum WorkExternalIdentifierType {
// OTHER_ID,
ARXIV,
ASIN,
ASIN_TLD,
BIBCODE,
DOI,
EID,
ISBN,
ISSN,
JFM,
JSTOR,
LCCN,
MR,
OCLC,
OL,
OSTI,
PMC,
PMID,
RFC,
SSRN,
ZBL
}

View File

@@ -0,0 +1,64 @@
/**
* 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.authority.orcid.model;
import java.util.Map;
/**
* http://support.orcid.org/knowledgebase/articles/118807
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class WorkTitle {
private String title;
private String subtitle;
private Map<String, String> translatedTitles;
public WorkTitle(String title, String subtitle, Map<String, String> translatedTitles) {
this.title = title;
this.subtitle = subtitle;
this.translatedTitles = translatedTitles;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSubtitle() {
return subtitle;
}
public void setSubtitle(String subtitle) {
this.subtitle = subtitle;
}
public String getTranslatedTitles(String languageCode) {
return translatedTitles.get(languageCode);
}
public void setTranslatedTitle(String languageCode, String translatedTitle) {
translatedTitles.put(languageCode, translatedTitle);
}
@Override
public String toString() {
return "WorkTitle{" +
"title='" + title + '\'' +
", subtitle='" + subtitle + '\'' +
", translatedTitles=" + translatedTitles +
'}';
}
}

View File

@@ -0,0 +1,57 @@
/**
* 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.authority.orcid.model;
/**
* http://support.orcid.org/knowledgebase/articles/118795
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public enum WorkType {
BOOK,
BOOK_CHAPTER,
BOOK_REVIEW,
DICTIONARY_ENTRY,
DISSERTATION,
ENCYCLOPEDIA_ARTICLE,
EDITED_BOOK,
JOURNAL_ARTICLE,
JOURNAL_ISSUE,
MAGAZINE_ARTICLE,
MANUAL,
ONLINE_RESOURCE,
NEWSLETTER_ARTICLE,
NEWSPAPER_ARTICLE,
REPORT,
RESEARCH_TOOL,
SUPERVISED_STUDENT_PUBLICATION,
TEST,
TRANSLATION,
WEBSITE,
CONFERENCE_ABSTRACT,
CONFERENCE_PAPER,
CONFERENCE_POSTER,
DISCLOSURE,
LICENSE,
PATENT,
REGISTERED_COPYRIGHT,
ARTISTIC_PERFORMANCE,
DATA_SET,
INVENTION,
LECTURE_SPEECH,
RESEARCH_TECHNIQUE,
SPIN_OFF_COMPANY,
STANDARDS_AND_POLICY,
TECHNICAL_STANDARD,
OTHER
}

View File

@@ -0,0 +1,34 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.orcid.xml;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public abstract class Converter<T> {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(Converter.class);
protected void processError(Document xml) {
String errorMessage = XMLErrors.getErrorMessage(xml);
log.error("The orcid-message reports an error: " + errorMessage);
}
public abstract T convert(Document document);
}

View File

@@ -0,0 +1,73 @@
/**
* 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.authority.orcid.xml;
import org.dspace.authority.util.XMLUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import javax.xml.xpath.XPathExpressionException;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class XMLErrors {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(XMLErrors.class);
private static final String ERROR_DESC = "/orcid-message/error-desc";
/**
* Evaluates whether a given xml document contains errors or not.
*
* @param xml The given xml document
* @return true if the given xml document is null
* or if it contains errors
*/
public static boolean check(Document xml) {
if (xml == null) {
return true;
}
String textContent = null;
try {
textContent = XMLUtils.getTextContent(xml, ERROR_DESC);
} catch (XPathExpressionException e) {
log.error("Error while checking for errors in orcid message", e);
}
return textContent == null;
}
public static String getErrorMessage(Document xml) {
if (xml == null) {
return "Did not receive an XML document.";
}
String textContent = null;
try {
textContent = XMLUtils.getTextContent(xml, ERROR_DESC);
} catch (XPathExpressionException e) {
log.error("Error while checking for errors in orcid message", e);
}
return textContent;
}
}

View File

@@ -0,0 +1,251 @@
/**
* 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.authority.orcid.xml;
import org.dspace.authority.orcid.model.Bio;
import org.dspace.authority.orcid.model.BioExternalIdentifier;
import org.dspace.authority.orcid.model.BioName;
import org.dspace.authority.orcid.model.BioResearcherUrl;
import org.dspace.authority.util.XMLUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.xpath.XPathExpressionException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class XMLtoBio extends Converter {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(XMLtoBio.class);
/**
* orcid-message XPATHs
*/
protected String ORCID_BIO = "//orcid-bio";
// protected String ORCID = "parent::*/orcid";
protected String ORCID = "parent::*/orcid-identifier/path";
protected String PERSONAL_DETAILS = "personal-details";
protected String GIVEN_NAMES = PERSONAL_DETAILS + "/given-names";
protected String FAMILY_NAME = PERSONAL_DETAILS + "/family-name";
protected String CREDIT_NAME = PERSONAL_DETAILS + "/credit-name";
protected String OTHER_NAMES = PERSONAL_DETAILS + "/other-names";
protected String OTHER_NAME = OTHER_NAMES + "/other-name";
protected String CONTACT_DETAILS = "contact-details";
protected String COUNTRY = CONTACT_DETAILS + "/address/country";
protected String KEYWORDS = "keywords";
protected String KEYWORD = KEYWORDS + "/keyword";
protected String EXTERNAL_IDENTIFIERS = "external-identifiers";
protected String EXTERNAL_IDENTIFIER = EXTERNAL_IDENTIFIERS + "/external-identifier";
protected String EXTERNAL_ID_ORCID = "external-id-orcid";
protected String EXTERNAL_ID_COMMNON_NAME = "external-id-common-name";
protected String EXTERNAL_ID_REFERENCE = "external-id-reference";
protected String EXTERNAL_ID_URL = "external-id-url";
protected String RESEARCHER_URLS = "researcher-urls";
protected String RESEARCHER_URL = "researcher-urls/researcher-url";
protected String URL_NAME = "url-name";
protected String URL = "url";
protected String BIOGRAPHY = ORCID_BIO + "/biography";
protected String AFFILIATIONS = ORCID_BIO + "/affiliation";
/**
* Regex
*/
protected String ORCID_NOT_FOUND = "ORCID [\\d-]* not found";
public List<Bio> convert(Document xml) {
List<Bio> result = new ArrayList<Bio>();
if (XMLErrors.check(xml)) {
try {
Iterator<Node> iterator = XMLUtils.getNodeListIterator(xml, ORCID_BIO);
while (iterator.hasNext()) {
Bio bio = convertBio(iterator.next());
result.add(bio);
}
} catch (XPathExpressionException e) {
log.error("Error in xpath syntax", e);
}
} else {
processError(xml);
}
return result;
}
private Bio convertBio(Node node) {
Bio bio = new Bio();
setOrcid(node,bio);
setPersonalDetails(node, bio);
setContactDetails(node, bio);
setKeywords(node, bio);
setExternalIdentifiers(node, bio);
setResearcherUrls(node, bio);
setBiography(node, bio);
return bio;
}
protected void processError(Document xml) {
String errorMessage = XMLErrors.getErrorMessage(xml);
if(errorMessage.matches(ORCID_NOT_FOUND))
{
// do something?
}
log.error("The orcid-message reports an error: " + errorMessage);
}
private void setOrcid(Node node, Bio bio) {
try {
String orcid = XMLUtils.getTextContent(node, ORCID);
bio.setOrcid(orcid);
} catch (XPathExpressionException e) {
log.debug("Error in finding the biography in bio xml.", e);
}
}
protected void setBiography(Node xml, Bio bio) {
try {
String biography = XMLUtils.getTextContent(xml, BIOGRAPHY);
bio.setBiography(biography);
} catch (XPathExpressionException e) {
log.error("Error in finding the biography in bio xml.", e);
}
}
protected void setResearcherUrls(Node xml, Bio bio) {
try {
NodeList researcher_urls = XMLUtils.getNodeList(xml, RESEARCHER_URL);
if (researcher_urls != null) {
for (int i = 0; i < researcher_urls.getLength(); i++) {
Node researcher_url = researcher_urls.item(i);
if (researcher_url.getNodeType() != Node.TEXT_NODE) {
String url_name = XMLUtils.getTextContent(researcher_url, URL_NAME);
String url = XMLUtils.getTextContent(researcher_url, URL);
BioResearcherUrl researcherUrl = new BioResearcherUrl(url_name, url);
bio.addResearcherUrl(researcherUrl);
}
}
}
} catch (XPathExpressionException e) {
log.error("Error in finding the researcher url in bio xml.", e);
}
}
protected void setExternalIdentifiers(Node xml, Bio bio) {
try {
Iterator<Node> iterator = XMLUtils.getNodeListIterator(xml, EXTERNAL_IDENTIFIER);
while (iterator.hasNext()) {
Node external_identifier = iterator.next();
String id_orcid = XMLUtils.getTextContent(external_identifier, EXTERNAL_ID_ORCID);
String id_common_name = XMLUtils.getTextContent(external_identifier, EXTERNAL_ID_COMMNON_NAME);
String id_reference = XMLUtils.getTextContent(external_identifier, EXTERNAL_ID_REFERENCE);
String id_url = XMLUtils.getTextContent(external_identifier, EXTERNAL_ID_URL);
BioExternalIdentifier externalIdentifier = new BioExternalIdentifier(id_orcid, id_common_name, id_reference, id_url);
bio.addExternalIdentifier(externalIdentifier);
}
} catch (XPathExpressionException e) {
log.error("Error in finding the external identifier in bio xml.", e);
}
}
protected void setKeywords(Node xml, Bio bio) {
try {
NodeList keywords = XMLUtils.getNodeList(xml, KEYWORD);
if (keywords != null) {
for (int i = 0; i < keywords.getLength(); i++) {
String keyword = keywords.item(i).getTextContent();
String[] split = keyword.split(",");
for (String k : split) {
bio.addKeyword(k.trim());
}
}
}
} catch (XPathExpressionException e) {
log.error("Error in finding the keywords in bio xml.", e);
}
}
protected void setContactDetails(Node xml, Bio bio) {
try {
String country = XMLUtils.getTextContent(xml, COUNTRY);
bio.setCountry(country);
} catch (XPathExpressionException e) {
log.error("Error in finding the country in bio xml.", e);
}
}
protected void setPersonalDetails(Node xml, Bio bio) {
BioName name = bio.getName();
try {
String givenNames = XMLUtils.getTextContent(xml, GIVEN_NAMES);
name.setGivenNames(givenNames);
} catch (XPathExpressionException e) {
log.error("Error in finding the given names in bio xml.", e);
}
try {
String familyName = XMLUtils.getTextContent(xml, FAMILY_NAME);
name.setFamilyName(familyName);
} catch (XPathExpressionException e) {
log.error("Error in finding the family name in bio xml.", e);
}
try {
String creditName = XMLUtils.getTextContent(xml, CREDIT_NAME);
name.setCreditName(creditName);
} catch (XPathExpressionException e) {
log.error("Error in finding the credit name in bio xml.", e);
}
try {
Iterator<Node> iterator = XMLUtils.getNodeListIterator(xml, OTHER_NAME);
while (iterator.hasNext()) {
Node otherName = iterator.next();
String textContent = otherName.getTextContent();
name.getOtherNames().add(textContent.trim());
}
} catch (XPathExpressionException e) {
log.error("Error in finding the other names in bio xml.", e);
}
}
}

View File

@@ -0,0 +1,239 @@
/**
* 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.authority.orcid.xml;
import org.dspace.authority.orcid.model.*;
import org.dspace.authority.util.*;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.xpath.XPathExpressionException;
import java.util.*;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class XMLtoWork extends Converter {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(XMLtoWork.class);
/**
* orcid-message XPATHs
*/
protected String ORCID_WORKS = "//orcid-works";
protected String ORCID_WORK = ORCID_WORKS + "/orcid-work";
protected String WORK_TITLE = "work-title";
protected String TITLE = WORK_TITLE + "/title";
protected String SUBTITLE = WORK_TITLE + "/subtitle";
protected String TRANSLATED_TITLES = WORK_TITLE + "/translated-title";
protected String TRANSLATED_TITLES_LANGUAGE = "@language-code";
protected String SHORT_DESCRIPTION = "short-description";
protected String WORK_CITATION = "work-citation";
protected String CITATION_TYPE = WORK_CITATION + "/work-citation-type";
protected String CITATION = WORK_CITATION + "/citation";
protected String WORK_TYPE = "work-type";
protected String PUBLICATION_DATE = "publication-date";
protected String YEAR = PUBLICATION_DATE + "/year";
protected String MONTH = PUBLICATION_DATE + "/month";
protected String DAY = PUBLICATION_DATE + "/day";
protected String WORK_EXTERNAL_IDENTIFIERS = "work-external-identifiers";
protected String WORK_EXTERNAL_IDENTIFIER = WORK_EXTERNAL_IDENTIFIERS + "/work-external-identifier";
protected String WORK_EXTERNAL_IDENTIFIER_TYPE = "work-external-identifier-type";
protected String WORK_EXTERNAL_IDENTIFIER_ID = "work-external-identifier-id";
protected String URL = "url";
protected String WORK_CONTRIBUTOR = "work-contributors";
protected String CONTRIBUTOR = WORK_CONTRIBUTOR+"/contributor";
protected String CONTRIBUTOR_ORCID = "contributor-orcid";
protected String CREDIT_NAME = "credit-name";
protected String CONTRIBUTOR_EMAIL = "contributor-email";
protected String CONTRIBUTOR_ATTRIBUTES = "contributor-attributes";
protected String CONTRIBUTOR_SEQUENCE = "contributor-sequence";
protected String CONTRIBUTOR_ROLE = "contributor-role";
protected String WORK_SOURCE = "work-source";
public List<Work> convert(Document document) {
List<Work> result = new ArrayList<Work>();
if (XMLErrors.check(document)) {
try {
Iterator<Node> iterator = XMLUtils.getNodeListIterator(document, ORCID_WORK);
while (iterator.hasNext()) {
Work work = convertWork(iterator.next());
result.add(work);
}
} catch (XPathExpressionException e) {
log.error("Error in xpath syntax", e);
}
} else {
processError(document);
}
return result;
}
protected Work convertWork(Node node) throws XPathExpressionException {
Work work = new Work();
setTitle(node, work);
setDescription(node, work);
setCitation(node, work);
setWorkType(node, work);
setPublicationDate(node, work);
setExternalIdentifiers(node, work);
setUrl(node, work);
setContributors(node, work);
setWorkSource(node, work);
return work;
}
protected void setWorkSource(Node node, Work work) throws XPathExpressionException {
String workSource = XMLUtils.getTextContent(node, WORK_SOURCE);
work.setWorkSource(workSource);
}
protected void setContributors(Node node, Work work) throws XPathExpressionException {
Set<Contributor> contributors = new HashSet<Contributor>();
Iterator<Node> iterator = XMLUtils.getNodeListIterator(node, CONTRIBUTOR);
while (iterator.hasNext()) {
Node nextContributorNode = iterator.next();
String orcid = XMLUtils.getTextContent(nextContributorNode, CONTRIBUTOR_ORCID);
String creditName = XMLUtils.getTextContent(nextContributorNode, CREDIT_NAME);
String email = XMLUtils.getTextContent(nextContributorNode, CONTRIBUTOR_EMAIL);
Set<ContributorAttribute> contributorAttributes = new HashSet<ContributorAttribute>();
NodeList attributeNodes = XMLUtils.getNodeList(nextContributorNode, CONTRIBUTOR_ATTRIBUTES);
Iterator<Node> attributesIterator = XMLUtils.getNodeListIterator(attributeNodes);
while (attributesIterator.hasNext()) {
Node nextAttribute = attributesIterator.next();
String roleText = XMLUtils.getTextContent(nextAttribute, CONTRIBUTOR_ROLE);
ContributorAttributeRole role = EnumUtils.lookup(ContributorAttributeRole.class, roleText);
String sequenceText = XMLUtils.getTextContent(nextAttribute, CONTRIBUTOR_SEQUENCE);
ContributorAttributeSequence sequence = EnumUtils.lookup(ContributorAttributeSequence.class, sequenceText);
ContributorAttribute attribute = new ContributorAttribute(role, sequence);
contributorAttributes.add(attribute);
}
Contributor contributor = new Contributor(orcid, creditName, email, contributorAttributes);
contributors.add(contributor);
}
work.setContributors(contributors);
}
protected void setUrl(Node node, Work work) throws XPathExpressionException {
String url = XMLUtils.getTextContent(node, URL);
work.setUrl(url);
}
protected void setExternalIdentifiers(Node node, Work work) throws XPathExpressionException {
Iterator<Node> iterator = XMLUtils.getNodeListIterator(node, WORK_EXTERNAL_IDENTIFIER);
while (iterator.hasNext()) {
Node work_external_identifier = iterator.next();
String typeText = XMLUtils.getTextContent(work_external_identifier, WORK_EXTERNAL_IDENTIFIER_TYPE);
WorkExternalIdentifierType type = EnumUtils.lookup(WorkExternalIdentifierType.class, typeText);
String id = XMLUtils.getTextContent(work_external_identifier, WORK_EXTERNAL_IDENTIFIER_ID);
WorkExternalIdentifier externalID = new WorkExternalIdentifier(type, id);
work.setWorkExternalIdentifier(externalID);
}
}
protected void setPublicationDate(Node node, Work work) throws XPathExpressionException {
String year = XMLUtils.getTextContent(node, YEAR);
String month = XMLUtils.getTextContent(node, MONTH);
String day = XMLUtils.getTextContent(node, DAY);
String publicationDate = year;
if (StringUtils.isNotBlank(month)) {
publicationDate += "-" + month;
if (StringUtils.isNotBlank(day)) {
publicationDate += "-" + day;
}
}
work.setPublicationDate(publicationDate);
}
protected void setWorkType(Node node, Work work) throws XPathExpressionException {
String workTypeText = XMLUtils.getTextContent(node, WORK_TYPE);
WorkType workType = EnumUtils.lookup(WorkType.class, workTypeText);
work.setWorkType(workType);
}
protected void setCitation(Node node, Work work) throws XPathExpressionException {
String typeText = XMLUtils.getTextContent(node, CITATION_TYPE);
CitationType type = EnumUtils.lookup(CitationType.class, typeText);
String citationtext = XMLUtils.getTextContent(node, CITATION);
Citation citation = new Citation(type, citationtext);
work.setCitation(citation);
}
protected void setDescription(Node node, Work work) throws XPathExpressionException {
String description = null;
description = XMLUtils.getTextContent(node, SHORT_DESCRIPTION);
work.setDescription(description);
}
protected void setTitle(Node node, Work work) throws XPathExpressionException {
String title = XMLUtils.getTextContent(node, TITLE);
String subtitle = XMLUtils.getTextContent(node, SUBTITLE);
Map<String, String> translatedTitles = new HashMap<String, String>();
NodeList nodeList = XMLUtils.getNodeList(node, TRANSLATED_TITLES);
Iterator<Node> iterator = XMLUtils.getNodeListIterator(nodeList);
while (iterator.hasNext()) {
Node languageNode = iterator.next();
String language = XMLUtils.getTextContent(languageNode, TRANSLATED_TITLES_LANGUAGE);
String translated_title = XMLUtils.getTextContent(languageNode, ".");
translatedTitles.put(language, translated_title);
}
WorkTitle workTitle = new WorkTitle(title, subtitle, translatedTitles);
work.setWorkTitle(workTitle);
}
}

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.authority.rest;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class HttpClientFactory {
public static HttpClient getNewHttpClient(boolean ignoreSSL) throws NoSuchAlgorithmException, KeyManagementException {
HttpClient httpClient;
if (ignoreSSL) {
httpClient = isIgnoreSSLCertificate();
} else {
httpClient = HttpClientBuilder.create().build();
}
return httpClient;
}
protected static HttpClient isIgnoreSSLCertificate() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("SSL");
// set up a TrustManager that trusts everything
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
// System.out.println("getAcceptedIssuers =============");
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
// System.out.println("checkClientTrusted =============");
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
// System.out.println("checkServerTrusted =============");
}
}}, new SecureRandom());
return HttpClientBuilder.create().setSslcontext(sslContext).build();
/*
HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
SSLSocketFactory sf = new SSLSocketFactory(sslContext);
sf.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
Scheme httpsScheme = new Scheme("https", 443, sf);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(httpsScheme);
return new DefaultHttpClient(new BasicClientConnectionManager(schemeRegistry));
*/
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.authority.rest;
import org.dspace.authority.util.XMLUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.w3c.dom.Document;
import java.io.InputStream;
import java.util.Scanner;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class RESTConnector {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(RESTConnector.class);
private String url;
public RESTConnector(String url) {
this.url = url;
}
public Document get(String path) {
Document document = null;
InputStream result = null;
path = trimSlashes(path);
String fullPath = url + '/' + path;
HttpGet httpGet = new HttpGet(fullPath);
try {
boolean ignoreSSL = ConfigurationManager.getBooleanProperty("orcid", "httpclient.ignoressl");
HttpClient httpClient = HttpClientFactory.getNewHttpClient(ignoreSSL);
HttpResponse getResponse = httpClient.execute(httpGet);
//do not close this httpClient
result = getResponse.getEntity().getContent();
document = XMLUtils.convertStreamToXML(result);
} catch (Exception e) {
getGotError(e, fullPath);
}
return document;
}
protected void getGotError(Exception e, String fullPath) {
log.error("Error in rest connector for path: "+fullPath, e);
}
public static String trimSlashes(String path) {
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
while (path.startsWith("/")) {
path = path.substring(1);
}
return path;
}
public static String convertStreamToString(InputStream is) {
Scanner s = new Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.authority.rest;
import org.dspace.authority.AuthorityValue;
import java.util.List;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public abstract class RestSource {
protected RESTConnector restConnector;
public RestSource(String url) {
this.restConnector = new RESTConnector(url);
}
/**
* TODO
* com.atmire.org.dspace.authority.rest.RestSource#queryAuthorities -> add field, so the source can decide whether to query /users or something else.
* -> implement subclasses
* -> implement usages
*/
public abstract List<AuthorityValue> queryAuthorities(String text, int max);
public abstract AuthorityValue queryAuthorityID(String id);
}

View File

@@ -0,0 +1,41 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.authority.util;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class EnumUtils {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(EnumUtils.class);
private static String getEnumName(String value) {
return StringUtils.isNotBlank(value) ?
value.toUpperCase().trim().replaceAll("[^a-zA-Z]", "_")
: null;
}
public static <E extends Enum<E>> E lookup(Class<E> enumClass, String enumName) {
try {
return Enum.valueOf(enumClass, getEnumName(enumName));
} catch (Exception ex) {
log.warn("Did not find an "+enumClass.getSimpleName()+" for value '"+enumName+"'");
return null;
}
}
}

View File

@@ -0,0 +1,153 @@
/**
* 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.authority.util;
import org.apache.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class XMLUtils {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(XMLUtils.class);
/**
* @param xml The starting context (a Node or a Document, for example).
* @return node.getTextContent() on the node that matches singleNodeXPath
* null if nothing matches the NodeListXPath
*/
public static String getTextContent(Node xml, String singleNodeXPath) throws XPathExpressionException {
String text = null;
Node node = getNode(xml, singleNodeXPath);
if (node != null) {
text = node.getTextContent();
}
return text;
}
/**
* @param xml The starting context (a Node or a Document, for example).
* @return A Node matches the NodeListXPath
* null if nothing matches the NodeListXPath
*/
public static Node getNode(Node xml, String NodeListXPath) throws XPathExpressionException {
Node result = null;
try {
result = XPathAPI.selectSingleNode(xml, NodeListXPath);
} catch (TransformerException e) {
log.error("Error", e);
}
return result;
}
/**
* @param xml The starting context (a Node or a Document, for example).
* @return A NodeList containing the nodes that match the NodeListXPath
* null if nothing matches the NodeListXPath
*/
public static NodeList getNodeList(Node xml, String NodeListXPath) throws XPathExpressionException {
NodeList nodeList = null;
try {
nodeList = XPathAPI.selectNodeList(xml, NodeListXPath);
} catch (TransformerException e) {
log.error("Error", e);
}
return nodeList;
}
public static Iterator<Node> getNodeListIterator(Node xml, String NodeListXPath) throws XPathExpressionException {
return getNodeListIterator(getNodeList(xml, NodeListXPath));
}
/**
* Creates an iterator for all direct child nodes within a given NodeList
* that are element nodes:
* node.getNodeType() == Node.ELEMENT_NODE
* node instanceof Element
*/
public static Iterator<Node> getNodeListIterator(final NodeList nodeList) {
return new Iterator<Node>() {
private Iterator<Node> nodeIterator;
private Node lastNode;
{
ArrayList<Node> nodes = new ArrayList<Node>();
if (nodeList != null) {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
//if (node.getNodeType() != Node.TEXT_NODE) {
if (node.getNodeType() == Node.ELEMENT_NODE && node instanceof Element) {
nodes.add(node);
}
}
}
nodeIterator = nodes.iterator();
}
@Override
public boolean hasNext() {
return nodeIterator.hasNext();
}
@Override
public Node next() {
lastNode = nodeIterator.next();
return lastNode;
}
@Override
public void remove() {
nodeIterator.remove();
// lastNode.drop();
}
};
}
public static Document convertStreamToXML(InputStream is) {
Document result = null;
if (is != null) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
result = builder.parse(is);
} catch (ParserConfigurationException e) {
log.error("Error", e);
} catch (SAXException e) {
log.error("Error", e);
} catch (IOException e) {
log.error("Error", e);
}
}
return result;
}
}

View File

@@ -45,7 +45,84 @@ public class DCValue
* Get the field in dot notation. i.e. schema.element.qualifier, as in dc.date.issued * Get the field in dot notation. i.e. schema.element.qualifier, as in dc.date.issued
* @return * @return
*/ */
public DCValue copy() {
DCValue copy = new DCValue();
copy.value = this.value;
copy.authority = this.authority;
copy.confidence = this.confidence;
copy.element = this.element;
copy.language = this.language;
copy.qualifier = this.qualifier;
copy.schema = this.schema;
return copy;
}
public String getField() { public String getField() {
return schema + "." + element + (qualifier==null?"":("." + qualifier)); return schema + "." + element + (qualifier==null?"":("." + qualifier));
} }
public boolean hasSameFieldAs(DCValue dcValue) {
if (dcValue == this) {
return true;
}
if (dcValue.element != null ? !dcValue.element.equals(this.element) : this.element != null) {
return false;
}
if (dcValue.qualifier != null ? !dcValue.qualifier.equals(this.qualifier) : this.qualifier != null) {
return false;
}
if (dcValue.schema != null ? !dcValue.schema.equals(this.schema) : this.schema != null) {
return false;
}
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DCValue dcValue = (DCValue) o;
if (confidence != dcValue.confidence) {
return false;
}
if (authority != null ? !authority.equals(dcValue.authority) : dcValue.authority != null) {
return false;
}
if (element != null ? !element.equals(dcValue.element) : dcValue.element != null) {
return false;
}
if (language != null ? !language.equals(dcValue.language) : dcValue.language != null) {
return false;
}
if (qualifier != null ? !qualifier.equals(dcValue.qualifier) : dcValue.qualifier != null) {
return false;
}
if (schema != null ? !schema.equals(dcValue.schema) : dcValue.schema != null) {
return false;
}
if (value != null ? !value.equals(dcValue.value) : dcValue.value != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = element != null ? element.hashCode() : 0;
result = 31 * result + (qualifier != null ? qualifier.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (language != null ? language.hashCode() : 0);
result = 31 * result + (schema != null ? schema.hashCode() : 0);
result = 31 * result + (authority != null ? authority.hashCode() : 0);
result = 31 * result + confidence;
return result;
}
} }

View File

@@ -24,6 +24,9 @@ import org.dspace.authorize.AuthorizeManager;
import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.ResourcePolicy;
import org.dspace.browse.BrowseException; import org.dspace.browse.BrowseException;
import org.dspace.browse.IndexBrowse; import org.dspace.browse.IndexBrowse;
import org.dspace.content.authority.ChoiceAuthorityManager;
import org.dspace.content.authority.Choices;
import org.dspace.content.authority.MetadataAuthorityManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
@@ -32,6 +35,7 @@ import org.dspace.content.authority.ChoiceAuthorityManager;
import org.dspace.event.Event; import org.dspace.event.Event;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.identifier.IdentifierException; import org.dspace.identifier.IdentifierException;
import org.dspace.identifier.IdentifierService; import org.dspace.identifier.IdentifierService;
@@ -41,6 +45,12 @@ import org.dspace.storage.rdbms.TableRowIterator;
import org.dspace.utils.DSpace; import org.dspace.utils.DSpace;
import org.dspace.versioning.VersioningService; import org.dspace.versioning.VersioningService;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;
/** /**
* Class representing an item in DSpace. * Class representing an item in DSpace.
* <P> * <P>

View File

@@ -7,6 +7,8 @@
*/ */
package org.dspace.content.authority; package org.dspace.content.authority;
import java.util.Map;
/** /**
* Record class to hold the data describing one option, or choice, for an * Record class to hold the data describing one option, or choice, for an
* authority-controlled metadata value. * authority-controlled metadata value.
@@ -25,6 +27,8 @@ public class Choice
/** The canonical text value to insert into MetadataValue's text field */ /** The canonical text value to insert into MetadataValue's text field */
public String value = null; public String value = null;
public Map<String, String> extras = null;
public Choice() public Choice()
{ {
} }
@@ -35,4 +39,11 @@ public class Choice
this.value = value; this.value = value;
this.label = label; this.label = label;
} }
public Choice(String authority, String label, String value, Map<String, String> extras) {
this.authority = authority;
this.label = label;
this.value = value;
this.extras = extras;
}
} }

View File

@@ -18,6 +18,11 @@ import org.dspace.content.MetadataField;
import org.dspace.core.ConfigurationManager; import org.dspace.core.ConfigurationManager;
import org.dspace.core.PluginManager; import org.dspace.core.PluginManager;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* Broker for ChoiceAuthority plugins, and for other information configured * Broker for ChoiceAuthority plugins, and for other information configured
* about the choice aspect of authority control for a metadata field. * about the choice aspect of authority control for a metadata field.
@@ -197,6 +202,19 @@ public final class ChoiceAuthorityManager
return ma.getMatches(fieldKey, query, collection, start, limit, locale); return ma.getMatches(fieldKey, query, collection, start, limit, locale);
} }
public Choices getMatches(String fieldKey, String query, int collection, int start, int limit, String locale, boolean externalInput) {
ChoiceAuthority ma = controller.get(fieldKey);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for field \"" + fieldKey
+ "\".");
}
if (externalInput && ma instanceof SolrAuthority) {
((SolrAuthority)ma).addExternalResultsInNextMatches();
}
return ma.getMatches(fieldKey, query, collection, start, limit, locale);
}
/** /**
* Wrapper that calls getBestMatch method of the plugin corresponding to * Wrapper that calls getBestMatch method of the plugin corresponding to
* the metadata field defined by single field key. * the metadata field defined by single field key.

View File

@@ -102,6 +102,12 @@ public class ChoicesXMLGenerator
AttributesImpl va = new AttributesImpl(); AttributesImpl va = new AttributesImpl();
va.addAttribute("", "authority", "authority", "string", mdav.authority == null ? "":mdav.authority); va.addAttribute("", "authority", "authority", "string", mdav.authority == null ? "":mdav.authority);
va.addAttribute("", "value", "value", "string", mdav.value); va.addAttribute("", "value", "value", "string", mdav.value);
if(mdav.extras != null){
for(String extraLabel : mdav.extras.keySet()){
va.addAttribute("", extraLabel, extraLabel, "string", mdav.extras.get(extraLabel));
}
}
if (result.defaultSelected == i) if (result.defaultSelected == i)
{ {
va.addAttribute("", "selected", "selected", "boolean", ""); va.addAttribute("", "selected", "selected", "boolean", "");

View File

@@ -0,0 +1,258 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.authority;
import org.dspace.authority.AuthoritySearchService;
import org.dspace.authority.AuthorityValue;
import org.dspace.authority.rest.RestSource;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CommonParams;
import org.dspace.core.ConfigurationManager;
import org.dspace.utils.DSpace;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
*
* @author Antoine Snyers (antoine at atmire.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
*/
public class SolrAuthority implements ChoiceAuthority {
private static final Logger log = Logger.getLogger(SolrAuthority.class);
private RestSource source = new DSpace().getServiceManager().getServiceByName("AuthoritySource", RestSource.class);
private boolean externalResults = false;
public Choices getMatches(String field, String text, int collection, int start, int limit, String locale, boolean bestMatch) {
if(limit == 0)
limit = 10;
SolrQuery queryArgs = new SolrQuery();
if (text == null || text.trim().equals("")) {
queryArgs.setQuery("*:*");
} else {
String searchField = "value";
String localSearchField = "";
try {
//A downside of the authors is that the locale is sometimes a number, make sure that this isn't one
Integer.parseInt(locale);
locale = null;
} catch (NumberFormatException e) {
//Everything is allright
}
if (locale != null && !"".equals(locale)) {
localSearchField = searchField + "_" + locale;
}
String query = "(" + toQuery(searchField, text) + ") ";
if (!localSearchField.equals("")) {
query += " or (" + toQuery(localSearchField, text) + ")";
}
queryArgs.setQuery(query);
}
queryArgs.addFilterQuery("field:" + field);
queryArgs.set(CommonParams.START, start);
//We add one to our facet limit so that we know if there are more matches
int maxNumberOfSolrResults = limit + 1;
if(externalResults){
maxNumberOfSolrResults = ConfigurationManager.getIntProperty("xmlui.lookup.select.size", 12);
}
queryArgs.set(CommonParams.ROWS, maxNumberOfSolrResults);
String sortField = "value";
String localSortField = "";
if (StringUtils.isNotBlank(locale)) {
localSortField = sortField + "_" + locale;
queryArgs.setSortField(localSortField, SolrQuery.ORDER.asc);
} else {
queryArgs.setSortField(sortField, SolrQuery.ORDER.asc);
}
Choices result;
try {
int max = 0;
boolean hasMore = false;
QueryResponse searchResponse = getSearchService().search(queryArgs);
SolrDocumentList authDocs = searchResponse.getResults();
ArrayList<Choice> choices = new ArrayList<Choice>();
if (authDocs != null) {
max = (int) searchResponse.getResults().getNumFound();
int maxDocs = authDocs.size();
if (limit < maxDocs)
maxDocs = limit;
List<AuthorityValue> alreadyPresent = new ArrayList<AuthorityValue>();
for (int i = 0; i < maxDocs; i++) {
SolrDocument solrDocument = authDocs.get(i);
if (solrDocument != null) {
AuthorityValue val = AuthorityValue.fromSolr(solrDocument);
Map<String, String> extras = val.choiceSelectMap();
extras.put("insolr", val.getId());
choices.add(new Choice(val.getId(), val.getValue(), val.getValue(), extras));
alreadyPresent.add(val);
}
}
if (externalResults && StringUtils.isNotBlank(text)) {
int sizeFromSolr = alreadyPresent.size();
int maxExternalResults = limit <= 10 ? Math.max(limit - sizeFromSolr, 2) : Math.max(limit - 10 - sizeFromSolr, 2) + limit - 10;
addExternalResults(text, choices, alreadyPresent, maxExternalResults);
}
// hasMore = (authDocs.size() == (limit + 1));
hasMore = true;
}
int confidence;
if (choices.size() == 0)
confidence = Choices.CF_NOTFOUND;
else if (choices.size() == 1)
confidence = Choices.CF_UNCERTAIN;
else
confidence = Choices.CF_AMBIGUOUS;
result = new Choices(choices.toArray(new Choice[choices.size()]), start, hasMore ? max : choices.size() + start, confidence, hasMore);
} catch (Exception e) {
log.error("Error while retrieving authority values {field: " + field + ", prefix:" + text + "}", e);
result = new Choices(true);
}
return result; //To change body of implemented methods use File | Settings | File Templates.
}
protected void addExternalResults(String text, ArrayList<Choice> choices, List<AuthorityValue> alreadyPresent, int max) {
if(source != null){
try {
List<AuthorityValue> values = source.queryAuthorities(text, max * 2); // max*2 because results get filtered
// filtering loop
Iterator<AuthorityValue> iterator = values.iterator();
while (iterator.hasNext()) {
AuthorityValue next = iterator.next();
if (alreadyPresent.contains(next)) {
iterator.remove();
}
}
// adding choices loop
int added = 0;
iterator = values.iterator();
while (iterator.hasNext() && added < max) {
AuthorityValue val = iterator.next();
Map<String, String> extras = val.choiceSelectMap();
extras.put("insolr", "false");
choices.add(new Choice(val.generateString(), val.getValue(), val.getValue(), extras));
added++;
}
} catch (Exception e) {
log.error("Error", e);
}
this.externalResults = false;
} else {
log.warn("external source for authority not configured");
}
}
private String toQuery(String searchField, String text) {
return searchField + ":\"" + text.toLowerCase().replaceAll(":", "\\:") + "*\" or " + searchField + ":\"" + text.toLowerCase().replaceAll(":", "\\:")+"\"";
}
@Override
public Choices getMatches(String field, String text, int collection, int start, int limit, String locale) {
return getMatches(field, text, collection, start, limit, locale, true);
}
@Override
public Choices getBestMatch(String field, String text, int collection, String locale) {
Choices matches = getMatches(field, text, collection, 0, 1, locale, false);
if (matches.values.length !=0 && !matches.values[0].value.equalsIgnoreCase(text)) {
matches = new Choices(false);
}
return matches;
}
@Override
public String getLabel(String field, String key, String locale) {
try {
if (log.isDebugEnabled()) {
log.debug("requesting label for key " + key + " using locale " + locale);
}
SolrQuery queryArgs = new SolrQuery();
queryArgs.setQuery("id:" + key);
queryArgs.setRows(1);
QueryResponse searchResponse = getSearchService().search(queryArgs);
SolrDocumentList docs = searchResponse.getResults();
if (docs.getNumFound() == 1) {
String label = null;
try {
label = (String) docs.get(0).getFieldValue("value_" + locale);
} catch (Exception e) {
//ok to fail here
}
if (label != null) {
if (log.isDebugEnabled()) {
log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value_" + locale);
}
return label;
}
try {
label = (String) docs.get(0).getFieldValue("value");
} catch (Exception e) {
log.error("couldn't get field value for key " + key,e);
}
if (label != null) {
if (log.isDebugEnabled()) {
log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value");
}
return label;
}
try {
label = (String) docs.get(0).getFieldValue("value_en");
} catch (Exception e) {
log.error("couldn't get field value for key " + key,e);
}
if (label != null) {
if (log.isDebugEnabled()) {
log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value_en");
}
return label;
}
}
} catch (Exception e) {
log.error("error occurred while trying to get label for key " + key,e);
}
return key;
}
public static AuthoritySearchService getSearchService() {
DSpace dspace = new DSpace();
org.dspace.kernel.ServiceManager manager = dspace.getServiceManager();
return manager.getServiceByName(AuthoritySearchService.class.getName(), AuthoritySearchService.class);
}
public void addExternalResultsInNextMatches() {
this.externalResults = true;
}
}

View File

@@ -63,6 +63,7 @@
<exclude>**/cocoon/**</exclude> <exclude>**/cocoon/**</exclude>
<exclude>**/scriptaculous/**</exclude> <exclude>**/scriptaculous/**</exclude>
<exclude>**/jquery*</exclude> <exclude>**/jquery*</exclude>
<exclude>**/Datatables/**</exclude>
<exclude>**/modernizr*</exclude> <exclude>**/modernizr*</exclude>
<exclude>**/DD_belated*</exclude> <exclude>**/DD_belated*</exclude>
<exclude>**/detectmobile*</exclude> <exclude>**/detectmobile*</exclude>

View File

@@ -91,6 +91,13 @@ public class EditItemMetadataForm extends AbstractDSpaceTransformer {
pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID)); pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID));
pageMeta.addMetadata("title").addContent(T_title); pageMeta.addMetadata("title").addContent(T_title);
pageMeta.addMetadata("stylesheet", "screen", "datatables", true).addContent("../../static/Datatables/DataTables-1.8.0/media/css/datatables.css");
// pageMeta.addMetadata("stylesheet", "screen", null, true).addContent("../../themes/AtmireModules/lib/css/datatables-overrides.css");
pageMeta.addMetadata("stylesheet", "screen", "person-lookup", true).addContent("lib/css/person-lookup.css");
pageMeta.addMetadata("javascript", "static", null, true).addContent("static/Datatables/DataTables-1.8.0/media/js/jquery.dataTables.min.js");
pageMeta.addMetadata("javascript", null, "person-lookup", true).addContent("lib/js/person-lookup.js");
pageMeta.addTrailLink(contextPath + "/", T_dspace_home); pageMeta.addTrailLink(contextPath + "/", T_dspace_home);
pageMeta.addTrailLink(contextPath + "/admin/item", T_item_trail); pageMeta.addTrailLink(contextPath + "/admin/item", T_item_trail);
pageMeta.addTrail().addContent(T_trail); pageMeta.addTrail().addContent(T_trail);
@@ -271,7 +278,11 @@ public class EditItemMetadataForm extends AbstractDSpaceTransformer {
if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey)) if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey))
{ {
mdValue.setChoices(fieldKey); mdValue.setChoices(fieldKey);
mdValue.setChoicesPresentation(Params.PRESENTATION_LOOKUP); if(Params.PRESENTATION_AUTHORLOOKUP.equals(cmgr.getPresentation(fieldKey))){
mdValue.setChoicesPresentation(Params.PRESENTATION_AUTHORLOOKUP);
}else{
mdValue.setChoicesPresentation(Params.PRESENTATION_LOOKUP);
}
mdValue.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey)); mdValue.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey));
} }
} }

View File

@@ -144,6 +144,11 @@ public class DescribeStep extends AbstractSubmissionStep
super.addPageMeta(pageMeta); super.addPageMeta(pageMeta);
int collectionID = submission.getCollection().getID(); int collectionID = submission.getCollection().getID();
pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID)); pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID));
pageMeta.addMetadata("stylesheet", "screen", "datatables", true).addContent("../../static/Datatables/DataTables-1.8.0/media/css/datatables.css");
// pageMeta.addMetadata("stylesheet", "screen", null, true).addContent("../../themes/AtmireModules/lib/css/datatables-overrides.css");
pageMeta.addMetadata("stylesheet", "screen", "person-lookup", true).addContent("lib/css/person-lookup.css");
pageMeta.addMetadata("javascript", null, "person-lookup", true).addContent("lib/js/person-lookup.js");
String jumpTo = submissionInfo.getJumpToField(); String jumpTo = submissionInfo.getJumpToField();
if (jumpTo != null) if (jumpTo != null)

View File

@@ -68,8 +68,7 @@ public class AJAXMenuGenerator extends AbstractGenerator
String locale = parameters.getParameter("locale",null); String locale = parameters.getParameter("locale",null);
log.debug("AJAX menu generator: field="+field+", query="+query+", start="+sstart+", limit="+slimit+", format="+format+", field="+field+", query="+query+", start="+sstart+", limit="+slimit+", format="+format+", locale = "+locale); log.debug("AJAX menu generator: field="+field+", query="+query+", start="+sstart+", limit="+slimit+", format="+format+", field="+field+", query="+query+", start="+sstart+", limit="+slimit+", format="+format+", locale = "+locale);
Choices result = Choices result = ChoiceAuthorityManager.getManager().getMatches(field, query, collection, start, limit, locale, true);
ChoiceAuthorityManager.getManager().getMatches(field, query, collection, start, limit, locale);
log.debug("Result count = "+result.values.length+", default="+result.defaultSelected); log.debug("Result count = "+result.values.length+", default="+result.defaultSelected);

View File

@@ -82,8 +82,9 @@ public class Params extends AbstractWingElement implements StructuralElement
public static final String PRESENTATION_SELECT = "select"; public static final String PRESENTATION_SELECT = "select";
public static final String PRESENTATION_SUGGEST = "suggest"; public static final String PRESENTATION_SUGGEST = "suggest";
public static final String PRESENTATION_LOOKUP = "lookup"; public static final String PRESENTATION_LOOKUP = "lookup";
public static final String PRESENTATION_AUTHORLOOKUP = "authorLookup";
public static final String PRESENTATION_NONE = "none"; public static final String PRESENTATION_NONE = "none";
public static final String[] PRESENTATIONS = { PRESENTATION_SELECT, PRESENTATION_SUGGEST, PRESENTATION_LOOKUP, PRESENTATION_NONE }; public static final String[] PRESENTATIONS = { PRESENTATION_SELECT, PRESENTATION_SUGGEST, PRESENTATION_LOOKUP, PRESENTATION_NONE, PRESENTATION_AUTHORLOOKUP };
/** *********** Parameter Attributes *************** */ /** *********** Parameter Attributes *************** */

View File

@@ -241,6 +241,7 @@
<map:parameter name="javascript.static#5" value="static/js/scriptaculous/builder.js"/> <map:parameter name="javascript.static#5" value="static/js/scriptaculous/builder.js"/>
<map:parameter name="javascript.static#6" value="static/js/scriptaculous/controls.js"/> <map:parameter name="javascript.static#6" value="static/js/scriptaculous/controls.js"/>
<map:parameter name="javascript.static#7" value="static/js/choice-support.js"/> <map:parameter name="javascript.static#7" value="static/js/choice-support.js"/>
<map:parameter name="javascript.static#8" value="static/Datatables/DataTables-1.8.0/media/js/jquery.dataTables.min.js"/>
</map:transform> </map:transform>
</map:match> </map:match>

View File

@@ -0,0 +1 @@
extras

View File

@@ -0,0 +1,11 @@
This DataTables plugin (v1.8.x) for jQuery was developed out of the desire to allow highly configurable access to HTML tables with advanced access features.
For detailed installation, usage and API instructions, please refer to the DataTables web-pages: http://www.datatables.net
Questions, feature requests and bug reports (etc) can all be asked on the DataTables forums: http://www.datatables.net/forums/
The DataTables source can be found in the media/js/ directory of this archive.
DataTables is released with dual licensing, using the GPL v2 (license-gpl2.txt) and an BSD style license (license-bsd.txt). Please see the corresponding license file for details of these licenses. You are free to use, modify and distribute this software, but all copyright information must remain.
If you discover any bugs in DataTables, have any suggestions for improvements or even if you just like using it, please free to get in touch with me: www.datatables.net/contact

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

View File

@@ -0,0 +1,10 @@
Copyright (c) 2008-2010, Allan Jardine
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Allan Jardine nor SpryMedia UK may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,574 @@
/*
* File: demo_table_jui.css
* CVS: $Id$
* Description: CSS descriptions for DataTables demo pages
* Author: Allan Jardine
* Created: Tue May 12 06:47:22 BST 2009
* Modified: $Date$ by $Author$
* Language: CSS
* Project: DataTables
*
* Copyright 2009 Allan Jardine. All Rights Reserved.
*
* ***************************************************************************
* DESCRIPTION
*
* The styles given here are suitable for the demos that are used with the standard DataTables
* distribution (see www.datatables.net). You will most likely wish to modify these styles to
* meet the layout requirements of your site.
*
* Common issues:
* 'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
* no conflict between the two pagination types. If you want to use full_numbers pagination
* ensure that you either have "example_alt_pagination" as a body class name, or better yet,
* modify that selector.
* Note that the path used for Images is relative. All images are by default located in
* ../images/ - relative to this CSS file.
*/
/*
* jQuery UI specific styling
*/
.paging_two_button .fg-button {
float: left;
cursor: pointer;
* cursor: hand;
}
.paging_full_numbers .fg-button {
padding: 2px 6px;
margin: 0;
cursor: pointer;
* cursor: hand;
}
.dataTables_paginate .ui-button,
.dataTables_paginate .fg-button {
margin-right: -0.1em !important;
}
.paging_full_numbers {
/*width: 350px !important;*/
width: 49%;
}
.fg-toolbar {
padding: 5px;
}
.dataTables_paginate {
width: auto;
}
div.dtcontainer table.display thead th {
padding: 3px 0px 3px 10px;
cursor: pointer;
* cursor: hand;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Everything below this line is the same as demo_table.css. This file is
* required for 'cleanliness' of the markup
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables features
*/
div.dtcontainer .dataTables_wrapper {
position: relative;
/*min-height: 302px;*/
/*_height: 302px;*/
clear: both;
}
div.dtcontainer .dataTables_wrapper {
padding-top:5px;
}
div.dtcontainer .dataTables_filter {
padding-bottom:5px;
}
div.dtcontainer .datatablefooter {
margin-top:5px;
overflow: hidden;
}
div.dtcontainer .dataTables_processing {
position: absolute;
top: 8px;
left: 50%;
width: 250px;
margin-left: -125px;
text-align: center;
color: #999;
font-size: 11px;
padding: 2px 0;
}
div.dtcontainer .dataTables_length {
width: 40%;
float: left;
}
div.dtcontainer .dataTables_filter {
width: 50%;
float: right;
text-align: right;
}
div.dtcontainer .dataTables_info {
width: 270px;
float: left;
}
div.dtcontainer .dataTables_paginate {
float: right;
text-align: right;
}
/* Pagination nested */
div.dtcontainer .paginate_disabled_previous,
div.dtcontainer .paginate_enabled_previous,
div.dtcontainer .paginate_disabled_next,
div.dtcontainer .paginate_enabled_next {
height: 19px;
width: 19px;
margin-left: 3px;
float: left;
}
div.dtcontainer .paginate_disabled_previous {
background-image: url('../images/back_disabled.jpg');
}
div.dtcontainer .paginate_enabled_previous {
background-image: url('../images/back_enabled.jpg');
}
div.dtcontainer .paginate_disabled_next {
background-image: url('../images/forward_disabled.jpg');
}
div.dtcontainer .paginate_enabled_next {
background-image: url('../images/forward_enabled.jpg');
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables display
*/
div.dtcontainer table.display {
margin: 0 auto;
width: 100%;
clear: both;
}
div.dtcontainer table.display tfoot th {
padding: 3px 10px;
border-top: 1px solid black;
font-weight: bold;
}
div.dtcontainer table.display tr.heading2 td {
border-bottom: 1px solid #aaa;
}
div.dtcontainer table.display td {
padding: 3px 10px;
}
div.dtcontainer table.display td.center {
text-align: center;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables sorting
*/
div.dtcontainer .sorting_asc {
background: url('../images/sort_asc.png') no-repeat center right;
}
div.dtcontainer .sorting_desc {
background: url('../images/sort_desc.png') no-repeat center right;
}
div.dtcontainer .sorting {
background: url('../images/sort_both.png') no-repeat center right;
}
div.dtcontainer .sorting_asc_disabled {
background: url('../images/sort_asc_disabled.png') no-repeat center right;
}
div.dtcontainer .sorting_desc_disabled {
background: url('../images/sort_desc_disabled.png') no-repeat center right;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables row classes
*/
div.dtcontainer table.display tr.odd.gradeA {
background-color: #ddffdd;
}
div.dtcontainer table.display tr.even.gradeA {
background-color: #eeffee;
}
div.dtcontainer table.display tr.odd.gradeA {
background-color: #ddffdd;
}
div.dtcontainer table.display tr.even.gradeA {
background-color: #eeffee;
}
div.dtcontainer table.display tr.odd.gradeC {
background-color: #ddddff;
}
div.dtcontainer table.display tr.even.gradeC {
background-color: #eeeeff;
}
div.dtcontainer table.display tr.odd.gradeX {
background-color: #ffdddd;
}
div.dtcontainer table.display tr.even.gradeX {
background-color: #ffeeee;
}
div.dtcontainer table.display tr.odd.gradeU {
background-color: #ddd;
}
div.dtcontainer table.display tr.even.gradeU {
background-color: #eee;
}
div.dtcontainer tr {
border: 1px solid #ebebeb !important;
}
div.dtcontainer td {
padding: 3px;
}
div.dtcontainer tr.odd {
background-color: #fafafa;
}
div.dtcontainer tr.even {
background-color: white;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Misc
*/
.dataTables_scroll {
clear: both;
}
div.dtcontainer .top,
div.dtcontainer .bottom {
padding: 15px;
background-color: #F5F5F5;
border: 1px solid #CCCCCC;
}
div.dtcontainer .top .dataTables_info {
float: none;
}
div.dtcontainer .clear {
clear: both;
}
div.dtcontainer .dataTables_empty {
text-align: center;
}
div.dtcontainer tfoot input {
margin: 0.5em 0;
width: 100%;
color: #444;
}
div.dtcontainer tfoot input.search_init {
color: #999;
}
div.dtcontainer td.group {
background-color: #d1cfd0;
border-bottom: 2px solid #A19B9E;
border-top: 2px solid #A19B9E;
}
div.dtcontainer td.details {
background-color: #d1cfd0;
border: 2px solid #A19B9E;
}
div.dtcontainer .example_alt_pagination div.dataTables_info {
width: 40%;
}
div.dtcontainer .paging_full_numbers span.paginate_button,
div.dtcontainer .paging_full_numbers span.paginate_active {
border: 1px solid #aaa;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
padding: 2px 5px;
margin: 0 3px;
cursor: pointer;
*cursor: hand;
}
div.dtcontainer .paging_full_numbers span.paginate_button {
background-color: #ddd;
}
div.dtcontainer .paging_full_numbers span.paginate_button:hover {
background-color: #ccc;
}
div.dtcontainer .paging_full_numbers span.paginate_active {
background-color: #99B3FF;
}
div.dtcontainer table.display tr.even.row_selected td {
background-color: #B0BED9;
}
div.dtcontainer table.display tr.odd.row_selected td {
background-color: #9FAFD1;
}
/*
* Sorting classes for columns
*/
/* For the standard odd/even */
div.dtcontainer tr.odd td.sorting_1,
div.dtcontainer tr.odd td.sorting_2,
div.dtcontainer tr.odd td {
background-color: #FAFAFA;
}
div.dtcontainer tr.even td.sorting_1,
div.dtcontainer tr.even td.sorting_2,
div.dtcontainer tr.even td {
background-color: #FFFFFF;
}
div.dtcontainer tr.odd td.sorting_1,
div.dtcontainer tr.even td.sorting_1 {
border: 1px solid #ebebeb;
}
/* For the Conditional-CSS grading rows */
/*
Colour calculations (based off the main row colours)
Level 1:
dd > c4
ee > d5
Level 2:
dd > d1
ee > e2
*/
/*/*div.dtcontainer tr.odd td.sorting_1 {*/
/*background-color: #c4c4ff;*/
/*}*/
/*/*div.dtcontainer tr.odd td.sorting_2 {*/
/*background-color: #d1d1ff;*/
/*}*/
/*/*div.dtcontainer tr.odd td {*/
/*background-color: #d1d1ff;*/
/*}*/
/*/*div.dtcontainer tr.even td.sorting_1 {*/
/*background-color: #d5d5ff;*/
/*}*/
/*/*div.dtcontainer tr.even td.sorting_2 {*/
/*background-color: #e2e2ff;*/
/*}*/
/*/*div.dtcontainer tr.even td {*/
/*background-color: #e2e2ff;*/
/*}*/
div.dtcontainer tr.odd.gradeC td.sorting_1 {
background-color: #c4c4ff;
}
div.dtcontainer tr.odd.gradeC td.sorting_2 {
background-color: #d1d1ff;
}
div.dtcontainer tr.odd.gradeC td {
background-color: #d1d1ff;
}
div.dtcontainer tr.even.gradeC td.sorting_1 {
background-color: #d5d5ff;
}
div.dtcontainer tr.even.gradeC td.sorting_2 {
background-color: #e2e2ff;
}
div.dtcontainer tr.even.gradeC td {
background-color: #e2e2ff;
}
div.dtcontainer tr.odd.gradeX td.sorting_1 {
background-color: #ffc4c4;
}
div.dtcontainer tr.odd.gradeX td.sorting_2 {
background-color: #ffd1d1;
}
div.dtcontainer tr.odd.gradeX td {
background-color: #ffd1d1;
}
div.dtcontainer tr.even.gradeX td.sorting_1 {
background-color: #ffd5d5;
}
div.dtcontainer tr.even.gradeX td.sorting_2 {
background-color: #ffe2e2;
}
div.dtcontainer tr.even.gradeX td {
background-color: #ffe2e2;
}
div.dtcontainer tr.odd.gradeU td.sorting_1 {
background-color: #c4c4c4;
}
div.dtcontainer tr.odd.gradeU td.sorting_2 {
background-color: #d1d1d1;
}
div.dtcontainer tr.odd.gradeU td {
background-color: #d1d1d1;
}
div.dtcontainer tr.even.gradeU td.sorting_1 {
background-color: #d5d5d5;
}
div.dtcontainer tr.even.gradeU td.sorting_2 {
background-color: #e2e2e2;
}
div.dtcontainer tr.even.gradeU td {
background-color: #e2e2e2;
}
/*
* Row highlighting example
*/
div.dtcontainer .ex_highlight #example tbody tr.even:hover,
div.dtcontainer #example tbody tr.even td.highlighted {
background-color: #ECFFB3;
}
div.dtcontainer .ex_highlight #example tbody tr.odd:hover,
div.dtcontainer #example tbody tr.odd td.highlighted {
background-color: #E6FF99;
}
div.dtcontainer .css_right {
float: right;
}
div.datatablesheader {
overflow: hidden;
width: 100%;
}
div.datatablescroller {
width: 640px;
height: 445px;
/*clear: both;*/
overflow-y: auto;
overflow-x: hidden;
background: #f2f2f2;
border: 1px solid #CCCCCC;
}
div.datatablescroller > table {
background: white;
}
div.bagvisibilitydiv {
display: none;
}
table.dttable {
clear: both;
display: none;
}
/*table#statistics_editorparts_BagTransformer_table_bitstreamtable,*/
/*table#statistics_editorparts_BagTransformer_table_itemtable,*/
/*.dataTables_wrapper {*/
/*display: none;*/
/*}*/
table.bitstreamtable {
width: 100%;
background-color: white;
}
div.tableslider {
width: 100%;
display: none;
}
span.itemspan {
line-height: 22px;
}
.dtcontainer.ui-dialog-content {
font-size: 11.2px;
}
table.dttable th {
padding: inherit;
}

View File

@@ -0,0 +1,150 @@
/*
* File: jquery.dataTables.min.js
* Version: 1.8.0
* Author: Allan Jardine (www.sprymedia.co.uk)
* Info: www.datatables.net
*
* Copyright 2008-2011 Allan Jardine, all rights reserved.
*
* This source file is free software, under either the GPL v2 license or a
* BSD style license, as supplied with this software.
*
* This source file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
*/
(function(i,wa,p){i.fn.dataTableSettings=[];var D=i.fn.dataTableSettings;i.fn.dataTableExt={};var o=i.fn.dataTableExt;o.sVersion="1.8.0";o.sErrMode="alert";o.iApiIndex=0;o.oApi={};o.afnFiltering=[];o.aoFeatures=[];o.ofnSearch={};o.afnSortData=[];o.oStdClasses={sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",
sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripOdd:"odd",sStripEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",
sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:""};o.oJUIClasses={sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left",
sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",sPageNextDisabled:"fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",sPageJUINext:"ui-icon ui-icon-circle-arrow-e",sPageJUIPrev:"ui-icon ui-icon-circle-arrow-w",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled",
sPageFirst:"first ui-corner-tl ui-corner-bl",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last ui-corner-tr ui-corner-br",sStripOdd:"odd",sStripEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"ui-state-default",sSortDesc:"ui-state-default",sSortable:"ui-state-default",
sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",sSortColumn:"sorting_",sSortJUIAsc:"css_right ui-icon ui-icon-triangle-1-n",sSortJUIDesc:"css_right ui-icon ui-icon-triangle-1-s",sSortJUI:"css_right ui-icon ui-icon-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollWrapper:"dataTables_scroll",
sScrollHead:"dataTables_scrollHead ui-state-default",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot ui-state-default",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"ui-state-default"};o.oPagination={two_button:{fnInit:function(g,l,r){var s,w,y;if(g.bJUI){s=p.createElement("a");w=p.createElement("a");y=p.createElement("span");y.className=g.oClasses.sPageJUINext;w.appendChild(y);y=p.createElement("span");y.className=g.oClasses.sPageJUIPrev;
s.appendChild(y)}else{s=p.createElement("div");w=p.createElement("div")}s.className=g.oClasses.sPagePrevDisabled;w.className=g.oClasses.sPageNextDisabled;s.title=g.oLanguage.oPaginate.sPrevious;w.title=g.oLanguage.oPaginate.sNext;l.appendChild(s);l.appendChild(w);i(s).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&r(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&r(g)});i(s).bind("selectstart.DT",function(){return false});i(w).bind("selectstart.DT",function(){return false});
if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");s.setAttribute("id",g.sTableId+"_previous");w.setAttribute("id",g.sTableId+"_next")}},fnUpdate:function(g){if(g.aanFeatures.p)for(var l=g.aanFeatures.p,r=0,s=l.length;r<s;r++)if(l[r].childNodes.length!==0){l[r].childNodes[0].className=g._iDisplayStart===0?g.oClasses.sPagePrevDisabled:g.oClasses.sPagePrevEnabled;l[r].childNodes[1].className=g.fnDisplayEnd()==g.fnRecordsDisplay()?g.oClasses.sPageNextDisabled:
g.oClasses.sPageNextEnabled}}},iFullNumbersShowPages:5,full_numbers:{fnInit:function(g,l,r){var s=p.createElement("span"),w=p.createElement("span"),y=p.createElement("span"),G=p.createElement("span"),x=p.createElement("span");s.innerHTML=g.oLanguage.oPaginate.sFirst;w.innerHTML=g.oLanguage.oPaginate.sPrevious;G.innerHTML=g.oLanguage.oPaginate.sNext;x.innerHTML=g.oLanguage.oPaginate.sLast;var v=g.oClasses;s.className=v.sPageButton+" "+v.sPageFirst;w.className=v.sPageButton+" "+v.sPagePrevious;G.className=
v.sPageButton+" "+v.sPageNext;x.className=v.sPageButton+" "+v.sPageLast;l.appendChild(s);l.appendChild(w);l.appendChild(y);l.appendChild(G);l.appendChild(x);i(s).bind("click.DT",function(){g.oApi._fnPageChange(g,"first")&&r(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&r(g)});i(G).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&r(g)});i(x).bind("click.DT",function(){g.oApi._fnPageChange(g,"last")&&r(g)});i("span",l).bind("mousedown.DT",function(){return false}).bind("selectstart.DT",
function(){return false});if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");s.setAttribute("id",g.sTableId+"_first");w.setAttribute("id",g.sTableId+"_previous");G.setAttribute("id",g.sTableId+"_next");x.setAttribute("id",g.sTableId+"_last")}},fnUpdate:function(g,l){if(g.aanFeatures.p){var r=o.oPagination.iFullNumbersShowPages,s=Math.floor(r/2),w=Math.ceil(g.fnRecordsDisplay()/g._iDisplayLength),y=Math.ceil(g._iDisplayStart/g._iDisplayLength)+1,G=
"",x,v=g.oClasses;if(w<r){s=1;x=w}else if(y<=s){s=1;x=r}else if(y>=w-s){s=w-r+1;x=w}else{s=y-Math.ceil(r/2)+1;x=s+r-1}for(r=s;r<=x;r++)G+=y!=r?'<span class="'+v.sPageButton+'">'+r+"</span>":'<span class="'+v.sPageButtonActive+'">'+r+"</span>";x=g.aanFeatures.p;var z,Y=function(L){g._iDisplayStart=(this.innerHTML*1-1)*g._iDisplayLength;l(g);L.preventDefault()},V=function(){return false};r=0;for(s=x.length;r<s;r++)if(x[r].childNodes.length!==0){z=i("span:eq(2)",x[r]);z.html(G);i("span",z).bind("click.DT",
Y).bind("mousedown.DT",V).bind("selectstart.DT",V);z=x[r].getElementsByTagName("span");z=[z[0],z[1],z[z.length-2],z[z.length-1]];i(z).removeClass(v.sPageButton+" "+v.sPageButtonActive+" "+v.sPageButtonStaticDisabled);if(y==1){z[0].className+=" "+v.sPageButtonStaticDisabled;z[1].className+=" "+v.sPageButtonStaticDisabled}else{z[0].className+=" "+v.sPageButton;z[1].className+=" "+v.sPageButton}if(w===0||y==w||g._iDisplayLength==-1){z[2].className+=" "+v.sPageButtonStaticDisabled;z[3].className+=" "+
v.sPageButtonStaticDisabled}else{z[2].className+=" "+v.sPageButton;z[3].className+=" "+v.sPageButton}}}}}};o.oSort={"string-asc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?-1:g>l?1:0},"string-desc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?1:g>l?-1:0},"html-asc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<
l?-1:g>l?1:0},"html-desc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<l?1:g>l?-1:0},"date-asc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return g-l},"date-desc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return l-
g},"numeric-asc":function(g,l){return(g=="-"||g===""?0:g*1)-(l=="-"||l===""?0:l*1)},"numeric-desc":function(g,l){return(l=="-"||l===""?0:l*1)-(g=="-"||g===""?0:g*1)}};o.aTypes=[function(g){if(typeof g=="number")return"numeric";else if(typeof g!="string")return null;var l,r=false;l=g.charAt(0);if("0123456789-".indexOf(l)==-1)return null;for(var s=1;s<g.length;s++){l=g.charAt(s);if("0123456789.".indexOf(l)==-1)return null;if(l=="."){if(r)return null;r=true}}return"numeric"},function(g){var l=Date.parse(g);
if(l!==null&&!isNaN(l)||typeof g=="string"&&g.length===0)return"date";return null},function(g){if(typeof g=="string"&&g.indexOf("<")!=-1&&g.indexOf(">")!=-1)return"html";return null}];o.fnVersionCheck=function(g){var l=function(x,v){for(;x.length<v;)x+="0";return x},r=o.sVersion.split(".");g=g.split(".");for(var s="",w="",y=0,G=g.length;y<G;y++){s+=l(r[y],3);w+=l(g[y],3)}return parseInt(s,10)>=parseInt(w,10)};o._oExternConfig={iNextUnique:0};i.fn.dataTable=function(g){function l(){this.fnRecordsTotal=
function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length};this.fnRecordsDisplay=function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length};this.fnDisplayEnd=function(){return this.oFeatures.bServerSide?this.oFeatures.bPaginate===false||this._iDisplayLength==-1?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd};this.sInstance=
this.oInstance=null;this.oFeatures={bPaginate:true,bLengthChange:true,bFilter:true,bSort:true,bInfo:true,bAutoWidth:true,bProcessing:false,bSortClasses:true,bStateSave:false,bServerSide:false,bDeferRender:false};this.oScroll={sX:"",sXInner:"",sY:"",bCollapse:false,bInfinite:false,iLoadGap:100,iBarWidth:0,bAutoCss:true};this.aanFeatures=[];this.oLanguage={sProcessing:"Processing...",sLengthMenu:"Show _MENU_ entries",sZeroRecords:"No matching records found",sEmptyTable:"No data available in table",
sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sSearch:"Search:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",bRegex:false,
bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripClasses=[];this.asDestoryStrips=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=this.fnPreDrawCallback=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=this.bDeferLoading=false;this.aoOpenRows=[];this.sDom="lfrtip";this.sPaginationType="two_button";
this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.sAjaxDataProp="aaData";this.bAjaxDataGet=true;this.jqXHR=null;this.fnServerData=function(a,b,c,d){d.jqXHR=i.ajax({url:a,data:b,success:c,dataType:"json",cache:false,error:function(f,e){e=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})};
this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=","+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=10;this._iDisplayStart=0;this._iDisplayEnd=10;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=o.oStdClasses;this.bSortCellsTop=this.bSorted=this.bFiltered=false;this.oInit=null}function r(a){return function(){var b=
[A(this[o.iApiIndex])].concat(Array.prototype.slice.call(arguments));return o.oApi[a].apply(this,b)}}function s(a){var b,c,d=a.iInitDisplayStart;if(a.bInitialised===false)setTimeout(function(){s(a)},200);else{xa(a);V(a);L(a,a.aoHeader);a.nTFoot&&L(a,a.aoFooter);K(a,true);a.oFeatures.bAutoWidth&&ea(a);b=0;for(c=a.aoColumns.length;b<c;b++)if(a.aoColumns[b].sWidth!==null)a.aoColumns[b].nTh.style.width=u(a.aoColumns[b].sWidth);if(a.oFeatures.bSort)R(a);else if(a.oFeatures.bFilter)M(a,a.oPreviousSearch);
else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}if(a.sAjaxSource!==null&&!a.oFeatures.bServerSide)a.fnServerData.call(a.oInstance,a.sAjaxSource,[],function(f){var e=f;if(a.sAjaxDataProp!=="")e=Z(a.sAjaxDataProp)(f);for(b=0;b<e.length;b++)v(a,e[b]);a.iInitDisplayStart=d;if(a.oFeatures.bSort)R(a);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}K(a,false);w(a,f)},a);else if(!a.oFeatures.bServerSide){K(a,false);w(a)}}}function w(a,b){a._bInitComplete=true;if(typeof a.fnInitComplete=="function")typeof b!=
"undefined"?a.fnInitComplete.call(a.oInstance,a,b):a.fnInitComplete.call(a.oInstance,a)}function y(a,b,c){n(a.oLanguage,b,"sProcessing");n(a.oLanguage,b,"sLengthMenu");n(a.oLanguage,b,"sEmptyTable");n(a.oLanguage,b,"sLoadingRecords");n(a.oLanguage,b,"sZeroRecords");n(a.oLanguage,b,"sInfo");n(a.oLanguage,b,"sInfoEmpty");n(a.oLanguage,b,"sInfoFiltered");n(a.oLanguage,b,"sInfoPostFix");n(a.oLanguage,b,"sSearch");if(typeof b.oPaginate!="undefined"){n(a.oLanguage.oPaginate,b.oPaginate,"sFirst");n(a.oLanguage.oPaginate,
b.oPaginate,"sPrevious");n(a.oLanguage.oPaginate,b.oPaginate,"sNext");n(a.oLanguage.oPaginate,b.oPaginate,"sLast")}typeof b.sEmptyTable=="undefined"&&typeof b.sZeroRecords!="undefined"&&n(a.oLanguage,b,"sZeroRecords","sEmptyTable");typeof b.sLoadingRecords=="undefined"&&typeof b.sZeroRecords!="undefined"&&n(a.oLanguage,b,"sZeroRecords","sLoadingRecords");c&&s(a)}function G(a,b){var c=a.aoColumns.length;b={sType:null,_bAutoType:true,bVisible:true,bSearchable:true,bSortable:true,asSorting:["asc","desc"],
sSortingClass:a.oClasses.sSortable,sSortingClassJUI:a.oClasses.sSortJUI,sTitle:b?b.innerHTML:"",sName:"",sWidth:null,sWidthOrig:null,sClass:null,fnRender:null,bUseRendered:true,iDataSort:c,mDataProp:c,fnGetData:null,fnSetData:null,sSortDataType:"std",sDefaultContent:null,sContentPadding:"",nTh:b?b:p.createElement("th"),nTf:null};a.aoColumns.push(b);if(typeof a.aoPreSearchCols[c]=="undefined"||a.aoPreSearchCols[c]===null)a.aoPreSearchCols[c]={sSearch:"",bRegex:false,bSmart:true};else{if(typeof a.aoPreSearchCols[c].bRegex==
"undefined")a.aoPreSearchCols[c].bRegex=true;if(typeof a.aoPreSearchCols[c].bSmart=="undefined")a.aoPreSearchCols[c].bSmart=true}x(a,c,null)}function x(a,b,c){b=a.aoColumns[b];if(typeof c!="undefined"&&c!==null){if(typeof c.sType!="undefined"){b.sType=c.sType;b._bAutoType=false}n(b,c,"bVisible");n(b,c,"bSearchable");n(b,c,"bSortable");n(b,c,"sTitle");n(b,c,"sName");n(b,c,"sWidth");n(b,c,"sWidth","sWidthOrig");n(b,c,"sClass");n(b,c,"fnRender");n(b,c,"bUseRendered");n(b,c,"iDataSort");n(b,c,"mDataProp");
n(b,c,"asSorting");n(b,c,"sSortDataType");n(b,c,"sDefaultContent");n(b,c,"sContentPadding")}b.fnGetData=Z(b.mDataProp);b.fnSetData=ya(b.mDataProp);if(!a.oFeatures.bSort)b.bSortable=false;if(!b.bSortable||i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableNone;b.sSortingClassJUI=""}else if(i.inArray("asc",b.asSorting)!=-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableAsc;b.sSortingClassJUI=a.oClasses.sSortJUIAscAllowed}else if(i.inArray("asc",
b.asSorting)==-1&&i.inArray("desc",b.asSorting)!=-1){b.sSortingClass=a.oClasses.sSortableDesc;b.sSortingClassJUI=a.oClasses.sSortJUIDescAllowed}}function v(a,b){var c;c=typeof b.length=="number"?b.slice():i.extend(true,{},b);b=a.aoData.length;var d={nTr:null,_iId:a.iNextId++,_aData:c,_anHidden:[],_sRowStripe:""};a.aoData.push(d);for(var f,e=0,h=a.aoColumns.length;e<h;e++){c=a.aoColumns[e];typeof c.fnRender=="function"&&c.bUseRendered&&c.mDataProp!==null&&N(a,b,e,c.fnRender({iDataRow:b,iDataColumn:e,
aData:d._aData,oSettings:a}));if(c._bAutoType&&c.sType!="string"){f=H(a,b,e,"type");if(f!==null&&f!==""){f=fa(f);if(c.sType===null)c.sType=f;else if(c.sType!=f)c.sType="string"}}}a.aiDisplayMaster.push(b);a.oFeatures.bDeferRender||z(a,b);return b}function z(a,b){var c=a.aoData[b],d;if(c.nTr===null){c.nTr=p.createElement("tr");typeof c._aData.DT_RowId!="undefined"&&c.nTr.setAttribute("id",c._aData.DT_RowId);typeof c._aData.DT_RowClass!="undefined"&&i(c.nTr).addClass(c._aData.DT_RowClass);for(var f=
0,e=a.aoColumns.length;f<e;f++){var h=a.aoColumns[f];d=p.createElement("td");d.innerHTML=typeof h.fnRender=="function"&&(!h.bUseRendered||h.mDataProp===null)?h.fnRender({iDataRow:b,iDataColumn:f,aData:c._aData,oSettings:a}):H(a,b,f,"display");if(h.sClass!==null)d.className=h.sClass;if(h.bVisible){c.nTr.appendChild(d);c._anHidden[f]=null}else c._anHidden[f]=d}}}function Y(a){var b,c,d,f,e,h,j,k,m;if(a.bDeferLoading||a.sAjaxSource===null){j=a.nTBody.childNodes;b=0;for(c=j.length;b<c;b++)if(j[b].nodeName.toUpperCase()==
"TR"){k=a.aoData.length;a.aoData.push({nTr:j[b],_iId:a.iNextId++,_aData:[],_anHidden:[],_sRowStripe:""});a.aiDisplayMaster.push(k);h=j[b].childNodes;d=e=0;for(f=h.length;d<f;d++){m=h[d].nodeName.toUpperCase();if(m=="TD"||m=="TH"){N(a,k,e,i.trim(h[d].innerHTML));e++}}}}j=$(a);h=[];b=0;for(c=j.length;b<c;b++){d=0;for(f=j[b].childNodes.length;d<f;d++){e=j[b].childNodes[d];m=e.nodeName.toUpperCase();if(m=="TD"||m=="TH")h.push(e)}}h.length!=j.length*a.aoColumns.length&&J(a,1,"Unexpected number of TD elements. Expected "+
j.length*a.aoColumns.length+" and got "+h.length+". DataTables does not support rowspan / colspan in the table body, and there must be one cell for each row/column combination.");d=0;for(f=a.aoColumns.length;d<f;d++){if(a.aoColumns[d].sTitle===null)a.aoColumns[d].sTitle=a.aoColumns[d].nTh.innerHTML;j=a.aoColumns[d]._bAutoType;m=typeof a.aoColumns[d].fnRender=="function";e=a.aoColumns[d].sClass!==null;k=a.aoColumns[d].bVisible;var t,q;if(j||m||e||!k){b=0;for(c=a.aoData.length;b<c;b++){t=h[b*f+d];if(j&&
a.aoColumns[d].sType!="string"){q=H(a,b,d,"type");if(q!==""){q=fa(q);if(a.aoColumns[d].sType===null)a.aoColumns[d].sType=q;else if(a.aoColumns[d].sType!=q)a.aoColumns[d].sType="string"}}if(m){q=a.aoColumns[d].fnRender({iDataRow:b,iDataColumn:d,aData:a.aoData[b]._aData,oSettings:a});t.innerHTML=q;a.aoColumns[d].bUseRendered&&N(a,b,d,q)}if(e)t.className+=" "+a.aoColumns[d].sClass;if(k)a.aoData[b]._anHidden[d]=null;else{a.aoData[b]._anHidden[d]=t;t.parentNode.removeChild(t)}}}}}function V(a){var b,c,
d;a.nTHead.getElementsByTagName("tr");if(a.nTHead.getElementsByTagName("th").length!==0){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);if(a.aoColumns[b].sTitle!=c.innerHTML)c.innerHTML=a.aoColumns[b].sTitle}}else{var f=p.createElement("tr");b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;c.innerHTML=a.aoColumns[b].sTitle;a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);f.appendChild(c)}i(a.nTHead).html("")[0].appendChild(f);
W(a.aoHeader,a.nTHead)}if(a.bJUI){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;f=p.createElement("div");f.className=a.oClasses.sSortJUIWrapper;i(c).contents().appendTo(f);var e=p.createElement("span");e.className=a.oClasses.sSortIcon;f.appendChild(e);c.appendChild(f)}}d=function(){this.onselectstart=function(){return false};return false};if(a.oFeatures.bSort)for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable!==false){ga(a,a.aoColumns[b].nTh,b);i(a.aoColumns[b].nTh).bind("mousedown.DT",
d)}else i(a.aoColumns[b].nTh).addClass(a.oClasses.sSortableNone);a.oClasses.sFooterTH!==""&&i(">tr>th",a.nTFoot).addClass(a.oClasses.sFooterTH);if(a.nTFoot!==null){c=S(a,null,a.aoFooter);b=0;for(d=a.aoColumns.length;b<d;b++)if(typeof c[b]!="undefined")a.aoColumns[b].nTf=c[b]}}function L(a,b,c){var d,f,e,h=[],j=[],k=a.aoColumns.length;if(typeof c=="undefined")c=false;d=0;for(f=b.length;d<f;d++){h[d]=b[d].slice();h[d].nTr=b[d].nTr;for(e=k-1;e>=0;e--)!a.aoColumns[e].bVisible&&!c&&h[d].splice(e,1);j.push([])}d=
0;for(f=h.length;d<f;d++){if(h[d].nTr){a=0;for(e=h[d].nTr.childNodes.length;a<e;a++)h[d].nTr.removeChild(h[d].nTr.childNodes[0])}e=0;for(b=h[d].length;e<b;e++){k=c=1;if(typeof j[d][e]=="undefined"){h[d].nTr.appendChild(h[d][e].cell);for(j[d][e]=1;typeof h[d+c]!="undefined"&&h[d][e].cell==h[d+c][e].cell;){j[d+c][e]=1;c++}for(;typeof h[d][e+k]!="undefined"&&h[d][e].cell==h[d][e+k].cell;){for(a=0;a<c;a++)j[d+a][e+k]=1;k++}h[d][e].cell.setAttribute("rowspan",c);h[d][e].cell.setAttribute("colspan",k)}}}}
function C(a){var b,c,d=[],f=0,e=false;b=a.asStripClasses.length;c=a.aoOpenRows.length;if(!(a.fnPreDrawCallback!==null&&a.fnPreDrawCallback.call(a.oInstance,a)===false)){a.bDrawing=true;if(typeof a.iInitDisplayStart!="undefined"&&a.iInitDisplayStart!=-1){a._iDisplayStart=a.oFeatures.bServerSide?a.iInitDisplayStart:a.iInitDisplayStart>=a.fnRecordsDisplay()?0:a.iInitDisplayStart;a.iInitDisplayStart=-1;E(a)}if(a.bDeferLoading){a.bDeferLoading=false;a.iDraw++}else if(a.oFeatures.bServerSide){if(!a.bDestroying&&
!za(a))return}else a.iDraw++;if(a.aiDisplay.length!==0){var h=a._iDisplayStart,j=a._iDisplayEnd;if(a.oFeatures.bServerSide){h=0;j=a.aoData.length}for(h=h;h<j;h++){var k=a.aoData[a.aiDisplay[h]];k.nTr===null&&z(a,a.aiDisplay[h]);var m=k.nTr;if(b!==0){var t=a.asStripClasses[f%b];if(k._sRowStripe!=t){i(m).removeClass(k._sRowStripe).addClass(t);k._sRowStripe=t}}if(typeof a.fnRowCallback=="function"){m=a.fnRowCallback.call(a.oInstance,m,a.aoData[a.aiDisplay[h]]._aData,f,h);if(!m&&!e){J(a,0,"A node was not returned by fnRowCallback");
e=true}}d.push(m);f++;if(c!==0)for(k=0;k<c;k++)m==a.aoOpenRows[k].nParent&&d.push(a.aoOpenRows[k].nTr)}}else{d[0]=p.createElement("tr");if(typeof a.asStripClasses[0]!="undefined")d[0].className=a.asStripClasses[0];e=a.oLanguage.sZeroRecords.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()));if(a.iDraw==1&&a.sAjaxSource!==null&&!a.oFeatures.bServerSide)e=a.oLanguage.sLoadingRecords;else if(typeof a.oLanguage.sEmptyTable!="undefined"&&a.fnRecordsTotal()===0)e=a.oLanguage.sEmptyTable;b=p.createElement("td");
b.setAttribute("valign","top");b.colSpan=X(a);b.className=a.oClasses.sRowEmpty;b.innerHTML=e;d[f].appendChild(b)}typeof a.fnHeaderCallback=="function"&&a.fnHeaderCallback.call(a.oInstance,i(">tr",a.nTHead)[0],aa(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);typeof a.fnFooterCallback=="function"&&a.fnFooterCallback.call(a.oInstance,i(">tr",a.nTFoot)[0],aa(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);f=p.createDocumentFragment();b=p.createDocumentFragment();if(a.nTBody){e=a.nTBody.parentNode;
b.appendChild(a.nTBody);if(!a.oScroll.bInfinite||!a._bInitComplete||a.bSorted||a.bFiltered){c=a.nTBody.childNodes;for(b=c.length-1;b>=0;b--)c[b].parentNode.removeChild(c[b])}b=0;for(c=d.length;b<c;b++)f.appendChild(d[b]);a.nTBody.appendChild(f);e!==null&&e.appendChild(a.nTBody)}for(b=a.aoDrawCallback.length-1;b>=0;b--)a.aoDrawCallback[b].fn.call(a.oInstance,a);a.bSorted=false;a.bFiltered=false;a.bDrawing=false;if(a.oFeatures.bServerSide){K(a,false);typeof a._bInitComplete=="undefined"&&w(a)}}}function ba(a){if(a.oFeatures.bSort)R(a,
a.oPreviousSearch);else if(a.oFeatures.bFilter)M(a,a.oPreviousSearch);else{E(a);C(a)}}function za(a){if(a.bAjaxDataGet){K(a,true);var b=a.aoColumns.length,c=[],d;a.iDraw++;c.push({name:"sEcho",value:a.iDraw});c.push({name:"iColumns",value:b});c.push({name:"sColumns",value:ha(a)});c.push({name:"iDisplayStart",value:a._iDisplayStart});c.push({name:"iDisplayLength",value:a.oFeatures.bPaginate!==false?a._iDisplayLength:-1});if(a.oFeatures.bFilter!==false){c.push({name:"sSearch",value:a.oPreviousSearch.sSearch});
c.push({name:"bRegex",value:a.oPreviousSearch.bRegex});for(d=0;d<b;d++){c.push({name:"sSearch_"+d,value:a.aoPreSearchCols[d].sSearch});c.push({name:"bRegex_"+d,value:a.aoPreSearchCols[d].bRegex});c.push({name:"bSearchable_"+d,value:a.aoColumns[d].bSearchable})}}if(a.oFeatures.bSort!==false){var f=a.aaSortingFixed!==null?a.aaSortingFixed.length:0,e=a.aaSorting.length;c.push({name:"iSortingCols",value:f+e});for(d=0;d<f;d++){c.push({name:"iSortCol_"+d,value:a.aaSortingFixed[d][0]});c.push({name:"sSortDir_"+
d,value:a.aaSortingFixed[d][1]})}for(d=0;d<e;d++){c.push({name:"iSortCol_"+(d+f),value:a.aaSorting[d][0]});c.push({name:"sSortDir_"+(d+f),value:a.aaSorting[d][1]})}for(d=0;d<b;d++)c.push({name:"bSortable_"+d,value:a.aoColumns[d].bSortable})}a.fnServerData.call(a.oInstance,a.sAjaxSource,c,function(h){Aa(a,h)},a);return false}else return true}function Aa(a,b){if(typeof b.sEcho!="undefined")if(b.sEcho*1<a.iDraw)return;else a.iDraw=b.sEcho*1;if(!a.oScroll.bInfinite||a.oScroll.bInfinite&&(a.bSorted||a.bFiltered))ia(a);
a._iRecordsTotal=b.iTotalRecords;a._iRecordsDisplay=b.iTotalDisplayRecords;var c=ha(a);if(c=typeof b.sColumns!="undefined"&&c!==""&&b.sColumns!=c)var d=Ba(a,b.sColumns);b=Z(a.sAjaxDataProp)(b);for(var f=0,e=b.length;f<e;f++)if(c){for(var h=[],j=0,k=a.aoColumns.length;j<k;j++)h.push(b[f][d[j]]);v(a,h)}else v(a,b[f]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=false;C(a);a.bAjaxDataGet=true;K(a,false)}function xa(a){var b=p.createElement("div");a.nTable.parentNode.insertBefore(b,a.nTable);
a.nTableWrapper=p.createElement("div");a.nTableWrapper.className=a.oClasses.sWrapper;a.sTableId!==""&&a.nTableWrapper.setAttribute("id",a.sTableId+"_wrapper");a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),f,e,h,j,k,m,t,q=0;q<d.length;q++){e=0;h=d[q];if(h=="<"){j=p.createElement("div");k=d[q+1];if(k=="'"||k=='"'){m="";for(t=2;d[q+t]!=k;){m+=d[q+t];t++}if(m=="H")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";else if(m==
"F")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";if(m.indexOf(".")!=-1){k=m.split(".");j.setAttribute("id",k[0].substr(1,k[0].length-1));j.className=k[1]}else if(m.charAt(0)=="#")j.setAttribute("id",m.substr(1,m.length-1));else j.className=m;q+=t}c.appendChild(j);c=j}else if(h==">")c=c.parentNode;else if(h=="l"&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange){f=Ca(a);e=1}else if(h=="f"&&a.oFeatures.bFilter){f=Da(a);e=1}else if(h=="r"&&a.oFeatures.bProcessing){f=
Ea(a);e=1}else if(h=="t"){f=Fa(a);e=1}else if(h=="i"&&a.oFeatures.bInfo){f=Ga(a);e=1}else if(h=="p"&&a.oFeatures.bPaginate){f=Ha(a);e=1}else if(o.aoFeatures.length!==0){j=o.aoFeatures;t=0;for(k=j.length;t<k;t++)if(h==j[t].cFeature){if(f=j[t].fnInit(a))e=1;break}}if(e==1&&f!==null){if(typeof a.aanFeatures[h]!="object")a.aanFeatures[h]=[];a.aanFeatures[h].push(f);c.appendChild(f)}}b.parentNode.replaceChild(a.nTableWrapper,b)}function Fa(a){if(a.oScroll.sX===""&&a.oScroll.sY==="")return a.nTable;var b=
p.createElement("div"),c=p.createElement("div"),d=p.createElement("div"),f=p.createElement("div"),e=p.createElement("div"),h=p.createElement("div"),j=a.nTable.cloneNode(false),k=a.nTable.cloneNode(false),m=a.nTable.getElementsByTagName("thead")[0],t=a.nTable.getElementsByTagName("tfoot").length===0?null:a.nTable.getElementsByTagName("tfoot")[0],q=typeof g.bJQueryUI!="undefined"&&g.bJQueryUI?o.oJUIClasses:o.oStdClasses;c.appendChild(d);e.appendChild(h);f.appendChild(a.nTable);b.appendChild(c);b.appendChild(f);
d.appendChild(j);j.appendChild(m);if(t!==null){b.appendChild(e);h.appendChild(k);k.appendChild(t)}b.className=q.sScrollWrapper;c.className=q.sScrollHead;d.className=q.sScrollHeadInner;f.className=q.sScrollBody;e.className=q.sScrollFoot;h.className=q.sScrollFootInner;if(a.oScroll.bAutoCss){c.style.overflow="hidden";c.style.position="relative";e.style.overflow="hidden";f.style.overflow="auto"}c.style.border="0";c.style.width="100%";e.style.border="0";d.style.width="150%";j.removeAttribute("id");j.style.marginLeft=
"0";a.nTable.style.marginLeft="0";if(t!==null){k.removeAttribute("id");k.style.marginLeft="0"}d=i(">caption",a.nTable);h=0;for(k=d.length;h<k;h++)j.appendChild(d[h]);if(a.oScroll.sX!==""){c.style.width=u(a.oScroll.sX);f.style.width=u(a.oScroll.sX);if(t!==null)e.style.width=u(a.oScroll.sX);i(f).scroll(function(){c.scrollLeft=this.scrollLeft;if(t!==null)e.scrollLeft=this.scrollLeft})}if(a.oScroll.sY!=="")f.style.height=u(a.oScroll.sY);a.aoDrawCallback.push({fn:Ia,sName:"scrolling"});a.oScroll.bInfinite&&
i(f).scroll(function(){if(!a.bDrawing)if(i(this).scrollTop()+i(this).height()>i(a.nTable).height()-a.oScroll.iLoadGap)if(a.fnDisplayEnd()<a.fnRecordsDisplay()){ja(a,"next");E(a);C(a)}});a.nScrollHead=c;a.nScrollFoot=e;return b}function Ia(a){var b=a.nScrollHead.getElementsByTagName("div")[0],c=b.getElementsByTagName("table")[0],d=a.nTable.parentNode,f,e,h,j,k,m,t,q,I=[];h=a.nTable.getElementsByTagName("thead");h.length>0&&a.nTable.removeChild(h[0]);if(a.nTFoot!==null){k=a.nTable.getElementsByTagName("tfoot");
k.length>0&&a.nTable.removeChild(k[0])}h=a.nTHead.cloneNode(true);a.nTable.insertBefore(h,a.nTable.childNodes[0]);if(a.nTFoot!==null){k=a.nTFoot.cloneNode(true);a.nTable.insertBefore(k,a.nTable.childNodes[1])}var O=S(a,h);f=0;for(e=O.length;f<e;f++){t=Ja(a,f);O[f].style.width=a.aoColumns[t].sWidth}a.nTFoot!==null&&P(function(B){B.style.width=""},k.getElementsByTagName("tr"));f=i(a.nTable).outerWidth();if(a.oScroll.sX===""){a.nTable.style.width="100%";if(i.browser.msie&&i.browser.version<=7)a.nTable.style.width=
u(i(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sXInner!=="")a.nTable.style.width=u(a.oScroll.sXInner);else if(f==i(d).width()&&i(d).height()<i(a.nTable).height()){a.nTable.style.width=u(f-a.oScroll.iBarWidth);if(i(a.nTable).outerWidth()>f-a.oScroll.iBarWidth)a.nTable.style.width=u(f)}else a.nTable.style.width=u(f);f=i(a.nTable).outerWidth();e=a.nTHead.getElementsByTagName("tr");h=h.getElementsByTagName("tr");P(function(B,F){m=B.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth=
"0";m.borderBottomWidth="0";m.height=0;q=i(B).width();F.style.width=u(q);I.push(q)},h,e);i(h).height(0);if(a.nTFoot!==null){j=k.getElementsByTagName("tr");k=a.nTFoot.getElementsByTagName("tr");P(function(B,F){m=B.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;q=i(B).width();F.style.width=u(q);I.push(q)},j,k);i(j).height(0)}P(function(B){B.innerHTML="";B.style.width=u(I.shift())},h);a.nTFoot!==null&&P(function(B){B.innerHTML="";B.style.width=u(I.shift())},
j);if(i(a.nTable).outerWidth()<f)if(a.oScroll.sX==="")J(a,1,"The table cannot fit into the current element which will cause column misalignment. It is suggested that you enable x-scrolling or increase the width the table has in which to be drawn");else a.oScroll.sXInner!==""&&J(a,1,"The table cannot fit into the current element which will cause column misalignment. It is suggested that you increase the sScrollXInner property to allow it to draw in a larger area, or simply remove that parameter to allow automatic calculation");
if(a.oScroll.sY==="")if(i.browser.msie&&i.browser.version<=7)d.style.height=u(a.nTable.offsetHeight+a.oScroll.iBarWidth);if(a.oScroll.sY!==""&&a.oScroll.bCollapse){d.style.height=u(a.oScroll.sY);j=a.oScroll.sX!==""&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0;if(a.nTable.offsetHeight<d.offsetHeight)d.style.height=u(i(a.nTable).height()+j)}j=i(a.nTable).outerWidth();c.style.width=u(j);b.style.width=u(j+a.oScroll.iBarWidth);if(a.nTFoot!==null){b=a.nScrollFoot.getElementsByTagName("div")[0];
c=b.getElementsByTagName("table")[0];b.style.width=u(a.nTable.offsetWidth+a.oScroll.iBarWidth);c.style.width=u(a.nTable.offsetWidth)}if(a.bSorted||a.bFiltered)d.scrollTop=0}function ca(a){if(a.oFeatures.bAutoWidth===false)return false;ea(a);for(var b=0,c=a.aoColumns.length;b<c;b++)a.aoColumns[b].nTh.style.width=a.aoColumns[b].sWidth}function Da(a){var b=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.f=="undefined"&&b.setAttribute("id",a.sTableId+"_filter");b.className=a.oClasses.sFilter;
b.innerHTML=a.oLanguage.sSearch+(a.oLanguage.sSearch===""?"":" ")+'<input type="text" />';var c=i("input",b);c.val(a.oPreviousSearch.sSearch.replace('"',"&quot;"));c.bind("keyup.DT",function(){for(var d=a.aanFeatures.f,f=0,e=d.length;f<e;f++)d[f]!=this.parentNode&&i("input",d[f]).val(this.value);this.value!=a.oPreviousSearch.sSearch&&M(a,{sSearch:this.value,bRegex:a.oPreviousSearch.bRegex,bSmart:a.oPreviousSearch.bSmart})});c.bind("keypress.DT",function(d){if(d.keyCode==13)return false});return b}
function M(a,b,c){Ka(a,b.sSearch,c,b.bRegex,b.bSmart);for(b=0;b<a.aoPreSearchCols.length;b++)La(a,a.aoPreSearchCols[b].sSearch,b,a.aoPreSearchCols[b].bRegex,a.aoPreSearchCols[b].bSmart);o.afnFiltering.length!==0&&Ma(a);a.bFiltered=true;a._iDisplayStart=0;E(a);C(a);ka(a,0)}function Ma(a){for(var b=o.afnFiltering,c=0,d=b.length;c<d;c++)for(var f=0,e=0,h=a.aiDisplay.length;e<h;e++){var j=a.aiDisplay[e-f];if(!b[c](a,da(a,j,"filter"),j)){a.aiDisplay.splice(e-f,1);f++}}}function La(a,b,c,d,f){if(b!==""){var e=
0;b=la(b,d,f);for(d=a.aiDisplay.length-1;d>=0;d--){f=ma(H(a,a.aiDisplay[d],c,"filter"),a.aoColumns[c].sType);if(!b.test(f)){a.aiDisplay.splice(d,1);e++}}}}function Ka(a,b,c,d,f){var e=la(b,d,f);if(typeof c=="undefined"||c===null)c=0;if(o.afnFiltering.length!==0)c=1;if(b.length<=0){a.aiDisplay.splice(0,a.aiDisplay.length);a.aiDisplay=a.aiDisplayMaster.slice()}else if(a.aiDisplay.length==a.aiDisplayMaster.length||a.oPreviousSearch.sSearch.length>b.length||c==1||b.indexOf(a.oPreviousSearch.sSearch)!==
0){a.aiDisplay.splice(0,a.aiDisplay.length);ka(a,1);for(c=0;c<a.aiDisplayMaster.length;c++)e.test(a.asDataSearch[c])&&a.aiDisplay.push(a.aiDisplayMaster[c])}else{var h=0;for(c=0;c<a.asDataSearch.length;c++)if(!e.test(a.asDataSearch[c])){a.aiDisplay.splice(c-h,1);h++}}a.oPreviousSearch.sSearch=b;a.oPreviousSearch.bRegex=d;a.oPreviousSearch.bSmart=f}function ka(a,b){a.asDataSearch.splice(0,a.asDataSearch.length);b=typeof b!="undefined"&&b==1?a.aiDisplayMaster:a.aiDisplay;for(var c=0,d=b.length;c<d;c++)a.asDataSearch[c]=
na(a,da(a,b[c],"filter"))}function na(a,b){var c="";if(typeof a.__nTmpFilter=="undefined")a.__nTmpFilter=p.createElement("div");for(var d=a.__nTmpFilter,f=0,e=a.aoColumns.length;f<e;f++)if(a.aoColumns[f].bSearchable)c+=ma(b[f],a.aoColumns[f].sType)+" ";if(c.indexOf("&")!==-1){d.innerHTML=c;c=d.textContent?d.textContent:d.innerText;c=c.replace(/\n/g," ").replace(/\r/g,"")}return c}function la(a,b,c){if(c){a=b?a.split(" "):oa(a).split(" ");a="^(?=.*?"+a.join(")(?=.*?")+").*$";return new RegExp(a,"i")}else{a=
b?a:oa(a);return new RegExp(a,"i")}}function ma(a,b){if(typeof o.ofnSearch[b]=="function")return o.ofnSearch[b](a);else if(b=="html")return a.replace(/\n/g," ").replace(/<.*?>/g,"");else if(typeof a=="string")return a.replace(/\n/g," ");else if(a===null)return"";return a}function R(a,b){var c,d,f,e,h=[],j=[],k=o.oSort;d=a.aoData;var m=a.aoColumns;if(!a.oFeatures.bServerSide&&(a.aaSorting.length!==0||a.aaSortingFixed!==null)){h=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();
for(c=0;c<h.length;c++){var t=h[c][0];f=pa(a,t);e=a.aoColumns[t].sSortDataType;if(typeof o.afnSortData[e]!="undefined"){var q=o.afnSortData[e](a,t,f);f=0;for(e=d.length;f<e;f++)N(a,f,t,q[f])}}c=0;for(d=a.aiDisplayMaster.length;c<d;c++)j[a.aiDisplayMaster[c]]=c;var I=h.length;a.aiDisplayMaster.sort(function(O,B){var F,qa;for(c=0;c<I;c++){F=m[h[c][0]].iDataSort;qa=m[F].sType;F=k[(qa?qa:"string")+"-"+h[c][1]](H(a,O,F,"sort"),H(a,B,F,"sort"));if(F!==0)return F}return k["numeric-asc"](j[O],j[B])})}if((typeof b==
"undefined"||b)&&!a.oFeatures.bDeferRender)T(a);a.bSorted=true;if(a.oFeatures.bFilter)M(a,a.oPreviousSearch,1);else{a.aiDisplay=a.aiDisplayMaster.slice();a._iDisplayStart=0;E(a);C(a)}}function ga(a,b,c,d){i(b).bind("click.DT",function(f){if(a.aoColumns[c].bSortable!==false){var e=function(){var h,j;if(f.shiftKey){for(var k=false,m=0;m<a.aaSorting.length;m++)if(a.aaSorting[m][0]==c){k=true;h=a.aaSorting[m][0];j=a.aaSorting[m][2]+1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")a.aaSorting.splice(m,
1);else{a.aaSorting[m][1]=a.aoColumns[h].asSorting[j];a.aaSorting[m][2]=j}break}k===false&&a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}else if(a.aaSorting.length==1&&a.aaSorting[0][0]==c){h=a.aaSorting[0][0];j=a.aaSorting[0][2]+1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")j=0;a.aaSorting[0][1]=a.aoColumns[h].asSorting[j];a.aaSorting[0][2]=j}else{a.aaSorting.splice(0,a.aaSorting.length);a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}R(a)};if(a.oFeatures.bProcessing){K(a,true);
setTimeout(function(){e();a.oFeatures.bServerSide||K(a,false)},0)}else e();typeof d=="function"&&d(a)}})}function T(a){var b,c,d,f,e,h=a.aoColumns.length,j=a.oClasses;for(b=0;b<h;b++)a.aoColumns[b].bSortable&&i(a.aoColumns[b].nTh).removeClass(j.sSortAsc+" "+j.sSortDesc+" "+a.aoColumns[b].sSortingClass);f=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable){e=a.aoColumns[b].sSortingClass;d=-1;for(c=0;c<f.length;c++)if(f[c][0]==
b){e=f[c][1]=="asc"?j.sSortAsc:j.sSortDesc;d=c;break}i(a.aoColumns[b].nTh).addClass(e);if(a.bJUI){c=i("span",a.aoColumns[b].nTh);c.removeClass(j.sSortJUIAsc+" "+j.sSortJUIDesc+" "+j.sSortJUI+" "+j.sSortJUIAscAllowed+" "+j.sSortJUIDescAllowed);c.addClass(d==-1?a.aoColumns[b].sSortingClassJUI:f[d][1]=="asc"?j.sSortJUIAsc:j.sSortJUIDesc)}}else i(a.aoColumns[b].nTh).addClass(a.aoColumns[b].sSortingClass);e=j.sSortColumn;if(a.oFeatures.bSort&&a.oFeatures.bSortClasses){d=Q(a);if(a.oFeatures.bDeferRender)i(d).removeClass(e+
"1 "+e+"2 "+e+"3");else if(d.length>=h)for(b=0;b<h;b++)if(d[b].className.indexOf(e+"1")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"1",""))}else if(d[b].className.indexOf(e+"2")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"2",""))}else if(d[b].className.indexOf(e+"3")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(" "+e+"3",""))}j=1;var k;for(b=0;b<f.length;b++){k=parseInt(f[b][0],
10);c=0;for(a=d.length/h;c<a;c++)d[h*c+k].className+=" "+e+j;j<3&&j++}}}function Ha(a){if(a.oScroll.bInfinite)return null;var b=p.createElement("div");b.className=a.oClasses.sPaging+a.sPaginationType;o.oPagination[a.sPaginationType].fnInit(a,b,function(c){E(c);C(c)});typeof a.aanFeatures.p=="undefined"&&a.aoDrawCallback.push({fn:function(c){o.oPagination[c.sPaginationType].fnUpdate(c,function(d){E(d);C(d)})},sName:"pagination"});return b}function ja(a,b){var c=a._iDisplayStart;if(b=="first")a._iDisplayStart=
0;else if(b=="previous"){a._iDisplayStart=a._iDisplayLength>=0?a._iDisplayStart-a._iDisplayLength:0;if(a._iDisplayStart<0)a._iDisplayStart=0}else if(b=="next")if(a._iDisplayLength>=0){if(a._iDisplayStart+a._iDisplayLength<a.fnRecordsDisplay())a._iDisplayStart+=a._iDisplayLength}else a._iDisplayStart=0;else if(b=="last")if(a._iDisplayLength>=0){b=parseInt((a.fnRecordsDisplay()-1)/a._iDisplayLength,10)+1;a._iDisplayStart=(b-1)*a._iDisplayLength}else a._iDisplayStart=0;else J(a,0,"Unknown paging action: "+
b);return c!=a._iDisplayStart}function Ga(a){var b=p.createElement("div");b.className=a.oClasses.sInfo;if(typeof a.aanFeatures.i=="undefined"){a.aoDrawCallback.push({fn:Na,sName:"information"});a.sTableId!==""&&b.setAttribute("id",a.sTableId+"_info")}return b}function Na(a){if(!(!a.oFeatures.bInfo||a.aanFeatures.i.length===0)){var b=a._iDisplayStart+1,c=a.fnDisplayEnd(),d=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),e=a.fnFormatNumber(b),h=a.fnFormatNumber(c),j=a.fnFormatNumber(d),k=a.fnFormatNumber(f);
if(a.oScroll.bInfinite)e=a.fnFormatNumber(1);e=a.fnRecordsDisplay()===0&&a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfoEmpty+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()===0?a.oLanguage.sInfoEmpty+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",j)+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfo.replace("_START_",e).replace("_END_",h).replace("_TOTAL_",k)+a.oLanguage.sInfoPostFix:a.oLanguage.sInfo.replace("_START_",e).replace("_END_",h).replace("_TOTAL_",
k)+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()))+a.oLanguage.sInfoPostFix;if(a.oLanguage.fnInfoCallback!==null)e=a.oLanguage.fnInfoCallback(a,b,c,d,f,e);a=a.aanFeatures.i;b=0;for(c=a.length;b<c;b++)i(a[b]).html(e)}}function Ca(a){if(a.oScroll.bInfinite)return null;var b='<select size="1" '+(a.sTableId===""?"":'name="'+a.sTableId+'_length"')+">",c,d;if(a.aLengthMenu.length==2&&typeof a.aLengthMenu[0]=="object"&&typeof a.aLengthMenu[1]=="object"){c=0;for(d=a.aLengthMenu[0].length;c<
d;c++)b+='<option value="'+a.aLengthMenu[0][c]+'">'+a.aLengthMenu[1][c]+"</option>"}else{c=0;for(d=a.aLengthMenu.length;c<d;c++)b+='<option value="'+a.aLengthMenu[c]+'">'+a.aLengthMenu[c]+"</option>"}b+="</select>";var f=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.l=="undefined"&&f.setAttribute("id",a.sTableId+"_length");f.className=a.oClasses.sLength;f.innerHTML=a.oLanguage.sLengthMenu.replace("_MENU_",b);i('select option[value="'+a._iDisplayLength+'"]',f).attr("selected",true);
i("select",f).bind("change.DT",function(){var e=i(this).val(),h=a.aanFeatures.l;c=0;for(d=h.length;c<d;c++)h[c]!=this.parentNode&&i("select",h[c]).val(e);a._iDisplayLength=parseInt(e,10);E(a);if(a.fnDisplayEnd()==a.fnRecordsDisplay()){a._iDisplayStart=a.fnDisplayEnd()-a._iDisplayLength;if(a._iDisplayStart<0)a._iDisplayStart=0}if(a._iDisplayLength==-1)a._iDisplayStart=0;C(a)});return f}function Ea(a){var b=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.r=="undefined"&&b.setAttribute("id",
a.sTableId+"_processing");b.innerHTML=a.oLanguage.sProcessing;b.className=a.oClasses.sProcessing;a.nTable.parentNode.insertBefore(b,a.nTable);return b}function K(a,b){if(a.oFeatures.bProcessing){a=a.aanFeatures.r;for(var c=0,d=a.length;c<d;c++)a[c].style.visibility=b?"visible":"hidden"}}function Ja(a,b){for(var c=-1,d=0;d<a.aoColumns.length;d++){a.aoColumns[d].bVisible===true&&c++;if(c==b)return d}return null}function pa(a,b){for(var c=-1,d=0;d<a.aoColumns.length;d++){a.aoColumns[d].bVisible===true&&
c++;if(d==b)return a.aoColumns[d].bVisible===true?c:null}return null}function U(a,b){var c,d;c=a._iDisplayStart;for(d=a._iDisplayEnd;c<d;c++)if(a.aoData[a.aiDisplay[c]].nTr==b)return a.aiDisplay[c];c=0;for(d=a.aoData.length;c<d;c++)if(a.aoData[c].nTr==b)return c;return null}function X(a){for(var b=0,c=0;c<a.aoColumns.length;c++)a.aoColumns[c].bVisible===true&&b++;return b}function E(a){a._iDisplayEnd=a.oFeatures.bPaginate===false?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength>a.aiDisplay.length||
a._iDisplayLength==-1?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Oa(a,b){if(!a||a===null||a==="")return 0;if(typeof b=="undefined")b=p.getElementsByTagName("body")[0];var c=p.createElement("div");c.style.width=u(a);b.appendChild(c);a=c.offsetWidth;b.removeChild(c);return a}function ea(a){var b=0,c,d=0,f=a.aoColumns.length,e,h=i("th",a.nTHead);for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d++;if(a.aoColumns[e].sWidth!==null){c=Oa(a.aoColumns[e].sWidthOrig,a.nTable.parentNode);if(c!==
null)a.aoColumns[e].sWidth=u(c);b++}}if(f==h.length&&b===0&&d==f&&a.oScroll.sX===""&&a.oScroll.sY==="")for(e=0;e<a.aoColumns.length;e++){c=i(h[e]).width();if(c!==null)a.aoColumns[e].sWidth=u(c)}else{b=a.nTable.cloneNode(false);e=a.nTHead.cloneNode(true);d=p.createElement("tbody");c=p.createElement("tr");b.removeAttribute("id");b.appendChild(e);if(a.nTFoot!==null){b.appendChild(a.nTFoot.cloneNode(true));P(function(k){k.style.width=""},b.getElementsByTagName("tr"))}b.appendChild(d);d.appendChild(c);
d=i("thead th",b);if(d.length===0)d=i("tbody tr:eq(0)>td",b);h=S(a,e);for(e=d=0;e<f;e++){var j=a.aoColumns[e];if(j.bVisible&&j.sWidthOrig!==null&&j.sWidthOrig!=="")h[e-d].style.width=u(j.sWidthOrig);else if(j.bVisible)h[e-d].style.width="";else d++}for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d=Pa(a,e);if(d!==null){d=d.cloneNode(true);if(a.aoColumns[e].sContentPadding!=="")d.innerHTML+=a.aoColumns[e].sContentPadding;c.appendChild(d)}}f=a.nTable.parentNode;f.appendChild(b);if(a.oScroll.sX!==""&&a.oScroll.sXInner!==
"")b.style.width=u(a.oScroll.sXInner);else if(a.oScroll.sX!==""){b.style.width="";if(i(b).width()<f.offsetWidth)b.style.width=u(f.offsetWidth)}else if(a.oScroll.sY!=="")b.style.width=u(f.offsetWidth);b.style.visibility="hidden";Qa(a,b);f=i("tbody tr:eq(0)",b).children();if(f.length===0)f=S(a,i("thead",b)[0]);if(a.oScroll.sX!==""){for(e=d=c=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c+=a.aoColumns[e].sWidthOrig===null?i(f[d]).outerWidth():parseInt(a.aoColumns[e].sWidth.replace("px",""),
10)+(i(f[d]).outerWidth()-i(f[d]).width());d++}b.style.width=u(c);a.nTable.style.width=u(c)}for(e=d=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c=i(f[d]).width();if(c!==null&&c>0)a.aoColumns[e].sWidth=u(c);d++}a.nTable.style.width=u(i(b).outerWidth());b.parentNode.removeChild(b)}}function Qa(a,b){if(a.oScroll.sX===""&&a.oScroll.sY!==""){i(b).width();b.style.width=u(i(b).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sX!=="")b.style.width=u(i(b).outerWidth())}function Pa(a,b){var c=
Ra(a,b);if(c<0)return null;if(a.aoData[c].nTr===null){var d=p.createElement("td");d.innerHTML=H(a,c,b,"");return d}return Q(a,c)[b]}function Ra(a,b){for(var c=-1,d=-1,f=0;f<a.aoData.length;f++){var e=H(a,f,b,"display")+"";e=e.replace(/<.*?>/g,"");if(e.length>c){c=e.length;d=f}}return d}function u(a){if(a===null)return"0px";if(typeof a=="number"){if(a<0)return"0px";return a+"px"}var b=a.charCodeAt(a.length-1);if(b<48||b>57)return a;return a+"px"}function Va(a,b){if(a.length!=b.length)return 1;for(var c=
0;c<a.length;c++)if(a[c]!=b[c])return 2;return 0}function fa(a){for(var b=o.aTypes,c=b.length,d=0;d<c;d++){var f=b[d](a);if(f!==null)return f}return"string"}function A(a){for(var b=0;b<D.length;b++)if(D[b].nTable==a)return D[b];return null}function aa(a){for(var b=[],c=a.aoData.length,d=0;d<c;d++)b.push(a.aoData[d]._aData);return b}function $(a){for(var b=[],c=0,d=a.aoData.length;c<d;c++)a.aoData[c].nTr!==null&&b.push(a.aoData[c].nTr);return b}function Q(a,b){var c=[],d,f,e,h,j;f=0;var k=a.aoData.length;
if(typeof b!="undefined"){f=b;k=b+1}for(f=f;f<k;f++){j=a.aoData[f];if(j.nTr!==null){b=[];e=0;for(h=j.nTr.childNodes.length;e<h;e++){d=j.nTr.childNodes[e].nodeName.toLowerCase();if(d=="td"||d=="th")b.push(j.nTr.childNodes[e])}e=d=0;for(h=a.aoColumns.length;e<h;e++)if(a.aoColumns[e].bVisible)c.push(b[e-d]);else{c.push(j._anHidden[e]);d++}}}return c}function oa(a){return a.replace(new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^)","g"),"\\$1")}function ra(a,b){for(var c=-1,d=
0,f=a.length;d<f;d++)if(a[d]==b)c=d;else a[d]>b&&a[d]--;c!=-1&&a.splice(c,1)}function Ba(a,b){b=b.split(",");for(var c=[],d=0,f=a.aoColumns.length;d<f;d++)for(var e=0;e<f;e++)if(a.aoColumns[d].sName==b[e]){c.push(e);break}return c}function ha(a){for(var b="",c=0,d=a.aoColumns.length;c<d;c++)b+=a.aoColumns[c].sName+",";if(b.length==d)return"";return b.slice(0,-1)}function J(a,b,c){a=a.sTableId===""?"DataTables warning: "+c:"DataTables warning (table id = '"+a.sTableId+"'): "+c;if(b===0)if(o.sErrMode==
"alert")alert(a);else throw a;else typeof console!="undefined"&&typeof console.log!="undefined"&&console.log(a)}function ia(a){a.aoData.splice(0,a.aoData.length);a.aiDisplayMaster.splice(0,a.aiDisplayMaster.length);a.aiDisplay.splice(0,a.aiDisplay.length);E(a)}function sa(a){if(!(!a.oFeatures.bStateSave||typeof a.bDestroying!="undefined")){var b,c,d,f="{";f+='"iCreate":'+(new Date).getTime()+",";f+='"iStart":'+(a.oScroll.bInfinite?0:a._iDisplayStart)+",";f+='"iEnd":'+(a.oScroll.bInfinite?a._iDisplayLength:
a._iDisplayEnd)+",";f+='"iLength":'+a._iDisplayLength+",";f+='"sFilter":"'+encodeURIComponent(a.oPreviousSearch.sSearch)+'",';f+='"sFilterEsc":'+!a.oPreviousSearch.bRegex+",";f+='"aaSorting":[ ';for(b=0;b<a.aaSorting.length;b++)f+="["+a.aaSorting[b][0]+',"'+a.aaSorting[b][1]+'"],';f=f.substring(0,f.length-1);f+="],";f+='"aaSearchCols":[ ';for(b=0;b<a.aoPreSearchCols.length;b++)f+='["'+encodeURIComponent(a.aoPreSearchCols[b].sSearch)+'",'+!a.aoPreSearchCols[b].bRegex+"],";f=f.substring(0,f.length-
1);f+="],";f+='"abVisCols":[ ';for(b=0;b<a.aoColumns.length;b++)f+=a.aoColumns[b].bVisible+",";f=f.substring(0,f.length-1);f+="]";b=0;for(c=a.aoStateSave.length;b<c;b++){d=a.aoStateSave[b].fn(a,f);if(d!=="")f=d}f+="}";Sa(a.sCookiePrefix+a.sInstance,f,a.iCookieDuration,a.sCookiePrefix,a.fnCookieCallback)}}function Ta(a,b){if(a.oFeatures.bStateSave){var c,d,f;d=ta(a.sCookiePrefix+a.sInstance);if(d!==null&&d!==""){try{c=typeof i.parseJSON=="function"?i.parseJSON(d.replace(/'/g,'"')):eval("("+d+")")}catch(e){return}d=
0;for(f=a.aoStateLoad.length;d<f;d++)if(!a.aoStateLoad[d].fn(a,c))return;a.oLoadedState=i.extend(true,{},c);a._iDisplayStart=c.iStart;a.iInitDisplayStart=c.iStart;a._iDisplayEnd=c.iEnd;a._iDisplayLength=c.iLength;a.oPreviousSearch.sSearch=decodeURIComponent(c.sFilter);a.aaSorting=c.aaSorting.slice();a.saved_aaSorting=c.aaSorting.slice();if(typeof c.sFilterEsc!="undefined")a.oPreviousSearch.bRegex=!c.sFilterEsc;if(typeof c.aaSearchCols!="undefined")for(d=0;d<c.aaSearchCols.length;d++)a.aoPreSearchCols[d]=
{sSearch:decodeURIComponent(c.aaSearchCols[d][0]),bRegex:!c.aaSearchCols[d][1]};if(typeof c.abVisCols!="undefined"){b.saved_aoColumns=[];for(d=0;d<c.abVisCols.length;d++){b.saved_aoColumns[d]={};b.saved_aoColumns[d].bVisible=c.abVisCols[d]}}}}}function Sa(a,b,c,d,f){var e=new Date;e.setTime(e.getTime()+c*1E3);c=wa.location.pathname.split("/");a=a+"_"+c.pop().replace(/[\/:]/g,"").toLowerCase();var h;if(f!==null){h=typeof i.parseJSON=="function"?i.parseJSON(b):eval("("+b+")");b=f(a,h,e.toGMTString(),
c.join("/")+"/")}else b=a+"="+encodeURIComponent(b)+"; expires="+e.toGMTString()+"; path="+c.join("/")+"/";f="";e=9999999999999;if((ta(a)!==null?p.cookie.length:b.length+p.cookie.length)+10>4096){a=p.cookie.split(";");for(var j=0,k=a.length;j<k;j++)if(a[j].indexOf(d)!=-1){var m=a[j].split("=");try{h=eval("("+decodeURIComponent(m[1])+")")}catch(t){continue}if(typeof h.iCreate!="undefined"&&h.iCreate<e){f=m[0];e=h.iCreate}}if(f!=="")p.cookie=f+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+c.join("/")+
"/"}p.cookie=b}function ta(a){var b=wa.location.pathname.split("/");a=a+"_"+b[b.length-1].replace(/[\/:]/g,"").toLowerCase()+"=";b=p.cookie.split(";");for(var c=0;c<b.length;c++){for(var d=b[c];d.charAt(0)==" ";)d=d.substring(1,d.length);if(d.indexOf(a)===0)return decodeURIComponent(d.substring(a.length,d.length))}return null}function W(a,b){b=b.getElementsByTagName("tr");var c,d,f,e,h,j,k,m,t=function(O,B,F){for(;typeof O[B][F]!="undefined";)F++;return F};a.splice(0,a.length);d=0;for(j=b.length;d<
j;d++)a.push([]);d=0;for(j=b.length;d<j;d++){f=0;for(k=b[d].childNodes.length;f<k;f++){c=b[d].childNodes[f];if(c.nodeName.toUpperCase()=="TD"||c.nodeName.toUpperCase()=="TH"){var q=c.getAttribute("colspan")*1,I=c.getAttribute("rowspan")*1;q=!q||q===0||q===1?1:q;I=!I||I===0||I===1?1:I;m=t(a,d,0);for(h=0;h<q;h++)for(e=0;e<I;e++){a[d+e][m+h]={cell:c,unique:q==1?true:false};a[d+e].nTr=b[d]}}}}}function S(a,b,c){var d=[];if(typeof c=="undefined"){c=a.aoHeader;if(typeof b!="undefined"){c=[];W(c,b)}}b=0;
for(var f=c.length;b<f;b++)for(var e=0,h=c[b].length;e<h;e++)if(c[b][e].unique&&(typeof d[e]=="undefined"||!a.bSortCellsTop))d[e]=c[b][e].cell;return d}function Ua(){var a=p.createElement("p"),b=a.style;b.width="100%";b.height="200px";var c=p.createElement("div");b=c.style;b.position="absolute";b.top="0px";b.left="0px";b.visibility="hidden";b.width="200px";b.height="150px";b.overflow="hidden";c.appendChild(a);p.body.appendChild(c);b=a.offsetWidth;c.style.overflow="scroll";a=a.offsetWidth;if(b==a)a=
c.clientWidth;p.body.removeChild(c);return b-a}function P(a,b,c){for(var d=0,f=b.length;d<f;d++)for(var e=0,h=b[d].childNodes.length;e<h;e++)if(b[d].childNodes[e].nodeType==1)typeof c!="undefined"?a(b[d].childNodes[e],c[d].childNodes[e]):a(b[d].childNodes[e])}function n(a,b,c,d){if(typeof d=="undefined")d=c;if(typeof b[c]!="undefined")a[d]=b[c]}function da(a,b,c){for(var d=[],f=0,e=a.aoColumns.length;f<e;f++)d.push(H(a,b,f,c));return d}function H(a,b,c,d){var f=a.aoColumns[c];if((c=f.fnGetData(a.aoData[b]._aData))===
undefined){if(a.iDrawError!=a.iDraw&&f.sDefaultContent===null){J(a,0,"Requested unknown parameter '"+f.mDataProp+"' from the data source for row "+b);a.iDrawError=a.iDraw}return f.sDefaultContent}if(c===null&&f.sDefaultContent!==null)c=f.sDefaultContent;if(d=="display"&&c===null)return"";return c}function N(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d)}function Z(a){if(a===null)return function(){return null};else if(typeof a=="function")return function(c){return a(c)};else if(typeof a==
"string"&&a.indexOf(".")!=-1){var b=a.split(".");return b.length==2?function(c){return c[b[0]][b[1]]}:b.length==3?function(c){return c[b[0]][b[1]][b[2]]}:function(c){for(var d=0,f=b.length;d<f;d++)c=c[b[d]];return c}}else return function(c){return c[a]}}function ya(a){if(a===null)return function(){};else if(typeof a=="function")return function(c,d){return a(c,d)};else if(typeof a=="string"&&a.indexOf(".")!=-1){var b=a.split(".");return b.length==2?function(c,d){c[b[0]][b[1]]=d}:b.length==3?function(c,
d){c[b[0]][b[1]][b[2]]=d}:function(c,d){for(var f=0,e=b.length-1;f<e;f++)c=c[b[f]];c[b[b.length-1]]=d}}else return function(c,d){c[a]=d}}this.oApi={};this.fnDraw=function(a){var b=A(this[o.iApiIndex]);if(typeof a!="undefined"&&a===false){E(b);C(b)}else ba(b)};this.fnFilter=function(a,b,c,d,f){var e=A(this[o.iApiIndex]);if(e.oFeatures.bFilter){if(typeof c=="undefined")c=false;if(typeof d=="undefined")d=true;if(typeof f=="undefined")f=true;if(typeof b=="undefined"||b===null){M(e,{sSearch:a,bRegex:c,
bSmart:d},1);if(f&&typeof e.aanFeatures.f!="undefined"){b=e.aanFeatures.f;c=0;for(d=b.length;c<d;c++)i("input",b[c]).val(a)}}else{e.aoPreSearchCols[b].sSearch=a;e.aoPreSearchCols[b].bRegex=c;e.aoPreSearchCols[b].bSmart=d;M(e,e.oPreviousSearch,1)}}};this.fnSettings=function(){return A(this[o.iApiIndex])};this.fnVersionCheck=o.fnVersionCheck;this.fnSort=function(a){var b=A(this[o.iApiIndex]);b.aaSorting=a;R(b)};this.fnSortListener=function(a,b,c){ga(A(this[o.iApiIndex]),a,b,c)};this.fnAddData=function(a,
b){if(a.length===0)return[];var c=[],d,f=A(this[o.iApiIndex]);if(typeof a[0]=="object")for(var e=0;e<a.length;e++){d=v(f,a[e]);if(d==-1)return c;c.push(d)}else{d=v(f,a);if(d==-1)return c;c.push(d)}f.aiDisplay=f.aiDisplayMaster.slice();if(typeof b=="undefined"||b)ba(f);return c};this.fnDeleteRow=function(a,b,c){var d=A(this[o.iApiIndex]);a=typeof a=="object"?U(d,a):a;var f=d.aoData.splice(a,1),e=i.inArray(a,d.aiDisplay);d.asDataSearch.splice(e,1);ra(d.aiDisplayMaster,a);ra(d.aiDisplay,a);typeof b==
"function"&&b.call(this,d,f);if(d._iDisplayStart>=d.aiDisplay.length){d._iDisplayStart-=d._iDisplayLength;if(d._iDisplayStart<0)d._iDisplayStart=0}if(typeof c=="undefined"||c){E(d);C(d)}return f};this.fnClearTable=function(a){var b=A(this[o.iApiIndex]);ia(b);if(typeof a=="undefined"||a)C(b)};this.fnOpen=function(a,b,c){var d=A(this[o.iApiIndex]);this.fnClose(a);var f=p.createElement("tr"),e=p.createElement("td");f.appendChild(e);e.className=c;e.colSpan=X(d);if(typeof b.jquery!="undefined"||typeof b==
"object")e.appendChild(b);else e.innerHTML=b;b=i("tr",d.nTBody);i.inArray(a,b)!=-1&&i(f).insertAfter(a);d.aoOpenRows.push({nTr:f,nParent:a});return f};this.fnClose=function(a){for(var b=A(this[o.iApiIndex]),c=0;c<b.aoOpenRows.length;c++)if(b.aoOpenRows[c].nParent==a){(a=b.aoOpenRows[c].nTr.parentNode)&&a.removeChild(b.aoOpenRows[c].nTr);b.aoOpenRows.splice(c,1);return 0}return 1};this.fnGetData=function(a,b){var c=A(this[o.iApiIndex]);if(typeof a!="undefined"){a=typeof a=="object"?U(c,a):a;if(typeof b!=
"undefined")return H(c,a,b,"");return typeof c.aoData[a]!="undefined"?c.aoData[a]._aData:null}return aa(c)};this.fnGetNodes=function(a){var b=A(this[o.iApiIndex]);if(typeof a!="undefined")return typeof b.aoData[a]!="undefined"?b.aoData[a].nTr:null;return $(b)};this.fnGetPosition=function(a){var b=A(this[o.iApiIndex]),c=a.nodeName.toUpperCase();if(c=="TR")return U(b,a);else if(c=="TD"||c=="TH"){c=U(b,a.parentNode);for(var d=Q(b,c),f=0;f<b.aoColumns.length;f++)if(d[f]==a)return[c,pa(b,f),f]}return null};
this.fnUpdate=function(a,b,c,d,f){var e=A(this[o.iApiIndex]);b=typeof b=="object"?U(e,b):b;if(i.isArray(a)&&typeof a=="object"){if(a.length!=e.aoColumns.length){J(e,0,"An array passed to fnUpdate must have the same number of columns as the table in question - in this case "+e.aoColumns.length);return 1}e.aoData[b]._aData=a.slice();for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(H(e,b,c),b,c,false,false)}else if(typeof a=="object"){e.aoData[b]._aData=i.extend(true,{},a);for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(H(e,
b,c),b,c,false,false)}else{a=a;N(e,b,c,a);if(e.aoColumns[c].fnRender!==null){a=e.aoColumns[c].fnRender({iDataRow:b,iDataColumn:c,aData:e.aoData[b]._aData,oSettings:e});e.aoColumns[c].bUseRendered&&N(e,b,c,a)}if(e.aoData[b].nTr!==null)Q(e,b)[c].innerHTML=a}c=i.inArray(b,e.aiDisplay);e.asDataSearch[c]=na(e,da(e,b,"filter"));if(typeof f=="undefined"||f)ca(e);if(typeof d=="undefined"||d)ba(e);return 0};this.fnSetColumnVis=function(a,b,c){var d=A(this[o.iApiIndex]),f,e;e=d.aoColumns.length;var h,j;if(d.aoColumns[a].bVisible!=
b){if(b){for(f=j=0;f<a;f++)d.aoColumns[f].bVisible&&j++;j=j>=X(d);if(!j)for(f=a;f<e;f++)if(d.aoColumns[f].bVisible){h=f;break}f=0;for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null)j?d.aoData[f].nTr.appendChild(d.aoData[f]._anHidden[a]):d.aoData[f].nTr.insertBefore(d.aoData[f]._anHidden[a],Q(d,f)[h])}else{f=0;for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null){h=Q(d,f)[a];d.aoData[f]._anHidden[a]=h;h.parentNode.removeChild(h)}}d.aoColumns[a].bVisible=b;L(d,d.aoHeader);d.nTFoot&&L(d,d.aoFooter);
f=0;for(e=d.aoOpenRows.length;f<e;f++)d.aoOpenRows[f].nTr.colSpan=X(d);if(typeof c=="undefined"||c){ca(d);C(d)}sa(d)}};this.fnPageChange=function(a,b){var c=A(this[o.iApiIndex]);ja(c,a);E(c);if(typeof b=="undefined"||b)C(c)};this.fnDestroy=function(){var a=A(this[o.iApiIndex]),b=a.nTableWrapper.parentNode,c=a.nTBody,d,f;a.bDestroying=true;i(a.nTableWrapper).find("*").andSelf().unbind(".DT");d=0;for(f=a.aoColumns.length;d<f;d++)a.aoColumns[d].bVisible===false&&this.fnSetColumnVis(d,true);i("tbody>tr>td."+
a.oClasses.sRowEmpty,a.nTable).parent().remove();if(a.nTable!=a.nTHead.parentNode){i(">thead",a.nTable).remove();a.nTable.appendChild(a.nTHead)}if(a.nTFoot&&a.nTable!=a.nTFoot.parentNode){i(">tfoot",a.nTable).remove();a.nTable.appendChild(a.nTFoot)}a.nTable.parentNode.removeChild(a.nTable);i(a.nTableWrapper).remove();a.aaSorting=[];a.aaSortingFixed=[];T(a);i($(a)).removeClass(a.asStripClasses.join(" "));if(a.bJUI){i("th",a.nTHead).removeClass([o.oStdClasses.sSortable,o.oJUIClasses.sSortableAsc,o.oJUIClasses.sSortableDesc,
o.oJUIClasses.sSortableNone].join(" "));i("th span."+o.oJUIClasses.sSortIcon,a.nTHead).remove();i("th",a.nTHead).each(function(){var e=i("div."+o.oJUIClasses.sSortJUIWrapper,this),h=e.contents();i(this).append(h);e.remove()})}else i("th",a.nTHead).removeClass([o.oStdClasses.sSortable,o.oStdClasses.sSortableAsc,o.oStdClasses.sSortableDesc,o.oStdClasses.sSortableNone].join(" "));a.nTableReinsertBefore?b.insertBefore(a.nTable,a.nTableReinsertBefore):b.appendChild(a.nTable);d=0;for(f=a.aoData.length;d<
f;d++)a.aoData[d].nTr!==null&&c.appendChild(a.aoData[d].nTr);a.nTable.style.width=u(a.sDestroyWidth);i(">tr:even",c).addClass(a.asDestoryStrips[0]);i(">tr:odd",c).addClass(a.asDestoryStrips[1]);d=0;for(f=D.length;d<f;d++)D[d]==a&&D.splice(d,1);a=null};this.fnAdjustColumnSizing=function(a){var b=A(this[o.iApiIndex]);ca(b);if(typeof a=="undefined"||a)this.fnDraw(false);else if(b.oScroll.sX!==""||b.oScroll.sY!=="")this.oApi._fnScrollDraw(b)};for(var ua in o.oApi)if(ua)this[ua]=r(ua);this.oApi._fnExternApiFunc=
r;this.oApi._fnInitalise=s;this.oApi._fnInitComplete=w;this.oApi._fnLanguageProcess=y;this.oApi._fnAddColumn=G;this.oApi._fnColumnOptions=x;this.oApi._fnAddData=v;this.oApi._fnCreateTr=z;this.oApi._fnGatherData=Y;this.oApi._fnBuildHead=V;this.oApi._fnDrawHead=L;this.oApi._fnDraw=C;this.oApi._fnReDraw=ba;this.oApi._fnAjaxUpdate=za;this.oApi._fnAjaxUpdateDraw=Aa;this.oApi._fnAddOptionsHtml=xa;this.oApi._fnFeatureHtmlTable=Fa;this.oApi._fnScrollDraw=Ia;this.oApi._fnAjustColumnSizing=ca;this.oApi._fnFeatureHtmlFilter=
Da;this.oApi._fnFilterComplete=M;this.oApi._fnFilterCustom=Ma;this.oApi._fnFilterColumn=La;this.oApi._fnFilter=Ka;this.oApi._fnBuildSearchArray=ka;this.oApi._fnBuildSearchRow=na;this.oApi._fnFilterCreateSearch=la;this.oApi._fnDataToSearch=ma;this.oApi._fnSort=R;this.oApi._fnSortAttachListener=ga;this.oApi._fnSortingClasses=T;this.oApi._fnFeatureHtmlPaginate=Ha;this.oApi._fnPageChange=ja;this.oApi._fnFeatureHtmlInfo=Ga;this.oApi._fnUpdateInfo=Na;this.oApi._fnFeatureHtmlLength=Ca;this.oApi._fnFeatureHtmlProcessing=
Ea;this.oApi._fnProcessingDisplay=K;this.oApi._fnVisibleToColumnIndex=Ja;this.oApi._fnColumnIndexToVisible=pa;this.oApi._fnNodeToDataIndex=U;this.oApi._fnVisbleColumns=X;this.oApi._fnCalculateEnd=E;this.oApi._fnConvertToWidth=Oa;this.oApi._fnCalculateColumnWidths=ea;this.oApi._fnScrollingWidthAdjust=Qa;this.oApi._fnGetWidestNode=Pa;this.oApi._fnGetMaxLenString=Ra;this.oApi._fnStringToCss=u;this.oApi._fnArrayCmp=Va;this.oApi._fnDetectType=fa;this.oApi._fnSettingsFromNode=A;this.oApi._fnGetDataMaster=
aa;this.oApi._fnGetTrNodes=$;this.oApi._fnGetTdNodes=Q;this.oApi._fnEscapeRegex=oa;this.oApi._fnDeleteIndex=ra;this.oApi._fnReOrderIndex=Ba;this.oApi._fnColumnOrdering=ha;this.oApi._fnLog=J;this.oApi._fnClearTable=ia;this.oApi._fnSaveState=sa;this.oApi._fnLoadState=Ta;this.oApi._fnCreateCookie=Sa;this.oApi._fnReadCookie=ta;this.oApi._fnDetectHeader=W;this.oApi._fnGetUniqueThs=S;this.oApi._fnScrollBarWidth=Ua;this.oApi._fnApplyToChildren=P;this.oApi._fnMap=n;this.oApi._fnGetRowData=da;this.oApi._fnGetCellData=
H;this.oApi._fnSetCellData=N;this.oApi._fnGetObjectDataFn=Z;this.oApi._fnSetObjectDataFn=ya;var va=this;return this.each(function(){var a=0,b,c,d,f;a=0;for(b=D.length;a<b;a++){if(D[a].nTable==this)if(typeof g=="undefined"||typeof g.bRetrieve!="undefined"&&g.bRetrieve===true)return D[a].oInstance;else if(typeof g.bDestroy!="undefined"&&g.bDestroy===true){D[a].oInstance.fnDestroy();break}else{J(D[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destory the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");
return}if(D[a].sTableId!==""&&D[a].sTableId==this.getAttribute("id")){D.splice(a,1);break}}var e=new l;D.push(e);var h=false,j=false;a=this.getAttribute("id");if(a!==null){e.sTableId=a;e.sInstance=a}else e.sInstance=o._oExternConfig.iNextUnique++;if(this.nodeName.toLowerCase()!="table")J(e,0,"Attempted to initialise DataTables on a node which is not a table: "+this.nodeName);else{e.nTable=this;e.oInstance=va.length==1?va:i(this).dataTable();e.oApi=va.oApi;e.sDestroyWidth=i(this).width();if(typeof g!=
"undefined"&&g!==null){e.oInit=g;n(e.oFeatures,g,"bPaginate");n(e.oFeatures,g,"bLengthChange");n(e.oFeatures,g,"bFilter");n(e.oFeatures,g,"bSort");n(e.oFeatures,g,"bInfo");n(e.oFeatures,g,"bProcessing");n(e.oFeatures,g,"bAutoWidth");n(e.oFeatures,g,"bSortClasses");n(e.oFeatures,g,"bServerSide");n(e.oFeatures,g,"bDeferRender");n(e.oScroll,g,"sScrollX","sX");n(e.oScroll,g,"sScrollXInner","sXInner");n(e.oScroll,g,"sScrollY","sY");n(e.oScroll,g,"bScrollCollapse","bCollapse");n(e.oScroll,g,"bScrollInfinite",
"bInfinite");n(e.oScroll,g,"iScrollLoadGap","iLoadGap");n(e.oScroll,g,"bScrollAutoCss","bAutoCss");n(e,g,"asStripClasses");n(e,g,"fnPreDrawCallback");n(e,g,"fnRowCallback");n(e,g,"fnHeaderCallback");n(e,g,"fnFooterCallback");n(e,g,"fnCookieCallback");n(e,g,"fnInitComplete");n(e,g,"fnServerData");n(e,g,"fnFormatNumber");n(e,g,"aaSorting");n(e,g,"aaSortingFixed");n(e,g,"aLengthMenu");n(e,g,"sPaginationType");n(e,g,"sAjaxSource");n(e,g,"sAjaxDataProp");n(e,g,"iCookieDuration");n(e,g,"sCookiePrefix");
n(e,g,"sDom");n(e,g,"bSortCellsTop");n(e,g,"oSearch","oPreviousSearch");n(e,g,"aoSearchCols","aoPreSearchCols");n(e,g,"iDisplayLength","_iDisplayLength");n(e,g,"bJQueryUI","bJUI");n(e.oLanguage,g,"fnInfoCallback");typeof g.fnDrawCallback=="function"&&e.aoDrawCallback.push({fn:g.fnDrawCallback,sName:"user"});typeof g.fnStateSaveCallback=="function"&&e.aoStateSave.push({fn:g.fnStateSaveCallback,sName:"user"});typeof g.fnStateLoadCallback=="function"&&e.aoStateLoad.push({fn:g.fnStateLoadCallback,sName:"user"});
if(e.oFeatures.bServerSide&&e.oFeatures.bSort&&e.oFeatures.bSortClasses)e.aoDrawCallback.push({fn:T,sName:"server_side_sort_classes"});else e.oFeatures.bDeferRender&&e.aoDrawCallback.push({fn:T,sName:"defer_sort_classes"});if(typeof g.bJQueryUI!="undefined"&&g.bJQueryUI){e.oClasses=o.oJUIClasses;if(typeof g.sDom=="undefined")e.sDom='<"H"lfr>t<"F"ip>'}if(e.oScroll.sX!==""||e.oScroll.sY!=="")e.oScroll.iBarWidth=Ua();if(typeof g.iDisplayStart!="undefined"&&typeof e.iInitDisplayStart=="undefined"){e.iInitDisplayStart=
g.iDisplayStart;e._iDisplayStart=g.iDisplayStart}if(typeof g.bStateSave!="undefined"){e.oFeatures.bStateSave=g.bStateSave;Ta(e,g);e.aoDrawCallback.push({fn:sa,sName:"state_save"})}if(typeof g.iDeferLoading!="undefined"){e.bDeferLoading=true;e._iRecordsTotal=g.iDeferLoading;e._iRecordsDisplay=g.iDeferLoading}if(typeof g.aaData!="undefined")j=true;if(typeof g!="undefined"&&typeof g.aoData!="undefined")g.aoColumns=g.aoData;if(typeof g.oLanguage!="undefined")if(typeof g.oLanguage.sUrl!="undefined"&&g.oLanguage.sUrl!==
""){e.oLanguage.sUrl=g.oLanguage.sUrl;i.getJSON(e.oLanguage.sUrl,null,function(t){y(e,t,true)});h=true}else y(e,g.oLanguage,false)}else g={};if(typeof g.asStripClasses=="undefined"){e.asStripClasses.push(e.oClasses.sStripOdd);e.asStripClasses.push(e.oClasses.sStripEven)}c=false;d=i(">tbody>tr",this);a=0;for(b=e.asStripClasses.length;a<b;a++)if(d.filter(":lt(2)").hasClass(e.asStripClasses[a])){c=true;break}if(c){e.asDestoryStrips=["",""];if(i(d[0]).hasClass(e.oClasses.sStripOdd))e.asDestoryStrips[0]+=
e.oClasses.sStripOdd+" ";if(i(d[0]).hasClass(e.oClasses.sStripEven))e.asDestoryStrips[0]+=e.oClasses.sStripEven;if(i(d[1]).hasClass(e.oClasses.sStripOdd))e.asDestoryStrips[1]+=e.oClasses.sStripOdd+" ";if(i(d[1]).hasClass(e.oClasses.sStripEven))e.asDestoryStrips[1]+=e.oClasses.sStripEven;d.removeClass(e.asStripClasses.join(" "))}c=[];var k;a=this.getElementsByTagName("thead");if(a.length!==0){W(e.aoHeader,a[0]);c=S(e)}if(typeof g.aoColumns=="undefined"){k=[];a=0;for(b=c.length;a<b;a++)k.push(null)}else k=
g.aoColumns;a=0;for(b=k.length;a<b;a++){if(typeof g.saved_aoColumns!="undefined"&&g.saved_aoColumns.length==b){if(k[a]===null)k[a]={};k[a].bVisible=g.saved_aoColumns[a].bVisible}G(e,c?c[a]:null)}if(typeof g.aoColumnDefs!="undefined")for(a=g.aoColumnDefs.length-1;a>=0;a--){var m=g.aoColumnDefs[a].aTargets;i.isArray(m)||J(e,1,"aTargets must be an array of targets, not a "+typeof m);c=0;for(d=m.length;c<d;c++)if(typeof m[c]=="number"&&m[c]>=0){for(;e.aoColumns.length<=m[c];)G(e);x(e,m[c],g.aoColumnDefs[a])}else if(typeof m[c]==
"number"&&m[c]<0)x(e,e.aoColumns.length+m[c],g.aoColumnDefs[a]);else if(typeof m[c]=="string"){b=0;for(f=e.aoColumns.length;b<f;b++)if(m[c]=="_all"||i(e.aoColumns[b].nTh).hasClass(m[c]))x(e,b,g.aoColumnDefs[a])}}if(typeof k!="undefined"){a=0;for(b=k.length;a<b;a++)x(e,a,k[a])}a=0;for(b=e.aaSorting.length;a<b;a++){if(e.aaSorting[a][0]>=e.aoColumns.length)e.aaSorting[a][0]=0;k=e.aoColumns[e.aaSorting[a][0]];if(typeof e.aaSorting[a][2]=="undefined")e.aaSorting[a][2]=0;if(typeof g.aaSorting=="undefined"&&
typeof e.saved_aaSorting=="undefined")e.aaSorting[a][1]=k.asSorting[0];c=0;for(d=k.asSorting.length;c<d;c++)if(e.aaSorting[a][1]==k.asSorting[c]){e.aaSorting[a][2]=c;break}}T(e);a=i(">thead",this);if(a.length===0){a=[p.createElement("thead")];this.appendChild(a[0])}e.nTHead=a[0];a=i(">tbody",this);if(a.length===0){a=[p.createElement("tbody")];this.appendChild(a[0])}e.nTBody=a[0];a=i(">tfoot",this);if(a.length>0){e.nTFoot=a[0];W(e.aoFooter,e.nTFoot)}if(j)for(a=0;a<g.aaData.length;a++)v(e,g.aaData[a]);
else Y(e);e.aiDisplay=e.aiDisplayMaster.slice();e.bInitialised=true;h===false&&s(e)}})}})(jQuery,window,document);

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/
*/
.ui-dialog.ui-widget{
padding: 0;
}
.ui-dialog .ui-dialog-content.ui-widget-content{
padding: 0;
overflow: visible;
}
.rgba.boxshadow .ui-dialog {
border: none;
-moz-box-shadow:0px 8px 12px rgba(0, 0, 0, 0.6);
-webkit-box-shadow:0px 8px 12px rgba(0, 0, 0, 0.6);
box-shadow:0px 8px 12px rgba(0, 0, 0, 0.6);
}
.ui-dialog .dataTables_paginate,
.ui-dialog .dataTables_filter{
float: right;
}
.ui-dialog .dataTables_processing{
float: left;
margin-left: 57px;
margin-top: 4px;
}
.ui-dialog .dataTables_info,
.ui-dialog .dataTables_length,
.ui-dialog .vcard-wrapper,
.ui-dialog table.dttable {
float: left;
}
.ui-dialog table.dttable {
display: table;
border-right: 1px solid #A5A5A5;
max-width: 202px;
}
.ie7 .ui-dialog table.dttable {
display: block;
}
.clickable {
cursor: pointer;
}
.ui-dialog .vcard-wrapper {
border-left: 1px solid #A5A5A5;
margin-left: -1px;
width: 368px;
padding: 15px;
}
.ie7 .ui-dialog .vcard-wrapper {
width: 358px;
}
.ui-dialog .ui-dialog-title {
display: block;
text-align: center;
margin: 0;
float: none;
}
.ui-dialog .ui-dialog-titlebar {
border-bottom: none;
-moz-border-radius-topleft: 5px;
-webkit-border-top-left-radius: 5px;
border-top-left-radius: 5px;
-moz-border-radius-topright: 5px;
-webkit-border-top-right-radius: 5px;
border-top-right-radius: 5px;
-moz-border-radius-bottomleft: 0;
-webkit-border-bottom-left-radius: 0;
border-bottom-left-radius: 0;
-moz-border-radius-bottomright: 0;
-webkit-border-bottom-right-radius: 0;
border-bottom-right-radius: 0;
}
.ui-dialog .dataTables_wrapper .ui-widget-header.ui-corner-tl.ui-corner-tr {
border-top: none;
}
.ui-dialog .dataTables_wrapper .ui-widget-header {
-moz-border-radius-topleft: 0;
-webkit-border-top-left-radius: 0;
border-top-left-radius: 0;
-moz-border-radius-topright: 0;
-webkit-border-top-right-radius: 0;
border-top-right-radius: 0;
-moz-border-radius-bottomleft: 0;
-webkit-border-bottom-left-radius: 0;
border-bottom-left-radius: 0;
-moz-border-radius-bottomright: 0;
-webkit-border-bottom-right-radius: 0;
border-bottom-right-radius: 0;
}
.ui-dialog .ui-dialog-content {
padding: 0;
overflow: visible;
}
.ui-dialog .ui-dialog-buttonpane {
border: none;
margin: 0;
}
.ui-dialog table.dttable tr.current-item,
.ui-dialog table.dttable tr.current-item td {
background-color: #005481;
color: white;
}
.ui-dialog .vcard {
margin: 0;
list-style: none;
}
.ui-dialog .vcard li {
margin-bottom: 15px;
line-height: 131%;
}
.ui-dialog .vcard li.vcard-add {
margin-bottom: 0;
}
.ui-dialog .vcard li.vcard-add input {
float: right;
}
.ui-dialog .vcard label {
font-weight: bold;
}
.ui-dialog .no-vcard-selected {
font-weight: bold;
font-size: 123.1%;
display: block;
text-align: center;
padding: 10px;
}
img.ds-authority-confidence {
display: none;
}
.notinsolr {
font-style: italic;
}

View File

@@ -0,0 +1,298 @@
/*
* 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/
*/
function AuthorLookup(url, authorityInput, collectionID) {
// TODO i18n
var content = $('<div title="Person Lookup">' +
'<table class="dttable">' +
'<thead>' +
'<th>Name</th>' +
'</thead>' +
'<tbody>' +
'<tr><td>Loading...<td></tr>' +
'</tbody>' +
'</table>' +
'<span class="no-vcard-selected">There\'s no one selected</span>' +
'<ul class="vcard" style="display: none;">' +
'<li><ul class="variable"/></li>'+
'<li class="vcard-insolr">' +
'<label>Items in this repository:&nbsp;</label>' +
'<span/>' +
'</li>' +
'<li class="vcard-add">' +
'<input class="ds-button-field" value="Add This Person" type="button"/>' +
'</li>' +
'</ul>' +
'</div>');
var moreButton = '<button id="lookup-more-button">show more</button>';
var lessButton = '<button id="lookup-less-button">show less</button>';
var button = moreButton;
var datatable = content.find("table.dttable");
datatable.dataTable({
"aoColumns": [
{
"bSortable": false,
"sWidth": "200px"
},
{
"bSortable": false,
"bSearchable": false,
"bVisible": false
}
],
"oLanguage": {
"sInfo": 'Showing _START_ to _END_ of _TOTAL_ people',
"sInfoEmpty": 'Showing 0 to 0 of 0 people',
"sInfoFiltered": '(filtered from _MAX_ total people)',
"sLengthMenu": '_MENU_ people/page',
"sZeroRecords": 'No people found'
},
"bAutoWidth": false,
"bJQueryUI": true,
"bProcessing": true,
"bSort": false,
"bPaginate": false,
"sPaginationType": "two_button",
"bServerSide": true,
"sAjaxSource": url,
"sDom": '<"H"lfr><"clearfix"t<"vcard-wrapper">><"F"ip>',
"fnInitComplete": function() {
content.find("table.dttable").show();
content.find("div.vcard-wrapper").append(content.find('.no-vcard-selected')).append(content.find('ul.vcard'));
content.dialog({
autoOpen: true,
resizable: false,
modal: false,
width: 600
});
var searchFilter = $('.dataTables_filter > input');
var initialInput = "";
if (authorityInput.indexOf('value_') != -1) {
initialInput = $('textarea[name='+ authorityInput+']').val();
}else{
initialInput = ($('input[name=' + authorityInput + '_last]').val() + " "+$('input[name=' + authorityInput + '_first]').val()).trim();
}
searchFilter.val(initialInput);
setTimeout(function () {
searchFilter.trigger($.Event("keyup", { keyCode: 13 }));
},50);
$('.dataTables_wrapper').parent().attr('style','width: auto; min-height: 121px; height: auto;');
},
"fnInfoCallback": function( oSettings, iStart, iEnd, iMax, iTotal, sPre ) {
return "Showing "+ iEnd + " results. "+button;
},
"fnRowCallback": function( nRow, aData, iDisplayIndex ) {
aData = aData[1];
var $row = $(nRow);
var authorityID = $(this).closest('.dataTables_wrapper').find('.vcard-wrapper .vcard').data('authorityID');
if (authorityID != undefined && aData['authority'] == authorityID) {
$row.addClass('current-item');
}
$row.addClass('clickable');
if(aData['insolr']=="false"){
$row.addClass("notinsolr");
}
$row.click(function() {
var $this = $(this);
$this.siblings('.current-item').removeClass('current-item');
$this.addClass('current-item');
var wrapper = $this.closest('.dataTables_wrapper').find('.vcard-wrapper');
wrapper.find('.no-vcard-selected:visible').hide();
var vcard = wrapper.find('.vcard');
vcard.data('authorityID', aData['authority']);
vcard.data('name', aData['value']);
var notDisplayed = ['insolr','value','authority'];
var predefinedOrder = ['last-name','first-name'];
var variable = vcard.find('.variable');
variable.empty();
predefinedOrder.forEach(function (entry) {
variableItem(aData, entry, variable);
});
for (var key in aData) {
if (aData.hasOwnProperty(key) && notDisplayed.indexOf(key) < 0 && predefinedOrder.indexOf(key) < 0) {
variableItem(aData, key, variable);
}
}
function variableItem(aData, key, variable) {
var label = key.replace(/-/g, ' ');
var dataString = '';
dataString += '<li class="vcard-' + key + '">' +
'<label>' + label + ': </label>';
if(key == 'orcid'){
dataString +='<span><a target="_blank" href="http://orcid.org/' + aData[key] + '">' + aData[key] + '</a></span>';
} else {
dataString += '<span>' + aData[key] + '</span>';
}
dataString += '</li>';
variable.append(dataString);
return label;
}
// vcard.find('.vcard-last-name span').text(aData['first-name']);
// vcard.find('.vcard-first-name span').text(aData['last-name']);
// vcard.find('.vcard-orcid span').empty().append('<a href="http://orcid.org/'+aData['symplectic']+'" target="_new">'+aData['symplectic']+'</a>');
if(aData['insolr']!="false"){
var discoverLink = window.orcid.contextPath + "/discover?filtertype=author&filter_relational_operator=authority&filter=" + aData['insolr'];
vcard.find('.vcard-insolr span').empty().append('<a href="'+ discoverLink+'" target="_new">view items</a>');
}else{
vcard.find('.vcard-insolr span').text("0");
}
vcard.find('.vcard-add input').click(function() {
if (authorityInput.indexOf('value_') != -1) {
// edit item
$('input[name=' + authorityInput + ']').val(vcard.find('.vcard-last-name span').text() + ', ' + vcard.find('.vcard-first-name span').text());
var oldAuthority = $('input[name=' + authorityInput + '_authority]');
oldAuthority.val(vcard.data('authorityID'));
$('textarea[name='+ authorityInput+']').val(vcard.data('name'));
} else {
// submission
$('input[name=' + authorityInput + '_last]').val(vcard.find('.vcard-last-name span').text());
$('input[name=' + authorityInput + '_first]').val(vcard.find('.vcard-first-name span').text());
$('input[name=' + authorityInput + '_authority]').val(vcard.data('authorityID'));
$('input[name=submit_'+ authorityInput +'_add]').click();
}
content.dialog('destroy');
});
vcard.show();
});
return nRow;
},
"fnDrawCallback": function() {
var wrapper = $(this).closest('.dataTables_wrapper');
if (wrapper.find('.current-item').length > 0) {
wrapper.find('.vcard-wrapper .no-vcard-selected:visible').hide();
wrapper.find('.vcard-wrapper .vcard:hidden').show();
}
else {
wrapper.find('.vcard-wrapper .vcard:visible').hide();
wrapper.find('.vcard-wrapper .no-vcard-selected:hidden').show();
}
$('#lookup-more-button').click(function () {
button = lessButton;
datatable.fnFilter($('.dataTables_filter > input').val());
});
$('#lookup-less-button').click(function () {
button = moreButton;
datatable.fnFilter($('.dataTables_filter > input').val());
});
},
"fnServerData": function (sSource, aoData, fnCallback) {
var sEcho;
var query;
var start;
var limit;
$.each(aoData, function() {
if (this.name == "sEcho") {
sEcho = this.value;
}
else if (this.name == "sSearch") {
query = this.value;
}
else if (this.name == "iDisplayStart") {
start = this.value;
}
else if (this.name == "iDisplayLength") {
limit = this.value;
}
});
if (collectionID == undefined) {
collectionID = '-1';
}
if (sEcho == undefined) {
sEcho = '';
}
if (query == undefined) {
query = '';
}
if (start == undefined) {
start = '0';
}
if (limit == undefined) {
limit = '0';
}
if (button == lessButton) {
limit = '20';
}
if (button == moreButton) {
limit = '10';
}
var data = [];
data.push({"name": "query", "value": query});
data.push({"name": "collection", "value": collectionID});
data.push({"name": "start", "value": start});
data.push({"name": "limit", "value": limit});
var $this = $(this);
$.ajax({
cache: false,
url: sSource,
dataType: 'xml',
data: data,
success: function (data) {
/* Translate AC XML to DT JSON */
var $xml = $(data);
var aaData = [];
$.each($xml.find('Choice'), function() {
// comes from org.dspace.content.authority.SolrAuthority.java
var choice = this;
var row = [];
var rowData = {};
for(var k = 0; k < choice.attributes.length; k++) {
var attr = choice.attributes[k];
rowData[attr.name] = attr.value;
}
row.push(rowData.value);
row.push(rowData);
aaData.push(row);
});
var nbFiltered = $xml.find('Choices').attr('total');
var total = $this.data('totalNbPeople');
if (total == undefined || (total * 1) < 1) {
total = nbFiltered;
$this.data('totalNbPeople', total);
}
var json = {
"sEcho": sEcho,
"iTotalRecords": total,
"iTotalDisplayRecords": nbFiltered,
"aaData": aaData
};
fnCallback(json);
}
});
}
});
}

View File

@@ -93,6 +93,45 @@
</input> </input>
</xsl:template> </xsl:template>
<xsl:template name="addLookupButtonAuthor">
<xsl:param name="isName" select="'missing value'"/>
<input type="button" name="{concat('lookup_',@n)}" class="ds-button-field ds-add-button" >
<xsl:attribute name="value">
<xsl:text>Lookup</xsl:text>
<xsl:if test="contains(dri:params/@operations,'add')">
<xsl:text> &amp; Add</xsl:text>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="onClick">
<xsl:text>javascript:AuthorLookup('</xsl:text>
<!-- URL -->
<xsl:value-of select="concat($context-path, '/choices/')"/>
<xsl:choose>
<xsl:when test="starts-with(@n, 'value_')">
<xsl:value-of select="dri:params/@choices"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@n"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>', '</xsl:text>
<xsl:value-of select="@n"/>
<xsl:text>', </xsl:text>
<!-- Collection ID for context -->
<xsl:choose>
<xsl:when test="/dri:document/dri:meta/dri:pageMeta/dri:metadata[@element='choice'][@qualifier='collection']">
<xsl:value-of select="/dri:document/dri:meta/dri:pageMeta/dri:metadata[@element='choice'][@qualifier='collection']"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-1</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>);</xsl:text>
</xsl:attribute>
</input>
</xsl:template>
<!-- Fragment to display an authority confidence icon. <!-- Fragment to display an authority confidence icon.
- Insert an invisible 1x1 image which gets "covered" by background - Insert an invisible 1x1 image which gets "covered" by background
- image as dictated by the CSS, so icons are easily adjusted in CSS. - image as dictated by the CSS, so icons are easily adjusted in CSS.

View File

@@ -102,6 +102,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'true'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
<xsl:if test="dri:params/@authorityControlled"> <xsl:if test="dri:params/@authorityControlled">
<xsl:variable name="confValue" select="dri:field/dri:value[@type='authority'][1]/@confidence"/> <xsl:variable name="confValue" select="dri:field/dri:value[@type='authority'][1]/@confidence"/>
@@ -131,11 +137,11 @@
<!-- Add buttons should be named "submit_[field]_add" so that we can ignore errors from required fields when simply adding new values--> <!-- Add buttons should be named "submit_[field]_add" so that we can ignore errors from required fields when simply adding new values-->
<input type="submit" value="Add" name="{concat('submit_',@n,'_add')}" class="ds-button-field ds-add-button"> <input type="submit" value="Add" name="{concat('submit_',@n,'_add')}" class="ds-button-field ds-add-button">
<!-- Make invisible if we have choice-lookup operation that provides its own Add. --> <!-- Make invisible if we have choice-lookup operation that provides its own Add. -->
<xsl:if test="dri:params/@choicesPresentation = 'lookup'"> <!--<xsl:if test="dri:params/@choicesPresentation = 'lookup'">
<xsl:attribute name="style"> <xsl:attribute name="style">
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>-->
</input> </input>
</xsl:if> </xsl:if>
<!-- insert choice mechansim and/or Add button here --> <!-- insert choice mechansim and/or Add button here -->
@@ -152,6 +158,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'true'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
<!-- place to store authority value --> <!-- place to store authority value -->
<xsl:if test="dri:params/@authorityControlled"> <xsl:if test="dri:params/@authorityControlled">
@@ -207,6 +219,11 @@
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>
<xsl:if test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:attribute name="style">
<xsl:text>display:none;</xsl:text>
</xsl:attribute>
</xsl:if>
</input> </input>
</xsl:if> </xsl:if>
<xsl:if test="$test"> <xsl:if test="$test">
@@ -281,7 +298,12 @@
<xsl:attribute name="style"> <xsl:attribute name="style">
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>
<xsl:if test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:attribute name="style">
<xsl:text>display:none;</xsl:text>
</xsl:attribute>
</xsl:if>
</input> </input>
</xsl:if> </xsl:if>
<br/> <br/>

View File

@@ -300,6 +300,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'true'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
<xsl:if test="dri:params/@authorityControlled"> <xsl:if test="dri:params/@authorityControlled">
<xsl:variable name="confValue" select="dri:field/dri:value[@type='authority'][1]/@confidence"/> <xsl:variable name="confValue" select="dri:field/dri:value[@type='authority'][1]/@confidence"/>
@@ -334,6 +340,11 @@
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>
<xsl:if test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:attribute name="style">
<xsl:text>display:none;</xsl:text>
</xsl:attribute>
</xsl:if>
</input> </input>
</xsl:if> </xsl:if>
<!-- insert choice mechansim and/or Add button here --> <!-- insert choice mechansim and/or Add button here -->
@@ -350,6 +361,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'true'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
<!-- place to store authority value --> <!-- place to store authority value -->
<xsl:if test="dri:params/@authorityControlled"> <xsl:if test="dri:params/@authorityControlled">
@@ -413,6 +430,11 @@
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>
<xsl:if test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:attribute name="style">
<xsl:text>display:none;</xsl:text>
</xsl:attribute>
</xsl:if>
</input> </input>
</xsl:if> </xsl:if>
<br/> <br/>
@@ -527,6 +549,11 @@
<xsl:text>display:none;</xsl:text> <xsl:text>display:none;</xsl:text>
</xsl:attribute> </xsl:attribute>
</xsl:if> </xsl:if>
<xsl:if test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:attribute name="style">
<xsl:text>display:none;</xsl:text>
</xsl:attribute>
</xsl:if>
</input> </input>
</xsl:if> </xsl:if>
@@ -560,6 +587,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'true'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
<br/> <br/>
<xsl:if test="dri:instance or dri:field/dri:instance"> <xsl:if test="dri:instance or dri:field/dri:instance">
@@ -907,6 +940,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'false'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
</xsl:when> </xsl:when>
@@ -1007,6 +1046,12 @@
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/> <xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template> </xsl:call-template>
</xsl:when> </xsl:when>
<xsl:when test="dri:params/@choicesPresentation = 'authorLookup'">
<xsl:call-template name="addLookupButtonAuthor">
<xsl:with-param name="isName" select="'false'"/>
<xsl:with-param name="confIndicator" select="$confidenceIndicatorID"/>
</xsl:call-template>
</xsl:when>
</xsl:choose> </xsl:choose>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>

View File

@@ -754,6 +754,10 @@ event.consumer.rdf.filters = All+All
event.consumer.versioning.class = org.dspace.versioning.VersioningConsumer event.consumer.versioning.class = org.dspace.versioning.VersioningConsumer
event.consumer.versioning.filters = Item+Install event.consumer.versioning.filters = Item+Install
# authority consumer
event.consumer.authority.class = org.dspace.authority.indexer.AuthorityConsumer
event.consumer.authority.filters = Item+Modify|Modify_Metadata
# ...set to true to enable testConsumer messages to standard output # ...set to true to enable testConsumer messages to standard output
#testConsumer.verbose = true #testConsumer.verbose = true
@@ -1526,12 +1530,16 @@ sherpa.romeo.url = http://www.sherpa.ac.uk/romeo/api29.php
# sherpa.romeo.apikey = YOUR-API-KEY # sherpa.romeo.apikey = YOUR-API-KEY
##### Authority Control Settings ##### ##### Authority Control Settings #####
#plugin.named.org.dspace.content.authority.ChoiceAuthority = \ #plugin.named.org.dspace.content.authority.ChoiceAuthority = \
# org.dspace.content.authority.SampleAuthority = Sample, \ # org.dspace.content.authority.SampleAuthority = Sample, \
# org.dspace.content.authority.LCNameAuthority = LCNameAuthority, \ # org.dspace.content.authority.LCNameAuthority = LCNameAuthority, \
# org.dspace.content.authority.SHERPARoMEOPublisher = SRPublisher, \ # org.dspace.content.authority.SHERPARoMEOPublisher = SRPublisher, \
# org.dspace.content.authority.SHERPARoMEOJournalTitle = SRJournalTitle # org.dspace.content.authority.SHERPARoMEOJournalTitle = SRJournalTitle, \
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
#Uncomment to enable ORCID authority control
#plugin.named.org.dspace.content.authority.ChoiceAuthority = \
# org.dspace.content.authority.SolrAuthority = SolrAuthorAuthority
## The DCInputAuthority plugin is automatically configured with every ## The DCInputAuthority plugin is automatically configured with every
## value-pairs element in input-forms.xml, namely: ## value-pairs element in input-forms.xml, namely:
@@ -1570,6 +1578,14 @@ sherpa.romeo.url = http://www.sherpa.ac.uk/romeo/api29.php
## See manual or org.dspace.content.authority.Choices source for descriptions. ## See manual or org.dspace.content.authority.Choices source for descriptions.
authority.minconfidence = ambiguous authority.minconfidence = ambiguous
# Configuration settings for ORCID based authority control, uncomment the lines below to enable configuration
#solr.authority.server=${solr.server}/authority
#choices.plugin.dc.contributor.author = SolrAuthorAuthority
#choices.presentation.dc.contributor.author = authorLookup
#authority.controlled.dc.contributor.author = true
#
#authority.author.indexer.field.1=dc.contributor.author
## demo: use LC plugin for author ## demo: use LC plugin for author
#choices.plugin.dc.contributor.author = LCNameAuthority #choices.plugin.dc.contributor.author = LCNameAuthority
#choices.presentation.dc.contributor.author = lookup #choices.presentation.dc.contributor.author = lookup

View File

@@ -427,4 +427,12 @@
</step> </step>
</command> </command>
<command>
<name>index-authority</name>
<description>Indexes all metadata fields that use solr authority</description>
<step>
<class>org.dspace.authority.indexer.AuthorityIndexClient</class>
</step>
</command>
</commands> </commands>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2002-2010, DuraSpace. All rights reserved
Licensed under the DuraSpace License.
A copy of the DuraSpace License has been included in this
distribution and is available at: http://www.dspace.org/license
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<context:annotation-config /> <!-- allows us to use spring annotations in beans -->
<!-- Authority control -->
<bean class="org.dspace.authority.AuthoritySolrServiceImpl" id="org.dspace.authority.AuthoritySearchService"/>
<alias name="org.dspace.authority.AuthoritySearchService" alias="org.dspace.authority.indexer.AuthorityIndexingService"/>
<bean id="dspace.DSpaceAuthorityIndexer" class="org.dspace.authority.indexer.DSpaceAuthorityIndexer"/>
<bean name="AuthorityTypes" class="org.dspace.authority.AuthorityTypes">
<property name="types">
<list>
<bean class="org.dspace.authority.orcid.OrcidAuthorityValue"/>
<bean class="org.dspace.authority.PersonAuthorityValue"/>
</list>
</property>
<property name="fieldDefaults">
<map>
<entry key="dc_contributor_author">
<bean class="org.dspace.authority.PersonAuthorityValue"/>
</entry>
</map>
</property>
</bean>
<alias name="OrcidSource" alias="AuthoritySource"/>
<bean name="OrcidSource" class="org.dspace.authority.orcid.Orcid">
<constructor-arg value="http://pub.orcid.org"/>
</bean>
</beans>

View File

@@ -0,0 +1,31 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The content of this page will be statically included into the top
of the admin page. Uncomment this as an example to see there the content
will show up.
<hr>
<i>This line will appear before the first table</i>
<tr>
<td colspan="2">
This row will be appended to the end of the first table
</td>
</tr>
<hr>
-->

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- If this file is found in the config directory, it will only be
loaded once at startup. If it is found in Solr's data
directory, it will be re-loaded every commit.
-->
<elevate>
<query text="foo bar">
<doc id="1" />
<doc id="2" />
<doc id="3" />
</query>
<query text="ipod">
<doc id="MA147LL/A" /> <!-- put the actual ipod at the top -->
<doc id="IW-02" exclude="true" /> <!-- exclude this cable -->
</query>
</elevate>

View File

@@ -0,0 +1,21 @@
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-----------------------------------------------------------------------
# Use a protected word file to protect against the stemmer reducing two
# unrelated words to the same base word.
# Some non-words that normally won't be encountered,
# just to test that they won't be stemmed.
dontstems
zwhacky

View File

@@ -0,0 +1,330 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
This is the Solr schema file. This file should be named "schema.xml" and
should be in the conf directory under the solr home
(i.e. ./solr/conf/schema.xml by default)
or located where the classloader for the Solr webapp can find it.
This example schema is the recommended starting point for users.
It should be kept correct and concise, usable out-of-the-box.
For more information, on how to customize this file, please see
http://wiki.apache.org/solr/SchemaXml
-->
<schema name="example" version="1.1">
<!-- attribute "name" is the name of this schema and is only used for display purposes.
Applications should change this to reflect the nature of the search collection.
version="1.1" is Solr's version number for the schema syntax and semantics. It should
not normally be changed by applications.
1.0: multiValued attribute did not exist, all fields are multiValued by nature
1.1: multiValued attribute introduced, false by default -->
<types>
<!-- field type definitions. The "name" attribute is
just a label to be used by field definitions. The "class"
attribute and any other attributes determine the real
behavior of the fieldType.
Class names starting with "solr" refer to java classes in the
org.apache.solr.analysis package.
-->
<!-- The StrField type is not analyzed, but indexed/stored verbatim.
- StrField and TextField support an optional compressThreshold which
limits compression (if enabled in the derived fields) to values which
exceed a certain size (in characters).
-->
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
<!-- boolean type: "true" or "false" -->
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
<!-- The optional sortMissingLast and sortMissingFirst attributes are
currently supported on types that are sorted internally as strings.
- If sortMissingLast="true", then a sort on this field will cause documents
without the field to come after documents with the field,
regardless of the requested sort order (asc or desc).
- If sortMissingFirst="true", then a sort on this field will cause documents
without the field to come before documents with the field,
regardless of the requested sort order.
- If sortMissingLast="false" and sortMissingFirst="false" (the default),
then default lucene sorting will be used which places docs without the
field first in an ascending sort and last in a descending sort.
-->
<!-- numeric field types that store and index the text
value verbatim (and hence don't support range queries, since the
lexicographic ordering isn't equal to the numeric ordering) -->
<fieldType name="integer" class="solr.IntField" omitNorms="true"/>
<fieldType name="long" class="solr.LongField" omitNorms="true"/>
<fieldType name="float" class="solr.FloatField" omitNorms="true"/>
<fieldType name="double" class="solr.DoubleField" omitNorms="true"/>
<!-- Numeric field types that manipulate the value into
a string value that isn't human-readable in its internal form,
but with a lexicographic ordering the same as the numeric ordering,
so that range queries work correctly. -->
<fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
<!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
is a more restricted form of the canonical representation of dateTime
http://www.w3.org/TR/xmlschema-2/#dateTime
The trailing "Z" designates UTC time and is mandatory.
Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
All other components are mandatory.
Expressions can also be used to denote calculations that should be
performed relative to "NOW" to determine the value, ie...
NOW/HOUR
... Round to the start of the current hour
NOW-1DAY
... Exactly 1 day prior to now
NOW/DAY+6MONTHS+3DAYS
... 6 months and 3 days in the future from the start of
the current day
Consult the DateField javadocs for more information.
-->
<fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
<!-- The "RandomSortField" is not used to store or search any
data. You can declare fields of this type it in your schema
to generate psuedo-random orderings of your docs for sorting
purposes. The ordering is generated based on the field name
and the version of the index, As long as the index version
remains unchanged, and the same field name is reused,
the ordering of the docs will be consistent.
If you want differend psuedo-random orderings of documents,
for the same version of the index, use a dynamicField and
change the name
-->
<fieldType name="random" class="solr.RandomSortField" indexed="true" />
<!-- solr.TextField allows the specification of custom text analyzers
specified as a tokenizer and a list of token filters. Different
analyzers may be specified for indexing and querying.
The optional positionIncrementGap puts space between multiple fields of
this type on the same document, with the purpose of preventing false phrase
matching across fields.
For more info on customizing your analyzer chain, please see
http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
-->
<!-- One can also specify an existing Analyzer class that has a
default constructor via the class attribute on the analyzer element
<fieldType name="text_greek" class="solr.TextField">
<analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
</fieldType>
-->
<!-- A text field that only splits on whitespace for exact matching of words -->
<fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
</analyzer>
</fieldType>
<!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
Synonyms and stopwords are customized by external files, and stemming is enabled.
Duplicate tokens at the same position (which may result from Stemmed Synonyms or
WordDelim parts) are removed.
-->
<fieldType name="text" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
-->
<!-- Case insensitive stop word removal.
enablePositionIncrements=true ensures that a 'gap' is left to
allow for accurate phrase queries.
-->
<filter class="solr.StopFilterFactory"
ignoreCase="true"
words="stopwords.txt"
enablePositionIncrements="true"
/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/>
<filter class="solr.LowerCaseFilterFactory"/>
<!--<filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>-->
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
<filter class="solr.ReversedWildcardFilterFactory" />
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
<filter class="solr.LowerCaseFilterFactory"/>
<!--<filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>-->
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
</analyzer>
</fieldType>
<!-- Less flexible matching, but less false matches. Probably not ideal for product names,
but may be good for SKUs. Can insert dashes in the wrong place and still match. -->
<fieldType name="textTight" class="solr.TextField" positionIncrementGap="100" >
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="false"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="0" generateNumberParts="0" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
<filter class="solr.LowerCaseFilterFactory"/>
<!--<filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt"/>-->
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
</analyzer>
</fieldType>
<!--
Setup simple analysis for spell checking
-->
<fieldType name="textSpell" class="solr.TextField" positionIncrementGap="100" >
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
</analyzer>
</fieldType>
<!-- This is an example of using the KeywordTokenizer along
With various TokenFilterFactories to produce a sortable field
that does not include some properties of the source text
-->
<fieldType name="alphaOnlySort" class="solr.TextField" sortMissingLast="true" omitNorms="true">
<analyzer>
<!-- KeywordTokenizer does no actual tokenizing, so the entire
input string is preserved as a single token
-->
<tokenizer class="solr.KeywordTokenizerFactory"/>
<!-- The LowerCase TokenFilter does what you expect, which can be
when you want your sorting to be case insensitive
-->
<filter class="solr.LowerCaseFilterFactory" />
<!-- The TrimFilter removes any leading or trailing whitespace -->
<filter class="solr.TrimFilterFactory" />
<!-- The PatternReplaceFilter gives you the flexibility to use
Java Regular expression to replace any sequence of characters
matching a pattern with an arbitrary replacement string,
which may include back refrences to portions of the orriginal
string matched by the pattern.
See the Java Regular Expression documentation for more
infomation on pattern and replacement string syntax.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
-->
<filter class="solr.PatternReplaceFilterFactory"
pattern="([^a-z])" replacement="" replace="all"
/>
</analyzer>
</fieldType>
<!-- since fields of this type are by default not stored or indexed, any data added to
them will be ignored outright
-->
<fieldtype name="ignored" stored="false" indexed="false" class="solr.StrField" />
<fieldType name="valueField" class="solr.TextField">
<analyzer>
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<fieldType name="tokenizedValueField" class="solr.TextField">
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
</types>
<fields>
<!-- Valid attributes for fields:
name: mandatory - the name for the field
type: mandatory - the name of a previously defined type from the
<types> section
indexed: true if this field should be indexed (searchable or sortable)
stored: true if this field should be retrievable
compressed: [false] if this field should be stored using gzip compression
(this will only apply if the field type is compressable; among
the standard field types, only TextField and StrField are)
multiValued: true if this field may contain multiple values per document
omitNorms: (expert) set to true to omit the norms associated with
this field (this disables length normalization and index-time
boosting for the field, and saves some memory). Only full-text
fields or fields that need an index-time boost need norms.
termVectors: [false] set to true to store the term vector for a given field.
When using MoreLikeThis, fields used for similarity should be stored for
best performance.
-->
<!-- catchall field, containing all other searchable text fields (implemented
via copyField further on in this schema -->
<field name="id" type="string" multiValued="false" indexed="true" stored="true" required="true"/>
<field name="field" type="string" multiValued="false" indexed="true" stored="true" required="true" />
<field name="email" type="string" multiValued="true" indexed="true" stored="true" required="false"/>
<field name="institution" type="string" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="value" type="text" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="orcid_id" type="string" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="first_name" type="text" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="last_name" type="text" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="name_variant" type="text" multiValued="true" indexed="true" stored="true" required="false"/>
<dynamicField name="label_*" type="text" multiValued="true" indexed="true" stored="true" required="false"/>
<field name="all_labels" type="text" multiValued="true" indexed="true" stored="true" required="false"/>
<copyField source="label_*" dest="all_labels"/>
<field name="deleted" type="boolean" multiValued="false" indexed="true" stored="true" required="false"/>
<field name="last_modified_date" type="date" indexed="true" stored="true" required="true" />
<field name="creation_date" type="date" indexed="true" stored="true" required="true" />
<field name="authority_type" type="string" multiValued="false" indexed="true" stored="true" required="false"/>
</fields>
<!-- Field to use to determine and enforce document uniqueness.
Unless this field is marked with required="false", it will be a required field
-->
<uniqueKey>id</uniqueKey>
<!-- field for the QueryParser to use when an explicit fieldname is absent -->
<defaultSearchField>value</defaultSearchField>
<!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
<solrQueryParser defaultOperator="OR"/>
</schema>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
pizza
history

View File

@@ -0,0 +1,57 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-----------------------------------------------------------------------
# a couple of test stopwords to test that the words are really being
# configured from this file:
stopworda
stopwordb
#Standard english stop words taken from Lucene's StopAnalyzer
an
and
are
as
at
be
but
by
for
if
in
into
is
it
no
not
of
on
or
s
such
t
that
the
their
then
there
these
they
this
to
was
will
with

View File

@@ -0,0 +1,31 @@
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-----------------------------------------------------------------------
#some test synonym mappings unlikely to appear in real input text
aaa => aaaa
bbb => bbbb1 bbbb2
ccc => cccc1,cccc2
a\=>a => b\=>b
a\,a => b\,b
fooaaa,baraaa,bazaaa
# Some synonym groups specific to this example
GB,gib,gigabyte,gigabytes
MB,mib,megabyte,megabytes
Television, Televisions, TV, TVs
#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming
#after us won't split it into two words.
# Synonym mappings can be used for spelling correction too
pixima => pixma

View File

@@ -0,0 +1,132 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<!--
Simple transform of Solr query results to HTML
-->
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
>
<xsl:output media-type="text/html; charset=UTF-8" encoding="UTF-8"/>
<xsl:variable name="title" select="concat('Solr search results (',response/result/@numFound,' documents)')"/>
<xsl:template match='/'>
<html>
<head>
<title><xsl:value-of select="$title"/></title>
<xsl:call-template name="css"/>
</head>
<body>
<h1><xsl:value-of select="$title"/></h1>
<div class="note">
This has been formatted by the sample "example.xsl" transform -
use your own XSLT to get a nicer page
</div>
<xsl:apply-templates select="response/result/doc"/>
</body>
</html>
</xsl:template>
<xsl:template match="doc">
<xsl:variable name="pos" select="position()"/>
<div class="doc">
<table width="100%">
<xsl:apply-templates>
<xsl:with-param name="pos"><xsl:value-of select="$pos"/></xsl:with-param>
</xsl:apply-templates>
</table>
</div>
</xsl:template>
<xsl:template match="doc/*[@name='score']" priority="100">
<xsl:param name="pos"></xsl:param>
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<xsl:value-of select="."/>
<xsl:if test="boolean(//lst[@name='explain'])">
<xsl:element name="a">
<!-- can't allow whitespace here -->
<xsl:attribute name="href">javascript:toggle("<xsl:value-of select="concat('exp-',$pos)" />");</xsl:attribute>?</xsl:element>
<br/>
<xsl:element name="div">
<xsl:attribute name="class">exp</xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="concat('exp-',$pos)" />
</xsl:attribute>
<xsl:value-of select="//lst[@name='explain']/str[position()=$pos]"/>
</xsl:element>
</xsl:if>
</td>
</tr>
</xsl:template>
<xsl:template match="doc/arr" priority="100">
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<ul>
<xsl:for-each select="*">
<li><xsl:value-of select="."/></li>
</xsl:for-each>
</ul>
</td>
</tr>
</xsl:template>
<xsl:template match="doc/*">
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
<xsl:template match="*"/>
<xsl:template name="css">
<script>
function toggle(id) {
var obj = document.getElementById(id);
obj.style.display = (obj.style.display != 'block') ? 'block' : 'none';
}
</script>
<style type="text/css">
body { font-family: "Lucida Grande", sans-serif }
td.name { font-style: italic; font-size:80%; }
td { vertical-align: top; }
ul { margin: 0px; margin-left: 1em; padding: 0px; }
.note { font-size:80%; }
.doc { margin-top: 1em; border-top: solid grey 1px; }
.exp { display: none; font-family: monospace; white-space: pre; }
</style>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,67 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<!--
Simple transform of Solr query results to Atom
-->
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output
method="xml"
encoding="utf-8"
media-type="text/xml; charset=UTF-8"
/>
<xsl:template match='/'>
<xsl:variable name="query" select="response/lst[@name='responseHeader']/lst[@name='params']/str[@name='q']"/>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Solr Atom 1.0 Feed</title>
<subtitle>
This has been formatted by the sample "example_atom.xsl" transform -
use your own XSLT to get a nicer Atom feed.
</subtitle>
<author>
<name>Apache Solr</name>
<email>solr-user@lucene.apache.org</email>
</author>
<link rel="self" type="application/atom+xml"
href="http://localhost:8983/solr/q={$query}&amp;wt=xslt&amp;tr=atom.xsl"/>
<updated>
<xsl:value-of select="response/result/doc[position()=1]/date[@name='timestamp']"/>
</updated>
<id>tag:localhost,2007:example</id>
<xsl:apply-templates select="response/result/doc"/>
</feed>
</xsl:template>
<!-- search results xslt -->
<xsl:template match="doc">
<xsl:variable name="id" select="str[@name='id']"/>
<entry>
<title><xsl:value-of select="str[@name='name']"/></title>
<link href="http://localhost:8983/solr/select?q={$id}"/>
<id>tag:localhost,2007:<xsl:value-of select="$id"/></id>
<summary><xsl:value-of select="arr[@name='features']"/></summary>
<updated><xsl:value-of select="date[@name='timestamp']"/></updated>
</entry>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,66 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<!--
Simple transform of Solr query results to RSS
-->
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output
method="xml"
encoding="utf-8"
media-type="text/xml; charset=UTF-8"
/>
<xsl:template match='/'>
<rss version="2.0">
<channel>
<title>Example Solr RSS 2.0 Feed</title>
<link>http://localhost:8983/solr</link>
<description>
This has been formatted by the sample "example_rss.xsl" transform -
use your own XSLT to get a nicer RSS feed.
</description>
<language>en-us</language>
<docs>http://localhost:8983/solr</docs>
<xsl:apply-templates select="response/result/doc"/>
</channel>
</rss>
</xsl:template>
<!-- search results xslt -->
<xsl:template match="doc">
<xsl:variable name="id" select="str[@name='id']"/>
<xsl:variable name="timestamp" select="date[@name='timestamp']"/>
<item>
<title><xsl:value-of select="str[@name='name']"/></title>
<link>
http://localhost:8983/solr/select?q=id:<xsl:value-of select="$id"/>
</link>
<description>
<xsl:value-of select="arr[@name='features']"/>
</description>
<pubDate><xsl:value-of select="$timestamp"/></pubDate>
<guid>
http://localhost:8983/solr/select?q=id:<xsl:value-of select="$id"/>
</guid>
</item>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,337 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Display the luke request handler with graphs
-->
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml"
version="1.0"
>
<xsl:output
method="html"
encoding="UTF-8"
media-type="text/html; charset=UTF-8"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
/>
<xsl:variable name="title">Solr Luke Request Handler Response</xsl:variable>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="solr-admin.css"/>
<link rel="icon" href="favicon.ico" type="image/ico"/>
<link rel="shortcut icon" href="favicon.ico" type="image/ico"/>
<title>
<xsl:value-of select="$title"/>
</title>
<xsl:call-template name="css"/>
</head>
<body>
<h1>
<xsl:value-of select="$title"/>
</h1>
<div class="doc">
<ul>
<xsl:if test="response/lst[@name='index']">
<li>
<a href="#index">Index Statistics</a>
</li>
</xsl:if>
<xsl:if test="response/lst[@name='fields']">
<li>
<a href="#fields">Field Statistics</a>
<ul>
<xsl:for-each select="response/lst[@name='fields']/lst">
<li>
<a href="#{@name}">
<xsl:value-of select="@name"/>
</a>
</li>
</xsl:for-each>
</ul>
</li>
</xsl:if>
<xsl:if test="response/lst[@name='doc']">
<li>
<a href="#doc">Document statistics</a>
</li>
</xsl:if>
</ul>
</div>
<xsl:if test="response/lst[@name='index']">
<h2><a name="index"/>Index Statistics</h2>
<xsl:apply-templates select="response/lst[@name='index']"/>
</xsl:if>
<xsl:if test="response/lst[@name='fields']">
<h2><a name="fields"/>Field Statistics</h2>
<xsl:apply-templates select="response/lst[@name='fields']"/>
</xsl:if>
<xsl:if test="response/lst[@name='doc']">
<h2><a name="doc"/>Document statistics</h2>
<xsl:apply-templates select="response/lst[@name='doc']"/>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template match="lst">
<xsl:if test="parent::lst">
<tr>
<td colspan="2">
<div class="doc">
<xsl:call-template name="list"/>
</div>
</td>
</tr>
</xsl:if>
<xsl:if test="not(parent::lst)">
<div class="doc">
<xsl:call-template name="list"/>
</div>
</xsl:if>
</xsl:template>
<xsl:template name="list">
<xsl:if test="count(child::*)>0">
<table>
<thead>
<tr>
<th colspan="2">
<p>
<a name="{@name}"/>
</p>
<xsl:value-of select="@name"/>
</th>
</tr>
</thead>
<tbody>
<xsl:choose>
<xsl:when
test="@name='histogram'">
<tr>
<td colspan="2">
<xsl:call-template name="histogram"/>
</td>
</tr>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</tbody>
</table>
</xsl:if>
</xsl:template>
<xsl:template name="histogram">
<div class="doc">
<xsl:call-template name="barchart">
<xsl:with-param name="max_bar_width">50</xsl:with-param>
<xsl:with-param name="iwidth">800</xsl:with-param>
<xsl:with-param name="iheight">160</xsl:with-param>
<xsl:with-param name="fill">blue</xsl:with-param>
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="barchart">
<xsl:param name="max_bar_width"/>
<xsl:param name="iwidth"/>
<xsl:param name="iheight"/>
<xsl:param name="fill"/>
<xsl:variable name="max">
<xsl:for-each select="int">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="bars">
<xsl:value-of select="count(int)"/>
</xsl:variable>
<xsl:variable name="bar_width">
<xsl:choose>
<xsl:when test="$max_bar_width &lt; ($iwidth div $bars)">
<xsl:value-of select="$max_bar_width"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$iwidth div $bars"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<table class="histogram">
<tbody>
<tr>
<xsl:for-each select="int">
<td>
<xsl:value-of select="."/>
<div class="histogram">
<xsl:attribute name="style">background-color: <xsl:value-of select="$fill"/>; width: <xsl:value-of select="$bar_width"/>px; height: <xsl:value-of select="($iheight*number(.)) div $max"/>px;</xsl:attribute>
</div>
</td>
</xsl:for-each>
</tr>
<tr>
<xsl:for-each select="int">
<td>
<xsl:value-of select="@name"/>
</td>
</xsl:for-each>
</tr>
</tbody>
</table>
</xsl:template>
<xsl:template name="keyvalue">
<xsl:choose>
<xsl:when test="@name">
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<xsl:value-of select="."/>
</td>
</tr>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="int|bool|long|float|double|uuid|date">
<xsl:call-template name="keyvalue"/>
</xsl:template>
<xsl:template match="arr">
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<ul>
<xsl:for-each select="child::*">
<li>
<xsl:apply-templates/>
</li>
</xsl:for-each>
</ul>
</td>
</tr>
</xsl:template>
<xsl:template match="str">
<xsl:choose>
<xsl:when test="@name='schema' or @name='index' or @name='flags'">
<xsl:call-template name="schema"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="keyvalue"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="schema">
<tr>
<td class="name">
<xsl:value-of select="@name"/>
</td>
<td class="value">
<xsl:if test="contains(.,'unstored')">
<xsl:value-of select="."/>
</xsl:if>
<xsl:if test="not(contains(.,'unstored'))">
<xsl:call-template name="infochar2string">
<xsl:with-param name="charList">
<xsl:value-of select="."/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</td>
</tr>
</xsl:template>
<xsl:template name="infochar2string">
<xsl:param name="i">1</xsl:param>
<xsl:param name="charList"/>
<xsl:variable name="char">
<xsl:value-of select="substring($charList,$i,1)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$char='I'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='I']"/> - </xsl:when>
<xsl:when test="$char='T'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='T']"/> - </xsl:when>
<xsl:when test="$char='S'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='S']"/> - </xsl:when>
<xsl:when test="$char='M'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='M']"/> - </xsl:when>
<xsl:when test="$char='V'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='V']"/> - </xsl:when>
<xsl:when test="$char='o'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='o']"/> - </xsl:when>
<xsl:when test="$char='p'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='p']"/> - </xsl:when>
<xsl:when test="$char='O'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='O']"/> - </xsl:when>
<xsl:when test="$char='L'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='L']"/> - </xsl:when>
<xsl:when test="$char='B'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='B']"/> - </xsl:when>
<xsl:when test="$char='C'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='C']"/> - </xsl:when>
<xsl:when test="$char='f'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='f']"/> - </xsl:when>
<xsl:when test="$char='l'">
<xsl:value-of select="/response/lst[@name='info']/lst/str[@name='l']"/> -
</xsl:when>
</xsl:choose>
<xsl:if test="not($i>=string-length($charList))">
<xsl:call-template name="infochar2string">
<xsl:with-param name="i">
<xsl:value-of select="$i+1"/>
</xsl:with-param>
<xsl:with-param name="charList">
<xsl:value-of select="$charList"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="css">
<style type="text/css">
<![CDATA[
td.name {font-style: italic; font-size:80%; }
.doc { margin: 0.5em; border: solid grey 1px; }
.exp { display: none; font-family: monospace; white-space: pre; }
div.histogram { background: none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;}
table.histogram { width: auto; vertical-align: bottom; }
table.histogram td, table.histogram th { text-align: center; vertical-align: bottom; border-bottom: 1px solid #ff9933; width: auto; }
]]>
</style>
</xsl:template>
</xsl:stylesheet>

View File

@@ -32,6 +32,7 @@
<core name="search" instanceDir="search" /> <core name="search" instanceDir="search" />
<core name="statistics" instanceDir="statistics" /> <core name="statistics" instanceDir="statistics" />
<core name="oai" instanceDir="oai" /> <core name="oai" instanceDir="oai" />
<core name="authority" instanceDir="authority" />
</cores> </cores>
</solr> </solr>