diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDao.java new file mode 100644 index 000000000..868a58e71 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDao.java @@ -0,0 +1,9 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccessLayer; +import eu.eudat.data.entities.FileUpload; + +import java.util.UUID; + +public interface FileUploadDao extends DatabaseAccessLayer { +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDaoImpl.java new file mode 100644 index 000000000..d50780bc5 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FileUploadDaoImpl.java @@ -0,0 +1,50 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccess; +import eu.eudat.data.dao.databaselayer.service.DatabaseService; +import eu.eudat.data.entities.FileUpload; +import eu.eudat.queryable.QueryableList; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +@Component("FileUploadDao") +public class FileUploadDaoImpl extends DatabaseAccess implements FileUploadDao { + + @Autowired + public FileUploadDaoImpl(DatabaseService databaseService) { + super(databaseService); + } + + @Override + public FileUpload createOrUpdate(FileUpload item) { + return getDatabaseService().createOrUpdate(item, FileUpload.class); + } +// + @Override + public CompletableFuture createOrUpdateAsync(FileUpload item) { + return CompletableFuture.supplyAsync(() -> this.createOrUpdate(item)); + } + + @Override + public FileUpload find(UUID id) { + return getDatabaseService().getQueryable(FileUpload.class).where((builder, root) -> builder.equal(root.get("id"), id)).getSingle(); + } + + @Override + public FileUpload find(UUID id, String hint) { + return null; + } + + @Override + public void delete(FileUpload item) { + this.getDatabaseService().delete(item); + } + + @Override + public QueryableList asQueryable() { + return this.getDatabaseService().getQueryable(FileUpload.class); + } +} \ No newline at end of file diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/FileUpload.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/FileUpload.java new file mode 100644 index 000000000..394bebfe5 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/FileUpload.java @@ -0,0 +1,129 @@ +package eu.eudat.data.entities; + +import eu.eudat.data.converters.DateToUTCConverter; +import eu.eudat.data.entities.helpers.EntityBinder; +import eu.eudat.queryable.queryableentity.DataEntity; +import org.hibernate.annotations.Type; + +import javax.persistence.*; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Entity +@Table(name = "\"FileUpload\"") +public class FileUpload implements DataEntity { + public enum EntityType { + DATASET, DMP + } + + @Id + @Column(name = "\"ID\"", updatable = false, nullable = false, columnDefinition = "BINARY(16)") + private UUID id; + + @Column(name = "\"Name\"", nullable = false) + private String name; + + @Column(name = "\"FileType\"", nullable = false) + private String fileType; + + @Column(name = "\"EntityId\"", nullable = false) + private UUID entityId; + + @Enumerated(EnumType.STRING) + @Type(type = "eu.eudat.configurations.typedefinition.PostgreSQLEnumType") + @Column(name = "\"EntityType\"", nullable = false) + private EntityType entityType; + + @Column(name = "\"CreatedAt\"", nullable = false) + @Convert(converter = DateToUTCConverter.class) + private Date createdAt; + + @Column(name = "\"IsDeleted\"", nullable = false) + private Boolean isDeleted; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "\"Creator\"") + private UserInfo creator; + + public UUID getId() { + return id; + } + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getFileType() { + return fileType; + } + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public UUID getEntityId() { + return entityId; + } + public void setEntityId(UUID entityId) { + this.entityId = entityId; + } + + public EntityType getEntityType() { + return entityType; + } + public void setEntityType(EntityType entityType) { + this.entityType = entityType; + } + + public Date getCreatedAt() { + return createdAt; + } + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public Boolean getIsDeleted() { + return isDeleted; + } + public void setIsDeleted(Boolean isDeleted) { + this.isDeleted = isDeleted; + } + + public UserInfo getCreator() { + return creator; + } + public void setCreator(UserInfo creator) { + this.creator = creator; + } + + @Override + public void update(FileUpload file) { + this.name = file.getName(); + this.fileType = file.getFileType(); + this.entityId = file.getEntityId(); + this.entityType = file.getEntityType(); + this.createdAt = file.getCreatedAt(); + this.isDeleted = file.getIsDeleted(); + this.creator = file.getCreator(); + } + + @Override + public UUID getKeys() { + return this.id; + } + + @Override + public FileUpload buildFromTuple(List tuple, List fields, String base) { + String currentBase = base.isEmpty() ? "" : base + "."; + if (fields.contains(currentBase + "id")) this.id = EntityBinder.fromTuple(tuple, currentBase + "id"); + this.creator = tuple.stream().map(x -> new UserInfo().buildFromTuple(tuple, fields , base.isEmpty() ? "creator" : base + "." + "creator")).collect(Collectors.toList()).get(0); + return this; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/configurations/typedefinition/PostgreSQLEnumType.java b/dmp-backend/web/src/main/java/eu/eudat/configurations/typedefinition/PostgreSQLEnumType.java new file mode 100644 index 000000000..26f6ebcc2 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/configurations/typedefinition/PostgreSQLEnumType.java @@ -0,0 +1,26 @@ +package eu.eudat.configurations.typedefinition; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + +public class PostgreSQLEnumType extends org.hibernate.type.EnumType { + + public void nullSafeSet( + PreparedStatement st, + Object value, + int index, + SharedSessionContractImplementor session) + throws HibernateException, SQLException { + st.setObject( + index, + value != null ? + ((Enum) value).name() : + null, + Types.OTHER + ); + } +} \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java index 68a3e32c8..c788adea7 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java @@ -10,6 +10,7 @@ import eu.eudat.exceptions.datasetwizard.DatasetWizardCannotUnlockException; import eu.eudat.exceptions.security.UnauthorisedException; import eu.eudat.logic.managers.DatasetManager; import eu.eudat.logic.managers.DatasetWizardManager; +import eu.eudat.logic.managers.FileManager; import eu.eudat.logic.managers.UserManager; import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.claims.ClaimedAuthorities; @@ -67,14 +68,17 @@ public class Datasets extends BaseController { private DatasetManager datasetManager; private ConfigLoader configLoader; private UserManager userManager; + private FileManager fileManager; @Autowired - public Datasets(ApiContext apiContext, Environment environment, DatasetManager datasetManager, ConfigLoader configLoader, UserManager userManager) { + public Datasets(ApiContext apiContext, Environment environment, DatasetManager datasetManager, ConfigLoader configLoader, UserManager userManager, + FileManager fileManager) { super(apiContext); this.environment = environment; this.datasetManager = datasetManager; this.configLoader = configLoader; this.userManager = userManager; + this.fileManager = fileManager; } /* @@ -268,6 +272,7 @@ public class Datasets extends BaseController { public @ResponseBody ResponseEntity> delete(@PathVariable(value = "id") UUID id, Principal principal) throws Exception { new DatasetWizardManager().delete(this.getApiContext(), id); + this.fileManager.markAllFilesOfEntityIdAsDeleted(id); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Deleted")); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/FileController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/FileController.java new file mode 100644 index 000000000..a064145bc --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/FileController.java @@ -0,0 +1,180 @@ +package eu.eudat.controllers; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.data.entities.FileUpload; +import eu.eudat.exceptions.security.UnauthorisedException; +import eu.eudat.logic.managers.DatasetProfileManager; +import eu.eudat.logic.security.claims.ClaimedAuthorities; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.logic.services.operations.DatabaseRepository; +import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; +import eu.eudat.logic.utilities.json.JsonSearcher; +import eu.eudat.models.HintedModelFactory; +import eu.eudat.models.data.datasetwizard.DatasetWizardModel; +import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.models.data.security.Principal; +import eu.eudat.types.ApiMessageCode; +import eu.eudat.types.Authorities; +import org.apache.poi.util.IOUtils; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.transaction.Transactional; +import java.io.*; +import java.nio.file.Files; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@RestController +@CrossOrigin +@RequestMapping(value = {"/api/file/"}) +public class FileController { + + private DatasetProfileManager datasetProfileManager; + private final Environment environment; + private DatabaseRepository databaseRepository; + + @Autowired + public FileController(DatasetProfileManager datasetProfileManager, Environment environment, ApiContext apiContext) { + this.datasetProfileManager = datasetProfileManager; + this.environment = environment; + this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); + } + + @RequestMapping(method = RequestMethod.POST, value = {"/upload"}) + public ResponseEntity> upload( + @RequestParam("file") MultipartFile file, @RequestParam("datasetProfileId") String datasetProfileId, @RequestParam("fieldId") String fieldId, + @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER}) Principal principal) + throws IllegalAccessException, IOException { + String uuid = UUID.randomUUID().toString(); + + eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = this.datasetProfileManager.getDatasetProfile(datasetProfileId); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + String json = mapper.writeValueAsString(datasetprofile.getSections());; + JsonNode propertiesJson = mapper.readTree(json); + Set fieldNodes = new HashSet<>(); + fieldNodes.addAll(JsonSearcher.findNodes(propertiesJson, "id", fieldId, false)); + +// AtomicReference exceptionMessage = null; + AtomicBoolean acceptedFile = new AtomicBoolean(false); + fieldNodes.forEach(node -> { + JsonNode data = node.get("data"); + if (data != null && !data.toString().equals("\"\"") && !data.toString().equals("null")) { + String stringValue = data.toString().replaceAll("=", ":"); + JSONObject dataObj = new JSONObject(stringValue); + Map dataMap = ((JSONObject) dataObj).toMap(); + if(dataMap.get("maxFileSizeInMB") != null && !dataMap.get("maxFileSizeInMB").toString().equals("\"\"") && !dataMap.get("maxFileSizeInMB").toString().equals("null")) { + if (file.getSize() <= Integer.parseInt(dataMap.get("maxFileSizeInMB").toString())*1048576) { + acceptedFile.set(true); + } +// else { +// exceptionMessage.set("The file is too large. Max file upload is " + dataMap.get("maxFileSizeInMB").toString() + " MB."); +// } + } + + if(acceptedFile.get() && data.get("types") != null && !data.get("types").toString().equals("\"\"") && !data.get("types").toString().equals("null")) { + acceptedFile.set(false); + + JSONArray types = new JSONArray(data.get("types").toString()); + + types.iterator().forEachRemaining(element -> { + Map typesMap = ((JSONObject) element).toMap(); + if(typesMap.get("value") != null && !typesMap.get("value").toString().equals("\"\"") && !typesMap.get("value").toString().equals("null")) { + if(file.getContentType().equals(typesMap.get("value").toString())) { + acceptedFile.set(true); + } + } + }); + } +// if(!acceptedFile.get()) { +// exceptionMessage.set("The file type is not accepted."); +// } + } + }); + + if(!acceptedFile.get()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("The uploaded file is too large or has an unaccepted type")); + } + + + File convFile = new File(this.environment.getProperty("temp.temp") + uuid); + convFile.createNewFile(); + FileOutputStream fos = new FileOutputStream(convFile); + fos.write(file.getBytes()); + fos.close(); + + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(uuid) + .status(ApiMessageCode.SUCCESS_MESSAGE).message("")); + } + + @RequestMapping(method = RequestMethod.POST, value = {"/delete-temp"}) + public ResponseEntity> upload(@RequestBody String filename) throws IllegalAccessException, IOException { + File convFile = new File(this.environment.getProperty("temp.temp") + filename); +// Boolean deleted = convFile.delete(); + Boolean deleted = Files.deleteIfExists(convFile.toPath()); + + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(deleted.toString()) + .status(ApiMessageCode.SUCCESS_MESSAGE).message("")); + } + + @Transactional + @RequestMapping(method = RequestMethod.GET, value = {"{id}"}, produces = "application/json") + public @ResponseBody + ResponseEntity download(@PathVariable String id + ,@ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal + ) throws IOException { + FileUpload fileUpload = databaseRepository.getFileUploadDao().find(UUID.fromString(id)); + if(fileUpload == null) { + throw new NoSuchElementException("File with id "+id+" not found"); + } + + if(fileUpload.getEntityType().name().equals(FileUpload.EntityType.DATASET.name())) { + eu.eudat.data.entities.Dataset datasetEntity = databaseRepository.getDatasetDao().find(fileUpload.getEntityId(), HintedModelFactory.getHint(DatasetWizardModel.class)); + if (datasetEntity == null) { + throw new NoSuchElementException("No dataset with id " + fileUpload.getEntityId() + " found. This dataset was related to the file with id " + id); + } + if (!datasetEntity.getDmp().isPublic() && datasetEntity.getDmp().getUsers() + .stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId()) + .collect(Collectors.toList()).size() == 0) + throw new UnauthorisedException(); + } + + FileEnvelope fileEnvelope = new FileEnvelope(); + fileEnvelope.setFilename(fileUpload.getName()); + File exportFile = new File(this.environment.getProperty("file.storage") + id); + fileEnvelope.setFile(exportFile); + + InputStream resource = new FileInputStream(fileEnvelope.getFile()); + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentLength(fileEnvelope.getFile().length()); + responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); + String fileName = fileEnvelope.getFilename().replace(" ", "_").replace(",", "_"); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName); + responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); + responseHeaders.set("Cache-Control", "no-store"); + responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); + + byte[] content = IOUtils.toByteArray(resource); + resource.close(); + return new ResponseEntity<>(content, + responseHeaders, + HttpStatus.OK); + + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java index d9633d312..3838fee4a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java @@ -52,7 +52,6 @@ public class UserInvitationController extends BaseController { public @ResponseBody // ResponseEntity>> getUsers(Principal principal) throws IllegalAccessException, InstantiationException { ResponseEntity>> getUsers(Principal principal, @RequestBody UserInfoRequestItem userInfoRequestItem) throws IllegalAccessException, InstantiationException { - System.out.println(userInfoRequestItem.getCriteria().getLike()); // List users = invitationsManager.getUsers(principal); List users = invitationsManager.getUsersWithCriteria(principal, userInfoRequestItem); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.SUCCESS_MESSAGE).payload(users)); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java index ffd26bb69..ab664f997 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java @@ -1,5 +1,7 @@ package eu.eudat.logic.managers; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.dao.criteria.*; @@ -109,9 +111,11 @@ public class DatasetManager { private ConfigLoader configLoader; private Environment environment; private final MetricsManager metricsManager; + private final FileManager fileManager; @Autowired - public DatasetManager(ApiContext apiContext, UserManager userManager, ConfigLoader configLoader, Environment environment, MetricsManager metricsManager) { + public DatasetManager(ApiContext apiContext, UserManager userManager, ConfigLoader configLoader, Environment environment, MetricsManager metricsManager, + FileManager fileManager) { this.apiContext = apiContext; this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); this.datasetRepository = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository(); @@ -120,6 +124,7 @@ public class DatasetManager { this.configLoader = configLoader; this.environment = environment; this.metricsManager = metricsManager; + this.fileManager = fileManager; } public DataTableData getPaged(DatasetTableRequest datasetTableRequest, Principal principal) throws Exception { @@ -608,9 +613,51 @@ public class DatasetManager { this.sendNotification(dataset1, dataset1.getDmp(), userInfo, NotificationType.DATASET_MODIFIED_FINALISED); } } + + this.deleteOldFilesAndAddNew(datasetWizardModel, userInfo); + + return dataset1; } + private void deleteOldFilesAndAddNew(DatasetWizardModel datasetWizardModel, UserInfo userInfo) throws JsonProcessingException { + // Files in DB for this entityId which are NOT DELETED + List fileUploads = fileManager.getCurrentFileUploadsForEntityId(datasetWizardModel.getId()); + List fileUploadIds = fileUploads.stream().map(fileUpload -> fileUpload.getId().toString()).collect(Collectors.toList()); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + String json = mapper.writeValueAsString(datasetWizardModel.getDatasetProfileDefinition());; + JsonNode propertiesJson = mapper.readTree(json); + + Set uploadNodes = new HashSet<>(); + uploadNodes.addAll(JsonSearcher.findNodes(propertiesJson, "renderStyle", "upload", true)); + + uploadNodes.forEach(node -> { + JsonNode value = node.get("value"); + if (value != null && !value.toString().equals("\"\"") && !value.toString().equals("null")) { + String stringValue = value.toString().replaceAll("=", ":"); + JSONObject values = new JSONObject(stringValue); + Map data = ((JSONObject) values).toMap(); + + int index = fileUploadIds.indexOf(data.get("id").toString()); + if(index != -1) { + // file in DB is the same as file in the Dataset + fileUploadIds.remove(index); + fileUploads.remove(index); + } else { + // new file + this.fileManager.createFile(data.get("id").toString(), data.get("name").toString(), data.get("type").toString(), datasetWizardModel.getId().toString(), FileUpload.EntityType.DATASET, userInfo); + } + } + }); + + // old files in DB that are not contained anymore in the Dataset -> mark them as Deleted + fileUploads.forEach(fileUpload -> { + fileManager.markOldFileAsDeleted(fileUpload); + }); + } + private void sendNotification(Dataset dataset, DMP dmp, UserInfo user, NotificationType notificationType) { List userDMPS = databaseRepository.getUserDmpDao().asQueryable().where(((builder, root) -> builder.equal(root.get("dmp").get("id"), dmp.getId()))).toList(); for (UserDMP userDMP : userDMPS) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FileManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FileManager.java new file mode 100644 index 000000000..cb65ffa34 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FileManager.java @@ -0,0 +1,96 @@ +package eu.eudat.logic.managers; + +import eu.eudat.data.entities.FileUpload; +import eu.eudat.data.entities.UserInfo; +import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.logic.services.operations.DatabaseRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Component +public class FileManager { + private static final Logger logger = LoggerFactory.getLogger(FileManager.class); + + private ApiContext apiContext; + private DatabaseRepository databaseRepository; + private Environment environment; + + @Autowired + public FileManager(ApiContext apiContext, Environment environment) { + this.apiContext = apiContext; + this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); + this.environment = environment; + } + + public String moveFromTmpToStorage(String filename) { + File tempFile = new File(this.environment.getProperty("temp.temp") + filename); + File newFile = new File(this.environment.getProperty("file.storage") + filename); + try { + return Files.move(tempFile.toPath(), newFile.toPath()).toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public boolean deleteFromStorage(String filename) { + File toBeDeletedFile = new File(this.environment.getProperty("file.storage") + filename); +// toBeDeletedFile.delete(); + try { + return Files.deleteIfExists(toBeDeletedFile.toPath()); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + public void markOldFileAsDeleted(FileUpload fileUpload) { + fileUpload.setIsDeleted(true); + databaseRepository.getFileUploadDao().createOrUpdate(fileUpload); + } + + public List getFileUploadsForEntityId(String entityId) { + return databaseRepository.getFileUploadDao().asQueryable() + .where((builder, root) -> builder.equal(root.get("entityId"), entityId)).toList(); + } + + public List getCurrentFileUploadsForEntityId(UUID entityId) { + return databaseRepository.getFileUploadDao().asQueryable() + .where((builder, root) -> builder.and( + builder.equal(root.get("entityId"), entityId), + builder.equal(root.get("isDeleted"), false))).toList(); + } + + public void markAllFilesOfEntityIdAsDeleted(UUID entityId) { + List fileUploads = this.getCurrentFileUploadsForEntityId(entityId); + fileUploads.forEach(fileUpload -> { + this.markOldFileAsDeleted(fileUpload); + }); + } + + public void createFile(String id, String fileName, String fileType, String entityId, FileUpload.EntityType entityType, UserInfo userInfo) { + FileUpload fileUpload = new FileUpload(); + fileUpload.setId(UUID.fromString(id)); + fileUpload.setName(fileName); + fileUpload.setFileType(fileType); + fileUpload.setEntityId(UUID.fromString(entityId)); + fileUpload.setEntityType(entityType); + fileUpload.setCreatedAt(new Date()); + fileUpload.setIsDeleted(false); + fileUpload.setCreator(userInfo); + databaseRepository.getFileUploadDao().createOrUpdate(fileUpload); + + this.moveFromTmpToStorage(fileUpload.getId().toString()); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java index a5bdf73fa..67225a76d 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java @@ -58,5 +58,7 @@ public interface DatabaseRepository { NotificationDao getNotificationDao(); + FileUploadDao getFileUploadDao(); + void detachEntity(T entity); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java index 0c737937c..7228b5b2f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java @@ -38,6 +38,7 @@ public class DatabaseRepositoryImpl implements DatabaseRepository { private FunderDao funderDao; private LockDao lockDao; private NotificationDao notificationDao; + private FileUploadDao fileUploadDao; private EntityManager entityManager; @@ -306,6 +307,17 @@ public class DatabaseRepositoryImpl implements DatabaseRepository { this.userDatasetProfileDao = userDatasetProfileDao; } + @Override + public FileUploadDao getFileUploadDao() { + return fileUploadDao; + } + + @Autowired + public void setFileUploadDao(FileUploadDao fileUploadDao) { + this.fileUploadDao = fileUploadDao; + } + + public void detachEntity(T entity) { this.entityManager.detach(entity); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java index b0bc60318..58786cb16 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java @@ -85,6 +85,8 @@ public class ModelBuilder { if (type.equals("freetext")) return (FieldData) new FreeTextData().fromData(data); if (type.equals("textarea")) return (FieldData) new TextAreaData().fromData(data); if (type.equals("richTextarea")) return (FieldData) new RichTextAreaData().fromData(data); + if (type.equals("upload")) return (FieldData) new UploadData().fromData(data); +// if (type.equals("table")) return (FieldData) new TableData().fromData(data); if (type.equals("datePicker")) return (FieldData) new DatePickerData().fromData(data); if (type.equals("externalDatasets")) return (FieldData) new ExternalDatasetsData().fromData(data); if (type.equals("dataRepositories")) return (FieldData) new DataRepositoriesData().fromData(data); @@ -130,6 +132,8 @@ public class ModelBuilder { if (type.equals("freetext")) return (FieldData) new FreeTextData().fromData(data); if (type.equals("textarea")) return (FieldData) new TextAreaData().fromData(data); if (type.equals("richTextarea")) return (FieldData) new RichTextAreaData().fromData(data); + if (type.equals("upload")) return (FieldData) new UploadData().fromData(data); +// if (type.equals("table")) return (FieldData) new TableData().fromData(data); if (type.equals("datePicker")) return (FieldData) new DatePickerData().fromData(data); if (type.equals("externalDatasets")) return (FieldData) new ExternalDatasetsData().fromData(data); if (type.equals("dataRepositories")) return (FieldData) new DataRepositoriesData().fromData(data); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java index bfaf52cbd..f4565a447 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java @@ -29,6 +29,8 @@ public class ViewStyle { FREE_TEXT("freetext"), TEXT_AREA("textarea"), RICH_TEXT_AREA("richTextarea"), + UPLOAD("upload"), +// TABLE("table"), DATE_PICKER("datePicker"), EXTERNAL_DATASETS("externalDatasets"), DATA_REPOSITORIES("dataRepositories"), diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/UploadData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/UploadData.java new file mode 100644 index 000000000..c4083f3aa --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/UploadData.java @@ -0,0 +1,158 @@ +package eu.eudat.models.data.components.commons.datafield; + +import eu.eudat.logic.utilities.interfaces.XmlSerializable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + + +public class UploadData extends FieldData { + public class Option implements XmlSerializable { + private String label; + private String value; + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public Element toXml(Document doc) { + Element option = doc.createElement("option"); + option.setAttribute("label", this.label); + option.setAttribute("value", this.value); + return option; + } + + @Override + public UploadData.Option fromXml(Element item) { + this.label = item.getAttribute("label"); + this.value = item.getAttribute("value"); + return this; + } + + + } + + private List types; + + public List getTypes() { + return types; + } + + public void setTypes(List types) { + this.types = types; + } + + private Integer maxFileSizeInMB; + + public Integer getMaxFileSizeInMB() { + return maxFileSizeInMB; + } + + public void setMaxFileSizeInMB(Integer maxFileSizeInMB) { + this.maxFileSizeInMB = maxFileSizeInMB; + } + + @Override + public UploadData fromData(Object data) { + this.types = new LinkedList(); + if (data != null) { + List> types = ((Map>>) data).get("types"); + for (Map map : types) { + UploadData.Option newOption = new UploadData.Option(); + newOption.setLabel(map.get("label")); + newOption.setValue(map.get("value")); + this.types.add(newOption); + } + this.setLabel(((Map) data).get("label")); + this.setMaxFileSizeInMB(((Map) data).get("maxFileSizeInMB")); + } + return this; + } + + @Override + public Object toData() { + return null; + } + + @Override + public Element toXml(Document doc) { + Element root = doc.createElement("data"); + Element element = doc.createElement("types"); + for (UploadData.Option type : this.types) { + element.appendChild(type.toXml(doc)); + } + root.setAttribute("label", this.getLabel()); + root.setAttribute("maxFileSizeInMB", this.getMaxFileSizeInMB().toString()); + root.appendChild(element); + return root; + } + + @Override + public UploadData fromXml(Element item) { + this.types = new LinkedList<>(); + Element optionsElement = (Element) item.getElementsByTagName("types").item(0); + this.setLabel(item.getAttribute("label")); + if(item.getAttribute("maxFileSizeInMB") != null) { + this.setMaxFileSizeInMB(Integer.parseInt(item.getAttribute("maxFileSizeInMB"))); + } + if (optionsElement != null) { + NodeList optionElements = optionsElement.getChildNodes(); + for (int temp = 0; temp < optionElements.getLength(); temp++) { + Node optionElement = optionElements.item(temp); + if (optionElement.getNodeType() == Node.ELEMENT_NODE) { + this.types.add(new UploadData.Option().fromXml((Element) optionElement)); + } + } + } + return this; + } + + @Override + public Map toMap(Element item) { + HashMap dataMap = new HashMap(); + dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("maxFileSizeInMB", item != null ? item.getAttribute("maxFileSizeInMB") : ""); + Element optionsElement = (Element) item.getElementsByTagName("types").item(0); + List> type =new LinkedList<>(); + + if (optionsElement != null) { + NodeList optionElements = optionsElement.getChildNodes(); + for (int temp = 0; temp < optionElements.getLength(); temp++) { + Node optionElement = optionElements.item(temp); + if (optionElement.getNodeType() == Node.ELEMENT_NODE) { + type.add(optionToMap((Element) optionElement)); + } + } + } + + dataMap.put("types", type != null ? type : null); + return dataMap; + } + + private Map optionToMap(Element item){ + HashMap dataMap = new HashMap(); + dataMap.put("label",item.getAttribute("label")); + dataMap.put("value",item.getAttribute("value")); + + return dataMap; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java index 6fe3c7741..2a006ec5b 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java @@ -61,7 +61,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin this.id = id; } - public int getOrdinal() { + public Integer getOrdinal() { return ordinal; } @@ -134,6 +134,9 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin } public List getValidations() { + if(this.validations == null) { + return null; + } return this.validations.stream().map(item -> (int) item.getValue()).collect(Collectors.toList()); } @@ -206,7 +209,11 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin List stringList = mapper.readValue(properties.get(this.id).toString(), LinkedList.class); this.value = stringList; } catch (JSONException | NullPointerException | IOException e) { - this.value = (String) properties.get(this.id); + try { + this.value = (String) properties.get(this.id); + } catch (ClassCastException ce) { + this.value = properties.get(this.id); + } } this.multiplicityItems = new LinkedList<>(); List compositeKeys = properties.keySet().stream().filter(keys -> keys.startsWith("multiple_" + this.getId())).collect(Collectors.toList()); @@ -250,7 +257,11 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin } valueBuilder.append("]"); fieldValues.put(this.id, valueBuilder.toString()); - }*/ else { + }*/ + else if ((this.viewStyle != null && this.viewStyle.getRenderStyle().equals("upload"))) { + fieldValues.put(this.id, this.value); + } + else { fieldValues.put(this.id, this.value.toString()); } } else { diff --git a/dmp-backend/web/src/main/resources/config/application.properties b/dmp-backend/web/src/main/resources/config/application.properties index a22106f97..e6d2f9a93 100644 --- a/dmp-backend/web/src/main/resources/config/application.properties +++ b/dmp-backend/web/src/main/resources/config/application.properties @@ -114,6 +114,9 @@ logging.config=classpath:logging/logback-${spring.profiles.active}.xml #############TEMP######### temp.temp=tmp/ +file.storage=uploads/ +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=10MB #############ZENODO######### zenodo.affiliation=ARGOS