diff --git a/doc/release-notes/8092-timestamp-of-data-access-request.md b/doc/release-notes/8092-timestamp-of-data-access-request.md new file mode 100644 index 00000000000..506b78018a9 --- /dev/null +++ b/doc/release-notes/8092-timestamp-of-data-access-request.md @@ -0,0 +1,3 @@ +A date column has been added to the restricted file access request overview, indicating when the earliest request by that user was made. + +An issue was fixed where where the request list was not updated when a request was approved or rejected. diff --git a/src/main/java/edu/harvard/iq/dataverse/DataFile.java b/src/main/java/edu/harvard/iq/dataverse/DataFile.java index 372cb872c46..28d814d9844 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DataFile.java +++ b/src/main/java/edu/harvard/iq/dataverse/DataFile.java @@ -5,12 +5,11 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.annotations.Expose; -import com.google.gson.annotations.SerializedName; import edu.harvard.iq.dataverse.DatasetVersion.VersionState; +import edu.harvard.iq.dataverse.authorization.RoleAssignee; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; import edu.harvard.iq.dataverse.dataaccess.DataAccess; import edu.harvard.iq.dataverse.dataaccess.StorageIO; -import edu.harvard.iq.dataverse.dataset.DatasetThumbnail; import edu.harvard.iq.dataverse.datasetutility.FileSizeChecker; import edu.harvard.iq.dataverse.ingest.IngestReport; import edu.harvard.iq.dataverse.ingest.IngestRequest; @@ -19,17 +18,17 @@ 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; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.persistence.*; @@ -47,9 +46,9 @@ query = "SELECT o FROM DataFile o WHERE o.creator.id=:creatorId"), @NamedQuery(name = "DataFile.findByReleaseUserId", query = "SELECT o FROM DataFile o WHERE o.releaseUser.id=:releaseUserId"), - @NamedQuery(name="DataFile.findDataFileByIdProtocolAuth", + @NamedQuery(name="DataFile.findDataFileByIdProtocolAuth", query="SELECT s FROM DataFile s WHERE s.identifier=:identifier AND s.protocol=:protocol AND s.authority=:authority"), - @NamedQuery(name="DataFile.findDataFileThatReplacedId", + @NamedQuery(name="DataFile.findDataFileThatReplacedId", query="SELECT s.id FROM DataFile s WHERE s.previousDataFileId=:identifier") }) @Entity @@ -73,7 +72,10 @@ public class DataFile extends DvObject implements Comparable { @Column( nullable = false ) @Pattern(regexp = "^.*/.*$", message = "{contenttype.slash}") private String contentType; - + + public void setFileAccessRequests(List fileAccessRequests) { + this.fileAccessRequests = fileAccessRequests; + } // @Expose // @SerializedName("storageIdentifier") @@ -747,22 +749,71 @@ public String getUnf() { } return null; } - - @ManyToMany - @JoinTable(name = "fileaccessrequests", - joinColumns = @JoinColumn(name = "datafile_id"), - inverseJoinColumns = @JoinColumn(name = "authenticated_user_id")) - private List fileAccessRequesters; + @OneToMany(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}, orphanRemoval = true) + private List fileAccessRequests; - public List getFileAccessRequesters() { - return fileAccessRequesters; + public List getFileAccessRequests() { + return fileAccessRequests; } - public void setFileAccessRequesters(List fileAccessRequesters) { - this.fileAccessRequesters = fileAccessRequesters; + public void addFileAccessRequester(AuthenticatedUser authenticatedUser) { + if (this.fileAccessRequests == null) { + this.fileAccessRequests = new ArrayList<>(); + } + + Set existingUsers = this.fileAccessRequests.stream() + .map(FileAccessRequest::getAuthenticatedUser) + .collect(Collectors.toSet()); + + if (existingUsers.contains(authenticatedUser)) { + return; + } + + 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); } - + + public boolean removeFileAccessRequester(RoleAssignee roleAssignee) { + 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; + } + + return false; + } + + public boolean containsFileAccessRequestFromUser(RoleAssignee roleAssignee) { + if (this.fileAccessRequests == null) { + return false; + } + + Set existingUsers = this.fileAccessRequests.stream() + .map(FileAccessRequest::getAuthenticatedUser) + .collect(Collectors.toSet()); + + return existingUsers.contains(roleAssignee); + } + public boolean isHarvested() { Dataset ownerDataset = this.getOwner(); diff --git a/src/main/java/edu/harvard/iq/dataverse/DataFileServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/DataFileServiceBean.java index 865cc4c4593..196f84b6877 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DataFileServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/DataFileServiceBean.java @@ -799,14 +799,13 @@ public void findFileMetadataOptimizedExperimental(Dataset owner, DatasetVersion } if (dataFile.isRestricted() && accessRequestFileIds.contains(dataFile.getId())) { - dataFile.setFileAccessRequesters(Collections.singletonList(au)); - } + dataFile.addFileAccessRequester(au); + } dataFiles.add(dataFile); filesMap.put(dataFile.getId(), i++); } - fileResults = null; - + logger.fine("Retrieved and cached "+i+" datafiles."); i = 0; diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java index dc8e2e3c2fb..aa9a8d2691a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java @@ -5044,10 +5044,9 @@ public boolean isFileAccessRequestMultiButtonRequired(){ // return false; } for (FileMetadata fmd : workingVersion.getFileMetadatas()){ + AuthenticatedUser authenticatedUser = (AuthenticatedUser) session.getUser(); //Change here so that if all restricted files have pending requests there's no Request Button - if ((!this.fileDownloadHelper.canDownloadFile(fmd) && (fmd.getDataFile().getFileAccessRequesters() == null - || ( fmd.getDataFile().getFileAccessRequesters() != null - && !fmd.getDataFile().getFileAccessRequesters().contains((AuthenticatedUser)session.getUser()))))){ + if ((!this.fileDownloadHelper.canDownloadFile(fmd) && !fmd.getDataFile().containsFileAccessRequestFromUser(authenticatedUser))) { return true; } } diff --git a/src/main/java/edu/harvard/iq/dataverse/FileAccessRequest.java b/src/main/java/edu/harvard/iq/dataverse/FileAccessRequest.java new file mode 100644 index 00000000000..76c5df4409a --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/FileAccessRequest.java @@ -0,0 +1,91 @@ +package edu.harvard.iq.dataverse; + +import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.io.Serializable; +import java.util.Date; + +@Entity +@Table(name = "fileaccessrequests") +public class FileAccessRequest { + @EmbeddedId + private FileAccessRequestKey id; + @ManyToOne + @MapsId("dataFile") + @JoinColumn(name = "datafile_id") + private DataFile dataFile; + @ManyToOne + @MapsId("authenticatedUser") + @JoinColumn(name = "authenticated_user_id") + private AuthenticatedUser authenticatedUser; + + @Temporal(value = TemporalType.TIMESTAMP) + @Column(name = "creation_time") + private Date creationTime; + + public FileAccessRequestKey getId() { + return id; + } + + public void setId(FileAccessRequestKey id) { + this.id = id; + } + + public DataFile getDataFile() { + return dataFile; + } + + public void setDataFile(DataFile dataFile) { + this.dataFile = dataFile; + } + + public AuthenticatedUser getAuthenticatedUser() { + return authenticatedUser; + } + + public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) { + this.authenticatedUser = authenticatedUser; + } + + public Date getCreationTime() { + return creationTime; + } + + public void setCreationTime(Date creationTime) { + this.creationTime = creationTime; + } + + @Embeddable + public static class FileAccessRequestKey implements Serializable { + @Column(name = "datafile_id") + private Long dataFile; + @Column(name = "authenticated_user_id") + private Long authenticatedUser; + + public Long getDataFile() { + return dataFile; + } + + public void setDataFile(Long dataFile) { + this.dataFile = dataFile; + } + + public Long getAuthenticatedUser() { + return authenticatedUser; + } + + public void setAuthenticatedUser(Long authenticatedUser) { + this.authenticatedUser = authenticatedUser; + } + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/FileDownloadHelper.java b/src/main/java/edu/harvard/iq/dataverse/FileDownloadHelper.java index ef7ed1a2010..850efc2f1ae 100644 --- a/src/main/java/edu/harvard/iq/dataverse/FileDownloadHelper.java +++ b/src/main/java/edu/harvard/iq/dataverse/FileDownloadHelper.java @@ -324,13 +324,12 @@ public void requestAccessIndirect() { private boolean processRequestAccess(DataFile file, Boolean sendNotification) { if (fileDownloadService.requestAccess(file.getId())) { // update the local file object so that the page properly updates - if(file.getFileAccessRequesters() == null){ - file.setFileAccessRequesters(new ArrayList()); - } - file.getFileAccessRequesters().add((AuthenticatedUser) session.getUser()); + AuthenticatedUser user = (AuthenticatedUser) session.getUser(); + file.addFileAccessRequester(user); + // create notification if necessary if (sendNotification) { - fileDownloadService.sendRequestFileAccessNotification(file, (AuthenticatedUser) session.getUser()); + fileDownloadService.sendRequestFileAccessNotification(file, user); } JsfHelper.addSuccessMessage(BundleUtil.getStringFromBundle("file.accessRequested.success")); return true; diff --git a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java index d0e9b085acf..a90489be29a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java @@ -489,7 +489,7 @@ public boolean requestAccess(Long fileId) { return false; } DataFile file = datafileService.find(fileId); - if (!file.getFileAccessRequesters().contains((AuthenticatedUser)session.getUser())) { + if (!file.containsFileAccessRequestFromUser(session.getUser())) { try { commandEngine.submit(new RequestAccessCommand(dvRequestService.getDataverseRequest(), file)); return true; diff --git a/src/main/java/edu/harvard/iq/dataverse/ManageFilePermissionsPage.java b/src/main/java/edu/harvard/iq/dataverse/ManageFilePermissionsPage.java index 09f067f772c..fd309790026 100644 --- a/src/main/java/edu/harvard/iq/dataverse/ManageFilePermissionsPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/ManageFilePermissionsPage.java @@ -5,6 +5,7 @@ */ package edu.harvard.iq.dataverse; +import edu.harvard.iq.dataverse.api.Util; import edu.harvard.iq.dataverse.authorization.AuthenticationProvider; import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean; import edu.harvard.iq.dataverse.authorization.DataverseRole; @@ -20,6 +21,7 @@ import edu.harvard.iq.dataverse.engine.command.impl.AssignRoleCommand; import edu.harvard.iq.dataverse.engine.command.impl.RevokeRoleCommand; import edu.harvard.iq.dataverse.util.BundleUtil; +import edu.harvard.iq.dataverse.util.DateUtil; import edu.harvard.iq.dataverse.util.JsfHelper; import static edu.harvard.iq.dataverse.util.JsfHelper.JH; import java.sql.Timestamp; @@ -34,10 +36,7 @@ import javax.inject.Named; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import org.apache.commons.lang3.StringUtils; -import org.primefaces.event.SelectEvent; -import org.primefaces.event.ToggleSelectEvent; -import org.primefaces.event.UnselectEvent; +import org.apache.commons.lang3.ObjectUtils; /** * @@ -83,7 +82,12 @@ public class ManageFilePermissionsPage implements java.io.Serializable { Dataset dataset = new Dataset(); private final TreeMap> roleAssigneeMap = new TreeMap<>(); private final TreeMap> fileMap = new TreeMap<>(); - private final TreeMap> fileAccessRequestMap = new TreeMap<>(); + + public TreeMap> getFileAccessRequestMap() { + return fileAccessRequestMap; + } + + private final TreeMap> fileAccessRequestMap = new TreeMap<>(); private boolean showDeleted = true; public boolean isShowDeleted() { @@ -110,11 +114,6 @@ public TreeMap> getFileMap() { return fileMap; } - public TreeMap> getFileAccessRequestMap() { - return fileAccessRequestMap; - } - - private boolean backingShowDeleted = true; public void showDeletedCheckboxChange() { @@ -125,7 +124,7 @@ public void showDeletedCheckboxChange() { } } - + public String init() { if (dataset.getId() != null) { dataset = datasetService.find(dataset.getId()); @@ -142,17 +141,17 @@ public String init() { initMaps(); return ""; } - + private void initMaps() { // initialize files and usergroup list roleAssigneeMap.clear(); fileMap.clear(); - fileAccessRequestMap.clear(); - + fileAccessRequestMap.clear(); + for (DataFile file : dataset.getFiles()) { - + // only include if the file is restricted (or its draft version is restricted) - //Added a null check in case there are files that have no metadata records SEK + //Added a null check in case there are files that have no metadata records SEK //for 6587 make sure that a file is in the current version befor adding to the fileMap SEK 2/11/2020 if (file.getFileMetadata() != null && (file.isRestricted() || file.getFileMetadata().isRestricted())) { //only test if file is deleted if it's restricted @@ -169,35 +168,67 @@ private void initMaps() { for (RoleAssignment ra : ras) { // for files, only show role assignments which can download if (ra.getRole().permissions().contains(Permission.DownloadFile)) { - raList.add(new RoleAssignmentRow(ra, roleAssigneeService.getRoleAssignee(ra.getAssigneeIdentifier(), true).getDisplayInfo(), fileIsDeleted)); - addFileToRoleAssignee(ra, fileIsDeleted); + raList.add(new RoleAssignmentRow(ra, roleAssigneeService.getRoleAssignee(ra.getAssigneeIdentifier(), true).getDisplayInfo(), fileIsDeleted)); + addFileToRoleAssignee(ra, fileIsDeleted); } } - + file.setDeleted(fileIsDeleted); - + fileMap.put(file, raList); - + // populate the file access requests map - for (AuthenticatedUser au : file.getFileAccessRequesters()) { - List requestedFiles = fileAccessRequestMap.get(au); - if (requestedFiles == null) { - requestedFiles = new ArrayList<>(); - AuthenticatedUser withProvider = authenticationService.getAuthenticatedUserWithProvider(au.getUserIdentifier()); - fileAccessRequestMap.put(withProvider, requestedFiles); - } - requestedFiles.add(file); + for (FileAccessRequest fileAccessRequest : file.getFileAccessRequests()) { + List requestedFiles = fileAccessRequestMap.get(fileAccessRequest.getAuthenticatedUser()); + if (requestedFiles == null) { + requestedFiles = new ArrayList<>(); + AuthenticatedUser withProvider = authenticationService.getAuthenticatedUserWithProvider(fileAccessRequest.getAuthenticatedUser().getUserIdentifier()); + fileAccessRequestMap.put(withProvider, requestedFiles); + } + requestedFiles.add(fileAccessRequest); } - } + } } - } - + public String getAuthProviderFriendlyName(String authProviderId){ - return AuthenticationProvider.getFriendlyName(authProviderId); } - + + Date getAccessRequestDate(List fileAccessRequests){ + if (fileAccessRequests == null) { + return null; + } + + // find the oldest date in the list of available and return a formatted date, or null if no dates were found + return fileAccessRequests.stream() + .filter(fileAccessRequest -> fileAccessRequest.getCreationTime() != null) + .min((a, b) -> ObjectUtils.compare(a.getCreationTime(), b.getCreationTime(), true)) + .map(FileAccessRequest::getCreationTime) + .orElse(null); + } + + public String formatAccessRequestDate(List fileAccessRequests){ + Date date = getAccessRequestDate(fileAccessRequests); + + if (date == null) { + return null; + } + + return DateUtil.formatDate(date); + } + + + public String formatAccessRequestTimestamp(List fileAccessRequests){ + Date date = getAccessRequestDate(fileAccessRequests); + + if (date == null) { + return null; + } + + return Util.getDateTimeFormat().format(date); + } + private void addFileToRoleAssignee(RoleAssignment assignment, boolean fileDeleted) { RoleAssignee ra = roleAssigneeService.getRoleAssignee(assignment.getAssigneeIdentifier()); List assignments = roleAssigneeMap.get(ra); @@ -354,7 +385,10 @@ public void initAssignDialogForFileRequester(AuthenticatedUser au) { fileRequester = au; selectedRoleAssignees = null; selectedFiles.clear(); - selectedFiles.addAll(fileAccessRequestMap.get(au)); + + for (FileAccessRequest fileAccessRequest : fileAccessRequestMap.get(au)) { + selectedFiles.add(fileAccessRequest.getDataFile()); + } showUserGroupMessages(); } @@ -374,20 +408,19 @@ public void grantAccess(ActionEvent evt) { sendNotification = true; } // remove request, if it exist - if (file.getFileAccessRequesters().remove(roleAssignee)) { + if (file.removeFileAccessRequester(roleAssignee)) { datafileService.save(file); - } - } - + } + } } if (sendNotification) { for (AuthenticatedUser au : roleAssigneeService.getExplicitUsers(roleAssignee)) { - userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.GRANTFILEACCESS, dataset.getId()); + userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.GRANTFILEACCESS, dataset.getId()); } } } - + initMaps(); } @@ -396,23 +429,31 @@ public void grantAccessToRequests(AuthenticatedUser au) { } public void grantAccessToAllRequests(AuthenticatedUser au) { - grantAccessToRequests(au, fileAccessRequestMap.get(au)); - } + List files = new ArrayList<>(); + + for (FileAccessRequest fileAccessRequest : fileAccessRequestMap.get(au)) { + files.add(fileAccessRequest.getDataFile()); + } + + grantAccessToRequests(au, files); + } private void grantAccessToRequests(AuthenticatedUser au, List files) { boolean actionPerformed = false; // Find the built in file downloader role (currently by alias) DataverseRole fileDownloaderRole = roleService.findBuiltinRoleByAlias(DataverseRole.FILE_DOWNLOADER); for (DataFile file : files) { - if (assignRole(au, file, fileDownloaderRole)) { - file.getFileAccessRequesters().remove(au); - datafileService.save(file); + if (assignRole(au, file, fileDownloaderRole)) { + if (file.removeFileAccessRequester(au)) { + datafileService.save(file); + } actionPerformed = true; } } + if (actionPerformed) { JsfHelper.addSuccessMessage(BundleUtil.getStringFromBundle("permission.fileAccessGranted", Arrays.asList(au.getDisplayInfo().getTitle()))); - userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.GRANTFILEACCESS, dataset.getId()); + userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.GRANTFILEACCESS, dataset.getId()); initMaps(); } @@ -423,24 +464,29 @@ public void rejectAccessToRequests(AuthenticatedUser au) { } public void rejectAccessToAllRequests(AuthenticatedUser au) { - rejectAccessToRequests(au, fileAccessRequestMap.get(au)); - } + List files = new ArrayList<>(); + + for (FileAccessRequest fileAccessRequest : fileAccessRequestMap.get(au)) { + files.add(fileAccessRequest.getDataFile()); + } + + rejectAccessToRequests(au, files); + } private void rejectAccessToRequests(AuthenticatedUser au, List files) { - boolean actionPerformed = false; - for (DataFile file : files) { - file.getFileAccessRequesters().remove(au); + boolean actionPerformed = false; + for (DataFile file : files) { + file.removeFileAccessRequester(au); datafileService.save(file); actionPerformed = true; } - if (actionPerformed) { JsfHelper.addSuccessMessage(BundleUtil.getStringFromBundle("permission.fileAccessRejected", Arrays.asList(au.getDisplayInfo().getTitle()))); - userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.REJECTFILEACCESS, dataset.getId()); + userNotificationService.sendNotification(au, new Timestamp(new Date().getTime()), UserNotification.Type.REJECTFILEACCESS, dataset.getId()); initMaps(); } - } + } private boolean assignRole(RoleAssignee ra, DataFile file, DataverseRole r) { try { diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Access.java b/src/main/java/edu/harvard/iq/dataverse/api/Access.java index 1d7241de181..02441a9ee11 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Access.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Access.java @@ -10,6 +10,7 @@ import edu.harvard.iq.dataverse.AuxiliaryFileServiceBean; import edu.harvard.iq.dataverse.DataCitation; import edu.harvard.iq.dataverse.DataFile; +import edu.harvard.iq.dataverse.FileAccessRequest; import edu.harvard.iq.dataverse.FileMetadata; import edu.harvard.iq.dataverse.DataFileServiceBean; import edu.harvard.iq.dataverse.Dataset; @@ -1427,7 +1428,7 @@ public Response requestFileAccess(@Context ContainerRequestContext crc, @PathPar return error(BAD_REQUEST, BundleUtil.getStringFromBundle("access.api.requestAccess.failure.invalidRequest")); } - if (dataFile.getFileAccessRequesters().contains(requestor)) { + if (dataFile.containsFileAccessRequestFromUser(requestor)) { return error(BAD_REQUEST, BundleUtil.getStringFromBundle("access.api.requestAccess.failure.requestExists")); } @@ -1478,17 +1479,17 @@ public Response listFileAccessRequests(@Context ContainerRequestContext crc, @Pa return error(FORBIDDEN, BundleUtil.getStringFromBundle("access.api.rejectAccess.failure.noPermissions")); } - List requesters = dataFile.getFileAccessRequesters(); + List requests = dataFile.getFileAccessRequests(); - if (requesters == null || requesters.isEmpty()) { + if (requests == null || requests.isEmpty()) { List args = Arrays.asList(dataFile.getDisplayName()); return error(BAD_REQUEST, BundleUtil.getStringFromBundle("access.api.requestList.noRequestsFound", args)); } JsonArrayBuilder userArray = Json.createArrayBuilder(); - for (AuthenticatedUser au : requesters) { - userArray.add(json(au)); + for (FileAccessRequest fileAccessRequest : requests) { + userArray.add(json(fileAccessRequest.getAuthenticatedUser())); } return ok(userArray); @@ -1534,7 +1535,7 @@ public Response grantFileAccess(@Context ContainerRequestContext crc, @PathParam try { engineSvc.submit(new AssignRoleCommand(ra, fileDownloaderRole, dataFile, dataverseRequest, null)); - if (dataFile.getFileAccessRequesters().remove(ra)) { + if (dataFile.removeFileAccessRequester(ra)) { dataFileService.save(dataFile); } @@ -1660,8 +1661,7 @@ public Response rejectFileAccess(@Context ContainerRequestContext crc, @PathPara return error(BAD_REQUEST, BundleUtil.getStringFromBundle("access.api.rejectAccess.failure.noPermissions")); } - if (dataFile.getFileAccessRequesters().contains(ra)) { - dataFile.getFileAccessRequesters().remove(ra); + if (dataFile.removeFileAccessRequester(ra)) { dataFileService.save(dataFile); try { diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/RequestAccessCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/RequestAccessCommand.java index 2fc3a5c525e..b87b9a73aa5 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/RequestAccessCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/RequestAccessCommand.java @@ -59,7 +59,7 @@ public DataFile execute(CommandContext ctxt) throws CommandException { if(FileUtil.isActivelyEmbargoed(file)) { throw new CommandException(BundleUtil.getStringFromBundle("file.requestAccess.notAllowed.embargoed"), this); } - file.getFileAccessRequesters().add(requester); + file.addFileAccessRequester(requester); if (sendNotification) { ctxt.fileDownload().sendRequestFileAccessNotification(this.file, requester); } diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 45807dc7cde..ef753f62caa 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -1097,6 +1097,8 @@ dataverse.permissionsFiles.usersOrGroups.tabHeader.id=ID dataverse.permissionsFiles.usersOrGroups.tabHeader.email=Email dataverse.permissionsFiles.usersOrGroups.tabHeader.authentication=Authentication dataverse.permissionsFiles.usersOrGroups.tabHeader.files=Files +dataverse.permissionsFiles.usersOrGroups.tabHeader.accessRequestDate=Date +dataverse.permissionsFiles.usersOrGroups.tabHeader.accessRequestDateNotAvailable=N/A dataverse.permissionsFiles.usersOrGroups.tabHeader.access=Access dataverse.permissionsFiles.usersOrGroups.file=File dataverse.permissionsFiles.usersOrGroups.files=Files diff --git a/src/main/resources/db/migration/V5.13.0.4__8092-timestamp-of-data-access-request.sql b/src/main/resources/db/migration/V5.13.0.4__8092-timestamp-of-data-access-request.sql new file mode 100644 index 00000000000..603b69bf47f --- /dev/null +++ b/src/main/resources/db/migration/V5.13.0.4__8092-timestamp-of-data-access-request.sql @@ -0,0 +1,24 @@ +-- remove multiple File access requests which are +-- in violation of the new key +CREATE TABLE tmp_fileaccessrequests (datafile_id bigint NOT NULL, authenticated_user_id INT); + +insert into tmp_fileaccessrequests +select distinct datafile_id, authenticated_user_id from fileaccessrequests; + +DROP TABLE fileaccessrequests; + +ALTER TABLE tmp_fileaccessrequests + RENAME TO fileaccessrequests; + +ALTER TABLE fileaccessrequests +ADD COLUMN IF NOT EXISTS creation_time TIMESTAMP WITHOUT TIME ZONE DEFAULT now(); + +ALTER TABLE fileaccessrequests ADD CONSTRAINT fileaccessrequests_pkey PRIMARY KEY (authenticated_user_id, datafile_id); +ALTER TABLE fileaccessrequests ADD CONSTRAINT fk_fileaccessrequests_authenticated_user_id FOREIGN KEY (authenticated_user_id) + REFERENCES public.authenticateduser (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION; +ALTER TABLE fileaccessrequests ADD CONSTRAINT fk_fileaccessrequests_datafile_id FOREIGN KEY (datafile_id) + REFERENCES public.dvobject (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION diff --git a/src/main/webapp/file-download-button-fragment.xhtml b/src/main/webapp/file-download-button-fragment.xhtml index ac1ec525b44..597d9a12786 100644 --- a/src/main/webapp/file-download-button-fragment.xhtml +++ b/src/main/webapp/file-download-button-fragment.xhtml @@ -24,14 +24,14 @@ - - - #{fileMetadata.dataFile.fileAccessRequesters.contains(dataverseSession.user) ? bundle['file.accessRequested'] : bundle['file.requestAccess']} + disabled="#{fileMetadata.dataFile.containsFileAccessRequestFromUser(dataverseSession.user)}"> + #{fileMetadata.dataFile.containsFileAccessRequestFromUser(dataverseSession.user) ? bundle['file.accessRequested'] : bundle['file.requestAccess']}
  • diff --git a/src/main/webapp/filesFragment.xhtml b/src/main/webapp/filesFragment.xhtml index ca696e67187..27b3cca0c2a 100644 --- a/src/main/webapp/filesFragment.xhtml +++ b/src/main/webapp/filesFragment.xhtml @@ -479,7 +479,7 @@ data-content="#{DatasetPage.ingestMessage} #{fileMetadata.dataFile.ingestReportMessage}"/> -
    +
    #{bundle['file.accessRequested']}  diff --git a/src/main/webapp/permissions-manage-files.xhtml b/src/main/webapp/permissions-manage-files.xhtml index 122ee2b616f..d3109da69a6 100644 --- a/src/main/webapp/permissions-manage-files.xhtml +++ b/src/main/webapp/permissions-manage-files.xhtml @@ -30,7 +30,7 @@
    #{bundle['dataverse.permissionsFiles.usersOrGroups']} - + #{bundle['dataverse.permissionsFiles.usersOrGroups.description']}
    @@ -58,9 +58,9 @@

    #{manageFilePermissionsPage.fileAccessRequestMap.size()} #{bundle['dataverse.permissions.requests']} -

    +

    - + @@ -68,7 +68,7 @@ - + @@ -81,20 +81,29 @@ - + + + + +
    + update=":#{p:resolveClientId('rolesPermissionsForm:userGroups', view)} + :#{p:resolveClientId('rolesPermissionsForm:restrictedFiles', view)} + #{p:resolveClientId('rolesPermissionsForm:usersGroups', view)} + @([id$=Messages])"> #{bundle['dataverse.permissionsFiles.assignDialog.grantBtn']} + update=":#{p:resolveClientId('rolesPermissionsForm:userGroups', view)} + :#{p:resolveClientId('rolesPermissionsForm:restrictedFiles', view)} + #{p:resolveClientId('rolesPermissionsForm:usersGroups', view)} + @([id$=Messages])"> #{bundle['dataverse.permissionsFiles.assignDialog.rejectBtn']}
    @@ -142,7 +151,7 @@
    #{bundle['dataverse.permissionsFiles.files']} - + #{bundle['dataverse.permissionsFiles.files.description']}
    @@ -162,7 +171,7 @@ -

    +

    @@ -204,7 +213,7 @@ oncomplete="PF('assignWidget').show();handleResizeDialog('assignDialog');"> #{bundle['dataverse.permissionsFiles.files.assignBtn']} - +
    @@ -221,21 +230,21 @@

    - + - + - + - + @@ -270,17 +279,17 @@
    - + @@ -298,11 +307,11 @@

    - -