forked from DSpace/DSpace
-
Notifications
You must be signed in to change notification settings - Fork 2
UFAL/share submission by email #780
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
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
57b7da2
Updated table workspace with share token, created endpoint to generat…
milanmajchrak afa496b
Added method to get workspaceitem via share token.
milanmajchrak 667738c
Added an endpoint for changing the submission's owner.
milanmajchrak c26cd0d
Fixed checkstyle issues
milanmajchrak 3e8a9c7
Added license headers
milanmajchrak 1414b71
Added test for fetching item with share Token
milanmajchrak df7b9fb
Added tests to check the owner is changed
milanmajchrak e74a2d4
Added better explanation why the BE must return Page object in the se…
milanmajchrak bbe0d95
Validate the user in the SubmissionController, it cannot be null
milanmajchrak f452e23
Updated email - some values are fetched directly from the configurati…
milanmajchrak a2149e6
Updated preAuthorization method to ADD instead of WRITE (write is use…
milanmajchrak 36f531f
Authorize the submitter which is trying to take sharing item via shar…
milanmajchrak e280948
Update integration test following the feature update
milanmajchrak ea95f6e
Fixed checkstyle issues
milanmajchrak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...space/storage/rdbms/sqlmigration/h2/V7.6_2024.09.30__Add_share_token_to_workspaceitem.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| -- | ||
| -- The contents of this file are subject to the license and copyright | ||
| -- detailed in the LICENSE and NOTICE files at the root of the source | ||
| -- tree and available online at | ||
| -- | ||
| -- http://www.dspace.org/license/ | ||
| -- | ||
|
|
||
| ALTER TABLE workspaceitem ADD share_token varchar(32); |
9 changes: 9 additions & 0 deletions
9
...storage/rdbms/sqlmigration/postgres/V7.6_2024.09.30__Add_share_token_to_workspaceitem.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| -- | ||
| -- The contents of this file are subject to the license and copyright | ||
| -- detailed in the LICENSE and NOTICE files at the root of the source | ||
| -- tree and available online at | ||
| -- | ||
| -- http://www.dspace.org/license/ | ||
| -- | ||
|
|
||
| ALTER TABLE workspaceitem ADD share_token varchar(32); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
dspace-server-webapp/src/main/java/org/dspace/app/rest/model/ShareSubmissionLinkDTO.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /** | ||
| * The contents of this file are subject to the license and copyright | ||
| * detailed in the LICENSE and NOTICE files at the root of the source | ||
| * tree and available online at | ||
| * | ||
| * http://www.dspace.org/license/ | ||
| */ | ||
| package org.dspace.app.rest.model; | ||
|
|
||
| /** | ||
| * This class represents a DTO that will be used to share a submission link. It will be used to return the share link | ||
| * to the user in the UI. | ||
| * | ||
| * @author Milan Majchrak (dspace at dataquest.sk) | ||
| */ | ||
| public class ShareSubmissionLinkDTO { | ||
|
|
||
| private String shareLink; | ||
|
|
||
| public ShareSubmissionLinkDTO() { } | ||
|
|
||
| public String getShareLink() { | ||
| return shareLink; | ||
| } | ||
|
|
||
| public void setShareLink(String shareLink) { | ||
| this.shareLink = shareLink; | ||
| } | ||
| } |
223 changes: 223 additions & 0 deletions
223
dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| /** | ||
| * The contents of this file are subject to the license and copyright | ||
| * detailed in the LICENSE and NOTICE files at the root of the source | ||
| * tree and available online at | ||
| * | ||
| * http://www.dspace.org/license/ | ||
| */ | ||
| package org.dspace.app.rest.repository; | ||
|
|
||
| import java.io.IOException; | ||
| import java.sql.SQLException; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
| import java.util.UUID; | ||
| import javax.mail.MessagingException; | ||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.ws.rs.BadRequestException; | ||
|
|
||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.apache.logging.log4j.Logger; | ||
| import org.dspace.app.rest.converter.ConverterService; | ||
| import org.dspace.app.rest.model.RestAddressableModel; | ||
| import org.dspace.app.rest.model.ShareSubmissionLinkDTO; | ||
| import org.dspace.app.rest.model.WorkspaceItemRest; | ||
| import org.dspace.app.rest.utils.Utils; | ||
| import org.dspace.authorize.AuthorizeException; | ||
| import org.dspace.authorize.service.AuthorizeService; | ||
| import org.dspace.content.WorkspaceItem; | ||
| import org.dspace.content.service.WorkspaceItemService; | ||
| import org.dspace.core.Constants; | ||
| import org.dspace.core.Context; | ||
| import org.dspace.core.Email; | ||
| import org.dspace.core.I18nUtil; | ||
| import org.dspace.eperson.EPerson; | ||
| import org.dspace.services.ConfigurationService; | ||
| import org.dspace.web.ContextUtil; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.context.annotation.Lazy; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.AccessDeniedException; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.util.CollectionUtils; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestMethod; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| /** | ||
| * This class' purpose is to provide an API for sharing an in-progress submission. It allows the user to generate | ||
| * a share link for a workspace item and to set the owner of the workspace item to the current user. | ||
| * | ||
| * @author Milan Majchrak (dspace at dataquest.sk) | ||
| */ | ||
| @RestController | ||
| @RequestMapping("/api/" + RestAddressableModel.SUBMISSION) | ||
| public class SubmissionController { | ||
| private static Logger log = org.apache.logging.log4j.LogManager.getLogger(SubmissionController.class); | ||
|
|
||
| @Autowired | ||
| WorkspaceItemService workspaceItemService; | ||
|
|
||
| @Autowired | ||
| ConfigurationService configurationService; | ||
|
|
||
| @Autowired | ||
| protected Utils utils; | ||
|
|
||
| @Autowired | ||
| AuthorizeService authorizeService; | ||
|
|
||
| @Lazy | ||
| @Autowired | ||
| protected ConverterService converter; | ||
|
|
||
| @PreAuthorize("hasPermission(#wsoId, 'WORKSPACEITEM', 'WRITE')") | ||
| @RequestMapping(method = RequestMethod.GET, value = "share") | ||
| public ResponseEntity<ShareSubmissionLinkDTO> generateShareLink(@RequestParam(name = "workspaceitemid") | ||
| Integer wsoId, HttpServletRequest request) | ||
| throws SQLException, AuthorizeException { | ||
|
|
||
| Context context = ContextUtil.obtainContext(request); | ||
| // Check the context is not null | ||
| this.validateContext(context); | ||
|
|
||
| // Get workspace item from ID | ||
| WorkspaceItem wsi = workspaceItemService.find(context, wsoId); | ||
| // Check the wsi does exist | ||
| validateWorkspaceItem(wsi, wsoId, null); | ||
|
|
||
| // Generate a share link | ||
| String shareToken = generateShareToken(); | ||
|
|
||
| // Update workspace item with share link | ||
| wsi.setShareToken(shareToken); | ||
| workspaceItemService.update(context, wsi); | ||
| // Without commit the changes are not persisted into the database | ||
| context.commit(); | ||
|
|
||
| // Get submitter email | ||
| EPerson currentUser = context.getCurrentUser(); | ||
| if (currentUser == null) { | ||
| String errorMessage = "The current user is not valid, it cannot be null."; | ||
| log.error(errorMessage); | ||
| throw new BadRequestException(errorMessage); | ||
| } | ||
|
|
||
| // Send email to submitter with share link | ||
| String shareLink = sendShareLinkEmail(context, wsi, currentUser); | ||
| if (StringUtils.isEmpty(shareLink)) { | ||
| String errorMessage = "The share link is empty."; | ||
| log.error(errorMessage); | ||
| throw new RuntimeException(errorMessage); | ||
| } | ||
|
|
||
| // Create a DTO with the share link for better processing in the FE | ||
| ShareSubmissionLinkDTO shareSubmissionLinkDTO = new ShareSubmissionLinkDTO(); | ||
| shareSubmissionLinkDTO.setShareLink(shareLink); | ||
|
|
||
| // Send share link in response | ||
| return ResponseEntity.ok().body(shareSubmissionLinkDTO); | ||
| } | ||
|
|
||
| @PreAuthorize("hasPermission(#wsoId, 'WORKSPACEITEM', 'WRITE')") | ||
| @RequestMapping(method = RequestMethod.GET, value = "setOwner") | ||
| public WorkspaceItemRest setOwner(@RequestParam(name = "shareToken") String shareToken, | ||
| @RequestParam(name = "workspaceitemid") Integer wsoId, | ||
| HttpServletRequest request) | ||
| throws SQLException, AuthorizeException { | ||
|
|
||
| Context context = ContextUtil.obtainContext(request); | ||
| // Check the context is not null | ||
| this.validateContext(context); | ||
|
|
||
| // Get workspace by share token | ||
| List<WorkspaceItem> wsiList = workspaceItemService.findByShareToken(context, shareToken); | ||
| // Check the wsi does exist | ||
| if (CollectionUtils.isEmpty(wsiList)) { | ||
| String errorMessage = "The workspace item with share token:" + shareToken + " does not exist."; | ||
| log.error(errorMessage); | ||
| throw new BadRequestException(errorMessage); | ||
| } | ||
|
|
||
| // Get the first workspace item - the only one | ||
| WorkspaceItem wsi = wsiList.get(0); | ||
| // Check the wsi does exist | ||
| validateWorkspaceItem(wsi, null, shareToken); | ||
|
|
||
| if (!authorizeService.authorizeActionBoolean(context, wsi.getItem(), Constants.READ)) { | ||
| String errorMessage = "The current user does not have rights to view the WorkflowItem"; | ||
| log.error(errorMessage); | ||
| throw new AccessDeniedException(errorMessage); | ||
| } | ||
|
|
||
| // Set the owner of the workspace item to the current user | ||
| EPerson currentUser = context.getCurrentUser(); | ||
| // If the current user is null, throw an exception | ||
| if (currentUser == null) { | ||
| String errorMessage = "The current user is not valid, it cannot be null."; | ||
| log.error(errorMessage); | ||
| throw new BadRequestException(errorMessage); | ||
| } | ||
|
|
||
| wsi.getItem().setSubmitter(currentUser); | ||
| workspaceItemService.update(context, wsi); | ||
| WorkspaceItemRest wsiRest = converter.toRest(wsi, utils.obtainProjection()); | ||
|
|
||
| // Without commit the changes are not persisted into the database | ||
| context.commit(); | ||
| return wsiRest; | ||
| } | ||
|
|
||
| private static String generateShareToken() { | ||
| // UUID generates a 36-char string with hyphens, so we can strip them to get a 32-char string | ||
| return UUID.randomUUID().toString().replace("-", "").substring(0, 32); | ||
vidiecan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private String sendShareLinkEmail(Context context, WorkspaceItem wsi, EPerson currentUser) { | ||
| // Get the UI URL from the configuration | ||
| String uiUrl = configurationService.getProperty("dspace.ui.url"); | ||
| // Get submitter email | ||
| String email = currentUser.getEmail(); | ||
| // Compose the url with the share token. The user will be redirected to the UI. | ||
| String shareTokenUrl = uiUrl + "/share-submission/change-submitter?share_token=" + wsi.getShareToken() + | ||
| "&workspaceitemid=" + wsi.getID(); | ||
| try { | ||
| Locale locale = context.getCurrentLocale(); | ||
| Email bean = Email.getEmail(I18nUtil.getEmailFilename(locale, "share_submission")); | ||
| bean.addArgument(shareTokenUrl); | ||
| bean.addRecipient(email); | ||
| bean.send(); | ||
| } catch (MessagingException | IOException e) { | ||
| String errorMessage = "Unable send the email because: " + e.getMessage(); | ||
| log.error(errorMessage); | ||
| throw new RuntimeException(errorMessage); | ||
| } | ||
| return shareTokenUrl; | ||
| } | ||
|
|
||
| /** | ||
| * Check if the context is valid - not null. If not, throw an exception. | ||
| */ | ||
| private void validateContext(Context context) { | ||
| if (context == null) { | ||
| String errorMessage = "The current context is not valid, it cannot be null."; | ||
| log.error(errorMessage); | ||
| throw new RuntimeException(errorMessage); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check if the workspace item is valid - not null. If not, throw an exception. The workspace item can be found by | ||
| * ID or the share token. | ||
| */ | ||
| private void validateWorkspaceItem(WorkspaceItem wsi, Integer wsoId, String shareToken) { | ||
| if (wsi == null) { | ||
| String identifier = wsoId != null ? wsoId.toString() : shareToken; | ||
| String identifierName = wsoId != null ? "ID" : "share token"; | ||
| String errorMessage = "The workspace item with " + identifierName + ":" + identifier + " does not exist."; | ||
| log.error(errorMessage); | ||
| throw new BadRequestException(errorMessage); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.