Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gdcc/8745 archival status UI #8748

2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,8 @@ The body is a JSON object that must contain a "status" which may be "success", "
export JSON='{"status":"failure","message":"Something went wrong"}'

curl -H "X-Dataverse-key: $API_TOKEN" -H "Content-Type:application/json" -X PUT "$SERVER_URL/api/datasets/:persistentId/$VERSION/archivalStatus?persistentId=$PERSISTENT_IDENTIFIER" -d "$JSON"

Note that if the configured archiver only supports archiving a single version, the call may return 409 CONFLICT if/when another version already has a non-null status.

Delete the Archival Status of a Dataset By Version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
65 changes: 62 additions & 3 deletions src/main/java/edu/harvard/iq/dataverse/DatasetPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
Expand Down Expand Up @@ -5543,10 +5545,8 @@ public void refreshPaginator() {
*/
public void archiveVersion(Long id) {
if (session.getUser() instanceof AuthenticatedUser) {
AuthenticatedUser au = ((AuthenticatedUser) session.getUser());

DatasetVersion dv = datasetVersionService.retrieveDatasetVersionByVersionId(id).getDatasetVersion();
String className = settingsService.getValueForKey(SettingsServiceBean.Key.ArchiverClassName);
String className = settingsWrapper.getValueForKey(SettingsServiceBean.Key.ArchiverClassName, null);
AbstractSubmitToArchiveCommand cmd = ArchiverUtil.createSubmitToArchiveCommand(className, dvRequestService.getDataverseRequest(), dv);
if (cmd != null) {
try {
Expand All @@ -5570,6 +5570,65 @@ public void archiveVersion(Long id) {
}
}
}

public boolean isArchivable() {
String className = settingsWrapper.getValueForKey(SettingsServiceBean.Key.ArchiverClassName, null);
sekmiller marked this conversation as resolved.
Show resolved Hide resolved
if (className != null) {
try {
Class<?> clazz = Class.forName(className);
Method m = clazz.getMethod("isArchivable", Dataset.class, SettingsWrapper.class);
Object[] params = { dataset, settingsWrapper };
return (Boolean) m.invoke(null, params);
} catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
logger.warning("Failed to call is Archivable on configured archiver class: " + className);
e.printStackTrace();
}
}
return false;
}

public boolean isVersionArchivable() {
// If this dataset isn't in an archivable collection return false
if (isArchivable()) {
boolean checkForArchivalCopy = false;
// Otherwise, we need to know if the archiver is single-version-only
// If it is, we have to check for an existing archived version to answer the
// question
String className = settingsWrapper.getValueForKey(SettingsServiceBean.Key.ArchiverClassName, null);
if (className != null) {
try {
Class<?> clazz = Class.forName(className);
Method m = clazz.getMethod("isSingleVersion", SettingsWrapper.class);
Object[] params = { settingsWrapper };
checkForArchivalCopy = (Boolean) m.invoke(null, params);
} catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
logger.warning("Failed to call is Archivable on configured archiver class: " + className);
e.printStackTrace();
}
if (checkForArchivalCopy) {
// If we have to check (single version archiving), we can't allow archiving if
// one version is already archived (or attempted - any non-null status)
return !isSomeVersionArchived();
}
// If we allow multiple versions or didn't find one that has had archiving run
// on it, we can archive, so return true
return true;
}
}
//not in an archivable collection
return false;
}

public boolean isSomeVersionArchived() {
for (DatasetVersion dv : dataset.getVersions()) {
if (dv.getArchivalCopyLocation() != null) {
return true;
}
}
return false;
}

private static Date getFileDateToCompare(FileMetadata fileMetadata) {
DataFile datafile = fileMetadata.getDataFile();
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Admin.java
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,13 @@ public Response submitDatasetVersionToArchive(@PathParam("id") String dsid, @Pat
String className = settingsService.getValueForKey(SettingsServiceBean.Key.ArchiverClassName);
AbstractSubmitToArchiveCommand cmd = ArchiverUtil.createSubmitToArchiveCommand(className, dvRequestService.getDataverseRequest(), dv);
if (cmd != null) {
if(ArchiverUtil.onlySingleVersionArchiving(cmd.getClass(), settingsService)) {
for (DatasetVersion version : ds.getVersions()) {
if ((dv != version) && version.getArchivalCopyLocation() != null) {
return error(Status.CONFLICT, "Dataset already archived.");
}
}
}
new Thread(new Runnable() {
public void run() {
try {
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Datasets.java
Original file line number Diff line number Diff line change
Expand Up @@ -3342,6 +3342,13 @@ public Response setDatasetVersionArchivalStatus(@PathParam("id") String datasetI
if (dsv == null) {
return error(Status.NOT_FOUND, "Dataset version not found");
}
if (isSingleVersionArchiving()) {
for (DatasetVersion version : dsv.getDataset().getVersions()) {
if ((!dsv.equals(version)) && (version.getArchivalCopyLocation() != null)) {
return error(Status.CONFLICT, "Dataset already archived.");
}
}
}

dsv.setArchivalCopyLocation(JsonUtil.prettyPrint(update));
dsv = datasetversionService.merge(dsv);
Expand Down Expand Up @@ -3386,4 +3393,20 @@ public Response deleteDatasetVersionArchivalStatus(@PathParam("id") String datas
return wr.getResponse();
}
}

private boolean isSingleVersionArchiving() {
String className = settingsService.getValueForKey(SettingsServiceBean.Key.ArchiverClassName, null);
if (className != null) {
Class<? extends AbstractSubmitToArchiveCommand> clazz;
try {
clazz = Class.forName(className).asSubclass(AbstractSubmitToArchiveCommand.class);
return ArchiverUtil.onlySingleVersionArchiving(clazz, settingsService);
} catch (ClassNotFoundException e) {
logger.warning(":ArchiverClassName does not refer to a known Archiver");
} catch (ClassCastException cce) {
logger.warning(":ArchiverClassName does not refer to an Archiver class");
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package edu.harvard.iq.dataverse.engine.command.impl;

import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetVersion;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.SettingsWrapper;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
Expand Down Expand Up @@ -158,4 +161,17 @@ public void run() {
return bagThread;
}

public static boolean isArchivable(Dataset dataset, SettingsWrapper settingsWrapper) {
return true;
}

//Check if the chosen archiver imposes single-version-only archiving - in a View context
public static boolean isSingleVersion(SettingsWrapper settingsWrapper) {
return false;
}

//Check if the chosen archiver imposes single-version-only archiving - in the API
public static boolean isSingleVersion(SettingsServiceBean settingsService) {
return false;
}
}
23 changes: 23 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/util/ArchiverUtil.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package edu.harvard.iq.dataverse.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Logger;

import edu.harvard.iq.dataverse.DatasetVersion;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.impl.AbstractSubmitToArchiveCommand;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;

/**
* Simple class to reflectively get an instance of the desired class for
Expand Down Expand Up @@ -35,4 +38,24 @@ public static AbstractSubmitToArchiveCommand createSubmitToArchiveCommand(String
}
return null;
}

public static boolean onlySingleVersionArchiving(Class<? extends AbstractSubmitToArchiveCommand> clazz, SettingsServiceBean settingsService) {
Method m;
try {
m = clazz.getMethod("isSingleVersion", SettingsServiceBean.class);
Object[] params = { settingsService };
return (Boolean) m.invoke(null, params);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return (AbstractSubmitToArchiveCommand.isSingleVersion(settingsService));
}
}
6 changes: 6 additions & 0 deletions src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1868,6 +1868,12 @@ file.dataFilesTab.versions.headers.summary=Summary
file.dataFilesTab.versions.headers.contributors=Contributors
file.dataFilesTab.versions.headers.contributors.withheld=Contributor name(s) withheld
file.dataFilesTab.versions.headers.published=Published on
file.dataFilesTab.versions.headers.archived=Archival Status
file.dataFilesTab.versions.headers.archived.success=Archived
file.dataFilesTab.versions.headers.archived.pending=Pending
file.dataFilesTab.versions.headers.archived.failure=Failed
file.dataFilesTab.versions.headers.archived.notarchived=Not Archived
file.dataFilesTab.versions.headers.archived.submit=Submit
file.dataFilesTab.versions.viewDiffBtn=View Differences
file.dataFilesTab.versions.citationMetadata=Citation Metadata:
file.dataFilesTab.versions.added=Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
UPDATE datasetversion SET archivalCopyLocation = CONCAT('{"status":"success", "message":"', archivalCopyLocation,'"}') where archivalCopyLocation is not null and not archivalCopyLocation='Attempted';
UPDATE datasetversion SET archivalCopyLocation = CONCAT('{"status":"failure", "message":"Attempted"}') where archivalCopyLocation='Attempted';
21 changes: 20 additions & 1 deletion src/main/webapp/dataset-versions.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
</p:column><!-- end: description column -->

<!-- contributor column -->
<p:column headerText="#{bundle['file.dataFilesTab.versions.headers.contributors']}" class="col-sm-3">
<p:column headerText="#{bundle['file.dataFilesTab.versions.headers.contributors']}" class="col-sm-2">
<ui:fragment rendered="#{!empty(versionTab.contributorNames) and !anonymized}">
<h:outputText value="#{versionTab.contributorNames}" />
</ui:fragment>
Expand All @@ -147,6 +147,25 @@
<h:outputText id="versionDate" value="#{versionTab.publicationDateAsString}" />
</ui:fragment>
</p:column><!-- end: date column -->
<!-- ToDo: change HeaderText to Bundled string, stop this fragment from being loaded for datasets during creation -->
sekmiller marked this conversation as resolved.
Show resolved Hide resolved
<p:column headerText="#{bundle['file.dataFilesTab.versions.headers.archived']}" class="col-sm-1" rendered="#{!empty(DatasetPage.dataset.id) and permissionServiceBean.on(DatasetPage.dataset).has('ViewUnpublishedDataset') and !versionTab.isDraft() and (DatasetPage.archivable or DatasetPage.someVersionArchived or not empty(versionTab.archivalCopyLocation))}">
<ui:fragment>
<ui:fragment rendered="#{'success' eq versionTab.archivalCopyLocationStatus}">
<h:outputLink id="archived" value="#{versionTab.archivalCopyLocationMessage}" target="_blank" rendered="#{DatasetPage.isSuperUser()}">
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.success']}" />
</h:outputLink>
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.success']}" rendered="#{!DatasetPage.isSuperUser()}"/>
</ui:fragment>
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.pending']}" rendered="#{'pending' eq versionTab.archivalCopyLocationStatus}" title="#{DatasetPage.isSuperUser() ? versionTab.archivalCopyLocationMessage : ''}"/>
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.failure']}" rendered="#{'failure' eq versionTab.archivalCopyLocationStatus}" title="#{DatasetPage.isSuperUser() ? versionTab.archivalCopyLocationMessage : ''}"/>
<p:commandLink rendered="#{DatasetPage.isSuperUser() and DatasetPage.versionArchivable and empty(versionTab.archivalCopyLocation)}" update="#{p:resolveClientId('datasetForm:tabView:versionsTable', view)},:messagePanel"
action="#{DatasetPage.archiveVersion(versionTab.id)}">
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.submit']}"/>
</p:commandLink>
<h:outputText value="#{bundle['file.dataFilesTab.versions.headers.archived.notarchived']}" rendered="#{(not DatasetPage.isSuperUser() or DatasetPage.someVersionArchived) and empty(versionTab.archivalCopyLocation)}"/>

</ui:fragment>
</p:column><!-- end: archivalCopy column -->
</p:dataTable>
<!-- / VERSIONS -->
</ui:fragment>
Expand Down