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

ADA/Guestbook-at-request #9599

Merged
Merged
Show file tree
Hide file tree
Changes from 132 commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
07c70d8
flyway update script
qqmyers Dec 6, 2022
c872695
fix duplicate action
qqmyers Dec 6, 2022
6c0dab8
add comment to check possible unused method
qqmyers Dec 6, 2022
76b3b18
debug logging
qqmyers Dec 6, 2022
bd603ec
more debug
qqmyers Dec 6, 2022
5e29a06
more debug
qqmyers Dec 6, 2022
0ce8d1c
try fix - use merged dataset
qqmyers Dec 6, 2022
6e7ab00
add id column in fileaccessrequest
qqmyers Dec 6, 2022
16b4c3c
add constraint directly
qqmyers Dec 6, 2022
3658b8e
remove old constraint, update id col
qqmyers Dec 6, 2022
6fda50c
syntax err
qqmyers Dec 6, 2022
b858cd3
another variant
qqmyers Dec 6, 2022
3ec1451
try try again
qqmyers Dec 6, 2022
76448e7
add fk constraint
qqmyers Dec 7, 2022
cdc669b
typos
qqmyers Dec 7, 2022
0ed931c
missing INT
qqmyers Dec 7, 2022
7eb0c57
add missing methods
qqmyers Dec 7, 2022
7efe254
guestbookresponse /datasetVersion fix
qqmyers Dec 22, 2022
2770fc6
merge fixes
qqmyers May 19, 2023
10f8a0a
update script naming and content
qqmyers May 19, 2023
560faf5
remove second action (merge issue)
qqmyers May 19, 2023
bc42df0
typo from merge
qqmyers May 19, 2023
57e984b
fix for #9601
qqmyers May 19, 2023
0c76f7b
remove QDR updates
qqmyers May 19, 2023
507428d
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers May 25, 2023
8b2937e
Add call to populate file lists
qqmyers May 25, 2023
7ddc046
hide accept terms buttons in download case
qqmyers May 25, 2023
2893b62
handle changes from #6919
qqmyers May 25, 2023
90186ed
fix query
qqmyers May 25, 2023
6bdbcb8
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jun 21, 2023
fd4d919
add gb fragment
qqmyers Jun 22, 2023
fc6a5e0
change render param - not clear this fragment stays here though
qqmyers Jun 22, 2023
1da94e8
add missing actionListener
qqmyers Jun 22, 2023
58bc6c9
missing actionListeners
qqmyers Jun 22, 2023
60c4db0
add missing params, change fileDownloadHelper to EJB
qqmyers Jun 22, 2023
1e8495c
use Inject and restore getter/setter
qqmyers Jun 22, 2023
1400e07
fix gb render conditions, initial fix for download buttons
qqmyers Jun 23, 2023
647a5d8
comment typo
qqmyers Jun 23, 2023
06b7cf1
avoid duplicate file counting and extra popup
qqmyers Jun 23, 2023
2b3a1cc
switch to guestbook-terms-popup-fragment
qqmyers Jun 23, 2023
51005ba
update fragment with license info and buttons for other views
qqmyers Jun 23, 2023
7e1036e
update grant access to set request state, add todos
qqmyers Jun 23, 2023
ce1ad60
only show newly CREATED requests for now
qqmyers Jun 24, 2023
6687e28
call correct script
qqmyers Jul 5, 2023
99f09fa
update method call to match changes in FileDownloadServiceBean
qqmyers Jul 5, 2023
66dccc9
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 5, 2023
ceeb13f
update flyway numbering
qqmyers Jul 5, 2023
f85d649
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 6, 2023
d5a19f2
add guestbookPopupAction from kebab for request access case
qqmyers Jul 6, 2023
91fafd7
add todo re: guestbook/no terms case
qqmyers Jul 6, 2023
fae00a7
Handle kebab menu, add resize to popup show request
qqmyers Jul 7, 2023
892d796
set state for grant and reject
qqmyers Jul 7, 2023
ef16a85
get requests by state/only get CREATED in perms page
qqmyers Jul 7, 2023
c07e960
only list unresolved (state=CREATED) access requests
qqmyers Jul 7, 2023
6016581
fix bug from #9257 which changed return type to FAR from DataFile
qqmyers Jul 7, 2023
0720c93
fix grant in popup from # files link in Files column
qqmyers Jul 7, 2023
267a5cc
only require one CREATED request per datafile, auth user
qqmyers Jul 7, 2023
4ba20d6
simplify states
qqmyers Jul 7, 2023
4e4cb28
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 7, 2023
e1e075c
typo in comment
qqmyers Jul 7, 2023
f980d7b
Fix idempotence
qqmyers Jul 7, 2023
6a0b0d0
ignore prior granted/rejected requests
qqmyers Jul 7, 2023
5cfcac1
make FileDownloadHelper visible in file.xhtml
qqmyers Jul 10, 2023
2e5b4fb
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 11, 2023
e87074a
add default setting
qqmyers Jul 11, 2023
f1b8174
comment typo
qqmyers Jul 11, 2023
8ead509
add setting to isGB popup req at download, remove mp cofig default
qqmyers Jul 11, 2023
5f76d81
fix logic, add to gb popup
qqmyers Jul 11, 2023
9e88d7d
initial attempt at per-collection setting
qqmyers Jul 12, 2023
8bbc36f
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 12, 2023
2d1c5c5
update flyway to add column guestbookatrequest
qqmyers Jul 12, 2023
39fefa0
change setting to match standard format
qqmyers Jul 12, 2023
1c201ad
minor fixes for collection-level option
qqmyers Jul 12, 2023
24490fa
update check to use effective value
qqmyers Jul 12, 2023
5db7a65
add api
qqmyers Jul 12, 2023
dc89f8f
add setting response when dataset has a value
qqmyers Jul 12, 2023
4405605
Don't allow setting this when the config setting is not set.
qqmyers Jul 12, 2023
5ce3eb3
report default/inherited setting as well.
qqmyers Jul 12, 2023
3cf323c
Missing help text
qqmyers Jul 12, 2023
8b91f2c
documentation/release note
qqmyers Jul 12, 2023
91e5a8e
Start making params indep of hostPage (dataset or file)
qqmyers Jul 17, 2023
033da23
use bean param for DatasetPage/FilePage
qqmyers Jul 18, 2023
0d29e43
remove unused legacy worldmap-related dialog
qqmyers Jul 18, 2023
f6caac7
Backward compat - only report open requests, fix err response code
qqmyers Jul 18, 2023
deb5a9f
don't submit empty gbr on access with gb at download
qqmyers Jul 18, 2023
5c79e55
remove debug
qqmyers Jul 18, 2023
a212aac
enhance request access email to owner
qqmyers Jul 18, 2023
e37547b
add response date to email gbr entry
qqmyers Jul 18, 2023
548cccf
remove unused method
qqmyers Jul 18, 2023
0f0f190
set creation date in constructors, refactor constructors
qqmyers Jul 18, 2023
4dff049
restore no-param constructor, cleanup
qqmyers Jul 18, 2023
9a45dcb
make sure arrays exist, don't recreate request, refactor
qqmyers Jul 19, 2023
8864bd7
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Jul 24, 2023
76a0dfd
blockDatasetForm doesn't exist in file page
qqmyers Jul 24, 2023
b7a81b1
fix multi-file request access
qqmyers Jul 24, 2023
7806030
fix dataset page download all buttons
qqmyers Jul 25, 2023
14c41ce
fix updating messages
qqmyers Jul 25, 2023
90fba4d
switch to formatted text
qqmyers Jul 25, 2023
adc106b
Add email/notification to requestor, add page messages for other routes
qqmyers Jul 25, 2023
e36ac4a
i18n gb text
qqmyers Jul 25, 2023
606b33b
i18n noResponse, change indenting
qqmyers Jul 25, 2023
953fb5b
don't show gb at download for isFileAccessRequest=false case and gbar
qqmyers Jul 25, 2023
2c5298e
cleanup
qqmyers Jul 25, 2023
ece78ca
add dataset name to title per request
qqmyers Jul 28, 2023
d4b28c1
session.getUser is guest when using requestAccess API
qqmyers Jul 28, 2023
1775be3
need to have request saved before sending notice
qqmyers Jul 28, 2023
3b2bde7
update test
qqmyers Jul 28, 2023
13ddb01
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Aug 1, 2023
1f145f8
enable html in emails
qqmyers Aug 2, 2023
fdc9bee
fix html message formatting
qqmyers Aug 2, 2023
af43198
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Aug 4, 2023
89cdb00
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Aug 7, 2023
369f1a8
Fix popup with no ToA
qqmyers Aug 8, 2023
e30429b
fix validation/error messages
qqmyers Aug 8, 2023
c97540f
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Sep 12, 2023
fa9d869
merge issues
qqmyers Sep 12, 2023
f6fe5dc
change gbar to Boolean to match flyway
qqmyers Sep 12, 2023
e313c7c
typo
qqmyers Sep 19, 2023
750069b
more String->Boolean changes
qqmyers Sep 19, 2023
35a5278
cleanup
qqmyers Sep 20, 2023
d06259f
Revert "more String->Boolean changes"
qqmyers Sep 20, 2023
3ad53d7
fix Dataverse UI ability to set GB at request param
qqmyers Sep 20, 2023
d910243
revert #5863
qqmyers Sep 20, 2023
4b5710d
fix revert, renames, add AccessRequest event type
qqmyers Sep 20, 2023
f848f00
use static final values for fixed event types
qqmyers Sep 21, 2023
cd82c2d
update queries, missed 'Subset' etc
qqmyers Sep 21, 2023
e506279
fix recursive calls
qqmyers Sep 21, 2023
bfce43e
update stored function, make update idempotent
qqmyers Sep 21, 2023
efa3d51
remove todo
qqmyers Sep 22, 2023
0350f99
add notes, reference issue/PR about the estimation method
qqmyers Sep 22, 2023
53a901c
update metrics api queries
qqmyers Sep 22, 2023
740f63b
fix query
qqmyers Sep 22, 2023
21c4a7f
downloadtype -> eventtype
qqmyers Sep 25, 2023
e557513
fix popup logic for previews, fix old bug
qqmyers Sep 25, 2023
ac45e1b
Merge remote-tracking branch 'IQSS/develop' into GDCC-GBAtRequest
qqmyers Sep 26, 2023
a887205
Fix merge issue
qqmyers Sep 26, 2023
6c7826f
Consolidate flyway scripts
qqmyers Sep 27, 2023
7881919
Update native-api.rst
qqmyers Sep 29, 2023
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
2 changes: 2 additions & 0 deletions doc/release-notes/9599-guestbook-at-request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Dataverse can now be configured (via the dataverse.files.guestbook-at-request option) to display any configured guestbook to users when they request restricted file(s) or when they download files (the historic default).
The global default defined by this setting can be overridden at the collection level on the collection page and at the individual dataset level by a superuser using the API. The default - showing guestbooks when files are downloaded - remains as it was in prior Dataverse versions.
45 changes: 45 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2230,6 +2230,51 @@ See :ref:`:CustomDatasetSummaryFields` in the Installation Guide for how the lis

curl "$SERVER_URL/api/datasets/summaryFieldNames"

.. _guestbook-at-request-api:

Configure When a Dataset Guestbook Appears (If Enabled)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default, users are asked to fill out a configured Guestbook when they down download files from a dataset. If enabled for a given Dataverse instance (see XYZ), users may instead be asked to fill out a Guestbook only when they request access to restricted files.
This is configured by a global default, collection-level settings, or directly at the dataset level via these API calls (superuser access is required to make changes).

To see the current choice for this dataset:

.. code-block:: bash

export SERVER_URL=https://demo.dataverse.org
export PERSISTENT_IDENTIFIER=doi:10.5072/FK2/YD5QDG

curl "$SERVER_URL/api/datasets/:persistentId/guestbookEntryAtRequest?persistentId=$PERSISTENT_IDENTIFIER"


The response will be true (guestbook displays when making a request), false (guestbook displays at download), or will indicate that the dataset inherits one of these settings.

To set the behavior for this dataset:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export PERSISTENT_IDENTIFIER=doi:10.5072/FK2/YD5QDG

curl -X PUT "-H:X-Dataverse-key:$API_TOKEN" -H Content-type:application/json -d true "$SERVER_URL/api/datasets/:persistentId/guestbookEntryAtRequest?persistentId=$PERSISTENT_IDENTIFIER"


This example uses true to set the behavior to guestbook at request. Note that this call will return a 403/Forbidden response if guestbook at request functionality is not enabled for this Dataverse instance.

The API can also be used to reset the dataset to use the default/inherited value:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export PERSISTENT_IDENTIFIER=doi:10.5072/FK2/YD5QDG

curl -X DELETE "-H:X-Dataverse-key:$API_TOKEN" -H Content-type:application/json "$SERVER_URL/api/datasets/:persistentId/guestbookEntryAtRequest?persistentId=$PERSISTENT_IDENTIFIER"



Files
-----

Expand Down
11 changes: 11 additions & 0 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2448,6 +2448,17 @@ This setting was added to keep S3 direct upload lightweight. When that feature i

See also :ref:`s3-direct-upload-features-disabled`.

.. _dataverse.files.guestbook-at-request:

dataverse.files.guestbook-at-request
++++++++++++++++++++++++++++++++++++

This setting enables functionality to allow guestbooks to be displayed when a user requests access to a restricted data file(s) or when a file is downloaded (the historic default). Providing a true/false value for this setting enables the functionality and provides a global default. The behavior can also be changed at the collection level via the user interface and by a superuser for a give dataset using the API.

See also :ref:`guestbook-at-request-api` in the API Guide, and .

Can also be set via *MicroProfile Config API* sources, e.g. the environment variable ``DATAVERSE_FILES_GUESTBOOK_AT_REQUEST``.

.. _feature-flags:

Feature Flags
Expand Down
2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/user/dataverse-management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Creating a Dataverse collection is easy but first you must be a registered user
* **Category**: Select a category that best describes the type of Dataverse collection this will be. For example, if this is a Dataverse collection for an individual researcher's datasets, select *Researcher*. If this is a Dataverse collection for an institution, select *Organization or Institution*.
* **Email**: This is the email address that will be used as the contact for this particular Dataverse collection. You can have more than one contact email address for your Dataverse collection.
* **Description**: Provide a description of this Dataverse collection. This will display on the landing page of your Dataverse collection and in the search result list. The description field supports certain HTML tags, if you'd like to format your text (<a>, <b>, <blockquote>, <br>, <code>, <del>, <dd>, <dl>, <dt>, <em>, <hr>, <h1>-<h3>, <i>, <img>, <kbd>, <li>, <ol>, <p>, <pre>, <s>, <sup>, <sub>, <strong>, <strike>, <u>, <ul>).
* **Dataset Metadata Langauge**: (If enabled) Select which language should be used when entering dataset metadata, or leave that choice to dataset creators.
* **Guestbook Entry Option**: (If enabled) Select whether guestbooks are displayed when a user requests access to restricted file(s) or when they initiate a download.
#. **Choose the sets of Metadata Fields for datasets in this Dataverse collection**:
* By default the metadata elements will be from the host Dataverse collection that this new Dataverse collection is created in.
* The Dataverse Software offers metadata standards for multiple domains. To learn more about the metadata standards in the Dataverse Software please check out the :doc:`/user/appendix`.
Expand Down
87 changes: 45 additions & 42 deletions src/main/java/edu/harvard/iq/dataverse/DataFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import edu.harvard.iq.dataverse.util.ShapefileHandler;
import edu.harvard.iq.dataverse.util.StringUtil;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Objects;
Expand All @@ -33,7 +32,7 @@
import jakarta.json.JsonArrayBuilder;
import jakarta.persistence.*;
import jakarta.validation.constraints.Pattern;
import org.hibernate.validator.constraints.NotBlank;
import jakarta.validation.constraints.NotBlank;

/**
*
Expand All @@ -53,9 +52,9 @@
})
@Entity
@Table(indexes = {@Index(columnList="ingeststatus")
, @Index(columnList="checksumvalue")
, @Index(columnList="contenttype")
, @Index(columnList="restricted")})
, @Index(columnList="checksumvalue")
, @Index(columnList="contenttype")
, @Index(columnList="restricted")})
public class DataFile extends DvObject implements Comparable {
private static final Logger logger = Logger.getLogger(DatasetPage.class.getCanonicalName());
private static final long serialVersionUID = 1L;
Expand All @@ -73,10 +72,6 @@ public class DataFile extends DvObject implements Comparable {
@Pattern(regexp = "^.*/.*$", message = "{contenttype.slash}")
private String contentType;

public void setFileAccessRequests(List<FileAccessRequest> fileAccessRequests) {
this.fileAccessRequests = fileAccessRequests;
}

// @Expose
// @SerializedName("storageIdentifier")
// @Column( nullable = false )
Expand Down Expand Up @@ -200,6 +195,28 @@ public String toString() {
@OneToMany(mappedBy="dataFile", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST})
private List<GuestbookResponse> guestbookResponses;

@OneToMany(mappedBy="dataFile",fetch = FetchType.LAZY,cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
private List<FileAccessRequest> fileAccessRequests;

@ManyToMany
@JoinTable(name = "fileaccessrequests",
joinColumns = @JoinColumn(name = "datafile_id"),
inverseJoinColumns = @JoinColumn(name = "authenticated_user_id"))
private List<AuthenticatedUser> fileAccessRequesters;


public List<FileAccessRequest> getFileAccessRequests(){
return fileAccessRequests;
}

public List<FileAccessRequest> getFileAccessRequests(FileAccessRequest.RequestState state){
return fileAccessRequests.stream().filter(far -> far.getState() == state).collect(Collectors.toList());
}

public void setFileAccessRequests(List<FileAccessRequest> fARs){
this.fileAccessRequests = fARs;
}

public List<GuestbookResponse> getGuestbookResponses() {
return guestbookResponses;
}
Expand Down Expand Up @@ -750,50 +767,38 @@ public String getUnf() {
return null;
}

@OneToMany(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}, orphanRemoval = true)
private List<FileAccessRequest> fileAccessRequests;
public List<AuthenticatedUser> getFileAccessRequesters() {
return fileAccessRequesters;
}

public List<FileAccessRequest> getFileAccessRequests() {
return fileAccessRequests;
public void setFileAccessRequesters(List<AuthenticatedUser> fileAccessRequesters) {
this.fileAccessRequesters = fileAccessRequesters;
}

public void addFileAccessRequester(AuthenticatedUser authenticatedUser) {

public void addFileAccessRequest(FileAccessRequest request) {
if (this.fileAccessRequests == null) {
this.fileAccessRequests = new ArrayList<>();
}

Set<AuthenticatedUser> existingUsers = this.fileAccessRequests.stream()
.map(FileAccessRequest::getAuthenticatedUser)
.collect(Collectors.toSet());
this.fileAccessRequests.add(request);
}

if (existingUsers.contains(authenticatedUser)) {
return;
public FileAccessRequest getAccessRequestForAssignee(RoleAssignee roleAssignee) {
if (this.fileAccessRequests == null) {
return null;
}

FileAccessRequest request = new FileAccessRequest();
request.setCreationTime(new Date());
request.setDataFile(this);
request.setAuthenticatedUser(authenticatedUser);

FileAccessRequest.FileAccessRequestKey key = new FileAccessRequest.FileAccessRequestKey();
key.setAuthenticatedUser(authenticatedUser.getId());
key.setDataFile(this.getId());

request.setId(key);

this.fileAccessRequests.add(request);
return this.fileAccessRequests.stream()
.filter(fileAccessRequest -> fileAccessRequest.getRequester().equals(roleAssignee) && fileAccessRequest.isStateCreated()).findFirst()
.orElse(null);
}

public boolean removeFileAccessRequester(RoleAssignee roleAssignee) {
public boolean removeFileAccessRequest(FileAccessRequest request) {
if (this.fileAccessRequests == null) {
return false;
}

FileAccessRequest request = this.fileAccessRequests.stream()
.filter(fileAccessRequest -> fileAccessRequest.getAuthenticatedUser().equals(roleAssignee))
.findFirst()
.orElse(null);

if (request != null) {
this.fileAccessRequests.remove(request);
return true;
Expand All @@ -802,13 +807,13 @@ public boolean removeFileAccessRequester(RoleAssignee roleAssignee) {
return false;
}

public boolean containsFileAccessRequestFromUser(RoleAssignee roleAssignee) {
public boolean containsActiveFileAccessRequestFromUser(RoleAssignee roleAssignee) {
if (this.fileAccessRequests == null) {
return false;
}

Set<AuthenticatedUser> existingUsers = this.fileAccessRequests.stream()
.map(FileAccessRequest::getAuthenticatedUser)
Set<AuthenticatedUser> existingUsers = getFileAccessRequests(FileAccessRequest.RequestState.CREATED).stream()
.map(FileAccessRequest::getRequester)
.collect(Collectors.toSet());

return existingUsers.contains(roleAssignee);
Expand Down Expand Up @@ -975,8 +980,6 @@ public String toJSON(){

public JsonObject asGsonObject(boolean prettyPrint){

String overarchingKey = "data";

GsonBuilder builder;
if (prettyPrint){ // Add pretty printing
builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting();
Expand Down
Loading
Loading