Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion doc/release-notes/12076-non-superuser-dataverse-linking.md

This file was deleted.

4 changes: 2 additions & 2 deletions doc/sphinx-guides/source/admin/dataverses-datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ Moves a Dataverse collection whose id is passed to an existing Dataverse collect
Link a Dataverse Collection
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Creates a link between a Dataverse collection and another Dataverse collection (see the :ref:`dataverse-linking` section of the User Guide for more information). ::
Creates a link between a Dataverse collection and another Dataverse collection (see the :ref:`dataverse-linking` section of the User Guide for more information). Only accessible to superusers. ::

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT http://$SERVER/api/dataverses/$linked-dataverse-alias/link/$linking-dataverse-alias

Unlink a Dataverse Collection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Removes a link between a Dataverse collection and another Dataverse collection. Accessible to users with Link Dataverse permission on the linking Dataverse collection. ::
Removes a link between a Dataverse collection and another Dataverse collection. Only accessible to superusers. ::

curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE http://$SERVER/api/dataverses/$linked-dataverse-alias/deleteLink/$linking-dataverse-alias

Expand Down
60 changes: 0 additions & 60 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3750,66 +3750,6 @@ The fully expanded example above (without environment variables) looks like this

curl -H "X-Dataverse-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/datasets/24/link/test"

Unlink a Dataset
~~~~~~~~~~~~~~~~

Removes a link between a dataset and a Dataverse collection (see :ref:`dataset-linking` section of Dataverse Collection Management in the User Guide for more information):

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export DATASET_ID=24
export DATAVERSE_ID=test

curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE "$SERVER_URL/api/datasets/$DATASET_ID/deleteLink/$DATAVERSE_ID"

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/datasets/24/deleteLink/test"

Link a Dataverse collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Creates a link between one Dataverse collection and another Dataverse collection (see :ref:`dataverse-linking` section of Dataverse Collection Management in the User Guide for more information):

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export LINKED_DATAVERSE_ID=linked-collection
export LINKING_DATAVERSE_ID=linking-collection

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT "$SERVER_URL/api/dataverses/$LINKED_DATAVERSE_ID/link/$LINKING_DATAVERSE_ID"

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 PUT "https://demo.dataverse.org/api/dataverses/linked-collection/link/linking-collection"

Unlink a Dataverse collection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Removes a link between one Dataverse collection and another Dataverse collection (see :ref:`dataverse-linking` section of Dataverse Collection Management in the User Guide for more information):

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export LINKED_DATAVERSE_ID=linked-collection
export LINKING_DATAVERSE_ID=linking-collection

curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE "$SERVER_URL/api/dataverses/$LINKED_DATAVERSE_ID/deleteLink/$LINKING_DATAVERSE_ID"

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/linked-collection/deleteLink/linking-collection"

Dataset Locks
~~~~~~~~~~~~~

Expand Down
8 changes: 4 additions & 4 deletions doc/sphinx-guides/source/user/dataverse-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,18 +221,18 @@ In order to link a dataset, you will need your account to have the "Link Dataset

To link a dataset to your Dataverse collection, you must navigate to that dataset and click the white "Link" button in the upper-right corner of the dataset page. This will open up a window where you can type in the name of the Dataverse collection that you would like to link the dataset to. Select your Dataverse collection and click the save button. This will establish the link, and the dataset will now appear under your Dataverse collection.

To remove an established link, navigate to the linked dataset's page and click the white "Unlink" button in the upper-right corner of the page.

A draft dataset can be linked to other Dataverse collections. It will only become publicly visible in the linked collection(s) after it has been published. To publish the dataset, your account must have the "Publish Dataset" permission for the Dataverse collection in which the dataset was originally created. Permissions in the linked Dataverse collections do not apply.

There is currently no way to remove established links in the UI. If you need to remove a link between a Dataverse collection and a dataset, please contact the support team for the Dataverse installation you are using (see the :ref:`unlink-a-dataset` section of the Admin Guide for more information).

.. _dataverse-linking:

Dataverse Collection Linking
============================

Similarly to dataset linking, Dataverse collection linking allows a Dataverse collection owner to "link" their Dataverse collection to another Dataverse collection, so the Dataverse collection being linked will appear in the linking Dataverse collection's list of contents without actually *being* in that Dataverse collection.
Similarly to dataset linking, Dataverse collection linking allows a Dataverse collection owner to "link" their Dataverse collection to another Dataverse collection, so the Dataverse collection being linked will appear in the linking Dataverse collection's list of contents without actually *being* in that Dataverse collection. Currently, the ability to link a Dataverse collection to another Dataverse collection is a superuser only feature.

In order to link a collection, you will need your account to have the "Link Dataverse" permission on the linking Dataverse collection.
If you need to have a Dataverse collection linked to your Dataverse collection, please contact the support team for the Dataverse installation you are using.

Publish Your Dataverse Collection
=================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,19 @@ public void save(DataverseLinkingDataverse dataverseLinkingDataverse) {
}
}

public DataverseLinkingDataverse findDataverseLinkingDataverse(Long linkingDataverseId, Long linkedDataverseId) {
public DataverseLinkingDataverse findDataverseLinkingDataverse(Long dataverseId, Long linkingDataverseId) {
try {
return em.createNamedQuery("DataverseLinkingDataverse.findByDataverseIdAndLinkingDataverseId", DataverseLinkingDataverse.class)
.setParameter("dataverseId", linkedDataverseId)
.setParameter("dataverseId", dataverseId)
.setParameter("linkingDataverseId", linkingDataverseId)
.getSingleResult();
} catch (jakarta.persistence.NoResultException e) {
logger.fine("No DataverseLinkingDataverse found for linkingDataverseId " + linkingDataverseId + " and linkedDataverseId " + linkedDataverseId);
logger.fine("No DataverseLinkingDataverse found for dataverseId " + dataverseId + " and linkedDataverseId " + linkingDataverseId);
return null;
}
}

public boolean alreadyLinked(Dataverse linkingDataverse, Dataverse linkedDataverse) {
return findDataverseLinkingDataverse(linkingDataverse.getId(), linkedDataverse.getId()) != null;
public boolean alreadyLinked(Dataverse definitionPoint, Dataverse dataverseToLinkTo) {
return findDataverseLinkingDataverse(dataverseToLinkTo.getId(), definitionPoint.getId()) != null;
}
}
62 changes: 33 additions & 29 deletions src/main/java/edu/harvard/iq/dataverse/DataversePage.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,31 +215,18 @@
}

public boolean showLinkingPopup() {
// Must be logged in
AuthenticatedUser au = getAuthenticatedUser();
if (au == null) {
String testquery = "";
if (session.getUser() == null) {
return false;
}
if (dataverse == null) {
return false;
}

// If there is an active search query, that's all that matters (plus having permission on ANY collection)
if (query != null && !query.isEmpty()) {
List<Dataverse> permitted = permissionService.findPermittedCollections(dvRequestService.getDataverseRequest(), au, Permission.LinkDataverse);
return permitted != null && !permitted.isEmpty();
}

// Otherwise (no active search), check if there is at least one OTHER eligible collection
// Eligible means: not the current collection and not in the parent tree
// Technically, eligible also means "not already linked", but in that case, we show the Link button anyway and have the Link dialog display a message about all eligible collections already being linked
List<Dataverse> dvsWithLinkPermission = permissionService.findPermittedCollections(dvRequestService.getDataverseRequest(), au, Permission.LinkDataverse);
if (dvsWithLinkPermission != null && !dvsWithLinkPermission.isEmpty()) {
List<Dataverse> eligibleDataverses = dataverseService.removeUnlinkableDataverses(dvsWithLinkPermission, dataverse, false);
return !eligibleDataverses.isEmpty();
if (query != null) {
testquery = query;
}

return false;
return (session.getUser().isSuperuser() && (dataverse.getOwner() != null || !testquery.isEmpty()));
}

public void setupLinkingPopup (String popupSetting){
Expand All @@ -254,18 +241,35 @@
public void updateLinkableDataverses() {
dataversesForLinking = new ArrayList<>();
linkingDVSelectItems = new ArrayList<>();


List<Dataverse> dvsWithLinkPermission = permissionService.findPermittedCollections(dvRequestService.getDataverseRequest(), getAuthenticatedUser(), Permission.LinkDataverse, "");

if (dvsWithLinkPermission != null && !dvsWithLinkPermission.isEmpty()) {
// for linking - make sure the link hasn't occurred and it's not in the tree
if (this.linkMode.equals(LinkMode.LINKDATAVERSE)) {
dataversesForLinking = dataverseService.removeUnlinkableDataverses(dvsWithLinkPermission, dataverse);
} else {
// for saved search, add all
dataversesForLinking = dvsWithLinkPermission;

//Since only a super user function add all dvs
dataversesForLinking = dataverseService.findAll();// permissionService.getDataversesUserHasPermissionOn(session.getUser(), Permission.PublishDataverse);

Check warning on line 246 in src/main/java/edu/harvard/iq/dataverse/DataversePage.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=IQSS_dataverse&issues=AaCGoTr7yH6p7lnmmVVA&open=AaCGoTr7yH6p7lnmmVVA&pullRequest=12674

/*
List<DataverseRole> roles = dataverseRoleServiceBean.getDataverseRolesByPermission(Permission.PublishDataverse, dataverse.getId());

Check warning on line 249 in src/main/java/edu/harvard/iq/dataverse/DataversePage.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This block of commented-out lines of code should be removed.

See more on https://sonarcloud.io/project/issues?id=IQSS_dataverse&issues=AaCGoTr7yH6p7lnmmVVB&open=AaCGoTr7yH6p7lnmmVVB&pullRequest=12674
List<String> types = new ArrayList();
types.add("Dataverse");
for (Long dvIdAsInt : permissionService.getDvObjectIdsUserHasRoleOn(session.getUser(), roles, types, false)) {
dataversesForLinking.add(dataverseService.find(dvIdAsInt));
}*/

//for linking - make sure the link hasn't occurred and its not int the tree
if (this.linkMode.equals(LinkMode.LINKDATAVERSE)) {

// remove this and it's parent tree
dataversesForLinking.remove(dataverse);
Dataverse testDV = dataverse;
while(testDV.getOwner() != null){
dataversesForLinking.remove(testDV.getOwner());
testDV = testDV.getOwner();
}

for (Dataverse removeLinked : linkingService.findLinkingDataverses(dataverse.getId())) {
dataversesForLinking.remove(removeLinked);
}
} else{
//for saved search add all

}

for (Dataverse selectDV : dataversesForLinking) {
Expand Down
39 changes: 17 additions & 22 deletions src/main/java/edu/harvard/iq/dataverse/DataverseServiceBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -552,30 +552,28 @@ public List<Dataverse> filterDataversesForLinking(String query, DataverseRequest
return dataverseList;
}

public List<Dataverse> removeUnlinkableDataverses(List<Dataverse> allWithPerms, DvObject dvo, boolean removeAlreadyLinked) {
public List<Dataverse> removeUnlinkableDataverses(List<Dataverse> allWithPerms, DvObject dvo) {
List<Dataverse> dataverseList = new ArrayList<>();
Dataset linkedDataset = null;
Dataverse linkedDataverse = null;
List<Object> alreadyLinkeddv_ids;

List<Dataverse> remove = new ArrayList<>();

if (removeAlreadyLinked) {
List<Object> alreadyLinkeddv_ids;
if ((dvo instanceof Dataset)) {
linkedDataset = (Dataset) dvo;
alreadyLinkeddv_ids = em.createNativeQuery("SELECT linkingdataverse_id FROM datasetlinkingdataverse WHERE dataset_id = " + linkedDataset.getId()).getResultList();
} else {
linkedDataverse = (Dataverse) dvo;
alreadyLinkeddv_ids = em.createNativeQuery("SELECT linkingdataverse_id FROM dataverselinkingdataverse WHERE dataverse_id = " + linkedDataverse.getId()).getResultList();
}

if ((dvo instanceof Dataset)) {
linkedDataset = (Dataset) dvo;
alreadyLinkeddv_ids = em.createNativeQuery("SELECT linkingdataverse_id FROM datasetlinkingdataverse WHERE dataset_id = " + linkedDataset.getId()).getResultList();
} else {
linkedDataverse = (Dataverse) dvo;
alreadyLinkeddv_ids = em.createNativeQuery("SELECT linkingdataverse_id FROM dataverselinkingdataverse WHERE dataverse_id = " + linkedDataverse.getId()).getResultList();
}
List<Dataverse> remove = new ArrayList<>();

if (alreadyLinkeddv_ids != null && !alreadyLinkeddv_ids.isEmpty()) {
alreadyLinkeddv_ids.stream().map((testDVId) -> this.find(testDVId)).forEachOrdered((removeIt) -> {
remove.add(removeIt);
});
}
if (alreadyLinkeddv_ids != null && !alreadyLinkeddv_ids.isEmpty()) {
alreadyLinkeddv_ids.stream().map((testDVId) -> this.find(testDVId)).forEachOrdered((removeIt) -> {
remove.add(removeIt);
});
}


if (dvo instanceof Dataverse dataverse) {
remove.add(dataverse);
Expand All @@ -599,11 +597,8 @@ public List<Dataverse> removeUnlinkableDataverses(List<Dataverse> allWithPerms,

return dataverseList;
}

public List<Dataverse> removeUnlinkableDataverses(List<Dataverse> allWithPerms, DvObject dvo) {
return removeUnlinkableDataverses(allWithPerms, dvo, true);
}



public List<Dataverse> filterDataversesForUnLinking(String query, DataverseRequest req, Dataset dataset) {
List<Object> alreadyLinkeddv_ids = em.createNativeQuery("SELECT linkingdataverse_id FROM datasetlinkingdataverse WHERE dataset_id = " + dataset.getId()).getResultList();
List<Dataverse> dataverseList = new ArrayList<>();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ private List<DatasetFieldType> parseFacets(JsonArray facetsArray) throws Wrapped

@DELETE
@AuthRequired
@Path("{linkedDataverseId}/deleteLink/{linkingDataverseId}")
@Path("{linkingDataverseId}/deleteLink/{linkedDataverseId}")
@Operation(summary = "Remove a dataverse link",
description = "Deletes a link from one dataverse to another linked dataverse.")
public Response deleteDataverseLinkingDataverse(@Context ContainerRequestContext crc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.util.logging.Logger;
import jakarta.json.JsonObjectBuilder;

@RequiredPermissions(Permission.LinkDataverse)
@RequiredPermissions(Permission.PublishDataverse)
public class CreateSavedSearchCommand extends AbstractCommand<SavedSearch> {

private static final Logger logger = Logger.getLogger(SavedSearchServiceBean.class.getCanonicalName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* @author sarahferry
*/

@RequiredPermissions( Permission.LinkDataverse )
@RequiredPermissions( Permission.EditDataverse )
public class DeleteDataverseLinkingDataverseCommand extends AbstractCommand<Dataverse> {

private final DataverseLinkingDataverse doomed;
Expand All @@ -42,6 +42,10 @@ public DeleteDataverseLinkingDataverseCommand(DataverseRequest aRequest, Dataver

@Override
public Dataverse execute(CommandContext ctxt) throws CommandException {
if ((!(getUser() instanceof AuthenticatedUser) || !getUser().isSuperuser())) {
throw new PermissionException("Delete dataverse linking dataverse can only be called by superusers.",
this, Collections.singleton(Permission.DeleteDataverse), editedDv);
}
Dataverse merged = ctxt.em().merge(editedDv);
DataverseLinkingDataverse doomedAndMerged = ctxt.em().merge(doomed);
ctxt.em().remove(doomedAndMerged);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public LinkDataverseCommand(DataverseRequest aRequest, Dataverse dataverse, Data

@Override
public DataverseLinkingDataverse execute(CommandContext ctxt) throws CommandException {
if ((!(getUser() instanceof AuthenticatedUser) || !getUser().isSuperuser())) {
throw new PermissionException("Link Dataverse can only be called by superusers.",
this, Collections.singleton(Permission.LinkDataverse), linkingDataverse);
}
if (linkedDataverse.equals(linkingDataverse)) {
throw new IllegalCommandException("Can't link a dataverse to itself", this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ public void removeLinks(DataverseRequest dvReq, SavedSearch savedSearch) throws

if (dvObjectThatDefinitionPointWillLinkTo.isInstanceofDataverse()) {
Dataverse linkedDataverse = (Dataverse) dvObjectThatDefinitionPointWillLinkTo;
DataverseLinkingDataverse dvld = dvLinkingService.findDataverseLinkingDataverse(linkingDataverse.getId(), linkedDataverse.getId());
DataverseLinkingDataverse dvld = dvLinkingService.findDataverseLinkingDataverse(linkedDataverse.getId(), linkingDataverse.getId());
if(dvld != null) {
Dataverse dv = commandEngine.submitInNewTransaction(new DeleteDataverseLinkingDataverseCommand(dvReq, linkingDataverse, dvld, true));
}
Expand Down
Loading
Loading