From 31c2fe2f1002a32b787bee77ff3d20ffabbba463 Mon Sep 17 00:00:00 2001 From: "CITE\\amentis" Date: Wed, 31 Jul 2024 14:52:47 +0300 Subject: [PATCH] implement public plan, description exports --- .../org/opencdmp/audit/AuditableAction.java | 6 +- .../DescriptionCommonModelBuilder.java | 49 +++++-- .../plan/PlanCommonModelBuilder.java | 39 +++++- .../service/deposit/DepositServiceImpl.java | 4 +- .../description/DescriptionService.java | 6 +- .../description/DescriptionServiceImpl.java | 49 +++++-- .../FileTransformerService.java | 4 +- .../FileTransformerServiceImpl.java | 56 ++++++-- .../controllers/DescriptionController.java | 36 +++++- .../FileTransformerController.java | 32 ++++- .../opencdmp/controllers/PlanController.java | 39 +++++- .../description/description.service.ts | 11 ++ .../file-transformer.http.service.ts | 10 ++ .../file-transformer.service.ts | 120 +++++++++++++----- .../app/core/services/plan/plan.service.ts | 10 ++ .../description-listing-item.component.html | 2 +- .../description-overview.component.html | 2 +- .../plan-listing-item.component.html | 2 +- .../overview/plan-overview.component.html | 2 +- 19 files changed, 394 insertions(+), 85 deletions(-) diff --git a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java index 1d78db0b3..64ec72962 100644 --- a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java +++ b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java @@ -47,6 +47,8 @@ public class AuditableAction { public static final EventId Plan_Validate = new EventId(5015, "Plan_Validate"); public static final EventId Plan_GetXml = new EventId(5016, "Plan_GetXml"); public static final EventId Plan_Import = new EventId(5017, "Plan_Import"); + public static final EventId Plan_GetPublicXml = new EventId(5017, "Plan_GetPublicXml"); + public static final EventId Plan_ExportPublic = new EventId(5018, "Plan_ExportPublic"); public static final EventId Description_Query = new EventId(6000, "Description_Query"); @@ -61,8 +63,10 @@ public class AuditableAction { public static final EventId Description_Validate = new EventId(6009, "Description_Validate"); public static final EventId Description_GetDescriptionSectionPermissions = new EventId(6010, "Description_GetDescriptionSectionPermissions"); public static final EventId Description_UpdateDescriptionTemplate = new EventId(6011, "Description_UpdateDescriptionTemplate"); + public static final EventId Description_GetXml = new EventId(6012, "Description_GetXml"); + public static final EventId Description_GetPublicXml = new EventId(6013, "Description_GetPublicXml"); + - public static final EventId Reference_Query = new EventId(7000, "Reference_Query"); public static final EventId Reference_Lookup = new EventId(7001, "Reference_Lookup"); public static final EventId Reference_Persist = new EventId(7002, "Reference_Persist"); diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/description/DescriptionCommonModelBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/description/DescriptionCommonModelBuilder.java index 1abf5fe1e..7a3dcebc4 100644 --- a/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/description/DescriptionCommonModelBuilder.java +++ b/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/description/DescriptionCommonModelBuilder.java @@ -12,13 +12,13 @@ import org.opencdmp.commonmodels.models.descriptiotemplate.DescriptionTemplateMo import org.opencdmp.commonmodels.models.plan.PlanModel; import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.XmlHandlingService; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.commons.enums.PlanAccessType; +import org.opencdmp.commons.enums.PlanStatus; import org.opencdmp.commons.types.description.PropertyDefinitionEntity; import org.opencdmp.commons.types.descriptiontemplate.DefinitionEntity; import org.opencdmp.convention.ConventionService; -import org.opencdmp.data.DescriptionEntity; -import org.opencdmp.data.DescriptionTemplateEntity; -import org.opencdmp.data.PlanDescriptionTemplateEntity; -import org.opencdmp.data.PlanEntity; +import org.opencdmp.data.*; import org.opencdmp.model.PlanDescriptionTemplate; import org.opencdmp.model.builder.commonmodels.BaseCommonModelBuilder; import org.opencdmp.model.builder.commonmodels.CommonModelBuilderItemResponse; @@ -36,9 +36,12 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.*; import java.util.stream.Collectors; +import static org.opencdmp.authorization.AuthorizationFlags.Public; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder { @@ -48,20 +51,22 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder authorize = EnumSet.of(AuthorizationFlags.None); private String repositoryId; @Autowired public DescriptionCommonModelBuilder( - ConventionService conventionService, - QueryFactory queryFactory, - BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService) { + ConventionService conventionService, + QueryFactory queryFactory, + BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager) { super(conventionService, new LoggerService(LoggerFactory.getLogger(DescriptionCommonModelBuilder.class))); this.queryFactory = queryFactory; this.builderFactory = builderFactory; this.jsonHandlingService = jsonHandlingService; this.xmlHandlingService = xmlHandlingService; + this.entityManager = entityManager; } public DescriptionCommonModelBuilder authorize(EnumSet values) { @@ -81,6 +86,12 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder> buildInternal(List data) throws MyApplicationException { this.logger.debug("building for {}", Optional.ofNullable(data).map(List::size).orElse(0)); @@ -161,8 +172,28 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder itemMap; - PlanQuery q = this.queryFactory.query(PlanQuery.class).authorize(this.authorize).disableTracking().ids(data.stream().map(DescriptionEntity::getPlanId).distinct().collect(Collectors.toList())); - itemMap = this.builderFactory.builder(PlanCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).setDisableDescriptions(true).authorize(this.authorize).asForeignKey(q, PlanEntity::getId); + PlanQuery q = null; + if (this.isPublic) { + try { + this.entityManager.disableTenantFilters(); + q = this.queryFactory.query(PlanQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(data.stream().map(DescriptionEntity::getPlanId).distinct().collect(Collectors.toList())).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public); + itemMap = this.builderFactory.builder(PlanCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).setDisableDescriptions(true).authorize(this.authorize).asForeignKey(q, PlanEntity::getId); + try { + this.entityManager.reloadTenantFilters(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } finally { + try { + this.entityManager.reloadTenantFilters(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + } else { + q = this.queryFactory.query(PlanQuery.class).authorize(this.authorize).disableTracking().ids(data.stream().map(DescriptionEntity::getPlanId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(PlanCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).setDisableDescriptions(true).authorize(this.authorize).asForeignKey(q, PlanEntity::getId); + } return itemMap; } diff --git a/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/plan/PlanCommonModelBuilder.java b/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/plan/PlanCommonModelBuilder.java index 611de965c..d99bb0f88 100644 --- a/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/plan/PlanCommonModelBuilder.java +++ b/backend/core/src/main/java/org/opencdmp/model/builder/commonmodels/plan/PlanCommonModelBuilder.java @@ -41,9 +41,12 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.*; import java.util.stream.Collectors; +import static org.opencdmp.authorization.AuthorizationFlags.Public; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PlanCommonModelBuilder extends BaseCommonModelBuilder { @@ -53,6 +56,7 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder values) { @@ -102,6 +107,12 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder> buildInternal(List data) throws MyApplicationException { this.logger.debug("building for {}", Optional.ofNullable(data).map(List::size).orElse(0)); @@ -204,8 +215,28 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder> itemMap; - DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).disableTracking().isActive(IsActive.Active).authorize(this.authorize).planIds(data.stream().map(PlanEntity::getId).distinct().collect(Collectors.toList())); - itemMap = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).authorize(this.authorize).asMasterKey(query, DescriptionEntity::getPlanId); + DescriptionQuery query = null; + if (this.isPublic) { + try { + this.entityManager.disableTenantFilters(); + query = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(EnumSet.of(Public)).planIds(data.stream().map(PlanEntity::getId).distinct().collect(Collectors.toList())).planSubQuery(this.queryFactory.query(PlanQuery.class).isActive(IsActive.Active).statuses(org.opencdmp.commons.enums.PlanStatus.Finalized).accessTypes(org.opencdmp.commons.enums.PlanAccessType.Public)); + itemMap = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).isPublic(this.isPublic).authorize(this.authorize).asMasterKey(query, DescriptionEntity::getPlanId); + try { + this.entityManager.reloadTenantFilters(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } finally { + try { + this.entityManager.reloadTenantFilters(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + } else { + query = this.queryFactory.query(DescriptionQuery.class).disableTracking().isActive(IsActive.Active).authorize(this.authorize).planIds(data.stream().map(PlanEntity::getId).distinct().collect(Collectors.toList())); + itemMap = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(this.repositoryId).useSharedStorage(this.useSharedStorage).isPublic(this.isPublic).authorize(this.authorize).asMasterKey(query, DescriptionEntity::getPlanId); + } return itemMap; } @@ -248,7 +279,7 @@ public class PlanCommonModelBuilder extends BaseCommonModelBuilder itemMap = new HashMap<>(); + Map itemMap = new HashMap<>(); PlanBlueprintQuery q = this.queryFactory.query(PlanBlueprintQuery.class).disableTracking().authorize(this.authorize).ids(data.stream().map(PlanEntity::getBlueprintId).distinct().collect(Collectors.toList())); List items = q.collectAs(new BaseFieldSet().ensure(PlanBlueprint._id).ensure(PlanBlueprint._definition)); for (PlanBlueprintEntity item : items){ diff --git a/backend/core/src/main/java/org/opencdmp/service/deposit/DepositServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/deposit/DepositServiceImpl.java index f98a77830..9c0656498 100644 --- a/backend/core/src/main/java/org/opencdmp/service/deposit/DepositServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/deposit/DepositServiceImpl.java @@ -296,8 +296,8 @@ public class DepositServiceImpl implements DepositService { accessToken = this.authenticate(authenticateRequest); } - org.opencdmp.model.file.FileEnvelope pdfFile = this.fileTransformerService.exportPlan(planEntity.getId(), source.getPdfTransformerId(),"pdf"); - org.opencdmp.model.file.FileEnvelope rda = this.fileTransformerService.exportPlan(planEntity.getId(), source.getRdaTransformerId(),"json"); + org.opencdmp.model.file.FileEnvelope pdfFile = this.fileTransformerService.exportPlan(planEntity.getId(), source.getPdfTransformerId(),"pdf", false); + org.opencdmp.model.file.FileEnvelope rda = this.fileTransformerService.exportPlan(planEntity.getId(), source.getRdaTransformerId(),"json", false); FileEnvelopeModel pdfEnvelope = new FileEnvelopeModel(); FileEnvelopeModel jsonEnvelope = new FileEnvelopeModel(); diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java index 2708e7033..766c39fcd 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java @@ -42,16 +42,18 @@ public interface DescriptionService { List validate(List descriptionIds) throws InvalidApplicationException; - ResponseEntity export(UUID id, String exportType) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; + ResponseEntity export(UUID id, String exportType, boolean isPublic) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; StorageFile uploadFieldFile(DescriptionFieldFilePersist model, MultipartFile file, FieldSet fields) throws IOException; StorageFileEntity getFieldFile(UUID descriptionId, UUID storageFileId); void updateDescriptionTemplate(UpdateDescriptionTemplatePersist model) throws InvalidApplicationException, IOException, JAXBException; - DescriptionImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; + DescriptionImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize, boolean isPublic) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; ResponseEntity exportXml(UUID id) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; + ResponseEntity exportPublicXml(UUID id) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; + Description importXml(DescriptionImportExport descriptionXml, UUID planId, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; Description importCommonModel(DescriptionModel model, UUID planId, FieldSet fields) throws MyForbiddenException, MyNotFoundException, InvalidApplicationException, IOException, JAXBException, ParserConfigurationException, TransformerException, InstantiationException, IllegalAccessException, SAXException; diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java index 35a1dfa16..24907f17a 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java @@ -50,10 +50,7 @@ import org.opencdmp.integrationevent.outbox.annotationentityremoval.AnnotationEn import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent; import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler; -import org.opencdmp.model.DescriptionValidationResult; -import org.opencdmp.model.PlanDescriptionTemplate; -import org.opencdmp.model.StorageFile; -import org.opencdmp.model.Tag; +import org.opencdmp.model.*; import org.opencdmp.model.builder.description.DescriptionBuilder; import org.opencdmp.model.deleter.DescriptionDeleter; import org.opencdmp.model.deleter.DescriptionReferenceDeleter; @@ -110,6 +107,8 @@ import java.time.Instant; import java.util.*; import java.util.stream.Collectors; +import static org.opencdmp.authorization.AuthorizationFlags.Public; + @Service public class DescriptionServiceImpl implements DescriptionService { @@ -912,10 +911,10 @@ public class DescriptionServiceImpl implements DescriptionService { //region file export @Override - public ResponseEntity export(UUID id, String exportType) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + public ResponseEntity export(UUID id, String exportType, boolean isPublic) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { HttpHeaders headers = new HttpHeaders(); - FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, null, exportType); //TODO get repo from config + FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, null, exportType, isPublic); //TODO get repo from config headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); byte[] data = fileEnvelope.getFile(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); @@ -1164,11 +1163,22 @@ public class DescriptionServiceImpl implements DescriptionService { //region Export @Override - public DescriptionImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { + public DescriptionImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize, boolean isPublic) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { logger.debug(new MapLogEntry("export xml").And("id", id)); if (!ignoreAuthorize) this.authorizationService.authorizeForce(Permission.ExportDescription); - DescriptionEntity data = this.queryFactory.query(DescriptionQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.All).isActive(IsActive.Active).first(); + DescriptionEntity data = null; + if (!isPublic) { + data = this.queryFactory.query(DescriptionQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.All).isActive(IsActive.Active).first(); + } else { + try { + this.entityManager.disableTenantFilters(); + data = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(id).planSubQuery(this.queryFactory.query(PlanQuery.class).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public)).first(); + this.entityManager.reloadTenantFilters(); + } finally { + this.entityManager.reloadTenantFilters(); + } + } if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); PropertyDefinitionEntity definition = this.jsonHandlingService.fromJson(PropertyDefinitionEntity.class, data.getProperties()); @@ -1183,7 +1193,28 @@ public class DescriptionServiceImpl implements DescriptionService { DescriptionEntity data = this.queryFactory.query(DescriptionQuery.class).disableTracking().ids(id).authorize(AuthorizationFlags.All).isActive(IsActive.Active).first(); if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); - String xml = this.xmlHandlingService.toXml(this.exportXmlEntity(data.getId(), false)); + String xml = this.xmlHandlingService.toXml(this.exportXmlEntity(data.getId(), false, false)); + this.accountingService.increase(UsageLimitTargetMetric.EXPORT_DESCRIPTION_XML_EXECUTION_COUNT.getValue()); + return this.responseUtilsService.buildResponseFileFromText(xml, data.getLabel() + ".xml"); + } + + @Override + public ResponseEntity exportPublicXml(UUID id) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { + logger.debug(new MapLogEntry("export xml").And("id", id)); + + this.authorizationService.authorizeForce(Permission.ExportDescription); + DescriptionEntity data = null; + try { + this.entityManager.disableTenantFilters(); + data = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(id).planSubQuery(this.queryFactory.query(PlanQuery.class).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public)).first(); + this.entityManager.reloadTenantFilters(); + } finally { + this.entityManager.reloadTenantFilters(); + } + + if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, PublicDescription.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + String xml = this.xmlHandlingService.toXml(this.exportXmlEntity(data.getId(), false, true)); this.accountingService.increase(UsageLimitTargetMetric.EXPORT_DESCRIPTION_XML_EXECUTION_COUNT.getValue()); return this.responseUtilsService.buildResponseFileFromText(xml, data.getLabel() + ".xml"); } diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java index 4aaf45cb8..8413e2347 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerService.java @@ -20,9 +20,9 @@ import java.util.UUID; public interface FileTransformerService { List getAvailableExportFileFormats() throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; - org.opencdmp.model.file.FileEnvelope exportPlan(UUID planId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; + org.opencdmp.model.file.FileEnvelope exportPlan(UUID planId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; - org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; + org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; PlanModel importPlan(PlanCommonModelConfig planCommonModelConfig) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, JAXBException; diff --git a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java index 1c5e55079..96e8c4860 100644 --- a/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/filetransformer/FileTransformerServiceImpl.java @@ -22,23 +22,19 @@ import org.opencdmp.commonmodels.models.descriptiotemplate.DescriptionTemplateMo import org.opencdmp.commonmodels.models.plan.PlanModel; import org.opencdmp.commonmodels.models.planblueprint.PlanBlueprintModel; import org.opencdmp.commons.JsonHandlingService; -import org.opencdmp.commons.enums.IsActive; -import org.opencdmp.commons.enums.StorageType; -import org.opencdmp.commons.enums.TenantConfigurationType; -import org.opencdmp.commons.enums.UsageLimitTargetMetric; +import org.opencdmp.commons.enums.*; import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.user.UserScope; import org.opencdmp.commons.types.filetransformer.FileTransformerSourceEntity; import org.opencdmp.commons.types.tenantconfiguration.FileTransformerTenantConfigurationEntity; import org.opencdmp.convention.ConventionService; -import org.opencdmp.data.DescriptionTemplateEntity; -import org.opencdmp.data.StorageFileEntity; -import org.opencdmp.data.TenantConfigurationEntity; +import org.opencdmp.data.*; import org.opencdmp.event.TenantConfigurationTouchedEvent; import org.opencdmp.filetransformerbase.interfaces.FileTransformerConfiguration; import org.opencdmp.filetransformerbase.models.misc.DescriptionImportModel; import org.opencdmp.filetransformerbase.models.misc.PlanImportModel; import org.opencdmp.filetransformerbase.models.misc.PreprocessingPlanModel; +import org.opencdmp.model.PublicPlan; import org.opencdmp.model.StorageFile; import org.opencdmp.model.builder.commonmodels.description.DescriptionCommonModelBuilder; import org.opencdmp.model.builder.commonmodels.descriptiontemplate.DescriptionTemplateCommonModelBuilder; @@ -82,6 +78,8 @@ import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.stream.Collectors; +import static org.opencdmp.authorization.AuthorizationFlags.Public; + @Service public class FileTransformerServiceImpl implements FileTransformerService { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(FileTransformerServiceImpl.class)); @@ -102,10 +100,11 @@ public class FileTransformerServiceImpl implements FileTransformerService { private final FileTransformerSourcesCacheService fileTransformerSourcesCacheService; private final UserScope userScope; private final AccountingService accountingService; + private final TenantEntityManager entityManager; @Autowired public FileTransformerServiceImpl(FileTransformerProperties fileTransformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCacheService fileTransformerConfigurationCacheService, AuthorizationService authorizationService, - QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, FileTransformerSourcesCacheService fileTransformerSourcesCacheService, UserScope userScope, AccountingService accountingService) { + QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource, ConventionService conventionService, TenantScope tenantScope, EncryptionService encryptionService, TenantProperties tenantProperties, JsonHandlingService jsonHandlingService, FileTransformerSourcesCacheService fileTransformerSourcesCacheService, UserScope userScope, AccountingService accountingService, TenantEntityManager entityManager) { this.fileTransformerProperties = fileTransformerProperties; this.tokenExchangeCacheService = tokenExchangeCacheService; this.fileTransformerConfigurationCacheService = fileTransformerConfigurationCacheService; @@ -122,6 +121,7 @@ public class FileTransformerServiceImpl implements FileTransformerService { this.fileTransformerSourcesCacheService = fileTransformerSourcesCacheService; this.userScope = userScope; this.accountingService = accountingService; + this.entityManager = entityManager; this.clients = new HashMap<>(); } @@ -256,14 +256,28 @@ public class FileTransformerServiceImpl implements FileTransformerService { } @Override - public org.opencdmp.model.file.FileEnvelope exportPlan(UUID planId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + public org.opencdmp.model.file.FileEnvelope exportPlan(UUID planId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { this.authorizationService.authorizeForce(Permission.ExportPlan); //GK: First get the right client FileTransformerRepository repository = this.getRepository(repositoryId); if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); //GK: Second get the Target Data Management Plan - PlanQuery query = this.queryFactory.query(PlanQuery.class).disableTracking().authorize(AuthorizationFlags.All).ids(planId); - PlanModel planFileTransformerModel = this.builderFactory.builder(PlanCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setRepositoryId(repository.getConfiguration().getFileTransformerId()).authorize(AuthorizationFlags.All).build(query.first()); + PlanEntity entity = null; + if (!isPublic) { + entity = this.queryFactory.query(PlanQuery.class).disableTracking().authorize(AuthorizationFlags.All).ids(planId).first(); + } else { + try { + this.entityManager.disableTenantFilters(); + entity = this.queryFactory.query(PlanQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(planId).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public).first(); + this.entityManager.reloadTenantFilters(); + } finally { + this.entityManager.reloadTenantFilters(); + } + } + + if (entity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + PlanModel planFileTransformerModel = this.builderFactory.builder(PlanCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setRepositoryId(repository.getConfiguration().getFileTransformerId()).isPublic(isPublic).authorize(AuthorizationFlags.All).build(entity); if (planFileTransformerModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale())); FileEnvelopeModel fileEnvelope = repository.exportPlan(planFileTransformerModel, format); @@ -280,15 +294,29 @@ public class FileTransformerServiceImpl implements FileTransformerService { } @Override - public org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + public org.opencdmp.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format, boolean isPublic) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { this.authorizationService.authorizeForce(Permission.ExportDescription); //GK: First get the right client FileTransformerRepository repository = this.getRepository(repositoryId); if (repository == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{format, FileTransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale())); //GK: Second get the Target Data Management Plan - DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(AuthorizationFlags.All).ids(descriptionId); - DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getFileTransformerId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.All).build(query.first()); + DescriptionEntity entity = null; + if (!isPublic){ + entity = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(AuthorizationFlags.All).ids(descriptionId).first(); + } else { + try { + this.entityManager.disableTenantFilters(); + entity = this.queryFactory.query(DescriptionQuery.class).disableTracking().authorize(EnumSet.of(Public)).ids(descriptionId).planSubQuery(this.queryFactory.query(PlanQuery.class).isActive(IsActive.Active).statuses(PlanStatus.Finalized).accessTypes(PlanAccessType.Public)).first(); + this.entityManager.reloadTenantFilters(); + } finally { + this.entityManager.reloadTenantFilters(); + } + } + + if (entity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getFileTransformerId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).isPublic(isPublic).authorize(AuthorizationFlags.All).build(entity); if (descriptionFileTransformerModel == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); FileEnvelopeModel fileEnvelope = repository.exportDescription(descriptionFileTransformerModel, format); diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java b/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java index a42c2599e..3da1f7dda 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DescriptionController.java @@ -336,7 +336,20 @@ public class DescriptionController { ) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("exporting description")); - return this.descriptionService.export(id, exportType); + return this.descriptionService.export(id, exportType, true); + } + + @GetMapping("{id}/export-public/{type}") + @OperationWithTenantHeader(summary = "Export a public description in various formats by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200")) + @Swagger404 + public ResponseEntity exportPublic( + @Parameter(name = "id", description = "The id of a public description to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id, + @Parameter(name = "type", description = "The type of the export", example = "rda", required = true) @PathVariable("type") String exportType + ) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("exporting description")); + + return this.descriptionService.export(id, exportType, false); } @PostMapping("field-file/upload") @@ -423,11 +436,28 @@ public class DescriptionController { public @ResponseBody ResponseEntity getXml( @Parameter(name = "id", description = "The id of a description to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID id ) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { - logger.debug(new MapLogEntry("export" + PlanBlueprint.class.getSimpleName()).And("id", id)); + logger.debug(new MapLogEntry("export" + Description.class.getSimpleName()).And("id", id)); ResponseEntity response = this.descriptionService.exportXml(id); - this.auditService.track(AuditableAction.PlanBlueprint_GetXml, Map.ofEntries( + this.auditService.track(AuditableAction.Description_GetXml, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id) + )); + return response; + } + + @RequestMapping(method = RequestMethod.GET, value = "/xml/export-public/{id}", produces = "application/xml") + @OperationWithTenantHeader(summary = "Export a public description in xml format by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200")) + @Swagger404 + public @ResponseBody ResponseEntity getPublicXml( + @Parameter(name = "id", description = "The id of a public description to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID id + ) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { + logger.debug(new MapLogEntry("export public" + PublicDescription.class.getSimpleName()).And("id", id)); + + ResponseEntity response = this.descriptionService.exportPublicXml(id); + + this.auditService.track(AuditableAction.Description_GetPublicXml, Map.ofEntries( new AbstractMap.SimpleEntry("id", id) )); return response; diff --git a/backend/web/src/main/java/org/opencdmp/controllers/FileTransformerController.java b/backend/web/src/main/java/org/opencdmp/controllers/FileTransformerController.java index cd3d1af0e..a0bd12265 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/FileTransformerController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/FileTransformerController.java @@ -77,7 +77,21 @@ public class FileTransformerController { logger.debug(new MapLogEntry("exporting plan")); HttpHeaders headers = new HttpHeaders(); - FileEnvelope fileEnvelope = this.fileTransformerService.exportPlan(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat()); + FileEnvelope fileEnvelope = this.fileTransformerService.exportPlan(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat(), false); + headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); + byte[] data = fileEnvelope.getFile(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + return new ResponseEntity<>(data, headers, HttpStatus.OK); + } + + @PostMapping("/export-public-plan") + @OperationWithTenantHeader(summary = "Export a public published plan", description = SwaggerHelpers.FileTransformer.endpoint_export_plans, + responses = @ApiResponse(description = "OK", responseCode = "200")) + public ResponseEntity exportPublicPlan(@RequestBody ExportRequestModel requestModel) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("exporting plan")); + HttpHeaders headers = new HttpHeaders(); + + FileEnvelope fileEnvelope = this.fileTransformerService.exportPlan(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat(), true); headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); byte[] data = fileEnvelope.getFile(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); @@ -91,7 +105,21 @@ public class FileTransformerController { logger.debug(new MapLogEntry("exporting description")); HttpHeaders headers = new HttpHeaders(); - FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat()); + FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat(), false); + headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); + byte[] data = fileEnvelope.getFile(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + return new ResponseEntity<>(data, headers, HttpStatus.OK); + } + + @PostMapping("/export-public-description") + @OperationWithTenantHeader(summary = "Export a public description", description = SwaggerHelpers.FileTransformer.endpoint_export_descriptions, + responses = @ApiResponse(description = "OK", responseCode = "200")) + public ResponseEntity exportPublicDescription(@RequestBody ExportRequestModel requestModel) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, InvalidApplicationException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("exporting description")); + HttpHeaders headers = new HttpHeaders(); + + FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat(), true); headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename()); byte[] data = fileEnvelope.getFile(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); diff --git a/backend/web/src/main/java/org/opencdmp/controllers/PlanController.java b/backend/web/src/main/java/org/opencdmp/controllers/PlanController.java index 3a8ce31aa..ac65a2ea3 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/PlanController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/PlanController.java @@ -408,7 +408,7 @@ public class PlanController { ) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("exporting plan").And("id", id).And("transformerId", transformerId).And("exportType", exportType)); - ResponseEntity bytes = this.planService.export(id, transformerId, exportType); + ResponseEntity bytes = this.planService.export(id, transformerId, exportType, false); this.auditService.track(AuditableAction.Plan_Export, Map.ofEntries( new AbstractMap.SimpleEntry("id", id), new AbstractMap.SimpleEntry("transformerId", transformerId), @@ -417,6 +417,26 @@ public class PlanController { return bytes; } + @GetMapping("{id}/export-public/{transformerId}/{type}") + @OperationWithTenantHeader(summary = "Export a public published plan in various formats by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200")) + @Swagger404 + public ResponseEntity exportPublic( + @Parameter(name = "id", description = "The id of a public published plan to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable("id") UUID id, + @PathVariable("transformerId") String transformerId, + @PathVariable("type") String exportType + ) throws InvalidApplicationException, IOException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("exporting plan").And("id", id).And("transformerId", transformerId).And("exportType", exportType)); + + ResponseEntity bytes = this.planService.export(id, transformerId, exportType, true); + this.auditService.track(AuditableAction.Plan_ExportPublic, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id), + new AbstractMap.SimpleEntry("transformerId", transformerId), + new AbstractMap.SimpleEntry("exportType", exportType) + )); + return bytes; + } + @PostMapping("{id}/invite-users") @OperationWithTenantHeader(summary = "Send user invitations for the plan by id") @Transactional @@ -467,6 +487,23 @@ public class PlanController { return response; } + @RequestMapping(method = RequestMethod.GET, value = "/xml/export-public/{id}", produces = "application/xml") + @OperationWithTenantHeader(summary = "Export a public published plan in xml format by id", description = "", + responses = @ApiResponse(description = "OK", responseCode = "200")) + @Swagger404 + public @ResponseBody ResponseEntity getPublicXml( + @Parameter(name = "id", description = "The id of a public published plan to export", example = "c0c163dc-2965-45a5-9608-f76030578609", required = true) @PathVariable UUID id + ) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { + logger.debug(new MapLogEntry("export public" + PublicPlan.class.getSimpleName()).And("id", id)); + + ResponseEntity response = this.planService.exportPublicXml(id); + + this.auditService.track(AuditableAction.Plan_GetPublicXml, Map.ofEntries( + new AbstractMap.SimpleEntry("id", id) + )); + return response; + } + @RequestMapping(method = RequestMethod.POST, value = "/xml/import") @OperationWithTenantHeader(summary = "Import a plan from an xml file", description = "", responses = @ApiResponse(description = "OK", responseCode = "200", content = @Content( diff --git a/frontend/src/app/core/services/description/description.service.ts b/frontend/src/app/core/services/description/description.service.ts index 737416951..def894caa 100644 --- a/frontend/src/app/core/services/description/description.service.ts +++ b/frontend/src/app/core/services/description/description.service.ts @@ -117,6 +117,17 @@ export class DescriptionService { return this.httpClient.get(url, { params: params, responseType: 'blob', observe: 'response', headers: headerXml }); } + downloadPublicXML(id: Guid): Observable> { + const url = `${this.apiBase}/xml/export-public/${id}`; + let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml'); + const params = new BaseHttpParams(); + params.interceptorContext = { + excludedInterceptors: [InterceptorType.JSONContentType] + }; + return this.httpClient.get(url, { params: params, responseType: 'blob', observe: 'response', headers: headerXml }); + } + + public updateDescriptionTemplate(item: UpdateDescriptionTemplatePersist): Observable { const url = `${this.apiBase}/update-description-template`; diff --git a/frontend/src/app/core/services/file-transformer/file-transformer.http.service.ts b/frontend/src/app/core/services/file-transformer/file-transformer.http.service.ts index bcbd719d7..b17ec26ef 100644 --- a/frontend/src/app/core/services/file-transformer/file-transformer.http.service.ts +++ b/frontend/src/app/core/services/file-transformer/file-transformer.http.service.ts @@ -30,8 +30,18 @@ export class FileTransformerHttpService extends BaseService { return this.http.post(url, {id: planId, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error))); } + exportPublicPlan(planId: Guid, repositoryId: string, format: string): Observable { + const url = `${this.apiBase}/export-public-plan`; + return this.http.post(url, {id: planId, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error))); + } + exportDescription(id: Guid, repositoryId: string, format: string): Observable { const url = `${this.apiBase}/export-description`; return this.http.post(url, {id: id, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error))); } + + exportPublicDescription(id: Guid, repositoryId: string, format: string): Observable { + const url = `${this.apiBase}/export-public-description`; + return this.http.post(url, {id: id, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error))); + } } diff --git a/frontend/src/app/core/services/file-transformer/file-transformer.service.ts b/frontend/src/app/core/services/file-transformer/file-transformer.service.ts index 7d83dcade..2d14ed76b 100644 --- a/frontend/src/app/core/services/file-transformer/file-transformer.service.ts +++ b/frontend/src/app/core/services/file-transformer/file-transformer.service.ts @@ -67,10 +67,11 @@ export class FileTransformerService extends BaseService { }); } - exportPlan(id: Guid, repositoryId: string, format: string) { + exportPlan(id: Guid, repositoryId: string, format: string, isPublic: boolean = false) { this._loading = true; if (repositoryId == this.xmlExportRepo.repositoryId) { - this.planService.downloadXML(id) + if (!isPublic) { + this.planService.downloadXML(id) .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); @@ -78,28 +79,56 @@ export class FileTransformerService extends BaseService { FileSaver.saveAs(blob, filename); }, error => this.httpErrorHandlingService.handleBackedRequestError(error)); - } else { - this.fileTransformerHttpService.exportPlan(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { - this._loading = false; - return null; - })) - .subscribe(result => { - if (result !== null) { - const blob = new Blob([result.body], { type: 'application/octet-stream' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); - + } else { + this.planService.downloadPublicXML(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); - this.analyticsService.trackDownload(AnalyticsService.trackPlan, format, id.toString()); - } - }, - error => this.httpErrorHandlingService.handleBackedRequestError(error)); + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } + } else { + if (!isPublic) { + this.fileTransformerHttpService.exportPlan(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { + this._loading = false; + return null; + })) + .subscribe(result => { + if (result !== null) { + const blob = new Blob([result.body], { type: 'application/octet-stream' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.analyticsService.trackDownload(AnalyticsService.trackPlan, format, id.toString()); + } + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } else { + this.fileTransformerHttpService.exportPublicPlan(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { + this._loading = false; + return null; + })) + .subscribe(result => { + if (result !== null) { + const blob = new Blob([result.body], { type: 'application/octet-stream' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.analyticsService.trackDownload(AnalyticsService.trackPlan, format, id.toString()); + } + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } } } - exportDescription(id: Guid, repositoryId: string, format: string) { + exportDescription(id: Guid, repositoryId: string, format: string, isPublic: boolean = false) { this._loading = true; if (repositoryId == this.xmlExportRepo.repositoryId) { - this.descriptionService.downloadXML(id) + if (!isPublic) { + this.descriptionService.downloadXML(id) .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'application/xml' }); @@ -107,21 +136,48 @@ export class FileTransformerService extends BaseService { FileSaver.saveAs(blob, filename); }, error => this.httpErrorHandlingService.handleBackedRequestError(error)); - } else { - this.fileTransformerHttpService.exportDescription(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { - this._loading = false; - return null; - })) - .subscribe(result => { - if (result !== null) { - const blob = new Blob([result.body], { type: 'application/octet-stream' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); - + } else { + this.descriptionService.downloadPublicXML(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); FileSaver.saveAs(blob, filename); - this.analyticsService.trackDownload(AnalyticsService.trackDescriptions, format, id.toString()); - } - }, - error => this.httpErrorHandlingService.handleBackedRequestError(error)); + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } + } else { + if (!isPublic) { + this.fileTransformerHttpService.exportDescription(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { + this._loading = false; + return null; + })) + .subscribe(result => { + if (result !== null) { + const blob = new Blob([result.body], { type: 'application/octet-stream' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.analyticsService.trackDownload(AnalyticsService.trackDescriptions, format, id.toString()); + } + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } else { + this.fileTransformerHttpService.exportPublicDescription(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => { + this._loading = false; + return null; + })) + .subscribe(result => { + if (result !== null) { + const blob = new Blob([result.body], { type: 'application/octet-stream' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(result.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.analyticsService.trackDownload(AnalyticsService.trackDescriptions, format, id.toString()); + } + }, + error => this.httpErrorHandlingService.handleBackedRequestError(error)); + } } } } diff --git a/frontend/src/app/core/services/plan/plan.service.ts b/frontend/src/app/core/services/plan/plan.service.ts index 14830d640..5f4bc6574 100644 --- a/frontend/src/app/core/services/plan/plan.service.ts +++ b/frontend/src/app/core/services/plan/plan.service.ts @@ -179,6 +179,16 @@ export class PlanService { return this.httpClient.get(url, { params: params, responseType: 'blob', observe: 'response', headers: headerXml }); } + downloadPublicXML(id: Guid): Observable> { + const url = `${this.apiBase}/xml/export-public/${id}`; + let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml'); + const params = new BaseHttpParams(); + params.interceptorContext = { + excludedInterceptors: [InterceptorType.JSONContentType] + }; + return this.httpClient.get(url, { params: params, responseType: 'blob', observe: 'response', headers: headerXml }); + } + uploadXml(file: File, label: string, reqFields: string[] = []): Observable { const url = `${this.apiBase}/xml/import`; const params = new BaseHttpParams(); diff --git a/frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html b/frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html index c34019b3d..dbf954c53 100644 --- a/frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html +++ b/frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html @@ -47,7 +47,7 @@ - diff --git a/frontend/src/app/ui/description/overview/description-overview.component.html b/frontend/src/app/ui/description/overview/description-overview.component.html index da244ce74..b9542f6f7 100644 --- a/frontend/src/app/ui/description/overview/description-overview.component.html +++ b/frontend/src/app/ui/description/overview/description-overview.component.html @@ -174,7 +174,7 @@ - diff --git a/frontend/src/app/ui/plan/listing/listing-item/plan-listing-item.component.html b/frontend/src/app/ui/plan/listing/listing-item/plan-listing-item.component.html index 7adb81db4..3c397350c 100644 --- a/frontend/src/app/ui/plan/listing/listing-item/plan-listing-item.component.html +++ b/frontend/src/app/ui/plan/listing/listing-item/plan-listing-item.component.html @@ -51,7 +51,7 @@ more_horiz - diff --git a/frontend/src/app/ui/plan/overview/plan-overview.component.html b/frontend/src/app/ui/plan/overview/plan-overview.component.html index 2bfc01a08..daa63f03b 100644 --- a/frontend/src/app/ui/plan/overview/plan-overview.component.html +++ b/frontend/src/app/ui/plan/overview/plan-overview.component.html @@ -228,7 +228,7 @@ -