mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
[CST-5269] configured sherpa service in submission
This commit is contained in:
@@ -31,6 +31,7 @@ import org.dspace.app.sherpa.v2.SHERPAResponse;
|
||||
import org.dspace.app.sherpa.v2.SHERPAUtils;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
/**
|
||||
* SHERPAService is responsible for making the HTTP call to the SHERPA v2 API
|
||||
@@ -91,6 +92,7 @@ public class SHERPAService {
|
||||
* @param query ISSN string to pass in an "issn equals" API query
|
||||
* @return SHERPAResponse containing an error or journal policies
|
||||
*/
|
||||
@Cacheable(key = "#query", cacheNames = "sherpa.searchByJournalISSN")
|
||||
public SHERPAResponse searchByJournalISSN(String query) {
|
||||
return performRequest("publication", "issn", "equals", query, 0, 1);
|
||||
}
|
||||
|
38
dspace-api/src/main/java/org/dspace/app/sherpa/cache/SherpaCacheEvictBeanLocator.java
vendored
Normal file
38
dspace-api/src/main/java/org/dspace/app/sherpa/cache/SherpaCacheEvictBeanLocator.java
vendored
Normal 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.app.sherpa.cache;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Exposes the Spring application's Sherpa cache evict service to the submission sherpaPolicyStep.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
@Component
|
||||
public class SherpaCacheEvictBeanLocator implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext context;
|
||||
|
||||
private static final String SHERPA_CACHE_EVICT_SERVICE = "sherpaCacheEvictService";
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
context = applicationContext;
|
||||
}
|
||||
|
||||
public static SherpaCacheEvictService getSherpaCacheEvictService() {
|
||||
return Objects.nonNull(context) ? (SherpaCacheEvictService) context.getBean(SHERPA_CACHE_EVICT_SERVICE) : null;
|
||||
}
|
||||
|
||||
}
|
38
dspace-api/src/main/java/org/dspace/app/sherpa/cache/SherpaCacheEvictService.java
vendored
Normal file
38
dspace-api/src/main/java/org/dspace/app/sherpa/cache/SherpaCacheEvictService.java
vendored
Normal 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.app.sherpa.cache;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Removes items from the sherpaSearchByJournalISSN cache.
|
||||
*
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
@Component
|
||||
public class SherpaCacheEvictService {
|
||||
|
||||
// The cache that is managed by this service.
|
||||
static final String CACHE_NAME = "sherpa.searchByJournalISSN";
|
||||
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
public void evictSingleCacheValue(String cacheKey) {
|
||||
Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).evict(cacheKey);
|
||||
}
|
||||
|
||||
public void evictAllCacheValues() {
|
||||
Objects.requireNonNull(cacheManager.getCache(CACHE_NAME)).clear();
|
||||
}
|
||||
|
||||
}
|
@@ -9,7 +9,6 @@ package org.dspace.app.sherpa.submit;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -63,19 +62,19 @@ public class SHERPASubmitService {
|
||||
* issnItemExtractor(s) in the SHERPA spring configuration.
|
||||
* The ISSNs are not validated with a regular expression or other rules - any values
|
||||
* extracted will be included in API queries.
|
||||
* Return the first not empty response from Sherpa
|
||||
* @see "dspace-dspace-addon-sherpa-configuration-services.xml"
|
||||
* @param context DSpace context
|
||||
* @param item DSpace item containing ISSNs to be checked
|
||||
* @return SHERPA v2 API response (policy data)
|
||||
*/
|
||||
public List<SHERPAResponse> searchRelatedJournals(Context context, Item item) {
|
||||
public SHERPAResponse searchRelatedJournals(Context context, Item item) {
|
||||
Set<String> issns = getISSNs(context, item);
|
||||
if (issns == null || issns.size() == 0) {
|
||||
return null;
|
||||
} else {
|
||||
// SHERPA v2 API no longer supports "OR'd" ISSN search, perform individual searches instead
|
||||
Iterator<String> issnIterator = issns.iterator();
|
||||
List<SHERPAResponse> responses = new LinkedList<>();
|
||||
while (issnIterator.hasNext()) {
|
||||
String issn = issnIterator.next();
|
||||
SHERPAResponse response = sherpaService.searchByJournalISSN(issn);
|
||||
@@ -83,14 +82,12 @@ public class SHERPASubmitService {
|
||||
// Continue with loop
|
||||
log.warn("Failed to look up SHERPA ROMeO result for ISSN: " + issn
|
||||
+ ": " + response.getMessage());
|
||||
} else if (!response.getJournals().isEmpty()) {
|
||||
// return this response, if it is not empty
|
||||
return response;
|
||||
}
|
||||
// Store this response, even if it has an error (useful for UI reporting)
|
||||
responses.add(response);
|
||||
}
|
||||
if (responses.isEmpty()) {
|
||||
responses.add(new SHERPAResponse("SHERPA ROMeO lookup failed"));
|
||||
}
|
||||
return responses;
|
||||
return new SHERPAResponse("SHERPA ROMeO lookup failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,10 +12,12 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.json.JSONArray;
|
||||
@@ -52,6 +54,9 @@ public class SHERPAResponse {
|
||||
// SHERPA URI (the human page version of this API response)
|
||||
private String uri;
|
||||
|
||||
@JsonIgnore
|
||||
private Date retrievalTime = new Date();
|
||||
|
||||
// Format enum - currently only JSON is supported
|
||||
public enum SHERPAFormat {
|
||||
JSON, XML
|
||||
@@ -542,4 +547,8 @@ public class SHERPAResponse {
|
||||
public SHERPASystemMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public Date getRetrievalTime() {
|
||||
return retrievalTime;
|
||||
}
|
||||
}
|
||||
|
@@ -131,6 +131,12 @@
|
||||
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
|
||||
<type>submission-form</type>
|
||||
</step-definition>
|
||||
|
||||
<step-definition id="sherpaPolicies" mandatory="true">
|
||||
<heading>submit.progressbar.sherpapolicy</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.SherpaPolicyStep</processing-class>
|
||||
<type>sherpaPolicy</type>
|
||||
</step-definition>
|
||||
</step-definitions>
|
||||
|
||||
<!-- The submission-definitions map lays out the detailed definition of -->
|
||||
@@ -166,6 +172,7 @@
|
||||
<!-- <step id="upload-with-embargo"/> -->
|
||||
<!-- <step id="extractionstep"/> -->
|
||||
<step id="defaultAC"/>
|
||||
<step id="sherpaPolicies"/>
|
||||
|
||||
<!--Step will be to Sign off on the License -->
|
||||
<step id="license"/>
|
||||
|
@@ -11,7 +11,6 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.dspace.AbstractUnitTest;
|
||||
import org.dspace.app.sherpa.v2.SHERPAResponse;
|
||||
@@ -109,20 +108,18 @@ public class SHERPASubmitServiceTest extends AbstractUnitTest {
|
||||
|
||||
// Get responses from SHERPA submit service, which should inspect item ISSNs and perform search
|
||||
// on the mock SHERPA service
|
||||
List<SHERPAResponse> responses = sherpaSubmitService.searchRelatedJournals(context, testItem);
|
||||
SHERPAResponse response = sherpaSubmitService.searchRelatedJournals(context, testItem);
|
||||
|
||||
// Make sure response is not null or empty
|
||||
assertTrue("Response list should not be null or empty",
|
||||
responses != null && !responses.isEmpty());
|
||||
assertTrue("Response should not be null", response != null);
|
||||
|
||||
// For each response (there should be only one based on test data) perform the standard set
|
||||
// of thorough parsing tests
|
||||
for (SHERPAResponse response : responses) {
|
||||
// Assert response is not error, or fail with message
|
||||
assertFalse("Response was flagged as 'isError'", response.isError());
|
||||
|
||||
// Skip remainder of parsing tests - these are already done in SHERPAServiceTEst
|
||||
}
|
||||
// Assert response is not error, or fail with message
|
||||
assertFalse("Response was flagged as 'isError'", response.isError());
|
||||
|
||||
// Skip remainder of parsing tests - these are already done in SHERPAServiceTEst
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -177,14 +177,18 @@ public class WorkspaceItemBuilder extends AbstractBuilder<WorkspaceItem, Workspa
|
||||
return addMetadataValue(MetadataSchemaEnum.DC.getName(), "subject", null, subject);
|
||||
}
|
||||
|
||||
public WorkspaceItemBuilder withAbstract(final String subject) {
|
||||
return addMetadataValue(MetadataSchemaEnum.DC.getName(),"description", "abstract", subject);
|
||||
public WorkspaceItemBuilder withIssn(String issn) {
|
||||
return addMetadataValue(MetadataSchemaEnum.DC.getName(), "identifier", "issn", issn);
|
||||
}
|
||||
|
||||
public WorkspaceItemBuilder withEntityType(final String entityType) {
|
||||
return addMetadataValue("dspace", "entity", "type", entityType);
|
||||
}
|
||||
|
||||
public WorkspaceItemBuilder withAbstract(final String subject) {
|
||||
return addMetadataValue(MetadataSchemaEnum.DC.getName(),"description", "abstract", subject);
|
||||
}
|
||||
|
||||
public WorkspaceItemBuilder grantLicense() {
|
||||
Item item = workspaceItem.getItem();
|
||||
String license;
|
||||
|
@@ -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.app.rest.model.step;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.dspace.app.sherpa.v2.SHERPAResponse;
|
||||
|
||||
/**
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class SherpaPolicy implements SectionData {
|
||||
|
||||
private static final long serialVersionUID = 2440249335255683173L;
|
||||
|
||||
private Date retrievalTime;
|
||||
|
||||
private SHERPAResponse sherpaResponse;
|
||||
|
||||
public Date getRetrievalTime() {
|
||||
return retrievalTime;
|
||||
}
|
||||
|
||||
public void setRetrievalTime(Date retrievalTime) {
|
||||
this.retrievalTime = retrievalTime;
|
||||
}
|
||||
|
||||
public SHERPAResponse getSherpaResponse() {
|
||||
return sherpaResponse;
|
||||
}
|
||||
|
||||
public void setSherpaResponse(SHERPAResponse sherpaResponse) {
|
||||
this.sherpaResponse = sherpaResponse;
|
||||
this.retrievalTime = sherpaResponse.getRetrievalTime();
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package org.dspace.app.rest.submit;
|
||||
|
||||
import org.dspace.app.sherpa.submit.SHERPASubmitService;
|
||||
import org.dspace.authorize.factory.AuthorizeServiceFactory;
|
||||
import org.dspace.authorize.service.AuthorizeService;
|
||||
import org.dspace.content.factory.ContentServiceFactory;
|
||||
@@ -35,5 +36,7 @@ public abstract class AbstractProcessingStep implements DataProcessingStep {
|
||||
protected MetadataFieldService metadataFieldService = ContentServiceFactory.getInstance().getMetadataFieldService();
|
||||
protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||
protected WorkspaceItemService workspaceItemService = ContentServiceFactory.getInstance().getWorkspaceItemService();
|
||||
protected SHERPASubmitService sherpaSubmitService = DSpaceServicesFactory.getInstance().getServiceManager()
|
||||
.getServiceByName("org.dspace.app.sherpa.submit.SHERPASubmitService", SHERPASubmitService.class);
|
||||
|
||||
}
|
||||
|
@@ -38,6 +38,7 @@ public interface DataProcessingStep extends RestProcessingStep {
|
||||
public static final String CCLICENSE_STEP_OPERATION_ENTRY = "cclicense/uri";
|
||||
public static final String ACCESS_CONDITION_STEP_OPERATION_ENTRY = "discoverable";
|
||||
public static final String ACCESS_CONDITION_POLICY_STEP_OPERATION_ENTRY = "accessConditions";
|
||||
public static final String SHERPA_RETRIEVAL_TIME = "retrievalTime";
|
||||
|
||||
public static final String UPLOAD_STEP_METADATA_PATH = "metadata";
|
||||
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.rest.submit.step;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.dspace.app.rest.model.patch.Operation;
|
||||
import org.dspace.app.rest.model.step.SherpaPolicy;
|
||||
import org.dspace.app.rest.submit.AbstractProcessingStep;
|
||||
import org.dspace.app.rest.submit.SubmissionService;
|
||||
import org.dspace.app.sherpa.cache.SherpaCacheEvictBeanLocator;
|
||||
import org.dspace.app.sherpa.cache.SherpaCacheEvictService;
|
||||
import org.dspace.app.sherpa.v2.SHERPAResponse;
|
||||
import org.dspace.app.util.SubmissionStepConfig;
|
||||
import org.dspace.content.InProgressSubmission;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.web.ContextUtil;
|
||||
|
||||
/**
|
||||
* @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com)
|
||||
*/
|
||||
public class SherpaPolicyStep extends AbstractProcessingStep {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public SherpaPolicy getData(SubmissionService submissionService, InProgressSubmission obj,
|
||||
SubmissionStepConfig config) throws Exception {
|
||||
SherpaPolicy result = new SherpaPolicy();
|
||||
Context context = ContextUtil.obtainCurrentRequestContext();
|
||||
SHERPAResponse response = sherpaSubmitService.searchRelatedJournals(context, obj.getItem());
|
||||
result.setSherpaResponse(response);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPatchProcessing(Context context, HttpServletRequest currentRequest, InProgressSubmission source,
|
||||
Operation op, SubmissionStepConfig stepConf) throws Exception {
|
||||
String path = op.getPath();
|
||||
SherpaCacheEvictService sherpaCacheEvictService = SherpaCacheEvictBeanLocator.getSherpaCacheEvictService();
|
||||
if (path.contains(SHERPA_RETRIEVAL_TIME)) {
|
||||
// uuid or issn?
|
||||
sherpaCacheEvictService.evictSingleCacheValue(source.getItem().getID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -23,7 +23,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils",
|
||||
"org.dspace.app.configuration", "org.dspace.iiif", "org.dspace.app.iiif"})
|
||||
"org.dspace.app.configuration", "org.dspace.iiif", "org.dspace.app.iiif", "org.dspace.app.sherpa.cache"})
|
||||
public class ApplicationConfig {
|
||||
// Allowed CORS origins ("Access-Control-Allow-Origin" header)
|
||||
// Can be overridden in DSpace configuration
|
||||
|
@@ -47,6 +47,24 @@
|
||||
<offheap unit="MB">4</offheap>
|
||||
</resources>
|
||||
</cache-template>
|
||||
<cache-template name="sherpa-default">
|
||||
<listeners>
|
||||
<listener>
|
||||
<class>org.dspace.app.rest.cache.CanvasCacheLogger</class>
|
||||
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
|
||||
<event-ordering-mode>UNORDERED</event-ordering-mode>
|
||||
<events-to-fire-on>CREATED</events-to-fire-on>
|
||||
<events-to-fire-on>EXPIRED</events-to-fire-on>
|
||||
<events-to-fire-on>REMOVED</events-to-fire-on>
|
||||
<events-to-fire-on>EVICTED</events-to-fire-on>
|
||||
</listener>
|
||||
</listeners>
|
||||
<resources>
|
||||
<heap>3000</heap>
|
||||
<offheap unit="MB">4</offheap>
|
||||
</resources>
|
||||
</cache-template>
|
||||
<cache alias="manifests" uses-template="iiif-default"/>
|
||||
<cache alias="canvasdimensions" uses-template="iiif-canvas"/>
|
||||
<cache alias="sherpa.searchByJournalISSN" uses-template="sherpa-default"/>
|
||||
</config>
|
||||
|
@@ -94,4 +94,11 @@ public class SubmissionAccessOptionRestRepositoryIT extends AbstractControllerIn
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
String tokenAdmin = getAuthToken(admin.getEmail(), password);
|
||||
getClient(tokenAdmin).perform(get("/api/config/submissionforms/sherpaPolicyStep"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
}
|
@@ -371,7 +371,9 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
|
||||
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
|
||||
.withTitle("Workspace Item 1")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withAuthor("Smith, Donald").withAuthor("Doe, John")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withIssn("222731-0582")
|
||||
.withAuthor("Doe, John")
|
||||
.withSubject("ExtraEntry")
|
||||
.build();
|
||||
|
||||
@@ -7262,4 +7264,43 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
|
||||
.andExpect(jsonPath("$.page.totalElements", is(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test99() throws Exception {
|
||||
context.turnOffAuthorisationSystem();
|
||||
|
||||
parentCommunity = CommunityBuilder.createCommunity(context)
|
||||
.withName("Parent Community")
|
||||
.build();
|
||||
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
|
||||
.withName("Collection 1")
|
||||
.build();
|
||||
WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1)
|
||||
.withTitle("Workspace Item 1")
|
||||
.withIssueDate("2017-10-17")
|
||||
.withAuthor("Smith, Donald")
|
||||
.withIssn("222731-0582")
|
||||
.withAuthor("Doe, John")
|
||||
.withSubject("ExtraEntry")
|
||||
.build();
|
||||
|
||||
context.restoreAuthSystemState();
|
||||
|
||||
String token = getAuthToken(eperson.getEmail(), password);
|
||||
getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// create a list of values to use in add operation
|
||||
List<Operation> operations = new ArrayList<>();
|
||||
operations.add(new RemoveOperation("/sections/sherpaPolicies/retrievalTime"));
|
||||
|
||||
String patchBody = getPatchContent(operations);
|
||||
getClient(token).perform(patch("/api/submission/workspaceitems/" + witem.getID())
|
||||
.content(patchBody)
|
||||
.contentType(MediaType.APPLICATION_JSON_PATCH_JSON))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
getClient(token).perform(get("/api/submission/workspaceitems/" + witem.getID()))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
}
|
@@ -203,6 +203,12 @@
|
||||
<processing-class>org.dspace.submit.step.SampleStep</processing-class>
|
||||
<type>sample</type>
|
||||
</step-definition>
|
||||
|
||||
<step-definition id="sherpaPolicies" mandatory="true">
|
||||
<heading>submit.progressbar.sherpapolicy</heading>
|
||||
<processing-class>org.dspace.app.rest.submit.step.SherpaPolicyStep</processing-class>
|
||||
<type>sherpaPolicy</type>
|
||||
</step-definition>
|
||||
</step-definitions>
|
||||
|
||||
<!-- The submission-definitions map lays out the detailed definition of -->
|
||||
@@ -237,6 +243,7 @@
|
||||
|
||||
<!--Step will be to Upload the item -->
|
||||
<step id="upload"/>
|
||||
<step id="sherpaPolicies"/>
|
||||
<!-- <step id="extractionstep"/> -->
|
||||
|
||||
<!-- Uncomment this step to allow the user to select a Creative Commons License -->
|
||||
|
Reference in New Issue
Block a user