Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
b1c4b46
New Dataset Move Notification
stevenwinship Sep 9, 2025
1766c5e
remove unused logger
stevenwinship Sep 9, 2025
e785c0b
fix docs
stevenwinship Sep 9, 2025
45b7571
fix notification user dataset original owner instead of new owner to …
stevenwinship Sep 9, 2025
a888e49
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Sep 26, 2025
16cf474
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Sep 30, 2025
80e5905
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Sep 30, 2025
46eb7ba
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 1, 2025
154f47c
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 8, 2025
8be9c78
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 10, 2025
411bbd3
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 16, 2025
f74130c
addressing comments
stevenwinship Oct 23, 2025
70edb6a
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 23, 2025
5a603fd
Merge branch '11670-notification-of-moved-datasets' of https://github…
stevenwinship Oct 23, 2025
3f8dfd0
Revert "addressing comments"
stevenwinship Oct 23, 2025
75d0e90
Revert "Merge branch '11670-notification-of-moved-datasets' of https:…
stevenwinship Oct 23, 2025
58328ec
Reapply "Merge branch '11670-notification-of-moved-datasets' of https…
stevenwinship Oct 23, 2025
cf20973
addressing comments
stevenwinship Oct 23, 2025
84ad073
remove tabs
stevenwinship Oct 23, 2025
d4e446e
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 23, 2025
3e44d76
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Oct 24, 2025
0eeac97
#11918 prelim check in
sekmiller Oct 27, 2025
f74a9cd
#11918 fix perms and test
sekmiller Oct 28, 2025
e21f007
#11918 add unit tests for command
sekmiller Oct 29, 2025
8888d00
#11918 fix create test for flush/new template
sekmiller Oct 30, 2025
4775c27
#11918 add get template by id
sekmiller Oct 31, 2025
9733e5e
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Nov 4, 2025
de585a3
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Nov 5, 2025
2d24a5a
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 5, 2025
a2714fd
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 5, 2025
e6507e1
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Nov 5, 2025
8069e6c
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 5, 2025
0b2cc27
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 6, 2025
09d7775
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 7, 2025
7585924
#11918 unused code
sekmiller Nov 7, 2025
09e4a28
#11918 add release notes
sekmiller Nov 10, 2025
e1d4f67
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 10, 2025
5a06245
#11918 update release note
sekmiller Nov 10, 2025
9a5454e
Update native-api.rst
sekmiller Nov 10, 2025
0dcc385
Merge branch '11918-template-apis' of https://github.com/IQSS/dataver…
sekmiller Nov 10, 2025
a62265d
#11918 code format
sekmiller Nov 12, 2025
fc64bdf
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 12, 2025
261d7ae
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 13, 2025
7fce175
#11918 CR fixes
sekmiller Nov 13, 2025
656b0b6
Update native-api.rst
sekmiller Nov 13, 2025
9ea3f16
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 18, 2025
09c7974
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 20, 2025
39f41d9
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Nov 21, 2025
967cf3c
Merge branch 'develop' into 11918-template-apis
sekmiller Nov 24, 2025
acf58a2
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 1, 2025
d73c3be
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 3, 2025
2d8054f
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 4, 2025
d080fd8
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 9, 2025
afa57c3
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 9, 2025
39ba71c
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 10, 2025
c2e75a6
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 12, 2025
7b9a106
Update CreateTemplateCommandTest.java
sekmiller Dec 12, 2025
b5fd128
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 17, 2025
6bbd331
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Dec 18, 2025
533dd7d
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 19, 2025
7ed46f0
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Dec 19, 2025
f81307c
Merge branch 'develop' into 11918-template-apis
sekmiller Dec 22, 2025
9d60560
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Jan 5, 2026
d9b4069
Merge branch 'develop' into 11670-notification-of-moved-datasets
stevenwinship Jan 5, 2026
3359c1b
fix test
stevenwinship Jan 5, 2026
766b006
Merge branch 'develop' into 11918-template-apis
sekmiller Jan 6, 2026
1c241ee
#11918 fix path
sekmiller Jan 7, 2026
954697d
Merge pull request #11969 from IQSS/11918-template-apis
ChengShi-1 Jan 7, 2026
f5049ac
Merge pull request #11805 from IQSS/11670-notification-of-moved-datasets
sekmiller Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes/11670-notification-of-moved-datasets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Notifications

New notification was added for Datasets moving between Dataverses.
Requires SettingsServiceBean.Key.SendNotificationOnDatasetMove setting to be enabled.

See #11670
17 changes: 17 additions & 0 deletions doc/release-notes/11918-template-apis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## New Endpoint: GET `/dataverses/{id}/template`

A new endpoint has been implemented to manage templates belonging to a given dataverse collection.

### Functionality
- Returns the template of the given {id} in json format.
- You must have add dataset permission in the collection in order to use this endpoint.

## New Endpoint: DELETE `/dataverses/{id}/template`

A new endpoint has been implemented to manage templates belonging to a given dataverse collection.

### Functionality
- Deletes the template of the given {id}.
- You must have Edit Dataverse permission in order to use this endpoint.


1 change: 1 addition & 0 deletions doc/sphinx-guides/source/admin/user-administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ This enables additional settings for each user in the notifications tab of their
* ``CREATEDS`` Your dataset is created
* ``CREATEDV`` Dataverse collection is created
* ``DATASETCREATED`` Dataset was created by user
* ``DATASETMOVED`` Dataset was moved by user
* ``FILESYSTEMIMPORT`` Dataset has been successfully uploaded and verified
* ``GRANTFILEACCESS`` Access to file is granted
* ``INGESTCOMPLETEDWITHERRORS`` Ingest completed with errors
Expand Down
42 changes: 42 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1576,6 +1576,48 @@ The fully expanded example above (without environment variables) looks like this

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X GET "https://demo.dataverse.org/api/dataverses/1/templates"

List Single Template by its Identifier
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Gets the json representation of a template by its ``id``:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export ID=1

curl -H "X-Dataverse-key:$API_TOKEN" -X GET "$SERVER_URL/api/dataverses/{ID}/template"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X GET "https://demo.dataverse.org/api/dataverses/1/template"

You must have Create Dataset permission within the given dataverse collection to invoke this api.

Delete a Template by its Identifier
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Deletes a template by its ``id``:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export ID=1

curl -H "X-Dataverse-key:$API_TOKEN" -X DELETE "$SERVER_URL/api/dataverses/{ID}/template"

The fully expanded example above (without environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X DELETE "https://demo.dataverse.org/api/dataverses/1/template"

You must have Edit Dataverse permission within the given dataverse collection to invoke this api.

Create a Template for a Collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/MailServiceBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ public String getMessageTextBasedOnNotification(UserNotification userNotificatio
String[] paramArrayDatasetCreated = {getDatasetLink(dataset), dataset.getDisplayName(), userNotification.getRequestor().getName(), dataset.getOwner().getDisplayName()};
messageText += MessageFormat.format(pattern, paramArrayDatasetCreated);
return messageText;
case DATASETMOVED:
dataset = (Dataset) targetObject;
pattern = BundleUtil.getStringFromBundle("notification.email.datasetWasMoved");
String[] paramArrayDatasetMoved = {getDatasetLink(dataset), dataset.getDisplayName(), userNotification.getRequestor().getName(), dataset.getOwner().getDisplayName()};
messageText += MessageFormat.format(pattern, paramArrayDatasetMoved);
return messageText;
case CREATEDS:
version = (DatasetVersion) targetObject;
String datasetCreatedMessage = BundleUtil.getStringFromBundle("notification.email.createDataset", Arrays.asList(
Expand Down Expand Up @@ -785,6 +791,7 @@ public Object getObjectOfNotification (UserNotification userNotification){
case GRANTFILEACCESS:
case REJECTFILEACCESS:
case DATASETCREATED:
case DATASETMOVED:
case DATASETMENTIONED:
return datasetService.find(userNotification.getObjectId());
case CREATEDS:
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/UserNotification.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public enum Type {
PUBLISHFAILED_PIDREG, WORKFLOW_SUCCESS, WORKFLOW_FAILURE, STATUSUPDATED, DATASETCREATED, DATASETMENTIONED,
GLOBUSUPLOADCOMPLETED, GLOBUSUPLOADCOMPLETEDWITHERRORS,
GLOBUSDOWNLOADCOMPLETED, GLOBUSDOWNLOADCOMPLETEDWITHERRORS, REQUESTEDFILEACCESS,
GLOBUSUPLOADREMOTEFAILURE, GLOBUSUPLOADLOCALFAILURE, PIDRECONCILED;
GLOBUSUPLOADREMOTEFAILURE, GLOBUSUPLOADLOCALFAILURE, PIDRECONCILED,
DATASETMOVED;

public String getDescription() {
return BundleUtil.getStringFromBundle("notification.typeDescription." + this.name());
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/edu/harvard/iq/dataverse/api/Datasets.java
Original file line number Diff line number Diff line change
Expand Up @@ -1430,8 +1430,7 @@ public Response moveDataset(@Context ContainerRequestContext crc, @PathParam("id
}
//Command requires Super user - it will be tested by the command
execCommand(new MoveDatasetCommand(
createDataverseRequest(u), ds, target, force
));
createDataverseRequest(u), ds, target, force, true));
return ok(BundleUtil.getStringFromBundle("datasets.api.moveDataset.success"));
} catch (WrappedResponse ex) {
if (ex.getCause() instanceof UnforcedCommandException) {
Expand Down
46 changes: 45 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.collect.Lists;
import com.google.api.client.util.ArrayMap;
import edu.harvard.iq.dataverse.*;
import static edu.harvard.iq.dataverse.api.AbstractApiBean.error;
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
import edu.harvard.iq.dataverse.api.datadeposit.SwordServiceBean;
import edu.harvard.iq.dataverse.api.dto.*;
Expand All @@ -23,6 +24,7 @@
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItemServiceBean;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.impl.*;
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
Expand Down Expand Up @@ -118,6 +120,9 @@ public class Dataverses extends AbstractApiBean {

@EJB
PermissionServiceBean permissionService;

@EJB
TemplateServiceBean templateService;

@EJB
DataverseFeaturedItemServiceBean dataverseFeaturedItemServiceBean;
Expand Down Expand Up @@ -1989,6 +1994,21 @@ public Response getTemplates(@Context ContainerRequestContext crc, @PathParam("i
return e.getResponse();
}
}

@GET
@AuthRequired
@Path("{id}/template/")
public Response getTemplate(@Context ContainerRequestContext crc, @PathParam("id") Long templateId) {
try {
Template template = templateService.find(templateId);
if (template == null){
return error(Response.Status.NOT_FOUND, "Template with id " + templateId + " - not found.");
}
return ok(jsonTemplate(execCommand(new GetTemplateCommand(createDataverseRequest(getRequestUser(crc)), template))));
} catch (WrappedResponse e) {
return e.getResponse();
}
}

@POST
@AuthRequired
Expand All @@ -2002,7 +2022,10 @@ public Response createTemplate(@Context ContainerRequestContext crc, String body
} catch (JsonParseException ex) {
return error(Status.BAD_REQUEST, MessageFormat.format(BundleUtil.getStringFromBundle("dataverse.createTemplate.error.jsonParseMetadataFields"), ex.getMessage()));
}
return ok(jsonTemplate(execCommand(new CreateTemplateCommand(newTemplateDTO.toTemplate(), createDataverseRequest(getRequestUser(crc)), dataverse, true))));
Template created = execCommand(new CreateTemplateCommand(newTemplateDTO.toTemplate(), createDataverseRequest(getRequestUser(crc)), dataverse, true));

return created("/dataverses/template/" + created.getId(), jsonTemplate(created));

} catch (WrappedResponse e) {
return e.getResponse();
}
Expand Down Expand Up @@ -2036,6 +2059,27 @@ public Response setMetadataLanguage(@Context ContainerRequestContext crc, @PathP
}, getRequestUser(crc));
}

@Path("{id}/template")
@AuthRequired
@DELETE
public Response deleteTemplate(@Context ContainerRequestContext crc, @PathParam("id") long id) {

Template doomed = templateService.find(id);
if (doomed == null) {
return error(Response.Status.NOT_FOUND, "Template with id " + id + " - not found.");
}

Dataverse dv = doomed.getDataverse();
List<Dataverse> dataverseWDefaultTemplate = templateService.findDataversesByDefaultTemplateId(doomed.getId());
try {
execCommand(new DeleteTemplateCommand(createDataverseRequest(getRequestUser(crc)), dv, doomed, dataverseWDefaultTemplate));
} catch (WrappedResponse wr) {
return handleWrappedResponse(wr);
}

return ok("Template " + doomed.getName() + " deleted.");
}

@GET
@AuthRequired
@Path("{identifier}/assignments/history")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ public void displayNotification() {
case GRANTFILEACCESS:
case REJECTFILEACCESS:
case DATASETCREATED:
case DATASETMOVED:
case DATASETMENTIONED:
userNotification.setTheObject(datasetService.find(userNotification.getObjectId()));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ public void move(){
HttpServletRequest httpServletRequest = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
DataverseRequest dataverseRequest = new DataverseRequest(authUser, httpServletRequest);
commandEngine.submit(new MoveDatasetCommand(
dataverseRequest, ds, target, false
));
dataverseRequest, ds, target, false, true));

logger.info("Moved " + dsPersistentId + " from " + srcAlias + " to " + dstAlias);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,15 @@ public Template execute(CommandContext ctxt) throws CommandException {
}

Template createdTemplate = ctxt.templates().save(template);

createdTemplate.setIsDefaultForDataverse(template.isIsDefaultForDataverse());
if (initialize && createdTemplate.isIsDefaultForDataverse()) {
dataverse.setDefaultTemplate(createdTemplate);
ctxt.em().merge(dataverse);
}
}

//Flush so that api response can include the id
ctxt.em().flush();
return createdTemplate;

}

private static void updateTermsOfUseAndAccess(CommandContext ctxt, Template template) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

package edu.harvard.iq.dataverse.engine.command.impl;

import edu.harvard.iq.dataverse.Template;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.engine.command.AbstractCommand;
import edu.harvard.iq.dataverse.engine.command.CommandContext;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
*
* @author stephenkraffmiller
*/
public class GetTemplateCommand extends AbstractCommand<Template> {

private final Template template;

public GetTemplateCommand (DataverseRequest aRequest, Template template){
super(aRequest, template.getDataverse());
this.template = template;
}



@Override
public Template execute(CommandContext ctxt) throws CommandException {
return template;
}

@Override
public Map<String, Set<Permission>> getRequiredPermissions() {
//at least need add dataset if not published also - view unpublished
Set<Permission> requiredPermissions = new HashSet<>();

requiredPermissions.add(Permission.AddDataset);
if (!template.getDataverse().isReleased()) {
requiredPermissions.add(Permission.ViewUnpublishedDataverse);
}

return Collections.singletonMap("", requiredPermissions);

}


}
Loading