diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/ICitationManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/ICitationManager.java index ebd5edac7..594e180a5 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/ICitationManager.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/ICitationManager.java @@ -85,4 +85,8 @@ CloseableIterator getAllGroupItems(IUser user, String groupId, String void deleteLocalGroupCitations(String groupId); + void deleteFile(IUser user, String zoteroGroupId, String itemId, String documentId) + throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException, + ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException; + } \ No newline at end of file diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesConnector.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesConnector.java index f18291610..be97729f0 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesConnector.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesConnector.java @@ -1,5 +1,7 @@ package edu.asu.diging.citesphere.core.service.giles; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; @@ -7,9 +9,17 @@ public interface IGilesConnector { - ResponseEntity sendRequest(IUser user, String endpoint, Class returnType) + ResponseEntity sendRequest(IUser user, String endpoint, Class returnType, HttpMethod httpMethod) throws HttpClientErrorException; byte[] getFile(IUser user, String fileId); - -} \ No newline at end of file + + /** + * Deletes a document for the given user. + * @param user The user performing the document deletion. + * @param documentId The ID of the document to be deleted. + * @return The HTTP status code indicating the success or failure of the delete operation. + * @throws RestClientException if there is an issue sending the delete request to the server. + */ + HttpStatus deleteDocument(IUser user, String documentId); +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesDeletionChecker.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesDeletionChecker.java new file mode 100644 index 000000000..3d330d27e --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/IGilesDeletionChecker.java @@ -0,0 +1,16 @@ +package edu.asu.diging.citesphere.core.service.giles; + +import org.springframework.social.zotero.exception.ZoteroConnectionException; + +import edu.asu.diging.citesphere.core.exceptions.CitationIsOutdatedException; +import edu.asu.diging.citesphere.core.exceptions.ZoteroHttpStatusException; +import edu.asu.diging.citesphere.core.exceptions.ZoteroItemCreationFailedException; +import edu.asu.diging.citesphere.model.bib.ICitation; +import edu.asu.diging.citesphere.model.bib.IGilesUpload; +import edu.asu.diging.citesphere.user.IUser; + +public interface IGilesDeletionChecker { + void addDocumentCitationMap(IGilesUpload upload, ICitation citation, String zoteroId, IUser user); + + void checkDeletion() throws ZoteroConnectionException, CitationIsOutdatedException, ZoteroHttpStatusException, ZoteroItemCreationFailedException; +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesConnector.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesConnector.java index f2f99f420..223b8713e 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesConnector.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesConnector.java @@ -8,6 +8,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; @@ -29,6 +30,9 @@ public class GilesConnector implements IGilesConnector { @Value("${giles_file_endpoint}") private String fileEndpoint; + @Value("${giles_document_endpoint}") + private String documentEndpoint; + @Autowired private InternalTokenManager internalTokenManager; @@ -44,17 +48,15 @@ public void init() { * @see edu.asu.diging.citesphere.core.service.giles.impl.IGilesConnector#sendRequest(edu.asu.diging.citesphere.user.IUser, java.lang.String, java.lang.Class) */ @Override - public ResponseEntity sendRequest(IUser user, String endpoint,Class returnType) throws HttpClientErrorException { + public ResponseEntity sendRequest(IUser user, String endpoint,Class returnType, HttpMethod httpMethod) throws HttpClientErrorException { String token = internalTokenManager.getAccessToken(user).getValue(); - HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(token); HttpEntity> requestEntity = new HttpEntity<>( headers); - return restTemplate.exchange( gilesBaseurl + endpoint, - HttpMethod.GET, requestEntity, returnType); + httpMethod, requestEntity, returnType); } /* (non-Javadoc) @@ -62,7 +64,13 @@ public ResponseEntity sendRequest(IUser user, String endpoint,Class re */ @Override public byte[] getFile(IUser user, String fileId) { - ResponseEntity content = sendRequest(user, fileEndpoint.replace("{0}", fileId), byte[].class); + ResponseEntity content = sendRequest(user, fileEndpoint.replace("{0}", fileId), byte[].class, HttpMethod.GET); return content.getBody(); } + + @Override + public HttpStatus deleteDocument(IUser user, String documentId) { + ResponseEntity response = sendRequest(user, documentEndpoint.replace("{documentId}", documentId), String.class, HttpMethod.DELETE); + return response.getStatusCode(); + } } diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesDeletionChecker.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesDeletionChecker.java new file mode 100644 index 000000000..d4172d8c1 --- /dev/null +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/giles/impl/GilesDeletionChecker.java @@ -0,0 +1,91 @@ +package edu.asu.diging.citesphere.core.service.giles.impl; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.social.zotero.exception.ZoteroConnectionException; +import org.springframework.stereotype.Component; + +import edu.asu.diging.citesphere.core.exceptions.CitationIsOutdatedException; +import edu.asu.diging.citesphere.core.exceptions.ZoteroHttpStatusException; +import edu.asu.diging.citesphere.core.exceptions.ZoteroItemCreationFailedException; +import edu.asu.diging.citesphere.core.service.ICitationManager; +import edu.asu.diging.citesphere.core.service.giles.IGilesConnector; +import edu.asu.diging.citesphere.core.service.giles.IGilesDeletionChecker; +import edu.asu.diging.citesphere.core.user.IUserManager; +import edu.asu.diging.citesphere.model.bib.ICitation; +import edu.asu.diging.citesphere.model.bib.IGilesUpload; +import edu.asu.diging.citesphere.user.IUser; + +@Component +@PropertySource({ "classpath:config.properties", + "${appConfigFile:classpath:}/app.properties" }) +public class GilesDeletionChecker implements IGilesDeletionChecker { + + @Autowired + private IUserManager userManager; + + @Autowired + private ICitationManager citationManager; + + @Autowired + private IGilesConnector gilesConnector; + + @Value("${giles_document_deletion_check_endpoint}") + private String gilesDeletionCheckEndpoint; + + private Queue> deletionQueue; + + @PostConstruct + public void init() { + deletionQueue = new ConcurrentLinkedQueue>(); + } + + @Override + public void addDocumentCitationMap(IGilesUpload upload, ICitation citation, String zoteroId, IUser user) { + HashMap documentCitationMap = new HashMap(); + documentCitationMap.put("documentId", upload.getDocumentId()); + documentCitationMap.put("citationKey", citation.getKey()); + documentCitationMap.put("userName", user.getUsername()); + documentCitationMap.put("zoteroId", zoteroId); + if (!deletionQueue.contains(documentCitationMap)) { + deletionQueue.add(documentCitationMap); + } + } + + @Override + @Scheduled(fixedDelay = 60000) + public void checkDeletion() throws ZoteroConnectionException, CitationIsOutdatedException, ZoteroHttpStatusException, ZoteroItemCreationFailedException { + for (Map documentCitationMap: deletionQueue) { + String documentId = documentCitationMap.get("documentId"); + String citationKey = documentCitationMap.get("citationKey"); + String zoteroId = documentCitationMap.get("zoteroId"); + ICitation citation = citationManager.getCitation(citationKey); + IUser user = userManager.findByUsername(documentCitationMap.get("userName")); + ResponseEntity response = gilesConnector.sendRequest(user, gilesDeletionCheckEndpoint.replace("{documentId}", documentId), String.class, HttpMethod.GET); + if(response.getStatusCode().equals(HttpStatus.OK)) { + for (Iterator gileUpload = citation.getGilesUploads().iterator(); gileUpload.hasNext();) { + IGilesUpload g = gileUpload.next(); + if (g.getDocumentId() != null && g.getDocumentId().equals(documentId)) { + gileUpload.remove(); + } + } + citationManager.updateCitation(user, zoteroId, citation); + deletionQueue.remove(documentCitationMap); + } + } + } + +} diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/impl/CitationManager.java b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/impl/CitationManager.java index b0b83137b..d85053d6e 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/impl/CitationManager.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/core/service/impl/CitationManager.java @@ -3,6 +3,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -18,6 +19,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.data.util.CloseableIterator; +import org.springframework.http.HttpStatus; import org.springframework.social.zotero.api.ZoteroUpdateItemsStatuses; import org.springframework.social.zotero.exception.ZoteroConnectionException; import org.springframework.stereotype.Service; @@ -37,12 +39,15 @@ import edu.asu.diging.citesphere.core.service.ICitationManager; import edu.asu.diging.citesphere.core.service.ICitationStore; import edu.asu.diging.citesphere.core.service.IGroupManager; +import edu.asu.diging.citesphere.core.service.giles.IGilesConnector; +import edu.asu.diging.citesphere.core.service.giles.IGilesDeletionChecker; import edu.asu.diging.citesphere.core.zotero.IZoteroManager; import edu.asu.diging.citesphere.data.bib.CitationGroupRepository; import edu.asu.diging.citesphere.data.bib.ICitationDao; import edu.asu.diging.citesphere.model.bib.ICitation; import edu.asu.diging.citesphere.model.bib.ICitationCollection; import edu.asu.diging.citesphere.model.bib.ICitationGroup; +import edu.asu.diging.citesphere.model.bib.IGilesUpload; import edu.asu.diging.citesphere.model.bib.ItemType; import edu.asu.diging.citesphere.model.bib.impl.BibField; import edu.asu.diging.citesphere.model.bib.impl.CitationGroup; @@ -83,6 +88,12 @@ public class CitationManager implements ICitationManager { @Autowired private IAsyncCitationProcessor asyncCitationProcessor; + + @Autowired + private IGilesConnector gilesConnector; + + @Autowired + private IGilesDeletionChecker gilesDeletionChecker; private Map> sortFunctions; @@ -494,4 +505,20 @@ public CitationPage getPrevAndNextCitation(IUser user, String groupId, String co public void deleteLocalGroupCitations(String groupId) { citationStore.deleteCitationByGroupId(groupId); } + + @Override + public void deleteFile(IUser user, String zoteroGroupId, String itemId, String documentId) + throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException, + ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException { + ICitation citation = getCitation(user, zoteroGroupId, itemId); + for (Iterator gileUpload = citation.getGilesUploads().iterator(); gileUpload.hasNext();) { + IGilesUpload g = gileUpload.next(); + if (g.getDocumentId() != null && g.getDocumentId().equals(documentId)) { + HttpStatus deletionStatus = gilesConnector.deleteDocument(user, documentId); + if (deletionStatus.equals(HttpStatus.OK)) { + gilesDeletionChecker.addDocumentCitationMap(g, citation, zoteroGroupId, user); + } + } + } + } } diff --git a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/ItemController.java b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/ItemController.java index a2b6a692b..b83c56e79 100644 --- a/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/ItemController.java +++ b/citesphere/src/main/java/edu/asu/diging/citesphere/web/user/ItemController.java @@ -2,24 +2,33 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; +import org.springframework.social.zotero.exception.ZoteroConnectionException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import edu.asu.diging.citesphere.core.exceptions.CannotFindCitationException; +import edu.asu.diging.citesphere.core.exceptions.CitationIsOutdatedException; import edu.asu.diging.citesphere.core.exceptions.GroupDoesNotExistException; import edu.asu.diging.citesphere.core.exceptions.ZoteroHttpStatusException; +import edu.asu.diging.citesphere.core.exceptions.ZoteroItemCreationFailedException; import edu.asu.diging.citesphere.core.search.service.SearchEngine; import edu.asu.diging.citesphere.core.service.ICitationManager; import edu.asu.diging.citesphere.core.service.impl.CitationPage; +import edu.asu.diging.citesphere.core.service.impl.async.AsyncDeleteCitationsResponse; import edu.asu.diging.citesphere.model.bib.ICitation; import edu.asu.diging.citesphere.user.IUser; +import edu.asu.diging.citesphere.model.bib.IGilesUpload; @Controller public class ItemController { @@ -54,6 +63,7 @@ public String getItem(Authentication authentication, Model model, @PathVariable( List notes = citationManager.getNotes((IUser)authentication.getPrincipal(), zoteroGroupId, itemId); model.addAttribute("notes", notes); model.addAttribute("citation", citation); + model.addAttribute("itemId", itemId); List fields = new ArrayList<>(); citationManager.getItemTypeFields((IUser)authentication.getPrincipal(), citation.getItemType()).forEach(f -> fields.add(f.getFilename())); model.addAttribute("fields", fields); @@ -66,4 +76,15 @@ public String getItem(Authentication authentication, Model model, @PathVariable( } return "auth/group/item"; } + + @RequestMapping(value = "/auth/group/{zoteroGroupId}/file/delete", method = RequestMethod.POST) + public ResponseEntity deleteFile(Authentication authentication, + @PathVariable("zoteroGroupId") String zoteroGroupId, + @RequestParam(value = "documentId", required = false) String documentId, + @RequestParam(value = "itemId", required = false) String itemId) + throws GroupDoesNotExistException, CannotFindCitationException, ZoteroHttpStatusException, + ZoteroConnectionException, CitationIsOutdatedException, ZoteroItemCreationFailedException { + citationManager.deleteFile((IUser) authentication.getPrincipal(), zoteroGroupId, itemId, documentId); + return new ResponseEntity<>(HttpStatus.OK); + } } diff --git a/citesphere/src/main/resources/config.properties b/citesphere/src/main/resources/config.properties index 4ddd8c306..855071bba 100644 --- a/citesphere/src/main/resources/config.properties +++ b/citesphere/src/main/resources/config.properties @@ -88,5 +88,7 @@ elasticsearch.connect.timeout=${elasticsearch.connect.timeout} giles_upload_endpoint=/api/v2/files/upload giles_check_endpoint=/api/v2/files/upload/check/ giles_file_endpoint=/api/v2/resources/files/{0}/content +giles_document_endpoint=/api/v2/resources/documents/{documentId} +giles_document_deletion_check_endpoint=/api/v2/files/deletion/check/{documentId} javers_default_author=${javers.default.author} diff --git a/citesphere/src/main/webapp/WEB-INF/views/auth/group/item.html b/citesphere/src/main/webapp/WEB-INF/views/auth/group/item.html index 428bfe38b..50a62f1c2 100644 --- a/citesphere/src/main/webapp/WEB-INF/views/auth/group/item.html +++ b/citesphere/src/main/webapp/WEB-INF/views/auth/group/item.html @@ -1,5 +1,7 @@ + + @@ -360,10 +410,11 @@

Files

    -
  • +
  • [[${upload.uploadedFile?.filename}]] + File is being processed... ([[${upload.progressId}]]) @@ -479,6 +530,24 @@ + +