diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateDto.java new file mode 100644 index 00000000000..9f2e999960a --- /dev/null +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateDto.java @@ -0,0 +1,84 @@ +/* + * SORMAS® - Surveillance Outbreak Response Management & Analysis System + * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.symeda.sormas.api.docgeneneration; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.EntityDto; +import de.symeda.sormas.api.i18n.Validations; +import de.symeda.sormas.api.utils.DataHelper; +import de.symeda.sormas.api.utils.FieldConstraints; + +public class DocumentTemplateDto extends EntityDto { + + private static final long serialVersionUID = 8591649635400893169L; + + public static final String I18N_PREFIX = "DocumentTemplate"; + public static final String DISEASE = "disease"; + public static final String FILE_NAME = "fileName"; + + @NotNull + private DocumentWorkflow workflow; + private Disease disease; + @Size(max = FieldConstraints.CHARACTER_LIMIT_BIG, message = Validations.textTooLong) + private String fileName; + + public static DocumentTemplateDto build(DocumentWorkflow documentWorkflow, String fileName) { + DocumentTemplateDto dto = new DocumentTemplateDto(); + dto.setUuid(DataHelper.createUuid()); + dto.setWorkflow(documentWorkflow); + dto.setFileName(fileName); + + return dto; + } + + public static DocumentTemplateDto build(DocumentWorkflow documentWorkflow, String fileName, Disease disease) { + DocumentTemplateDto dto = build(documentWorkflow, fileName); + dto.setDisease(disease); + + return dto; + } + + public DocumentWorkflow getWorkflow() { + return workflow; + } + + public void setWorkflow(DocumentWorkflow workflow) { + this.workflow = workflow; + } + + public Disease getDisease() { + return disease; + } + + public void setDisease(Disease disease) { + this.disease = disease; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public DocumentTemplateReferenceDto toReference() { + return new DocumentTemplateReferenceDto(getUuid(), fileName); + } +} diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateFacade.java index ed431525eb3..fff4aca3ad9 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateFacade.java @@ -5,32 +5,33 @@ import javax.ejb.Remote; +import de.symeda.sormas.api.Disease; + @Remote public interface DocumentTemplateFacade { byte[] generateDocumentDocxFromEntities( - DocumentWorkflow documentWorkflow, - String templateName, + DocumentTemplateReferenceDto templateReference, DocumentTemplateEntities entities, Properties extraProperties) throws DocumentTemplateException; String generateDocumentTxtFromEntities( - DocumentWorkflow documentWorkflow, - String templateName, + DocumentTemplateReferenceDto templateReference, DocumentTemplateEntities entities, Properties extraProperties) throws DocumentTemplateException; - List getAvailableTemplates(DocumentWorkflow documentWorkflow); + List getAvailableTemplates(DocumentWorkflow documentWorkflow, Disease disease); - DocumentVariables getDocumentVariables(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException; + boolean isExistingTemplateFile(DocumentWorkflow documentWorkflow, Disease disease, String templateName); - boolean isExistingTemplate(DocumentWorkflow documentWorkflow, String templateName); + DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException; - void writeDocumentTemplate(DocumentWorkflow documentWorkflow, String templateName, byte[] document) throws DocumentTemplateException; + DocumentTemplateDto saveDocumentTemplate(DocumentTemplateDto template, byte[] document) + throws DocumentTemplateException; - boolean deleteDocumentTemplate(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException; + boolean deleteDocumentTemplate(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException; - byte[] getDocumentTemplate(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException; + byte[] getDocumentTemplateContent(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException; } diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateReferenceDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateReferenceDto.java new file mode 100644 index 00000000000..f3ae8bdbe52 --- /dev/null +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/DocumentTemplateReferenceDto.java @@ -0,0 +1,25 @@ +/* + * SORMAS® - Surveillance Outbreak Response Management & Analysis System + * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.symeda.sormas.api.docgeneneration; + +import de.symeda.sormas.api.ReferenceDto; + +public class DocumentTemplateReferenceDto extends ReferenceDto { + + public DocumentTemplateReferenceDto(String uuid, String caption) { + super(uuid, caption); + } +} diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/EventDocumentFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/EventDocumentFacade.java index 3d323c795f8..13971a70757 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/EventDocumentFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/EventDocumentFacade.java @@ -21,23 +21,28 @@ import javax.ejb.Remote; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.event.EventReferenceDto; @Remote public interface EventDocumentFacade { - String getGeneratedDocument(String templateName, EventReferenceDto eventReference, Properties extraProperties, Boolean shouldUploadGeneratedDoc) + String getGeneratedDocument( + DocumentTemplateReferenceDto templateReferenceDto, + EventReferenceDto eventReference, + Properties extraProperties, + Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException; Map getGeneratedDocuments( - String templateName, + DocumentTemplateReferenceDto templateReference, List eventReferences, Properties extraProperties, Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException; - List getAvailableTemplates(); + List getAvailableTemplates(Disease disease); - DocumentVariables getDocumentVariables(String templateName) throws DocumentTemplateException; + DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException; } diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderDocumentOptionsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderDocumentOptionsDto.java index dd7e406c78a..6e17f81dbcf 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderDocumentOptionsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderDocumentOptionsDto.java @@ -1,16 +1,15 @@ package de.symeda.sormas.api.docgeneneration; +import java.io.Serializable; +import java.util.Properties; + import de.symeda.sormas.api.sample.PathogenTestReferenceDto; import de.symeda.sormas.api.sample.SampleReferenceDto; import de.symeda.sormas.api.vaccination.VaccinationReferenceDto; -import javax.validation.constraints.NotNull; -import java.io.Serializable; -import java.util.Properties; - public class QuarantineOrderDocumentOptionsDto implements Serializable { - private String templateFile; + private DocumentTemplateReferenceDto template; private SampleReferenceDto sample; private PathogenTestReferenceDto pathogenTest; private VaccinationReferenceDto vaccinationReference; @@ -19,12 +18,12 @@ public class QuarantineOrderDocumentOptionsDto implements Serializable { private DocumentWorkflow documentWorkflow; - public String getTemplateFile() { - return templateFile; + public DocumentTemplateReferenceDto getTemplate() { + return template; } - public void setTemplateFile(String templateFile) { - this.templateFile = templateFile; + public void setTemplate(DocumentTemplateReferenceDto template) { + this.template = template; } public SampleReferenceDto getSample() { diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderFacade.java index dfda902b11d..66ee00927d3 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/docgeneneration/QuarantineOrderFacade.java @@ -32,8 +32,7 @@ public interface QuarantineOrderFacade { byte[] getGeneratedDocument( - String templateName, - DocumentWorkflow workflow, + DocumentTemplateReferenceDto templateReference, RootEntityType rootEntityType, ReferenceDto rootEntityReference, SampleReferenceDto sampleReference, @@ -44,22 +43,21 @@ byte[] getGeneratedDocument( throws DocumentTemplateException; Map getGeneratedDocuments( - String templateName, - DocumentWorkflow workflow, + DocumentTemplateReferenceDto templateReference, List rootEntityReferences, Properties extraProperties, Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException; Map getGeneratedDocumentsForEventParticipants( - String templateName, - List rootEntityReferences, - Disease eventDisease, - Properties extraProperties, - Boolean shouldUploadGeneratedDoc) - throws DocumentTemplateException; + DocumentTemplateReferenceDto templateReference, + List rootEntityReferences, + Disease eventDisease, + Properties extraProperties, + Boolean shouldUploadGeneratedDoc) + throws DocumentTemplateException; - List getAvailableTemplates(DocumentWorkflow workflow); + List getAvailableTemplates(DocumentWorkflow workflow, Disease disease); - DocumentVariables getDocumentVariables(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException; + DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException; } diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailFacade.java index 127e707212d..c45aa09a2c4 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailFacade.java @@ -24,6 +24,7 @@ import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.common.progress.ProcessedEntity; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.document.DocumentReferenceDto; @@ -33,7 +34,7 @@ @Remote public interface ExternalEmailFacade { - List getTemplateNames(DocumentWorkflow documentWorkflow); + List getTemplates(DocumentWorkflow documentWorkflow); List getAttachableDocuments(DocumentWorkflow documentWorkflow, String relatedEntityUuid); diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsDto.java index 64d40170261..48599792a4a 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsDto.java @@ -23,6 +23,7 @@ import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.audit.AuditedClass; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.QuarantineOrderDocumentOptionsDto; import de.symeda.sormas.api.docgeneneration.RootEntityType; @@ -36,7 +37,7 @@ public class ExternalEmailOptionsDto implements Serializable { public static final String I18N_PREFIX = "ExternalEmailOptions"; - public static final String TEMPLATE_NAME = "templateName"; + public static final String TEMPLATE = "template"; public static final String RECIPIENT_EMAIL = "recipientEmail"; public static final String ATTACHED_DOCUMENTS = "attachedDocuments"; @@ -47,8 +48,7 @@ public class ExternalEmailOptionsDto implements Serializable { @NotNull(message = Validations.requiredField) private ReferenceDto rootEntityReference; @NotNull(message = Validations.requiredField) - @Size(min = 1, message = Validations.requiredField) - private String templateName; + private DocumentTemplateReferenceDto template; @NotNull(message = Validations.requiredField) @Size(min = 1, message = Validations.requiredField) private String recipientEmail; @@ -78,12 +78,12 @@ public void setRootEntityReference(ReferenceDto rootEntityReference) { this.rootEntityReference = rootEntityReference; } - public String getTemplateName() { - return templateName; + public DocumentTemplateReferenceDto getTemplate() { + return template; } - public void setTemplateName(String templateName) { - this.templateName = templateName; + public void setTemplate(DocumentTemplateReferenceDto template) { + this.template = template; } public String getRecipientEmail() { diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsWithAttachmentsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsWithAttachmentsDto.java index 366a1e86b8a..1e2e3cee2eb 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsWithAttachmentsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalemail/ExternalEmailOptionsWithAttachmentsDto.java @@ -21,14 +21,12 @@ package de.symeda.sormas.api.externalemail; import java.io.Serializable; -import java.util.Properties; import java.util.Set; -import javax.validation.Valid; import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import de.symeda.sormas.api.audit.AuditedClass; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EmailAttachementDto; import de.symeda.sormas.api.docgeneneration.QuarantineOrderDocumentOptionsDto; @@ -51,8 +49,7 @@ public class ExternalEmailOptionsWithAttachmentsDto implements Serializable { @NotNull(message = Validations.requiredField) private final RootEntityType rootEntityType; @NotNull(message = Validations.requiredField) - @Size(min = 1, message = Validations.requiredField) - private String templateName; + private DocumentTemplateReferenceDto template; private Set attachedDocuments; private QuarantineOrderDocumentOptionsDto quarantineOrderDocumentOptionsDto; @@ -70,12 +67,12 @@ public RootEntityType getRootEntityType() { return rootEntityType; } - public String getTemplateName() { - return templateName; + public DocumentTemplateReferenceDto getTemplate() { + return template; } - public void setTemplateName(String templateName) { - this.templateName = templateName; + public void setTemplate(DocumentTemplateReferenceDto template) { + this.template = template; } public Set getAttachedDocuments() { diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index a745b646a23..cac81380ac0 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -1394,6 +1394,7 @@ public interface Captions { String documentNoDocuments = "documentNoDocuments"; String DocumentTemplate = "DocumentTemplate"; String DocumentTemplate_buttonUploadTemplate = "DocumentTemplate.buttonUploadTemplate"; + String DocumentTemplate_disease = "DocumentTemplate.disease"; String DocumentTemplate_documentTemplateGuide = "DocumentTemplate.documentTemplateGuide"; String DocumentTemplate_documentUploadWarning = "DocumentTemplate.documentUploadWarning"; String DocumentTemplate_EventHandout = "DocumentTemplate.EventHandout"; @@ -1407,6 +1408,7 @@ public interface Captions { String DocumentTemplate_exampleTemplateEventParticipants = "DocumentTemplate.exampleTemplateEventParticipants"; String DocumentTemplate_exampleTemplateTravelEntries = "DocumentTemplate.exampleTemplateTravelEntries"; String DocumentTemplate_exampleTemplateTravelEntryEmail = "DocumentTemplate.exampleTemplateTravelEntryEmail"; + String DocumentTemplate_fileName = "DocumentTemplate.fileName"; String DocumentTemplate_fileTooBig = "DocumentTemplate.fileTooBig"; String DocumentTemplate_notUploaded = "DocumentTemplate.notUploaded"; String DocumentTemplate_plural = "DocumentTemplate.plural"; diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java index 5ee37de0065..ec0559d766b 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java @@ -325,6 +325,7 @@ public interface Strings { String errorDeletingDocumentTemplate = "errorDeletingDocumentTemplate"; String errorDocumentGeneration = "errorDocumentGeneration"; String errorDocumentGenerationMultipleDiseasses = "errorDocumentGenerationMultipleDiseasses"; + String errorDocumentTemplateWorkflowChangeNotAllowed = "errorDocumentTemplateWorkflowChangeNotAllowed"; String errorEntityNotEditable = "errorEntityNotEditable"; String errorEntityOutdated = "errorEntityOutdated"; String errorEnvironmentSampleNoDispatchRight = "errorEnvironmentSampleNoDispatchRight"; diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 1f6a4f527d2..1a1c0b040a9 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -1127,6 +1127,8 @@ documentNoDocuments=There are no documents for this %s bulkActionCreatDocuments=Create quarantine order documents # DocumentTemplate DocumentTemplate=Document Template +DocumentTemplate.fileName=File name +DocumentTemplate.disease=Disease DocumentTemplate.buttonUploadTemplate=Upload Template DocumentTemplate.documentTemplateGuide=Document Template Guide DocumentTemplate.plural=Document Templates diff --git a/sormas-api/src/main/resources/strings.properties b/sormas-api/src/main/resources/strings.properties index 798dab4b750..aa9c0c3ea24 100644 --- a/sormas-api/src/main/resources/strings.properties +++ b/sormas-api/src/main/resources/strings.properties @@ -441,6 +441,7 @@ errorExternalEmailAttachmentCannotEncrypt=Can't send email with attachments. The errorAdverseEventNotEditable = This adverse event is not editable anymore errorAdverseEventInvestigationNotEditable = This adverse event investigation is not editable anymore errorExternalEmailMissingPersonEmailAddress=This person does not have an email address +errorDocumentTemplateWorkflowChangeNotAllowed=The workflow of this document template cannot be changed. # headings headingAccessDenied = Access denied diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/action/ActionService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/action/ActionService.java index 12698b51144..d0deeea0f54 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/action/ActionService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/action/ActionService.java @@ -612,11 +612,12 @@ public long countActions(ActionCriteria actionCriteria) { } @Override - public void deletePermanent(Action action) { + public boolean deletePermanent(Action action) { documentService.getRelatedToEntity(DocumentRelatedEntityType.ACTION, action.getUuid()).forEach(d -> documentService.markAsDeleted(d)); super.deletePermanent(action); + return false; } @Override diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java index 4d4d35ab8aa..fd359454347 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java @@ -1033,7 +1033,7 @@ public Predicate createDefaultFilter(CriteriaBuilder cb, From root) { } @Override - public void deletePermanent(Case caze) { + public boolean deletePermanent(Case caze) { // Delete all tasks associated with this case Optional.ofNullable(caze.getTasks()).ifPresent(tl -> tl.forEach(t -> taskService.deletePermanent(t))); @@ -1111,6 +1111,7 @@ public void deletePermanent(Case caze) { deleteCaseLinks(caze); super.deletePermanent(caze); + return false; } private void initVisitSubqueryForDeletion(CriteriaBuilder cb, Root visitRoot, Subquery contactVisitsSubquery) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/AdoService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/AdoService.java index 111cd6abb80..a625f731622 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/AdoService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/AdoService.java @@ -34,10 +34,11 @@ public interface AdoService { /** * DELETES an entity from the database! - * + * * @param ado + * @return */ - void deletePermanent(ADO ado); + boolean deletePermanent(ADO ado); /** * Speichert ein neues Objekt in der Datenbank. diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java index f1fb15356d4..1b022714d67 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/BaseAdoService.java @@ -517,9 +517,10 @@ public void persist(ADO persistme) { } @Override - public void deletePermanent(ADO ado) { + public boolean deletePermanent(ADO ado) { em.remove(em.contains(ado) ? ado : em.merge(ado)); // todo: investigate why the entity might be detached (example: AdditionalTest) em.flush(); + return false; } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java index d81b66d14cb..b051759007e 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/common/StartupShutdownService.java @@ -14,6 +14,7 @@ */ package de.symeda.sormas.backend.common; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -26,6 +27,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; @@ -64,6 +66,7 @@ import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.Language; import de.symeda.sormas.api.customizableenum.CustomizableEnumType; +import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.externaljournal.PatientDiaryConfig; import de.symeda.sormas.api.externaljournal.SymptomJournalConfig; import de.symeda.sormas.api.externaljournal.UserConfig; @@ -88,6 +91,8 @@ import de.symeda.sormas.backend.deletionconfiguration.DeletionConfigurationService; import de.symeda.sormas.backend.disease.DiseaseConfiguration; import de.symeda.sormas.backend.disease.DiseaseConfigurationService; +import de.symeda.sormas.backend.docgeneration.DocumentTemplate; +import de.symeda.sormas.backend.docgeneration.DocumentTemplateService; import de.symeda.sormas.backend.feature.FeatureConfigurationFacadeEjb; import de.symeda.sormas.backend.feature.FeatureConfigurationService; import de.symeda.sormas.backend.importexport.ImportFacadeEjb.ImportFacadeEjbLocal; @@ -186,6 +191,8 @@ public class StartupShutdownService { private CustomizableEnumFacadeEjb.CustomizableEnumFacadeEjbLocal customizableEnumFacade; @EJB private CustomizableEnumValueService customizableEnumValueService; + @EJB + private DocumentTemplateService documentTemplateService; @Inject private Event passwordResetEvent; @@ -868,6 +875,9 @@ private void upgrade() { case 516: fillDefaultUserRole(DefaultUserRole.ENVIRONMENTAL_SURVEILLANCE_USER); break; + case 553: + createEntitiesForDocumentTemplates(); + break; default: throw new NoSuchElementException(DataHelper.toStringNullable(versionNeedingUpgrade)); } @@ -1019,6 +1029,21 @@ private void createMissingDiseaseConfigurations() { }); } + private void createEntitiesForDocumentTemplates() { + Map> templateFiles = documentTemplateService.getAllTemplateFiles(); + + templateFiles.forEach((workflow, files) -> { + for (File file : files) { + DocumentTemplate documentTemplate = new DocumentTemplate(); + documentTemplate.setUuid(DataHelper.createUuid()); + documentTemplate.setWorkflow(workflow); + documentTemplate.setFileName(file.getName()); + + documentTemplateService.ensurePersisted(documentTemplate); + } + }); + } + @PreDestroy public void shutdown() { auditLogger.logApplicationStop(); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java index 3c8f1cd2b22..07b51eb8efa 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/contact/ContactService.java @@ -1722,7 +1722,7 @@ public void restore(Contact contact) { } @Override - public void deletePermanent(Contact contact) { + public boolean deletePermanent(Contact contact) { // Delete all tasks associated with this case Optional.ofNullable(contact.getTasks()).ifPresent(tl -> tl.forEach(t -> taskService.deletePermanent(t))); @@ -1763,6 +1763,7 @@ public void deletePermanent(Contact contact) { deleteContactLinks(contact); super.deletePermanent(contact); + return false; } private void deleteContactLinks(Contact contact) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocGenerationHelper.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocGenerationHelper.java index bd1ba299331..94b7b993860 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocGenerationHelper.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocGenerationHelper.java @@ -13,6 +13,7 @@ import de.symeda.sormas.api.action.ActionReferenceDto; import de.symeda.sormas.api.caze.CaseReferenceDto; import de.symeda.sormas.api.contact.ContactReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.document.DocumentDto; import de.symeda.sormas.api.document.DocumentRelatedEntityDto; import de.symeda.sormas.api.document.DocumentRelatedEntityType; @@ -45,13 +46,13 @@ public DocumentRelatedEntityType getDocumentRelatedEntityType(ReferenceDto rootE } } - public String getDocumentFileName(ReferenceDto rootEntityReference, String templateFileName) { + public String getDocumentFileName(ReferenceDto rootEntityReference, DocumentTemplateReferenceDto templateReference) { List docs = documentFacade.getDocumentsRelatedToEntity(getDocumentRelatedEntityType(rootEntityReference), rootEntityReference.getUuid()); return generateNewFileName( docs.stream().map(DocumentDto::getName).collect(Collectors.toList()), DataHelper.getShortUuid(rootEntityReference), - '-' + templateFileName); + '-' + templateReference.getCaption()); } private String generateNewFileName(List docs, String shortUuid, String templateFileName) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplate.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplate.java new file mode 100644 index 00000000000..1babf8c57ac --- /dev/null +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplate.java @@ -0,0 +1,68 @@ +/* + * SORMAS® - Surveillance Outbreak Response Management & Analysis System + * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.symeda.sormas.backend.docgeneration; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; + +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; +import de.symeda.sormas.api.utils.FieldConstraints; +import de.symeda.sormas.backend.common.AbstractDomainObject; + +@Entity(name = "documenttemplates") +public class DocumentTemplate extends AbstractDomainObject { + + private static final long serialVersionUID = -8191658284208086022L; + + public static final String WORKFLOW = "workflow"; + public static final String DISEASE = "disease"; + public static final String DOCUMENT_PATH = "documentPath"; + + private DocumentWorkflow workflow; + private Disease disease; + private String fileName; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + public DocumentWorkflow getWorkflow() { + return workflow; + } + + public void setWorkflow(DocumentWorkflow workflow) { + this.workflow = workflow; + } + + @Enumerated(EnumType.STRING) + public Disease getDisease() { + return disease; + } + + public void setDisease(Disease disease) { + this.disease = disease; + } + + @Column(length = FieldConstraints.CHARACTER_LIMIT_BIG) + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } +} diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjb.java index ae62fc5870c..79f2dd24828 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjb.java @@ -17,14 +17,8 @@ import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.Set; @@ -32,22 +26,27 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.Nullable; import javax.annotation.security.PermitAll; import javax.ejb.EJB; import javax.ejb.LocalBean; import javax.ejb.Stateless; +import javax.persistence.criteria.Predicate; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.EntityDtoAccessHelper; import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.caze.CaseReferenceDto; import de.symeda.sormas.api.contact.ContactReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateEntities; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentTemplateFacade; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.DocumentWorkflowType; @@ -72,6 +71,7 @@ import de.symeda.sormas.api.uuid.HasUuid; import de.symeda.sormas.backend.caze.CaseFacadeEjb.CaseFacadeEjbLocal; import de.symeda.sormas.backend.common.ConfigFacadeEjb.ConfigFacadeEjbLocal; +import de.symeda.sormas.backend.common.CriteriaBuilderHelper; import de.symeda.sormas.backend.contact.ContactFacadeEjb.ContactFacadeEjbLocal; import de.symeda.sormas.backend.event.EventFacadeEjb.EventFacadeEjbLocal; import de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal; @@ -85,6 +85,7 @@ import de.symeda.sormas.backend.travelentry.TravelEntryFacadeEjb.TravelEntryFacadeEjbLocal; import de.symeda.sormas.backend.user.UserFacadeEjb.UserFacadeEjbLocal; import de.symeda.sormas.backend.user.UserService; +import de.symeda.sormas.backend.util.DtoHelper; import de.symeda.sormas.backend.util.RightsAllowed; @Stateless(name = "DocumentTemplateFacade") @@ -94,6 +95,9 @@ public class DocumentTemplateFacadeEjb implements DocumentTemplateFacade { public static final int EMAIL_SUBJECT_MAX_LENGTH = 50; public static final String EMAIL_TEMPLATE_SUBJECT_PREFIX = "#"; + @EJB + private DocumentTemplateService documentTemplateService; + @EJB private ConfigFacadeEjbLocal configFacade; @@ -144,18 +148,20 @@ public class DocumentTemplateFacadeEjb implements DocumentTemplateFacade { @Override @PermitAll public byte[] generateDocumentDocxFromEntities( - DocumentWorkflow documentWorkflow, - String templateName, + DocumentTemplateReferenceDto templateReference, DocumentTemplateEntities entities, Properties extraProperties) throws DocumentTemplateException { + DocumentTemplate template = documentTemplateService.getByReferenceDto(templateReference); + DocumentWorkflow documentWorkflow = template.getWorkflow(); + if (!documentWorkflow.isDocx()) { throw new DocumentTemplateException( String.format(I18nProperties.getString(Strings.messageWrongTemplateFileType), documentWorkflow, documentWorkflow.getFileExtension())); } // 1. Read template from custom directory - File templateFile = getTemplateFile(documentWorkflow, templateName); + File templateFile = documentTemplateService.getTemplateFile(template); // 2. Extract document variables DocumentVariables documentVariables = getTemplateVariablesDocx(templateFile); @@ -170,24 +176,27 @@ public byte[] generateDocumentDocxFromEntities( @Override @PermitAll public String generateDocumentTxtFromEntities( - DocumentWorkflow documentWorkflow, - String templateName, + DocumentTemplateReferenceDto templateReference, DocumentTemplateEntities entities, Properties extraProperties) throws DocumentTemplateException { - if (documentWorkflow.isDocx()) { + DocumentTemplate template = documentTemplateService.getByReferenceDto(templateReference); + if (template.getWorkflow().isDocx()) { throw new DocumentTemplateException( - String.format(I18nProperties.getString(Strings.messageWrongTemplateFileType), documentWorkflow, documentWorkflow.getFileExtension())); + String.format( + I18nProperties.getString(Strings.messageWrongTemplateFileType), + template.getWorkflow(), + template.getWorkflow().getFileExtension())); } // 1. Read template from custom directory - File templateFile = getTemplateFile(documentWorkflow, templateName); + File templateFile = documentTemplateService.getTemplateFile(template); // 2. Extract document variables DocumentVariables documentVariables = getTemplateVariablesTxt(templateFile); // 3. prepare properties - Properties properties = prepareProperties(documentWorkflow, entities, extraProperties, documentVariables); + Properties properties = prepareProperties(template.getWorkflow(), entities, extraProperties, documentVariables); // 4. generate document return generateDocumentTxt(templateFile, properties); @@ -283,34 +292,38 @@ private String generateDocumentTxt(File templateFile, Properties properties) { @Override @PermitAll - public List getAvailableTemplates(DocumentWorkflow documentWorkflow) { - File workflowTemplateDir = new File(getWorkflowTemplateDirPath(documentWorkflow).toUri()); - if (!workflowTemplateDir.exists() || !workflowTemplateDir.isDirectory()) { - return Collections.emptyList(); - } - File[] availableTemplates = - workflowTemplateDir.listFiles((d, name) -> name.toLowerCase().endsWith("." + documentWorkflow.getFileExtension())); - if (availableTemplates == null) { - return Collections.emptyList(); - } - return Arrays.stream(availableTemplates).map(File::getName).sorted(String::compareTo).collect(Collectors.toList()); + public List getAvailableTemplates(DocumentWorkflow documentWorkflow, @Nullable Disease disease) { + List templates = documentTemplateService.getByPredicate((cb, root, cq) -> { + Predicate diseasePredicate = null; + + if (disease != null) { + diseasePredicate = cb.or(cb.isNull(root.get(DocumentTemplate.DISEASE)), cb.equal(root.get(DocumentTemplate.DISEASE), disease)); + } + + return CriteriaBuilderHelper.and(cb, cb.equal(root.get(DocumentTemplate.WORKFLOW), documentWorkflow), diseasePredicate); + }); + + return templates.stream().map(DocumentTemplateFacadeEjb::toDto).collect(Collectors.toList()); } @Override @RightsAllowed({ UserRight._DOCUMENT_TEMPLATE_MANAGEMENT, UserRight._EMAIL_TEMPLATE_MANAGEMENT }) - public boolean isExistingTemplate(DocumentWorkflow documentWorkflow, String templateName) { + public boolean isExistingTemplateFile(DocumentWorkflow documentWorkflow, Disease disease, String templateName) { assertRequredUserRight(documentWorkflow); - File templateFile = new File(getWorkflowTemplateDirPath(documentWorkflow).resolve(templateName).toUri()); - return templateFile.exists(); + return documentTemplateService.existsFile(documentWorkflow, disease, templateName); + } @Override @PermitAll - public DocumentVariables getDocumentVariables(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException { - File templateFile = getTemplateFile(documentWorkflow, templateName); + public DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + DocumentTemplate template = documentTemplateService.getByReferenceDto(templateReference); + DocumentWorkflow documentWorkflow = template.getWorkflow(); + + File templateFile = documentTemplateService.getTemplateFile(template); DocumentVariables documentVariables = documentWorkflow.isDocx() ? getTemplateVariablesDocx(templateFile) : getTemplateVariablesTxt(templateFile); Set propertyKeys = documentVariables.getVariables(); @@ -326,16 +339,18 @@ public DocumentVariables getDocumentVariables(DocumentWorkflow documentWorkflow, @RightsAllowed({ UserRight._DOCUMENT_TEMPLATE_MANAGEMENT, UserRight._EMAIL_TEMPLATE_MANAGEMENT }) - public void writeDocumentTemplate(DocumentWorkflow documentWorkflow, String templateName, byte[] document) throws DocumentTemplateException { + public DocumentTemplateDto saveDocumentTemplate(DocumentTemplateDto template, byte[] document) throws DocumentTemplateException { + DocumentWorkflow documentWorkflow = template.getWorkflow(); assertRequredUserRight(documentWorkflow); - if (!documentWorkflow.getFileExtension().equalsIgnoreCase(FilenameUtils.getExtension(templateName))) { - throw new DocumentTemplateException(I18nProperties.getString(Strings.headingWrongFileType)); + String fileName = template.getFileName(); + if (!documentWorkflow.getFileExtension().equalsIgnoreCase(FilenameUtils.getExtension(fileName))) { + throw new ValidationRuntimeException(I18nProperties.getString(Strings.headingWrongFileType)); } - String path = FilenameUtils.getPath(templateName); + String path = FilenameUtils.getPath(fileName); if (StringUtils.isNotBlank(path)) { - throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorIllegalFilename), templateName)); + throw new ValidationRuntimeException(String.format(I18nProperties.getString(Strings.errorIllegalFilename), fileName)); } ByteArrayInputStream templateInputStream = new ByteArrayInputStream(document); @@ -349,56 +364,47 @@ public void writeDocumentTemplate(DocumentWorkflow documentWorkflow, String temp validateEmailTemplate(document); } - Path workflowTemplateDirPath = getWorkflowTemplateDirPath(documentWorkflow); - try { - Files.createDirectories(workflowTemplateDirPath); - } catch (IOException e) { - throw new DocumentTemplateException(I18nProperties.getString(Strings.errorCreatingTemplateDirectory)); - } - try (FileOutputStream fileOutputStream = - new FileOutputStream(new File(workflowTemplateDirPath.resolve(FilenameUtils.getName(templateName)).toUri()))) { - fileOutputStream.write(document); - } catch (IOException e) { - throw new DocumentTemplateException(I18nProperties.getString(Strings.errorWritingTemplate)); + DocumentTemplate existingTemplate = documentTemplateService.getByReferenceDto(template.toReference()); + if (existingTemplate != null) { + if (existingTemplate.getWorkflow() != template.getWorkflow()) { + throw new ValidationRuntimeException(I18nProperties.getString(Strings.errorDocumentTemplateWorkflowChangeNotAllowed)); + } + if (existingTemplate.getDisease() != template.getDisease() || existingTemplate.getFileName() != template.getFileName()) { + documentTemplateService.deleteTemplateFile(existingTemplate); + } } + + DocumentTemplate documentTemplate = fillOrBuildEntity(template, existingTemplate); + documentTemplateService.ensurePersisted(documentTemplate, document); + + return toDto(documentTemplate); } @Override @RightsAllowed({ UserRight._DOCUMENT_TEMPLATE_MANAGEMENT, UserRight._EMAIL_TEMPLATE_MANAGEMENT }) - public boolean deleteDocumentTemplate(DocumentWorkflow documentWorkflow, String fileName) throws DocumentTemplateException { - assertRequredUserRight(documentWorkflow); + public boolean deleteDocumentTemplate(DocumentTemplateReferenceDto templateReference) { + DocumentTemplate template = documentTemplateService.getByReferenceDto(templateReference); - File templateFile = new File(getWorkflowTemplateDirPath(documentWorkflow).resolve(fileName).toUri()); - if (templateFile.exists() && templateFile.isFile()) { - return templateFile.delete(); - } else { - throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorFileNotFound), fileName)); - } + assertRequredUserRight(template.getWorkflow()); + + return documentTemplateService.deletePermanent(template); } @Override @RightsAllowed({ UserRight._DOCUMENT_TEMPLATE_MANAGEMENT, UserRight._EMAIL_TEMPLATE_MANAGEMENT }) - public byte[] getDocumentTemplate(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException { - assertRequredUserRight(documentWorkflow); + public byte[] getDocumentTemplateContent(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + DocumentTemplate template = documentTemplateService.getByReferenceDto(templateReference); + assertRequredUserRight(template.getWorkflow()); try { - return FileUtils.readFileToByteArray(getTemplateFile(documentWorkflow, templateName)); + return FileUtils.readFileToByteArray(documentTemplateService.getTemplateFile(template)); } catch (IOException e) { - throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorReadingTemplate), templateName)); - } - } - - private File getTemplateFile(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException { - File templateFile = new File(getWorkflowTemplateDirPath(documentWorkflow).resolve(templateName).toString()); - - if (!templateFile.exists()) { - throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorFileNotFound), templateName)); + throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorReadingTemplate), template.getFileName())); } - return templateFile; } private DocumentVariables getTemplateVariablesDocx(File templateFile) throws DocumentTemplateException { @@ -423,10 +429,6 @@ private String getVariableBaseName(String propertyKey) { return matcher.matches() ? matcher.group(1) : ""; } - private Path getWorkflowTemplateDirPath(DocumentWorkflow documentWorkflow) { - return Paths.get(configFacade.getCustomFilesPath()).resolve("docgeneration").resolve(documentWorkflow.getTemplateDirectory()); - } - private EntityDtoAccessHelper.IReferenceDtoResolver getReferenceDtoResolver() { EntityDtoAccessHelper.IReferenceDtoResolver referenceDtoResolver = referenceDto -> { if (referenceDto != null) { @@ -482,6 +484,31 @@ private static void validateEmailTemplate(byte[] document) { } } + public static DocumentTemplateDto toDto(DocumentTemplate source) { + if (source == null) { + return null; + } + + DocumentTemplateDto target = new DocumentTemplateDto(); + DtoHelper.fillDto(target, source); + + target.setWorkflow(source.getWorkflow()); + target.setDisease(source.getDisease()); + target.setFileName(source.getFileName()); + + return target; + } + + private DocumentTemplate fillOrBuildEntity(DocumentTemplateDto source, DocumentTemplate target) { + target = DtoHelper.fillOrBuildEntity(source, target, DocumentTemplate::new, true); + + target.setWorkflow(source.getWorkflow()); + target.setDisease(source.getDisease()); + target.setFileName(source.getFileName()); + + return target; + } + public static EmailTemplateTexts splitTemplateContent(String content) { return splitTemplateContent(content, true); } @@ -499,6 +526,11 @@ private static EmailTemplateTexts splitTemplateContent(String templateString, bo return new EmailTemplateTexts(cleanupSubject ? subjectLine.substring(1).trim() : subjectLine, content); } + @PermitAll + public DocumentTemplateDto getByUuid(String uuid) { + return toDto(documentTemplateService.getByUuid(uuid)); + } + public static final class EmailTemplateTexts { private final String subject; @@ -518,7 +550,7 @@ public String getContent() { } } - public class ObjectFormatter { + public static class ObjectFormatter { public Object format(Object value) { return EntityDtoAccessHelper.formatObject(value); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateService.java new file mode 100644 index 00000000000..7aa7cba0c79 --- /dev/null +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateService.java @@ -0,0 +1,141 @@ +/* + * SORMAS® - Surveillance Outbreak Response Management & Analysis System + * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.symeda.sormas.backend.docgeneration; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.ejb.EJB; +import javax.ejb.LocalBean; +import javax.ejb.Stateless; + +import org.apache.commons.io.FilenameUtils; + +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; +import de.symeda.sormas.api.i18n.I18nProperties; +import de.symeda.sormas.api.i18n.Strings; +import de.symeda.sormas.backend.common.BaseAdoService; +import de.symeda.sormas.backend.common.ConfigFacadeEjb; + +@Stateless +@LocalBean +public class DocumentTemplateService extends BaseAdoService { + + @EJB + private ConfigFacadeEjb.ConfigFacadeEjbLocal configFacade; + + public DocumentTemplateService() { + super(DocumentTemplate.class); + } + + @Override + public boolean deletePermanent(DocumentTemplate documentTemplate) { + boolean fileDeleted = deleteTemplateFile(documentTemplate); + if (fileDeleted) { + super.deletePermanent(documentTemplate); + } + + return fileDeleted; + } + + public boolean deleteTemplateFile(DocumentTemplate documentTemplate) { + String fileName = documentTemplate.getFileName(); + File templateFile = new File(getTemplateDirPath(documentTemplate).resolve(fileName).toUri()); + + boolean deleted = false; + boolean exists = templateFile.exists(); + if (!exists) { + return true; + } + + if (templateFile.isFile()) { + deleted = templateFile.delete(); + } + + return deleted; + } + + private Path getTemplateDirPath(DocumentTemplate documentTemplate) { + return getTemplateDirPath(documentTemplate.getWorkflow(), documentTemplate.getDisease()); + } + + private Path getTemplateDirPath(DocumentWorkflow documentWorkflow, Disease disease) { + Path path = Paths.get(configFacade.getCustomFilesPath()).resolve("docgeneration").resolve(documentWorkflow.getTemplateDirectory()); + + if (disease != null) { + path = path.resolve(disease.name()); + } + + return path; + } + + public boolean existsFile(DocumentWorkflow documentWorkflow, Disease disease, String templateName) { + File templateFile = new File(this.getTemplateDirPath(documentWorkflow, disease).resolve(templateName).toUri()); + return templateFile.exists(); + } + + public void ensurePersisted(DocumentTemplate documentTemplate, byte[] document) throws DocumentTemplateException { + Path workflowTemplateDirPath = getTemplateDirPath(documentTemplate.getWorkflow(), documentTemplate.getDisease()); + try { + Files.createDirectories(workflowTemplateDirPath); + } catch (IOException e) { + throw new DocumentTemplateException(I18nProperties.getString(Strings.errorCreatingTemplateDirectory)); + } + + try (FileOutputStream fileOutputStream = + new FileOutputStream(new File(workflowTemplateDirPath.resolve(FilenameUtils.getName(documentTemplate.getFileName())).toUri()))) { + fileOutputStream.write(document); + ensurePersisted(documentTemplate); + } catch (IOException e) { + throw new DocumentTemplateException(I18nProperties.getString(Strings.errorWritingTemplate)); + } + } + + public File getTemplateFile(DocumentTemplate template) throws DocumentTemplateException { + File templateFile = new File(getTemplateDirPath(template).resolve(template.getFileName()).toString()); + + if (!templateFile.exists()) { + throw new DocumentTemplateException(String.format(I18nProperties.getString(Strings.errorFileNotFound), template.getFileName())); + } + return templateFile; + } + + public Map> getAllTemplateFiles() { + Map> templateFiles = new HashMap<>(); + + for (DocumentWorkflow workflow : DocumentWorkflow.values()) { + Path templateDirPath = getTemplateDirPath(workflow, null); + File templateDir = templateDirPath.toFile(); + + if (templateDir.exists() && templateDir.isDirectory()) { + templateFiles.put(workflow, Stream.of(templateDir.listFiles()).filter(File::isFile).collect(Collectors.toList())); + } + } + + return templateFiles; + } +} diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjb.java index 02f6ab5a25f..d30496f7bef 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjb.java @@ -13,10 +13,13 @@ import org.apache.commons.io.IOUtils; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.action.ActionCriteria; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateEntities; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EventDocumentFacade; @@ -50,7 +53,7 @@ public class EventDocumentFacadeEjb implements EventDocumentFacade { @Override public String getGeneratedDocument( - String templateName, + DocumentTemplateReferenceDto templateReference, EventReferenceDto eventReference, Properties extraProperties, Boolean shouldUploadGeneratedDoc) @@ -64,13 +67,13 @@ public String getGeneratedDocument( entities .addEntity(RootEntityType.ROOT_EVENT_PARTICIPANTS, eventParticipantFacade.getAllActiveEventParticipantsByEvent(eventReference.getUuid())); - String body = documentTemplateFacade.generateDocumentTxtFromEntities(DOCUMENT_WORKFLOW, templateName, entities, extraProperties); - String styledHtml = createStyledHtml(templateName, body); + String body = documentTemplateFacade.generateDocumentTxtFromEntities(templateReference, entities, extraProperties); + String styledHtml = createStyledHtml(templateReference.getCaption(), body); if (shouldUploadGeneratedDoc) { byte[] documentToSave = styledHtml.getBytes(StandardCharsets.UTF_8);//mandatory UTF_8 try { helper.saveDocument( - helper.getDocumentFileName(eventReference, templateName), + helper.getDocumentFileName(eventReference, templateReference), DocumentDto.MIME_TYPE_DEFAULT, documentToSave.length, helper.getDocumentRelatedEntityType(eventReference), @@ -85,7 +88,7 @@ public String getGeneratedDocument( @Override public Map getGeneratedDocuments( - String templateName, + DocumentTemplateReferenceDto templateReference, List eventReferences, Properties extraProperties, Boolean shouldUploadGeneratedDoc) @@ -93,7 +96,7 @@ public Map getGeneratedDocuments( Map documents = new HashMap<>(eventReferences.size()); for (EventReferenceDto referenceDto : eventReferences) { - String documentContent = getGeneratedDocument(templateName, referenceDto, extraProperties, shouldUploadGeneratedDoc); + String documentContent = getGeneratedDocument(templateReference, referenceDto, extraProperties, shouldUploadGeneratedDoc); documents.put(referenceDto, documentContent.getBytes(StandardCharsets.UTF_8)); } @@ -101,13 +104,13 @@ public Map getGeneratedDocuments( } @Override - public List getAvailableTemplates() { - return documentTemplateFacade.getAvailableTemplates(DOCUMENT_WORKFLOW); + public List getAvailableTemplates(Disease disease) { + return documentTemplateFacade.getAvailableTemplates(DOCUMENT_WORKFLOW, disease); } @Override - public DocumentVariables getDocumentVariables(String templateName) throws DocumentTemplateException { - return documentTemplateFacade.getDocumentVariables(DOCUMENT_WORKFLOW, templateName); + public DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + return documentTemplateFacade.getDocumentVariables(templateReference); } private String createStyledHtml(String title, String body) throws DocumentTemplateException { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjb.java index 8933054aeb8..c92a09cb6e7 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjb.java @@ -26,8 +26,10 @@ import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.ReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateEntities; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.QuarantineOrderFacade; @@ -59,8 +61,7 @@ public class QuarantineOrderFacadeEjb implements QuarantineOrderFacade { @Override public byte[] getGeneratedDocument( - String templateName, - DocumentWorkflow workflow, + DocumentTemplateReferenceDto templateReference, RootEntityType rootEntityType, ReferenceDto rootEntityReference, SampleReferenceDto sampleReference, @@ -72,9 +73,9 @@ public byte[] getGeneratedDocument( DocumentTemplateEntities entities = entitiesBuilder .getQuarantineOrderEntities(rootEntityType, rootEntityReference, sampleReference, pathogenTestReference, vaccinationReference); - byte[] documentToSave = documentTemplateFacade.generateDocumentDocxFromEntities(workflow, templateName, entities, extraProperties); + byte[] documentToSave = documentTemplateFacade.generateDocumentDocxFromEntities(templateReference, entities, extraProperties); if (shouldUploadGeneratedDoc) { - uploadDocument(helper.getDocumentFileName(rootEntityReference, templateName), rootEntityReference, documentToSave); + uploadDocument(helper.getDocumentFileName(rootEntityReference, templateReference), rootEntityReference, documentToSave); } return documentToSave; } @@ -105,22 +106,23 @@ private boolean isFileSizeLimitExceeded(int length) { @Override public Map getGeneratedDocuments( - String templateName, - DocumentWorkflow workflow, + DocumentTemplateReferenceDto templateReference, List rootEntityReferences, Properties extraProperties, Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException { + DocumentTemplateDto template = documentTemplateFacade.getByUuid(templateReference.getUuid()); + Map quarantineOrderEntities = - entitiesBuilder.getQuarantineOrderEntities(workflow, rootEntityReferences); + entitiesBuilder.getQuarantineOrderEntities(template.getWorkflow(), rootEntityReferences); - return getGeneratedDocuments(templateName, workflow, quarantineOrderEntities, extraProperties, shouldUploadGeneratedDoc); + return getGeneratedDocuments(templateReference, quarantineOrderEntities, extraProperties, shouldUploadGeneratedDoc); } @Override public Map getGeneratedDocumentsForEventParticipants( - String templateName, + DocumentTemplateReferenceDto templateReference, List rootEntityReferences, Disease eventDisease, Properties extraProperties, @@ -130,17 +132,11 @@ public Map getGeneratedDocumentsForEventParticipants( Map quarantineOrderEntities = entitiesBuilder.getEventParticipantQuarantineOrderEntities(rootEntityReferences, eventDisease); - return getGeneratedDocuments( - templateName, - DocumentWorkflow.QUARANTINE_ORDER_EVENT_PARTICIPANT, - quarantineOrderEntities, - extraProperties, - shouldUploadGeneratedDoc); + return getGeneratedDocuments(templateReference, quarantineOrderEntities, extraProperties, shouldUploadGeneratedDoc); } private Map getGeneratedDocuments( - String templateName, - DocumentWorkflow workflow, + DocumentTemplateReferenceDto templateReference, Map quarantineOrderEntities, Properties extraProperties, Boolean shouldUploadGeneratedDoc) @@ -149,10 +145,9 @@ private Map getGeneratedDocuments( Map documents = new HashMap<>(quarantineOrderEntities.size()); for (Map.Entry entities : quarantineOrderEntities.entrySet()) { - byte[] documentContent = - documentTemplateFacade.generateDocumentDocxFromEntities(workflow, templateName, entities.getValue(), extraProperties); + byte[] documentContent = documentTemplateFacade.generateDocumentDocxFromEntities(templateReference, entities.getValue(), extraProperties); if (shouldUploadGeneratedDoc) { - uploadDocument(helper.getDocumentFileName(entities.getKey(), templateName), entities.getKey(), documentContent); + uploadDocument(helper.getDocumentFileName(entities.getKey(), templateReference), entities.getKey(), documentContent); } documents.put(entities.getKey(), documentContent); } @@ -161,13 +156,13 @@ private Map getGeneratedDocuments( } @Override - public List getAvailableTemplates(DocumentWorkflow workflow) { - return documentTemplateFacade.getAvailableTemplates(workflow); + public List getAvailableTemplates(DocumentWorkflow workflow, Disease disease) { + return documentTemplateFacade.getAvailableTemplates(workflow, disease); } @Override - public DocumentVariables getDocumentVariables(DocumentWorkflow documentWorkflow, String templateName) throws DocumentTemplateException { - return documentTemplateFacade.getDocumentVariables(documentWorkflow, templateName); + public DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + return documentTemplateFacade.getDocumentVariables(templateReference); } @LocalBean diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/TemplateEngine.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/TemplateEngine.java index 3f7da3c0d99..f0b40263530 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/TemplateEngine.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/docgeneration/TemplateEngine.java @@ -55,6 +55,7 @@ import de.symeda.sormas.api.i18n.I18nProperties; import de.symeda.sormas.api.i18n.Strings; import de.symeda.sormas.api.utils.HtmlHelper; +import de.symeda.sormas.api.utils.ValidationRuntimeException; import fr.opensagres.xdocreport.core.XDocReportException; import fr.opensagres.xdocreport.document.IXDocReport; import fr.opensagres.xdocreport.document.registry.XDocReportRegistry; @@ -181,13 +182,15 @@ public String generateDocumentTxt(Properties properties, File templateFile) { return Jsoup.clean(stringWriter.toString(), "", HTML_TEMPLATE_WHITELIST, outputSettings); } - public void validateTemplateDocx(InputStream templateInputStream) throws DocumentTemplateException { + public void validateTemplateDocx(InputStream templateInputStream) { try { IXDocReport report = readXDocReport(templateInputStream); FieldsExtractor extractor = FieldsExtractor.create(); report.extractFields(extractor); } catch (XDocReportException | IOException e) { - throw new DocumentTemplateException(I18nProperties.getString(Strings.errorProcessingTemplate)); + throw new ValidationRuntimeException(I18nProperties.getString(Strings.errorProcessingTemplate)); + } catch (DocumentTemplateException e) { + throw new ValidationRuntimeException(e.getMessage()); } } @@ -214,11 +217,11 @@ protected IXDocReport readXDocReport(InputStream templateInputStream) throws Doc } } - public void validateTemplateTxt(InputStream templateInputStream) throws DocumentTemplateException { + public void validateTemplateTxt(InputStream templateInputStream) { getFieldExtractorTxt(new InputStreamReader(templateInputStream), "validate"); } - private FieldsExtractor getFieldExtractorTxt(Reader templateFileReader, String templateName) throws DocumentTemplateException { + private FieldsExtractor getFieldExtractorTxt(Reader templateFileReader, String templateName) { FieldsExtractor extractor = FieldsExtractor.create(); ExtractVariablesVelocityVisitor visitor = new ExtractVariablesVelocityVisitor(extractor); try { @@ -228,7 +231,7 @@ private FieldsExtractor getFieldExtractorTxt(Reader templateFile document.jjtAccept(visitor, null); return extractor; } catch (ParseException e) { - throw new DocumentTemplateException(I18nProperties.getString(Strings.errorProcessingTemplate)); + throw new ValidationRuntimeException(I18nProperties.getString(Strings.errorProcessingTemplate)); } } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventParticipantService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventParticipantService.java index a9941a14f7e..781b2fb686a 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventParticipantService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventParticipantService.java @@ -333,7 +333,7 @@ public void restore(EventParticipant eventParticipant) { } @Override - public void deletePermanent(EventParticipant eventParticipant) { + public boolean deletePermanent(EventParticipant eventParticipant) { if (eventParticipant.getSamples() != null) { for (Sample sample : eventParticipant.getSamples()) { if (sample.getAssociatedCase() == null && sample.getAssociatedContact() == null) { @@ -354,6 +354,7 @@ public void deletePermanent(EventParticipant eventParticipant) { }); super.deletePermanent(eventParticipant); + return false; } public List getAllUuidsByEventUuids(List eventUuids) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java index 7814c312f48..89f07b85306 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/event/EventService.java @@ -638,7 +638,7 @@ public void restore(Event event) { } @Override - public void deletePermanent(Event event) { + public boolean deletePermanent(Event event) { // Delete all tasks associated with this event List tasks = taskService.findBy(new TaskCriteria().event(new EventReferenceDto(event.getUuid())), true); @@ -677,6 +677,7 @@ public void deletePermanent(Event event) { removeFromSubordinateEvents(event); super.deletePermanent(event); + return false; } private void removeFromSubordinateEvents(Event event) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java index c61d503ac4f..aac294570ad 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java @@ -51,6 +51,7 @@ import de.symeda.sormas.api.common.progress.ProcessedEntity; import de.symeda.sormas.api.common.progress.ProcessedEntityStatus; import de.symeda.sormas.api.contact.ContactReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateEntities; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; @@ -157,8 +158,8 @@ public class ExternalEmailFacadeEjb implements ExternalEmailFacade { private DocGenerationHelper docGenerationHelper; @Override - public List getTemplateNames(DocumentWorkflow documentWorkflow) { - return documentTemplateFacade.getAvailableTemplates(documentWorkflow); + public List getTemplates(DocumentWorkflow documentWorkflow) { + return documentTemplateFacade.getAvailableTemplates(documentWorkflow, null); } @Override @@ -200,7 +201,7 @@ private void sendEmail(@Valid ExternalEmailOptionsDto options, boolean onlyLinke String personalizedFileName = null; Map filesToBeEncryped = new HashMap<>(); - if (options.getQuarantineOrderDocumentOptions() != null && options.getQuarantineOrderDocumentOptions().getTemplateFile() != null) { + if (options.getQuarantineOrderDocumentOptions() != null && options.getQuarantineOrderDocumentOptions().getTemplate() != null) { Map.Entry fileFromTemplate = createFileFromTemplate(options); personalizedFile = fileFromTemplate.getKey(); personalizedFileName = fileFromTemplate.getValue(); @@ -232,8 +233,7 @@ private void sendEmail(@Valid ExternalEmailOptionsDto options, boolean onlyLinke emailAttachments = attachmentService.createEncryptedPdfs(filesToBeEncryped, password); } - String generatedText = - documentTemplateFacade.generateDocumentTxtFromEntities(options.getDocumentWorkflow(), options.getTemplateName(), documentEntities, null); + String generatedText = documentTemplateFacade.generateDocumentTxtFromEntities(options.getTemplate(), documentEntities, null); EmailTemplateTexts emailTexts = splitTemplateContent(generatedText); try { @@ -281,8 +281,7 @@ private void sendEmail(@Valid ExternalEmailOptionsDto options, boolean onlyLinke private Map.Entry createFileFromTemplate(ExternalEmailOptionsDto emailOptions) throws DocumentTemplateException, IOException { QuarantineOrderDocumentOptionsDto documentOptions = emailOptions.getQuarantineOrderDocumentOptions(); byte[] generatedDocument = quarantineOrderFacade.getGeneratedDocument( - documentOptions.getTemplateFile(), - documentOptions.getDocumentWorkflow(), + documentOptions.getTemplate(), emailOptions.getRootEntityType(), emailOptions.getRootEntityReference(), null, @@ -291,7 +290,7 @@ private Map.Entry createFileFromTemplate(ExternalEmailOptionsDto e documentOptions.getExtraProperties(), // document uploading will be handled in another place false); - String fileName = docGenerationHelper.getDocumentFileName(emailOptions.getRootEntityReference(), documentOptions.getTemplateFile()); + String fileName = docGenerationHelper.getDocumentFileName(emailOptions.getRootEntityReference(), documentOptions.getTemplate()); File tempTemplateFile = Paths.get(configFacade.getTempFilesPath()).resolve(fileName).toFile(); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(Files.newOutputStream(tempTemplateFile.toPath())); bufferedOutputStream.write(generatedDocument); @@ -341,7 +340,7 @@ public List sendBulkEmail(@Valid ExternalEmailOptionsWithAttach try { ExternalEmailOptionsDto emailOptions = new ExternalEmailOptionsDto(options.getDocumentWorkflow(), options.getRootEntityType(), entityRef); - emailOptions.setTemplateName(options.getTemplateName()); + emailOptions.setTemplate(options.getTemplate()); emailOptions.setAttachedDocuments(attachedDocuments); emailOptions.setRootEntityReference(entityRef); @@ -481,7 +480,7 @@ private ManualMessageLog createMessageLog( log.setRecipientPerson(personService.getByReferenceDto(personRef)); log.setSentDate(new Date()); log.setEmailAddress(options.getRecipientEmail()); - log.setUsedTemplate(options.getTemplateName()); + log.setUsedTemplate(options.getTemplate().getCaption()); log.setAttachedDocuments(attachedDocumentsName); // `*Service::getByReferenceDto` does a null check, so we don't need to do it here diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageService.java index f4bd04ba457..d163cb10330 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageService.java @@ -50,9 +50,10 @@ public ExternalMessageService() { } @Override - public void deletePermanent(ExternalMessage externalMessage) { + public boolean deletePermanent(ExternalMessage externalMessage) { super.deletePermanent(externalMessage); + return false; } @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/labmessage/SampleReportService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/labmessage/SampleReportService.java index 8153c30db63..542a4ca6fd0 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/labmessage/SampleReportService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/labmessage/SampleReportService.java @@ -15,8 +15,7 @@ package de.symeda.sormas.backend.externalmessage.labmessage; -import de.symeda.sormas.backend.common.BaseAdoService; -import de.symeda.sormas.backend.common.DeletableAdo; +import java.util.Optional; import javax.ejb.EJB; import javax.ejb.LocalBean; @@ -25,7 +24,9 @@ import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import java.util.Optional; + +import de.symeda.sormas.backend.common.BaseAdoService; +import de.symeda.sormas.backend.common.DeletableAdo; @Stateless @LocalBean @@ -47,10 +48,11 @@ public Predicate createDefaultFilter(CriteriaBuilder cb, Root root) } @Override - public void deletePermanent(SampleReport sampleReport) { + public boolean deletePermanent(SampleReport sampleReport) { Optional.ofNullable(sampleReport.getTestReports()).ifPresent(testReports -> testReports.forEach(t -> testReportService.deletePermanent(t))); super.deletePermanent(sampleReport); + return false; } } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/immunization/ImmunizationService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/immunization/ImmunizationService.java index a52daa2c309..5b3e12e0d7e 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/immunization/ImmunizationService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/immunization/ImmunizationService.java @@ -22,7 +22,6 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.stream.Collectors; import javax.ejb.EJB; import javax.ejb.LocalBean; @@ -56,7 +55,6 @@ import de.symeda.sormas.api.immunization.ImmunizationStatus; import de.symeda.sormas.api.immunization.MeansOfImmunization; import de.symeda.sormas.api.person.PersonAssociation; -import de.symeda.sormas.api.person.PersonCriteria; import de.symeda.sormas.api.sormastosormas.share.incoming.ShareRequestStatus; import de.symeda.sormas.backend.caze.Case; import de.symeda.sormas.backend.common.AbstractCoreAdoService; @@ -73,7 +71,6 @@ import de.symeda.sormas.backend.person.Person; import de.symeda.sormas.backend.person.PersonJoins; import de.symeda.sormas.backend.person.PersonJurisdictionPredicateValidator; -import de.symeda.sormas.backend.person.PersonQueryContext; import de.symeda.sormas.backend.person.PersonService; import de.symeda.sormas.backend.sormastosormas.origin.SormasToSormasOriginInfo; import de.symeda.sormas.backend.sormastosormas.share.outgoing.ShareRequestInfo; @@ -90,7 +87,6 @@ import de.symeda.sormas.backend.util.QueryHelper; import de.symeda.sormas.backend.vaccination.LastVaccinationDate; import de.symeda.sormas.backend.vaccination.Vaccination; -import org.hibernate.query.Query; @Stateless @LocalBean @@ -130,7 +126,7 @@ public Long getIdByUuid(@NotNull String uuid) { } @Override - public void deletePermanent(Immunization immunization) { + public boolean deletePermanent(Immunization immunization) { // Remove the immunization from any S2S share info referencing it sormasToSormasShareInfoService.getByAssociatedEntity(SormasToSormasShareInfo.IMMUNIZATION, immunization.getUuid()).forEach(s -> { @@ -152,6 +148,7 @@ public void deletePermanent(Immunization immunization) { } super.deletePermanent(immunization); + return false; } public List getEntriesList(Long personId, Disease disease, Integer first, Integer max) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java index b681af27ae7..8ea0fd1cd9a 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/person/PersonService.java @@ -1054,13 +1054,14 @@ private Subquery createSubquery( } @Override - public void deletePermanent(Person person) { + public boolean deletePermanent(Person person) { manualMessageLogService.getByPersonUuid(person.getUuid()) .forEach(manualMessageLog -> manualMessageLogService.deletePermanent(manualMessageLog)); visitService.deletePersonVisits(Collections.singletonList(person.getUuid())); super.deletePermanent(person); + return false; } @Override diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/sample/SampleService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/sample/SampleService.java index f516b0e2a61..729ff1e9312 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/sample/SampleService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/sample/SampleService.java @@ -1038,7 +1038,7 @@ private Predicate allAssignedEntitiesAreArchived(CriteriaBuilder cb, SampleJoins } @Override - public void deletePermanent(Sample sample) { + public boolean deletePermanent(Sample sample) { // Delete all pathogen tests of this sample for (PathogenTest pathogenTest : sample.getPathogenTests()) { @@ -1063,6 +1063,7 @@ public void deletePermanent(Sample sample) { deleteSampleLinks(sample); super.deletePermanent(sample); + return false; } @Override diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/travelentry/services/TravelEntryService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/travelentry/services/TravelEntryService.java index 7b006c17a25..775b1d97b91 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/travelentry/services/TravelEntryService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/travelentry/services/TravelEntryService.java @@ -277,7 +277,7 @@ public EditPermissionType getEditPermissionType(TravelEntry travelEntry) { } @Override - public void deletePermanent(TravelEntry travelEntry) { + public boolean deletePermanent(TravelEntry travelEntry) { // Delete all tasks associated with this travel entry List tasks = taskService.findBy( new TaskCriteria().travelEntry( @@ -295,6 +295,7 @@ public void deletePermanent(TravelEntry travelEntry) { .forEach(document -> documentService.markAsDeleted(document)); super.deletePermanent(travelEntry); + return false; } public TravelEntry getLastTravelEntry() { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java index 2ba7576570b..a435131a022 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/user/UserRoleService.java @@ -175,7 +175,7 @@ public boolean isCaptionUnique(String excludedUuid, String caption) { } @Override - public void deletePermanent(UserRole userRole) { + public boolean deletePermanent(UserRole userRole) { List usersWithRole = userService.getAllWithRole(userRole); for (User u : usersWithRole) { @@ -191,5 +191,6 @@ public void deletePermanent(UserRole userRole) { } super.deletePermanent(userRole); + return false; } } diff --git a/sormas-backend/src/main/resources/META-INF/persistence.xml b/sormas-backend/src/main/resources/META-INF/persistence.xml index 002827d82d3..9d440b08f53 100644 --- a/sormas-backend/src/main/resources/META-INF/persistence.xml +++ b/sormas-backend/src/main/resources/META-INF/persistence.xml @@ -91,6 +91,7 @@ de.symeda.sormas.backend.adverseeventsfollowingimmunization.entity.AefiInvestigation de.symeda.sormas.backend.specialcaseaccess.SpecialCaseAccess de.symeda.sormas.backend.selfreport.SelfReport + de.symeda.sormas.backend.docgeneration.DocumentTemplate true diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index fb32127ab77..5b55069ae30 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -13668,4 +13668,34 @@ INSERT INTO userroles_userrights (userrole_id, userright) SELECT id, 'ADVERSE_EV INSERT INTO userroles_userrights (userrole_id, userright) SELECT id, 'ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_EXPORT' FROM public.userroles WHERE userroles.linkeddefaultuserrole in ('ADMIN','NATIONAL_USER'); INSERT INTO schema_version (version_number, comment) VALUES (552, 'Adverse Events Following Immunization (AEFI) - Entities #12634'); + + +-- 2024-10-23 Add "Disease" Attribute to Document Templates for Filtering #13160 +CREATE TABLE documenttemplates ( + id bigint not null, + uuid varchar(36) not null unique, + changedate timestamp not null, + creationdate timestamp not null, + change_user_id bigint, + + workflow varchar(255) not null, + disease varchar(255), + fileName text, + + sys_period tstzrange not null, + primary key(id) +); + +ALTER TABLE documenttemplates OWNER TO sormas_user; +ALTER TABLE documenttemplates ADD CONSTRAINT fk_change_user_id FOREIGN KEY (change_user_id) REFERENCES users (id); + +CREATE TABLE documenttemplates_history (LIKE documenttemplates); +CREATE TRIGGER versioning_trigger BEFORE INSERT OR UPDATE ON documenttemplates + FOR EACH ROW EXECUTE PROCEDURE versioning('sys_period', 'documenttemplates_history', true); +CREATE TRIGGER delete_history_trigger + AFTER DELETE ON documenttemplates + FOR EACH ROW EXECUTE PROCEDURE delete_history_trigger('documenttemplates_history', 'id'); +ALTER TABLE documenttemplates_history OWNER TO sormas_user; + +INSERT INTO schema_version (version_number, comment, upgradeneeded) VALUES (553, 'Add "Disease" Attribute to Document Templates for Filtering #13160', true); -- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/AbstractBeanTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/AbstractBeanTest.java index a8dcfb20fbf..4a940ef86a5 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/AbstractBeanTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/AbstractBeanTest.java @@ -155,6 +155,7 @@ import de.symeda.sormas.backend.disease.DiseaseConfigurationFacadeEjb.DiseaseConfigurationFacadeEjbLocal; import de.symeda.sormas.backend.disease.DiseaseConfigurationService; import de.symeda.sormas.backend.docgeneration.DocumentTemplateFacadeEjb.DocumentTemplateFacadeEjbLocal; +import de.symeda.sormas.backend.docgeneration.DocumentTemplateService; import de.symeda.sormas.backend.docgeneration.EventDocumentFacadeEjb; import de.symeda.sormas.backend.docgeneration.QuarantineOrderFacadeEjb; import de.symeda.sormas.backend.document.DocumentFacadeEjb; @@ -899,6 +900,10 @@ public DocumentTemplateFacade getDocumentTemplateFacade() { return getBean(DocumentTemplateFacadeEjbLocal.class); } + public DocumentTemplateService getDocumentTemplateService() { + return getBean(DocumentTemplateService.class); + } + public QuarantineOrderFacade getQuarantineOrderFacade() { return getBean(QuarantineOrderFacadeEjb.QuarantineOrderFacadeEjbLocal.class); } diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/AbstractDocGenerationTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/AbstractDocGenerationTest.java index 913be35319d..187a888afb2 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/AbstractDocGenerationTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/AbstractDocGenerationTest.java @@ -6,6 +6,9 @@ import org.junit.jupiter.api.AfterEach; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; +import de.symeda.sormas.api.utils.DataHelper; import de.symeda.sormas.backend.AbstractBeanTest; import de.symeda.sormas.backend.MockProducer; import de.symeda.sormas.backend.common.ConfigFacadeEjb; @@ -36,4 +39,19 @@ protected void resetDefaultNullReplacement() { public void teardown() throws URISyntaxException { reset(); } + + protected DocumentTemplate createDocumentTemplate(DocumentWorkflow workflow, String templateFileName) { + DocumentTemplate template = new DocumentTemplate(); + template.setUuid(DataHelper.createUuid()); + template.setFileName(templateFileName); + template.setWorkflow(workflow); + + getDocumentTemplateService().persist(template); + + return template; + } + + protected static DocumentTemplateReferenceDto toReference(DocumentTemplate documentTemplate) { + return new DocumentTemplateReferenceDto(documentTemplate.getUuid(), documentTemplate.getFileName()); + } } diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjbTest.java index 8f410e4df3a..69cec0fe7ce 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/DocumentTemplateFacadeEjbTest.java @@ -27,14 +27,18 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentTemplateFacade; +import de.symeda.sormas.api.utils.DataHelper; import de.symeda.sormas.api.utils.ValidationRuntimeException; import de.symeda.sormas.backend.MockProducer; import de.symeda.sormas.backend.common.ConfigFacadeEjb; @@ -54,46 +58,74 @@ public void writeAndDeleteTemplateTest() throws IOException, URISyntaxException, String testDirectory = "target" + File.separator + "doctest"; byte[] document = IOUtils.toByteArray(getClass().getResourceAsStream("/docgeneration/quarantine/Quarantine.docx")); MockProducer.getProperties().setProperty(ConfigFacadeEjb.CUSTOM_FILES_PATH, testDirectory); - documentTemplateFacade.writeDocumentTemplate(QUARANTINE_ORDER_CASE, "TemplateFileToBeDeleted.docx", document); - assertTrue(documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE).contains("TemplateFileToBeDeleted.docx")); - assertTrue(documentTemplateFacade.deleteDocumentTemplate(QUARANTINE_ORDER_CASE, "TemplateFileToBeDeleted.docx")); - assertFalse(documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE).contains("TemplateFileToBeDeleted.docx")); + + DocumentTemplateDto templateDto = DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "TemplateFileToBeDeleted.docx"); + documentTemplateFacade.saveDocumentTemplate(templateDto, document); + + assertTrue(documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE, null).contains(templateDto)); + + DocumentTemplateDto templateForCovid = DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "TemplateFileToBeDeletedCovid.docx"); + templateForCovid.setDisease(Disease.CORONAVIRUS); + documentTemplateFacade.saveDocumentTemplate(templateForCovid, document); + + List templatesForCovid = documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE, Disease.CORONAVIRUS); + assertTrue(templatesForCovid.contains(templateDto)); + assertTrue(templatesForCovid.contains(templateForCovid)); + + List templatesForLassa = documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE, Disease.LASSA); + assertTrue(templatesForLassa.contains(templateDto)); + assertFalse(templatesForLassa.contains(templateForCovid)); + + assertTrue(documentTemplateFacade.deleteDocumentTemplate(templateDto.toReference())); + assertFalse(documentTemplateFacade.getAvailableTemplates(QUARANTINE_ORDER_CASE, null).contains(templateDto)); + FileUtils.deleteDirectory(new File(testDirectory)); resetCustomPath(); } @Test - public void isExistingTemplateTest() { - assertTrue(documentTemplateFacade.isExistingTemplate(QUARANTINE_ORDER_CASE, "Quarantine.docx")); - assertFalse(documentTemplateFacade.isExistingTemplate(QUARANTINE_ORDER_CASE, "ThisTemplateDoesNotExist.docx")); + public void isExistingTemplateFileTest() { + assertTrue(documentTemplateFacade.isExistingTemplateFile(QUARANTINE_ORDER_CASE, null, "Quarantine.docx")); + assertFalse(documentTemplateFacade.isExistingTemplateFile(QUARANTINE_ORDER_CASE, null, "ThisTemplateDoesNotExist.docx")); } @Test public void validateTemplateTest() throws IOException { try { - documentTemplateFacade.writeDocumentTemplate(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.txt", new byte[0]); + documentTemplateFacade + .saveDocumentTemplate(DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.txt"), new byte[0]); fail("Invalid file extension not recognized."); - } catch (DocumentTemplateException e) { + } catch (ValidationRuntimeException e) { assertEquals("Wrong file type", e.getMessage()); + } catch (DocumentTemplateException e) { + fail("Invalid file extension not recognized.", e); } try { - documentTemplateFacade.writeDocumentTemplate(QUARANTINE_ORDER_CASE, "../TemplateFileToBeValidated.docx", new byte[0]); + documentTemplateFacade + .saveDocumentTemplate(DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "../TemplateFileToBeValidated.docx"), new byte[0]); fail("Invalid file extension not recognized."); - } catch (DocumentTemplateException e) { + } catch (ValidationRuntimeException e) { assertEquals("Illegal file name: ../TemplateFileToBeValidated.docx", e.getMessage()); + } catch (DocumentTemplateException e) { + fail("Invalid file extension not recognized.", e); } try { - documentTemplateFacade.writeDocumentTemplate(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.docx", new byte[0]); + documentTemplateFacade + .saveDocumentTemplate(DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.docx"), new byte[0]); fail("Invalid docx file not recognized."); - } catch (DocumentTemplateException e) { + } catch (ValidationRuntimeException e) { assertEquals("The template file is corrupt.", e.getMessage()); + } catch (DocumentTemplateException e) { + fail("Invalid docx file not recognized", e); } try { byte[] document = IOUtils.toByteArray(getClass().getResourceAsStream("/docgeneration/quarantine/FaultyTemplate.docx")); - documentTemplateFacade.writeDocumentTemplate(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.docx", document); + documentTemplateFacade.saveDocumentTemplate(DocumentTemplateDto.build(QUARANTINE_ORDER_CASE, "TemplateFileToBeValidated.docx"), document); fail("Syntax error not recognized."); - } catch (DocumentTemplateException e) { + } catch (ValidationRuntimeException e) { assertEquals("Error processing template.", e.getMessage()); + } catch (DocumentTemplateException e) { + fail("Syntax error not recognized.", e); } } @@ -101,38 +133,41 @@ public void validateTemplateTest() throws IOException { public void testEmailTemplateValidation() throws DocumentTemplateException { assertThrows( ValidationRuntimeException.class, - () -> documentTemplateFacade - .writeDocumentTemplate(CASE_EMAIL, "CaseEmailTemplate.txt", "Email template without subject".getBytes(StandardCharsets.UTF_8))); + () -> documentTemplateFacade.saveDocumentTemplate( + DocumentTemplateDto.build(CASE_EMAIL, "CaseEmailTemplate.txt"), + "Email template without subject".getBytes(StandardCharsets.UTF_8))); assertThrows( ValidationRuntimeException.class, - () -> documentTemplateFacade.writeDocumentTemplate( - CASE_EMAIL, - "CaseEmailTemplate.txt", + () -> documentTemplateFacade.saveDocumentTemplate( + DocumentTemplateDto.build(CASE_EMAIL, "CaseEmailTemplate.txt"), "Email template without subject\nSecond line".getBytes(StandardCharsets.UTF_8))); assertThrows( ValidationRuntimeException.class, - () -> documentTemplateFacade.writeDocumentTemplate( - CASE_EMAIL, - "CaseEmailTemplate.txt", + () -> documentTemplateFacade.saveDocumentTemplate( + DocumentTemplateDto.build(CASE_EMAIL, "CaseEmailTemplate.txt"), "#\nEmail template without subject\nSecond line".getBytes(StandardCharsets.UTF_8))); assertThrows( ValidationRuntimeException.class, - () -> documentTemplateFacade.writeDocumentTemplate( - CASE_EMAIL, - "CaseEmailTemplate.txt", + () -> documentTemplateFacade.saveDocumentTemplate( + DocumentTemplateDto.build(CASE_EMAIL, "CaseEmailTemplate.txt"), "# \nEmail template without subject\nSecond line".getBytes(StandardCharsets.UTF_8))); assertThrows( ValidationRuntimeException.class, - () -> documentTemplateFacade.writeDocumentTemplate( - CASE_EMAIL, - "CaseEmailTemplate.txt", + () -> documentTemplateFacade.saveDocumentTemplate( + DocumentTemplateDto.build(CASE_EMAIL, "CaseEmailTemplate.txt"), "*Subject\nEmail template without subject\nSecond line".getBytes(StandardCharsets.UTF_8))); } @Test public void readTemplateTest() throws DocumentTemplateException { - byte[] template = documentTemplateFacade.getDocumentTemplate(QUARANTINE_ORDER_CASE, "Quarantine.docx"); + DocumentTemplate templateEntity = new DocumentTemplate(); + templateEntity.setUuid(DataHelper.createUuid()); + templateEntity.setWorkflow(QUARANTINE_ORDER_CASE); + templateEntity.setFileName("Quarantine.docx"); + + getDocumentTemplateService().ensurePersisted(templateEntity); + byte[] template = documentTemplateFacade.getDocumentTemplateContent(toReference(templateEntity)); assertEquals(12731, template.length); } } diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjbTest.java index 441e8356e8d..222f107e9a6 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/EventDocumentFacadeEjbTest.java @@ -9,6 +9,8 @@ import java.io.StringWriter; import java.net.URISyntaxException; import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import org.apache.commons.io.FilenameUtils; @@ -20,6 +22,7 @@ import de.symeda.sormas.api.action.ActionContext; import de.symeda.sormas.api.action.ActionDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EventDocumentFacade; import de.symeda.sormas.api.event.EventDto; import de.symeda.sormas.api.event.EventInvestigationStatus; @@ -129,11 +132,19 @@ public void generateEventHandoutTest() throws IOException, URISyntaxException, D File[] testCasesHtml = testCasesDir.listFiles((d, name) -> name.endsWith(".html")); assertNotNull(testCasesHtml); + Map documentTemplates = new HashMap<>(testCasesHtml.length); for (File testCaseHtml : testCasesHtml) { String testcaseBasename = FilenameUtils.getBaseName(testCaseHtml.getName()); + DocumentTemplate documentTemplate = createDocumentTemplate(DocumentWorkflow.EVENT_HANDOUT, testcaseBasename + ".html"); + documentTemplates.put(testcaseBasename, documentTemplate); + } + + for (File testCaseHtml : testCasesHtml) { + String testcaseBasename = FilenameUtils.getBaseName(testCaseHtml.getName()); + DocumentTemplate documentTemplate = documentTemplates.get(testcaseBasename); String htmlText = - eventDocumentFacade.getGeneratedDocument(testcaseBasename + ".html", eventDto.toReference(), new Properties(), Boolean.FALSE); + eventDocumentFacade.getGeneratedDocument(toReference(documentTemplate), eventDto.toReference(), new Properties(), Boolean.FALSE); String actual = cleanLineSeparators( htmlText.replaceAll("

Event-ID: [A-Z0-9-]*

", "

Event-ID: STN3WX-5JTGYV-IU2LRM-4UHCSOEE

")); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjbTest.java index 5dac8076044..a885d1ecf9c 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/docgeneration/QuarantineOrderFacadeEjbTest.java @@ -44,6 +44,7 @@ import de.symeda.sormas.api.caze.CaseReferenceDto; import de.symeda.sormas.api.contact.ContactDto; import de.symeda.sormas.api.contact.ContactReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; @@ -69,9 +70,7 @@ import de.symeda.sormas.api.travelentry.TravelEntryReferenceDto; import de.symeda.sormas.api.user.DefaultUserRole; import de.symeda.sormas.api.user.UserDto; -import de.symeda.sormas.backend.MockProducer; import de.symeda.sormas.backend.TestDataCreator; -import de.symeda.sormas.backend.common.ConfigFacadeEjb; public class QuarantineOrderFacadeEjbTest extends AbstractDocGenerationTest { @@ -85,6 +84,11 @@ public class QuarantineOrderFacadeEjbTest extends AbstractDocGenerationTest { private SampleDto sampleDto; private PathogenTestDto pathogenTestDto; + private DocumentTemplate caseDocumentTemplate; + private DocumentTemplate contactDocumentTemplate; + private DocumentTemplate eventParticipantDocumentTemplate; + private DocumentTemplate travelEntryDocumentTemplate; + @BeforeEach public void setup() throws URISyntaxException { TestDataCreator.RDCF rdcf = creator.createRDCF("Region", "District", "Community", "Facility", "PointOfEntry"); @@ -167,6 +171,12 @@ public void setup() throws URISyntaxException { travelEntryDto.setResponsibleRegion(rdcf.region); travelEntryDto.setResponsibleDistrict(rdcf.district); travelEntryDto = getTravelEntryFacade().save(travelEntryDto); + + caseDocumentTemplate = createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_CASE, "Quarantine.docx"); + createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_CASE, "FaultyTemplate.docx"); + contactDocumentTemplate = createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_CONTACT, "Quarantine.docx"); + eventParticipantDocumentTemplate = createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_EVENT_PARTICIPANT, "Quarantine.docx"); + travelEntryDocumentTemplate = createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_TRAVEL_ENTRY, "Quarantine.docx"); } private Date parseDate(String dateString) { @@ -182,7 +192,7 @@ public void generateQuarantineOrderCaseTest() throws IOException, DocumentTempla generateQuarantineOrderTest( RootEntityType.ROOT_CASE, caseDataDto.toReference(), - DocumentWorkflow.QUARANTINE_ORDER_CASE, + caseDocumentTemplate, sampleDto.toReference(), pathogenTestDto.toReference(), "QuarantineCase.cmp"); @@ -193,7 +203,7 @@ public void generateQuarantineOrderContactTest() throws IOException, DocumentTem generateQuarantineOrderTest( RootEntityType.ROOT_CONTACT, contactDto.toReference(), - DocumentWorkflow.QUARANTINE_ORDER_CONTACT, + contactDocumentTemplate, null, null, "QuarantineContact.cmp"); @@ -204,7 +214,7 @@ public void generateQuarantineOrderEventParticipantTest() throws IOException, Do generateQuarantineOrderTest( RootEntityType.ROOT_EVENT_PARTICIPANT, eventParticipantDto.toReference(), - DocumentWorkflow.QUARANTINE_ORDER_EVENT_PARTICIPANT, + eventParticipantDocumentTemplate, null, null, "QuarantineEvent.cmp"); @@ -215,7 +225,7 @@ public void generateQuarantineOrderTravelEntryTest() throws IOException, Documen generateQuarantineOrderTest( RootEntityType.ROOT_TRAVEL_ENTRY, travelEntryDto.toReference(), - DocumentWorkflow.QUARANTINE_ORDER_TRAVEL_ENTRY, + travelEntryDocumentTemplate, null, null, "QuarantineTravelEntry.cmp"); @@ -229,7 +239,7 @@ public void generateQuarantineOrderCustomNullReplacementTest() throws IOExceptio generateQuarantineOrderTest( RootEntityType.ROOT_CASE, rootEntityReference, - DocumentWorkflow.QUARANTINE_ORDER_CASE, + caseDocumentTemplate, null, null, "QuarantineCaseEmptyNullReplacement.cmp"); @@ -238,7 +248,7 @@ public void generateQuarantineOrderCustomNullReplacementTest() throws IOExceptio generateQuarantineOrderTest( RootEntityType.ROOT_CASE, rootEntityReference, - DocumentWorkflow.QUARANTINE_ORDER_CASE, + caseDocumentTemplate, null, null, "QuarantineCaseCustomNullReplacement.cmp"); @@ -252,11 +262,10 @@ public void testBulkCaseDocumentCreation() throws DocumentTemplateException, IOE properties.setProperty("extraremark1", "the first remark"); properties.setProperty("extra.remark.no3", "the third remark"); - DocumentWorkflow workflow = DocumentWorkflow.QUARANTINE_ORDER_CASE; Map documentContents = quarantineOrderFacadeEjb - .getGeneratedDocuments("Quarantine.docx", workflow, Collections.singletonList(rootEntityReference), properties, false); + .getGeneratedDocuments(toReference(caseDocumentTemplate), Collections.singletonList(rootEntityReference), properties, false); - verifyGeneratedDocument(rootEntityReference, workflow, "QuarantineCase.cmp", documentContents.get(rootEntityReference)); + verifyGeneratedDocument(rootEntityReference, caseDocumentTemplate, "QuarantineCase.cmp", documentContents.get(rootEntityReference)); } @Test @@ -266,11 +275,10 @@ public void testBulkContactDocumentCreation() throws DocumentTemplateException, properties.setProperty("extraremark1", "the first remark"); properties.setProperty("extra.remark.no3", "the third remark"); - DocumentWorkflow workflow = DocumentWorkflow.QUARANTINE_ORDER_CONTACT; Map documentContents = quarantineOrderFacadeEjb - .getGeneratedDocuments("Quarantine.docx", workflow, Collections.singletonList(rootEntityReference), properties, false); + .getGeneratedDocuments(toReference(contactDocumentTemplate), Collections.singletonList(rootEntityReference), properties, false); - verifyGeneratedDocument(rootEntityReference, workflow, "QuarantineContact.cmp", documentContents.get(rootEntityReference)); + verifyGeneratedDocument(rootEntityReference, contactDocumentTemplate, "QuarantineContact.cmp", documentContents.get(rootEntityReference)); } @Test @@ -281,21 +289,24 @@ public void testBulkEventParticipantDocumentCreation() throws DocumentTemplateEx properties.setProperty("extraremark1", "the first remark"); properties.setProperty("extra.remark.no3", "the third remark"); - DocumentWorkflow workflow = DocumentWorkflow.QUARANTINE_ORDER_EVENT_PARTICIPANT; Map documentContents = quarantineOrderFacadeEjb.getGeneratedDocumentsForEventParticipants( - "Quarantine.docx", + toReference(eventParticipantDocumentTemplate), Collections.singletonList(rootEntityReference), eventDto.getDisease(), properties, false); - verifyGeneratedDocument(rootEntityReference, workflow, "QuarantineEvent.cmp", documentContents.get(rootEntityReference)); + verifyGeneratedDocument( + rootEntityReference, + eventParticipantDocumentTemplate, + "QuarantineEvent.cmp", + documentContents.get(rootEntityReference)); } private void generateQuarantineOrderTest( RootEntityType rootEntityType, ReferenceDto rootEntityReference, - DocumentWorkflow documentWorkflow, + DocumentTemplate template, SampleReferenceDto sampleReference, PathogenTestReferenceDto pathogenTest, String comparisonFile) @@ -308,11 +319,10 @@ private void generateQuarantineOrderTest( verifyGeneratedDocument( rootEntityReference, - documentWorkflow, + template, comparisonFile, quarantineOrderFacadeEjb.getGeneratedDocument( - "Quarantine.docx", - documentWorkflow, + toReference(template), rootEntityType, rootEntityReference, sampleReference, @@ -324,19 +334,19 @@ private void generateQuarantineOrderTest( private void verifyGeneratedDocument( ReferenceDto rootEntityReference, - DocumentWorkflow documentWorkflow, + DocumentTemplate docuembtTemplate, String comparisonFile, byte[] documentContent) throws IOException, DocumentTemplateException { - DocumentVariables documentVariables = quarantineOrderFacadeEjb.getDocumentVariables(documentWorkflow, "Quarantine.docx"); + DocumentVariables documentVariables = quarantineOrderFacadeEjb.getDocumentVariables(toReference(docuembtTemplate)); List additionalVariables = documentVariables.getAdditionalVariables(); String rootEntityName; List expectedVariables = Arrays.asList("extraremark1", "extra.remark2", "extra.remark.no3"); List expectedUsedEntities = new ArrayList<>(Arrays.asList("person", "user", "sample", "pathogenTest")); - switch (documentWorkflow) { + switch (docuembtTemplate.getWorkflow()) { case QUARANTINE_ORDER_CASE: rootEntityName = "case"; break; @@ -391,17 +401,11 @@ private void verifyGeneratedDocument( } @Test - public void getAvailableTemplatesTest() throws URISyntaxException { - List availableTemplates = quarantineOrderFacadeEjb.getAvailableTemplates(DocumentWorkflow.QUARANTINE_ORDER_CASE); + public void getAvailableTemplatesTest() { + List availableTemplates = quarantineOrderFacadeEjb.getAvailableTemplates(DocumentWorkflow.QUARANTINE_ORDER_CASE, null); assertEquals(2, availableTemplates.size()); - assertTrue(availableTemplates.contains("Quarantine.docx")); - assertTrue(availableTemplates.contains("FaultyTemplate.docx")); - - MockProducer.getProperties().setProperty(ConfigFacadeEjb.CUSTOM_FILES_PATH, "thisDirectoryDoesNotExist"); - - assertTrue(quarantineOrderFacadeEjb.getAvailableTemplates(DocumentWorkflow.QUARANTINE_ORDER_CASE).isEmpty()); - - resetCustomPath(); + assertTrue(availableTemplates.stream().anyMatch(dt -> dt.getFileName().equals("Quarantine.docx"))); + assertTrue(availableTemplates.stream().anyMatch(dt -> dt.getFileName().equals("FaultyTemplate.docx"))); } private DocumentWorkflow getDocumentWorkflow(ReferenceDto reference) { diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java index a5a811d255c..efe9c0f1122 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java @@ -62,6 +62,7 @@ import de.symeda.sormas.api.caze.CaseReferenceDto; import de.symeda.sormas.api.contact.ContactDto; import de.symeda.sormas.api.contact.ContactStatus; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EmailAttachementDto; @@ -96,6 +97,7 @@ import de.symeda.sormas.backend.common.messaging.InvalidPhoneNumberException; import de.symeda.sormas.backend.common.messaging.SmsService; import de.symeda.sormas.backend.docgeneration.AbstractDocGenerationTest; +import de.symeda.sormas.backend.docgeneration.DocumentTemplate; import de.symeda.sormas.backend.document.Document; import de.symeda.sormas.backend.document.DocumentRelatedEntity; @@ -107,6 +109,10 @@ public class ExternalEmailFacadeEjbTest extends AbstractDocGenerationTest { private PersonDto personDto; private LocationDto locationDto2; + private DocumentTemplate caseEmailTemplate; + private DocumentTemplate quarantineOrderTemplate; + private DocumentTemplate contactEmailTemplate; + @Mock private EmailService emailService; @Mock @@ -154,6 +160,10 @@ public void init() { personDto.setEmailAddress("testEmail@email.com"); getPersonFacade().save(personDto); + + caseEmailTemplate = createDocumentTemplate(DocumentWorkflow.CASE_EMAIL, "CaseEmail.txt"); + quarantineOrderTemplate = createDocumentTemplate(DocumentWorkflow.QUARANTINE_ORDER_CASE, "Quarantine.docx"); + contactEmailTemplate = createDocumentTemplate(DocumentWorkflow.CONTACT_EMAIL, "ContactEmail.txt"); } @BeforeEach @@ -165,21 +175,28 @@ public void setup() throws URISyntaxException { public void testGetAvailableTemplates() throws DocumentTemplateException { loginWith(admin); - getDocumentTemplateFacade() - .writeDocumentTemplate(DocumentWorkflow.CASE_EMAIL, "CaseEmailMock.txt", "#Subject\nContent".getBytes(StandardCharsets.UTF_8)); - getDocumentTemplateFacade() - .writeDocumentTemplate(DocumentWorkflow.CONTACT_EMAIL, "ContactEmailMock.txt", "#Subject\nContent".getBytes(StandardCharsets.UTF_8)); + getDocumentTemplateFacade().saveDocumentTemplate( + DocumentTemplateDto.build(DocumentWorkflow.CASE_EMAIL, "CaseEmailMock.txt"), + "#Subject\nContent".getBytes(StandardCharsets.UTF_8)); + getDocumentTemplateFacade().saveDocumentTemplate( + DocumentTemplateDto.build(DocumentWorkflow.CONTACT_EMAIL, "ContactEmailMock.txt"), + "#Subject\nContent".getBytes(StandardCharsets.UTF_8)); loginWith(userDto); - List templateNames = getExternalEmailFacade().getTemplateNames(DocumentWorkflow.CASE_EMAIL); + List templates = getExternalEmailFacade().getTemplates(DocumentWorkflow.CASE_EMAIL); - assertThat(templateNames.size(), is(2)); + assertThat(templates.size(), is(2)); // should return predefined test template "CaseEmail.txt" and the one just created by the test - assertThat(templateNames, containsInAnyOrder("CaseEmail.txt", "CaseEmailMock.txt")); + assertThat( + templates.stream().map(DocumentTemplateDto::getFileName).collect(Collectors.toList()), + containsInAnyOrder("CaseEmail.txt", "CaseEmailMock.txt")); assertThat( - getExternalEmailFacade().getTemplateNames(DocumentWorkflow.CONTACT_EMAIL), + getExternalEmailFacade().getTemplates(DocumentWorkflow.CONTACT_EMAIL) + .stream() + .map(DocumentTemplateDto::getFileName) + .collect(Collectors.toList()), containsInAnyOrder("ContactEmail.txt", "ContactEmailMock.txt")); } @@ -297,7 +314,7 @@ public void testSendEmailToCasePerson() }).when(emailService).sendEmail(any(), any(), any(), any()); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); getExternalEmailFacade().sendEmail(options); @@ -336,7 +353,7 @@ public void testSendEmailToContactPerson() ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CONTACT_EMAIL, RootEntityType.ROOT_CONTACT, contact.toReference()); - options.setTemplateName("ContactEmail.txt"); + options.setTemplate(toReference(contactEmailTemplate)); options.setRecipientEmail("test@mail.com"); getExternalEmailFacade().sendEmail(options); @@ -397,7 +414,7 @@ public void testSendEmailWithAttachments() }).when(emailService).sendEmail(any(), any(), any(), any()); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); options.setAttachedDocuments(documents.stream().map(DocumentDto::toReference).collect(Collectors.toSet())); @@ -476,7 +493,7 @@ public void testEncryptAttachmentsWithRandomPassword() }).when(smsService).sendSms(any(), any()); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); options.setAttachedDocuments(documents.stream().map(DocumentDto::toReference).collect(Collectors.toSet())); @@ -497,7 +514,7 @@ public void testSendEmailWithUnsupportedAttachment() throws MessagingException, createDocument("SomeDocument.txt", DocumentRelatedEntityType.CASE, caze.getUuid(), "Some content".getBytes(StandardCharsets.UTF_8)); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); options.setAttachedDocuments(Collections.singleton(document.toReference())); @@ -518,7 +535,7 @@ public void testSendAttachmentWithUnavailablePassword() throws MessagingExceptio createDocument("SomeDocument.txt", DocumentRelatedEntityType.CASE, caze.getUuid(), "Some content".getBytes(StandardCharsets.UTF_8)); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); options.setAttachedDocuments(Collections.singleton(document.toReference())); @@ -541,7 +558,7 @@ public void testSendAttachmentNotRelatedToEntity() throws MessagingException, IO createDocument("SomeDocument.txt", DocumentRelatedEntityType.CONTACT, "mock-uuid", "Some content".getBytes(StandardCharsets.UTF_8)); ExternalEmailOptionsDto options = new ExternalEmailOptionsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE, caze.toReference()); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); options.setRecipientEmail("test@mail.com"); options.setAttachedDocuments(Collections.singleton(document.toReference())); @@ -613,7 +630,7 @@ public void testSendBulkEmailToCasePerson() throws MessagingException, IOExcepti ExternalEmailOptionsWithAttachmentsDto options = new ExternalEmailOptionsWithAttachmentsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); DocumentDto documentDto = DocumentDto.build(); documentDto.setUploadingUser(admin.toReference()); @@ -702,10 +719,10 @@ public void testSendBulkEmailToCasePersonAttachmentsAndTemplateDocument() throws ExternalEmailOptionsWithAttachmentsDto options = new ExternalEmailOptionsWithAttachmentsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); QuarantineOrderDocumentOptionsDto quarantineOrderDocumentOptions = new QuarantineOrderDocumentOptionsDto(); - quarantineOrderDocumentOptions.setTemplateFile("Quarantine.docx"); + quarantineOrderDocumentOptions.setTemplate(toReference(quarantineOrderTemplate)); quarantineOrderDocumentOptions.setExtraProperties(new Properties()); quarantineOrderDocumentOptions.setShouldUploadGeneratedDoc(false); quarantineOrderDocumentOptions.setDocumentWorkflow(DocumentWorkflow.QUARANTINE_ORDER_CASE); @@ -799,9 +816,9 @@ public void testSendBulkEmailToCasePersonTemplateDocument() throws MessagingExce ExternalEmailOptionsWithAttachmentsDto options = new ExternalEmailOptionsWithAttachmentsDto(DocumentWorkflow.CASE_EMAIL, RootEntityType.ROOT_CASE); - options.setTemplateName("CaseEmail.txt"); + options.setTemplate(toReference(caseEmailTemplate)); QuarantineOrderDocumentOptionsDto quarantineOrderDocumentOptions = new QuarantineOrderDocumentOptionsDto(); - quarantineOrderDocumentOptions.setTemplateFile("Quarantine.docx"); + quarantineOrderDocumentOptions.setTemplate(toReference(quarantineOrderTemplate)); quarantineOrderDocumentOptions.setExtraProperties(new Properties()); quarantineOrderDocumentOptions.setShouldUploadGeneratedDoc(true); quarantineOrderDocumentOptions.setDocumentWorkflow(DocumentWorkflow.QUARANTINE_ORDER_CASE); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/importexport/DatabaseExportServiceTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/importexport/DatabaseExportServiceTest.java index e6b42ec468c..0746186095b 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/importexport/DatabaseExportServiceTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/importexport/DatabaseExportServiceTest.java @@ -26,6 +26,7 @@ import de.symeda.sormas.backend.adverseeventsfollowingimmunization.entity.AdverseEvents; import de.symeda.sormas.backend.adverseeventsfollowingimmunization.entity.Aefi; import de.symeda.sormas.backend.adverseeventsfollowingimmunization.entity.AefiInvestigation; +import de.symeda.sormas.backend.docgeneration.DocumentTemplate; import de.symeda.sormas.backend.environment.Environment; import de.symeda.sormas.backend.environment.environmentsample.EnvironmentSample; import de.symeda.sormas.backend.immunization.entity.DirectoryImmunization; @@ -70,7 +71,8 @@ public void testGetConfigFullyDefined() { SelfReport.class, Aefi.class, AdverseEvents.class, - AefiInvestigation.class); + AefiInvestigation.class, + DocumentTemplate.class); @Test public void test_all_entities_have_export_configuration() { diff --git a/sormas-backend/src/test/resources/META-INF/persistence.xml b/sormas-backend/src/test/resources/META-INF/persistence.xml index 3d878a502f2..5182f469a14 100644 --- a/sormas-backend/src/test/resources/META-INF/persistence.xml +++ b/sormas-backend/src/test/resources/META-INF/persistence.xml @@ -89,6 +89,7 @@ de.symeda.sormas.backend.externalmessage.labmessage.SampleReport de.symeda.sormas.backend.specialcaseaccess.SpecialCaseAccess de.symeda.sormas.backend.selfreport.SelfReport + de.symeda.sormas.backend.docgeneration.DocumentTemplate true diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/MainScreen.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/MainScreen.java index 00ecea96b08..260e045d60f 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/MainScreen.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/MainScreen.java @@ -315,7 +315,7 @@ && permitted(FeatureType.ADVERSE_EVENTS_FOLLOWING_IMMUNIZATION_MANAGEMENT, UserR } if (permitted(FeatureType.TRAVEL_ENTRIES, UserRight.TRAVEL_ENTRY_MANAGEMENT_ACCESS) - && FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_GERMANY)) { + && FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_LUXEMBOURG)) { ControllerProvider.getTravelEntryController().registerViews(navigator); menu.addView( TravelEntriesView.class, diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateSection.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateSection.java index 4b6509ca069..f141b16e31c 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateSection.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateSection.java @@ -39,15 +39,15 @@ public class DocumentTemplateSection extends VerticalLayout { private static final long serialVersionUID = 379271838736314055L; - public DocumentTemplateSection(DocumentWorkflow documentWorkflow) { + public DocumentTemplateSection(DocumentWorkflow documentWorkflow, boolean hasDisease) { HorizontalLayout sectionHeader = new HorizontalLayout(); - DocumentTemplatesGrid documentTemplatesGrid = new DocumentTemplatesGrid(documentWorkflow); + DocumentTemplatesGrid documentTemplatesGrid = new DocumentTemplatesGrid(documentWorkflow, hasDisease); documentTemplatesGrid.setWidth(700, Unit.PIXELS); Label quarantineTemplatesLabel = new Label(documentWorkflow.toString()); quarantineTemplatesLabel.addStyleName(H3); - Button uploadButton = buildUploadButton(documentWorkflow, documentTemplatesGrid); + Button uploadButton = buildUploadButton(documentWorkflow, documentTemplatesGrid, hasDisease); sectionHeader.addComponents(quarantineTemplatesLabel, uploadButton); sectionHeader.setComponentAlignment(uploadButton, Alignment.MIDDLE_RIGHT); sectionHeader.setWidth(700, Unit.PIXELS); @@ -57,9 +57,9 @@ public DocumentTemplateSection(DocumentWorkflow documentWorkflow) { setExpandRatio(documentTemplatesGrid, 1F); } - private Button buildUploadButton(DocumentWorkflow documentWorkflow, DocumentTemplatesGrid documentTemplatesGrid) { + private Button buildUploadButton(DocumentWorkflow documentWorkflow, DocumentTemplatesGrid documentTemplatesGrid, boolean hasDisease) { return ButtonHelper.createIconButton(I18nProperties.getCaption(Captions.DocumentTemplate_uploadTemplate), VaadinIcons.UPLOAD, e -> { - Window window = VaadinUiUtil.showPopupWindow(new DocumentTemplateUploadLayout(documentWorkflow)); + Window window = VaadinUiUtil.showPopupWindow(new DocumentTemplateUploadLayout(documentWorkflow, hasDisease)); window.setCaption(String.format(I18nProperties.getCaption(Captions.DocumentTemplate_uploadWorkflowTemplate), documentWorkflow)); window.addCloseListener(c -> documentTemplatesGrid.reload()); }, ValoTheme.BUTTON_PRIMARY); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateUploadLayout.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateUploadLayout.java index bf8a1c4fb78..05391e5c8ff 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateUploadLayout.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplateUploadLayout.java @@ -16,15 +16,19 @@ package de.symeda.sormas.ui.configuration.docgeneration; import java.util.Map; +import java.util.function.Supplier; import com.vaadin.icons.VaadinIcons; import com.vaadin.server.ClassResource; import com.vaadin.server.FileDownloader; import com.vaadin.ui.Button; +import com.vaadin.ui.ComboBox; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; import com.vaadin.v7.ui.Upload; +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.FacadeProvider; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; @@ -42,6 +46,7 @@ public class DocumentTemplateUploadLayout extends VerticalLayout { protected Upload upload; private ImportLayoutComponent importGuideComponent; private final DocumentWorkflow documentWorkflow; + private final boolean hasDisease; private static final Map templateInfoData = Map.ofEntries( Map.entry( @@ -79,9 +84,10 @@ public class DocumentTemplateUploadLayout extends VerticalLayout { DocumentTemplateInfoData .forEmailTemplate(Captions.DocumentTemplate_exampleTemplateTravelEntryEmail, "ExampleDocumentTemplateTravelEntryEmail.txt"))); - public DocumentTemplateUploadLayout(DocumentWorkflow documentWorkflow) { + public DocumentTemplateUploadLayout(DocumentWorkflow documentWorkflow, boolean hasDisease) { super(); this.documentWorkflow = documentWorkflow; + this.hasDisease = hasDisease; addDownloadResourcesComponent(); addUploadResourceComponent(); } @@ -116,13 +122,30 @@ private void addUploadResourceComponent() { ImportLayoutComponent uploadTemplateComponent = new ImportLayoutComponent(2, headline, infoText, null, null); addComponent(uploadTemplateComponent); - DocumentTemplateReceiver receiver = new DocumentTemplateReceiver(documentWorkflow); + VerticalLayout uploadLatout = new VerticalLayout(); + uploadLatout.setMargin(false); + uploadLatout.setSpacing(false); + uploadLatout.setSizeFull(); + addComponent(uploadLatout); + + Supplier diseaseSupplier = () -> null; + if (hasDisease) { + ComboBox diseaseComboBox = new ComboBox<>(); + diseaseComboBox.setCaption(I18nProperties.getCaption(Captions.disease)); + diseaseComboBox.setItems(FacadeProvider.getDiseaseConfigurationFacade().getAllDiseases(true, true, true)); + diseaseComboBox.setPlaceholder(I18nProperties.getString(Strings.all)); + diseaseComboBox.setEmptySelectionAllowed(true); + uploadLatout.addComponent(diseaseComboBox); + diseaseSupplier = diseaseComboBox::getValue; + } + + DocumentTemplateReceiver receiver = new DocumentTemplateReceiver(documentWorkflow, diseaseSupplier); upload = new Upload("", receiver); upload.setButtonCaption(I18nProperties.getCaption(Captions.DocumentTemplate_buttonUploadTemplate)); CssStyles.style(upload, CssStyles.VSPACE_2); upload.addStartedListener(receiver); upload.addSucceededListener(receiver); - addComponent(upload); + uploadLatout.addComponent(upload); } private void addDownloadResource(String caption, VaadinIcons icon, ClassResource resource) { diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesGrid.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesGrid.java index 24978bdfb57..706301cd92e 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesGrid.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesGrid.java @@ -31,6 +31,7 @@ import com.vaadin.ui.Notification; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.i18n.Captions; @@ -39,23 +40,30 @@ import de.symeda.sormas.ui.utils.ButtonHelper; import de.symeda.sormas.ui.utils.VaadinUiUtil; -public class DocumentTemplatesGrid extends Grid { +public class DocumentTemplatesGrid extends Grid { private static final long serialVersionUID = 2589713987152595369L; private final DocumentWorkflow documentWorkflow; - public DocumentTemplatesGrid(DocumentWorkflow documentWorkflow) { - super(String.class); + public DocumentTemplatesGrid(DocumentWorkflow documentWorkflow, boolean hasDisease) { + super(DocumentTemplateDto.class); this.documentWorkflow = documentWorkflow; setSizeFull(); - List availableTemplates = FacadeProvider.getDocumentTemplateFacade().getAvailableTemplates(documentWorkflow); - ListDataProvider dataProvider = DataProvider.fromStream(availableTemplates.stream()); + List availableTemplates = FacadeProvider.getDocumentTemplateFacade().getAvailableTemplates(documentWorkflow, null); + ListDataProvider dataProvider = DataProvider.fromStream(availableTemplates.stream()); setDataProvider(dataProvider); - removeAllColumns(); - addColumn(String::toString).setCaption(I18nProperties.getString(Strings.fileName)).setExpandRatio(1); + setColumns(DocumentTemplateDto.FILE_NAME); + if (hasDisease) { + addColumn(DocumentTemplateDto.DISEASE); + } + + for (Column column : getColumns()) { + column.setCaption(I18nProperties.getPrefixCaption(DocumentTemplateDto.I18N_PREFIX, column.getId(), column.getCaption())); + } + addComponentColumn(this::buildActionButtons).setCaption(I18nProperties.getCaption(Captions.eventActionsView)) .setWidth(100) .setStyleGenerator(item -> "v-align-center"); @@ -67,20 +75,20 @@ public DocumentTemplatesGrid(DocumentWorkflow documentWorkflow) { public void reload() { // This is bad practice but it works (unlike refreshAll), and in this case its sufficient - List availableTemplates = FacadeProvider.getDocumentTemplateFacade().getAvailableTemplates(documentWorkflow); + List availableTemplates = FacadeProvider.getDocumentTemplateFacade().getAvailableTemplates(documentWorkflow, null); setItems(availableTemplates); getDataProvider().refreshAll(); setHeightByRows(Math.max(1, availableTemplates.size())); } - private Button buildDeleteButton(String templateFileName) { + private Button buildDeleteButton(DocumentTemplateDto template) { return ButtonHelper.createIconButton( "", VaadinIcons.TRASH, e -> VaadinUiUtil - .showDeleteConfirmationWindow(String.format(I18nProperties.getString(Strings.confirmationDeleteFile), templateFileName), () -> { + .showDeleteConfirmationWindow(String.format(I18nProperties.getString(Strings.confirmationDeleteFile), template.getFileName()), () -> { try { - FacadeProvider.getDocumentTemplateFacade().deleteDocumentTemplate(documentWorkflow, templateFileName); + FacadeProvider.getDocumentTemplateFacade().deleteDocumentTemplate(template.toReference()); } catch (DocumentTemplateException ex) { new Notification( I18nProperties.getString(Strings.errorDeletingDocumentTemplate), @@ -92,21 +100,21 @@ private Button buildDeleteButton(String templateFileName) { })); } - private Button buildViewDocumentButton(String templateFileName) { + private Button buildViewDocumentButton(DocumentTemplateDto template) { Button viewButton = ButtonHelper.createIconButton(VaadinIcons.DOWNLOAD); StreamResource streamResource = new StreamResource((StreamResource.StreamSource) () -> { try { - return new ByteArrayInputStream(FacadeProvider.getDocumentTemplateFacade().getDocumentTemplate(documentWorkflow, templateFileName)); + return new ByteArrayInputStream(FacadeProvider.getDocumentTemplateFacade().getDocumentTemplateContent(template.toReference())); } catch (DocumentTemplateException e) { new Notification( - String.format(I18nProperties.getString(Strings.errorReadingTemplate), templateFileName), + String.format(I18nProperties.getString(Strings.errorReadingTemplate), template.getFileName()), e.getMessage(), Notification.Type.ERROR_MESSAGE, false).show(Page.getCurrent()); return null; } - }, templateFileName); + }, template.getFileName()); FileDownloader fileDownloader = new FileDownloader(streamResource); fileDownloader.extend(viewButton); fileDownloader.setFileDownloadResource(streamResource); @@ -114,11 +122,11 @@ private Button buildViewDocumentButton(String templateFileName) { return viewButton; } - private HorizontalLayout buildActionButtons(String s) { + private HorizontalLayout buildActionButtons(DocumentTemplateDto template) { HorizontalLayout horizontalLayout = new HorizontalLayout(); - horizontalLayout.addComponent(buildViewDocumentButton(s)); - horizontalLayout.addComponent(buildDeleteButton(s)); + horizontalLayout.addComponent(buildViewDocumentButton(template)); + horizontalLayout.addComponent(buildDeleteButton(template)); horizontalLayout.setSpacing(false); horizontalLayout.setMargin(false); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesView.java index 3f5fcaf0220..fea59946f8b 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/DocumentTemplatesView.java @@ -37,11 +37,11 @@ public DocumentTemplatesView() { super(VIEW_NAME); gridLayout = new VerticalLayout( - new DocumentTemplateSection(QUARANTINE_ORDER_CASE), - new DocumentTemplateSection(QUARANTINE_ORDER_CONTACT), - new DocumentTemplateSection(QUARANTINE_ORDER_EVENT_PARTICIPANT), - new DocumentTemplateSection(QUARANTINE_ORDER_TRAVEL_ENTRY), - new DocumentTemplateSection(EVENT_HANDOUT)); + new DocumentTemplateSection(QUARANTINE_ORDER_CASE, true), + new DocumentTemplateSection(QUARANTINE_ORDER_CONTACT, true), + new DocumentTemplateSection(QUARANTINE_ORDER_EVENT_PARTICIPANT, true), + new DocumentTemplateSection(QUARANTINE_ORDER_TRAVEL_ENTRY, true), + new DocumentTemplateSection(EVENT_HANDOUT, true)); gridLayout.setWidth(100, Unit.PERCENTAGE); gridLayout.setMargin(true); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/emailtemplate/EmailTemplatesView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/emailtemplate/EmailTemplatesView.java index 3ae757f1033..4e989fef819 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/emailtemplate/EmailTemplatesView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/docgeneration/emailtemplate/EmailTemplatesView.java @@ -37,10 +37,10 @@ public EmailTemplatesView() { super(VIEW_NAME); VerticalLayout gridLayout = new VerticalLayout( - new DocumentTemplateSection(CASE_EMAIL), - new DocumentTemplateSection(CONTACT_EMAIL), - new DocumentTemplateSection(EVENT_PARTICIPANT_EMAIL), - new DocumentTemplateSection(TRAVEL_ENTRY_EMAIL)); + new DocumentTemplateSection(CASE_EMAIL, false), + new DocumentTemplateSection(CONTACT_EMAIL, false), + new DocumentTemplateSection(EVENT_PARTICIPANT_EMAIL, false), + new DocumentTemplateSection(TRAVEL_ENTRY_EMAIL, false)); gridLayout.setWidth(100, Unit.PERCENTAGE); gridLayout.setMargin(true); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactDataView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactDataView.java index f0745c2c774..a374a345851 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactDataView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/contact/ContactDataView.java @@ -28,7 +28,6 @@ import de.symeda.sormas.api.EditPermissionType; import de.symeda.sormas.api.FacadeProvider; import de.symeda.sormas.api.caze.CaseDataDto; -import de.symeda.sormas.api.caze.CaseReferenceDto; import de.symeda.sormas.api.contact.ContactClassification; import de.symeda.sormas.api.contact.ContactDto; import de.symeda.sormas.api.contact.ContactLogic; @@ -293,7 +292,12 @@ protected void initView(String params) { layout.addSidePanelComponent(new SideComponentLayout(documentList), DOCUMENTS_LOC); } - QuarantineOrderDocumentsComponent.addComponentToLayout(layout, contactDto, documentList); + Disease disease = contactDto.getDisease(); + if (disease == null && caseDto != null) { + disease = caseDto.getDisease(); + } + + QuarantineOrderDocumentsComponent.addComponentToLayout(layout, contactDto, disease, documentList); if (UiUtil.permitted(FeatureType.EXTERNAL_EMAILS, UserRight.EXTERNAL_EMAIL_SEND)) { ExternalEmailSideComponent externalEmailSideComponent = diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/AbstractDocgenerationLayout.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/AbstractDocgenerationLayout.java index 7e67258e814..742c36004ed 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/AbstractDocgenerationLayout.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/AbstractDocgenerationLayout.java @@ -20,7 +20,6 @@ import java.util.Properties; import java.util.function.Function; -import org.apache.commons.lang3.StringUtils; import org.slf4j.LoggerFactory; import com.vaadin.icons.VaadinIcons; @@ -41,7 +40,11 @@ import com.vaadin.ui.Window; import com.vaadin.ui.themes.ValoTheme; +import de.symeda.sormas.api.Disease; +import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; @@ -56,23 +59,24 @@ public abstract class AbstractDocgenerationLayout extends VerticalLayout { protected final Button createButton; protected final Button cancelButton; - public ComboBox templateSelector; + public ComboBox templateSelector; public final VerticalLayout additionalVariablesComponent; public final VerticalLayout additionalParametersComponent; public FileDownloader fileDownloader; public DocumentVariables documentVariables; public CheckBox checkBoxUploadGeneratedDoc; public HorizontalLayout buttonBar; + private Disease defaultDisease; - public boolean isCheckBoxAfterTemplateSelector; - - public AbstractDocgenerationLayout( + protected AbstractDocgenerationLayout( + Disease defaultDisease, String captionTemplateSelector, - Function fileNameFunction, + Function fileNameFunction, boolean isMultiFilesMode, - boolean isCheckBoxAfterTemplateSelector) { + boolean uploadCheckboxFirst) { + + this.defaultDisease = defaultDisease; - this.isCheckBoxAfterTemplateSelector = isCheckBoxAfterTemplateSelector; additionalVariablesComponent = new VerticalLayout(); additionalVariablesComponent.setSpacing(false); additionalVariablesComponent.setMargin(new MarginInfo(false, false, true, false)); @@ -84,11 +88,13 @@ public AbstractDocgenerationLayout( hideTextfields(); hideAdditionalParameters(); - if (isCheckBoxAfterTemplateSelector) { + if (uploadCheckboxFirst) { + addDiseaseSelector(defaultDisease); addTemplateSelector(captionTemplateSelector, fileNameFunction); addCheckboxUploadButton(isMultiFilesMode); } else { addCheckboxUploadButton(isMultiFilesMode); + addDiseaseSelector(defaultDisease); addTemplateSelector(captionTemplateSelector, fileNameFunction); } @@ -125,19 +131,40 @@ private void addCheckboxUploadButton(boolean isMultiFilesMode) { } } - private void addTemplateSelector(String captionTemplateSelector, Function fileNameFunction) { + private void addDiseaseSelector(Disease defaultDisease) { + ComboBox diseaseSelector = new ComboBox<>(); + diseaseSelector.setCaption(I18nProperties.getCaption(Captions.disease)); + diseaseSelector.setItems(FacadeProvider.getDiseaseConfigurationFacade().getAllDiseases(true, true, true)); + diseaseSelector.setPlaceholder(I18nProperties.getString(Strings.all)); + diseaseSelector.setEmptySelectionAllowed(true); + diseaseSelector.setWidth(100F, Unit.PERCENTAGE); + diseaseSelector.addStyleName(CssStyles.SOFT_REQUIRED); + diseaseSelector.setValue(defaultDisease); + + diseaseSelector.addValueChangeListener(e -> { + Disease disease = e.getValue(); + templateSelector.setValue(null); + + templateSelector.setItems(getAvailableTemplates(disease)); + templateSelector.setItemCaptionGenerator(DocumentTemplateDto::getFileName); + }); + + addComponent(diseaseSelector); + } + + private void addTemplateSelector(String captionTemplateSelector, Function fileNameFunction) { templateSelector = new ComboBox<>(captionTemplateSelector); templateSelector.setWidth(100F, Unit.PERCENTAGE); templateSelector.addValueChangeListener(e -> { - String templateFile = e.getValue(); - boolean isValidTemplateFile = StringUtils.isNotBlank(templateFile); + DocumentTemplateDto template = e.getValue(); + boolean isValidTemplateFile = template != null; createButton.setEnabled(isValidTemplateFile); additionalVariablesComponent.removeAllComponents(); hideTextfields(); documentVariables = null; if (isValidTemplateFile) { try { - documentVariables = getDocumentVariables(templateFile); + documentVariables = getDocumentVariables(template.toReference()); List additionalVariables = documentVariables.getAdditionalVariables(); if (additionalVariables != null && !additionalVariables.isEmpty()) { for (String variable : additionalVariables) { @@ -149,7 +176,7 @@ private void addTemplateSelector(String captionTemplateSelector, Function function) { } } - private void setStreamResource(String templateFile, String fileName) { - StreamResource streamResource = createStreamResource(templateFile, fileName); + private void setStreamResource(DocumentTemplateDto template, String fileName) { + StreamResource streamResource = createStreamResource(template, fileName); if (fileDownloader == null) { fileDownloader = new FileDownloader(streamResource); fileDownloader.extend(createButton); @@ -235,19 +263,12 @@ protected boolean shouldUploadGeneratedDocument() { return checkBoxUploadGeneratedDoc != null && Boolean.TRUE.equals(checkBoxUploadGeneratedDoc.getValue()); } - protected abstract List getAvailableTemplates(); + protected abstract List getAvailableTemplates(Disease disease); - protected abstract DocumentVariables getDocumentVariables(String templateFile) throws IOException, DocumentTemplateException; + protected abstract DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) + throws IOException, DocumentTemplateException; - protected abstract StreamResource createStreamResource(String templateFile, String filename); + protected abstract StreamResource createStreamResource(DocumentTemplateDto template, String filename); protected abstract String getWindowCaption(); - - public boolean isCheckBoxAfterTemplateSelector() { - return isCheckBoxAfterTemplateSelector; - } - - public void setCheckBoxAfterTemplateSelector(boolean checkBoxAfterTemplateSelector) { - isCheckBoxAfterTemplateSelector = checkBoxAfterTemplateSelector; - } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/DocGenerationController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/DocGenerationController.java index 95086c7a8ef..cd73d296e73 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/DocGenerationController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/DocGenerationController.java @@ -39,10 +39,12 @@ import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.FacadeProvider; import de.symeda.sormas.api.ReferenceDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EventDocumentFacade; import de.symeda.sormas.api.docgeneneration.QuarantineOrderFacade; import de.symeda.sormas.api.docgeneneration.RootEntityType; +import de.symeda.sormas.api.event.EventDto; import de.symeda.sormas.api.event.EventParticipantReferenceDto; import de.symeda.sormas.api.event.EventReferenceDto; import de.symeda.sormas.api.i18n.Captions; @@ -65,22 +67,23 @@ public void showQuarantineOrderDocumentDialog( RootEntityType rootEntityType, ReferenceDto referenceDto, DocumentWorkflow workflow, + Disease defaultDisease, SampleCriteria sampleCriteria, VaccinationCriteria vaccinationCriteria, DocumentListComponent documentListComponent) { showDialog( new QuarantineOrderLayout( workflow, + defaultDisease, sampleCriteria, vaccinationCriteria, documentListComponent, - (templateFile, sample, pathogenTest, vaccination, extraProperties, shouldUploadGeneratedDoc) -> { + (template, sample, pathogenTest, vaccination, extraProperties, shouldUploadGeneratedDoc) -> { QuarantineOrderFacade quarantineOrderFacade = FacadeProvider.getQuarantineOrderFacade(); return new ByteArrayInputStream( quarantineOrderFacade.getGeneratedDocument( - templateFile, - workflow, + template.toReference(), rootEntityType, referenceDto, sample, @@ -89,7 +92,7 @@ public void showQuarantineOrderDocumentDialog( extraProperties, shouldUploadGeneratedDoc)); }, - (templateFile) -> getDocumentFileName(referenceDto, templateFile))); + (template) -> getDocumentFileName(referenceDto, template))); } public void showBulkQuarantineOrderDocumentDialog(List referenceDtos, DocumentWorkflow workflow) { @@ -100,13 +103,14 @@ public void showBulkQuarantineOrderDocumentDialog(List referenceDt null, null, null, - (templateFile, sample, pathogenTest, vaccination, extraProperties, shouldUploadGeneratedDoc) -> { + null, + (template, sample, pathogenTest, vaccination, extraProperties, shouldUploadGeneratedDoc) -> { QuarantineOrderFacade quarantineOrderFacade = FacadeProvider.getQuarantineOrderFacade(); Map generatedDocumentContents = - quarantineOrderFacade.getGeneratedDocuments(templateFile, workflow, referenceDtos, extraProperties, shouldUploadGeneratedDoc); + quarantineOrderFacade.getGeneratedDocuments(template.toReference(), referenceDtos, extraProperties, shouldUploadGeneratedDoc); - return generateZip(templateFile, shouldUploadGeneratedDoc, generatedDocumentContents); + return generateZip(template, shouldUploadGeneratedDoc, generatedDocumentContents); }, (templateFile) -> filename)); @@ -117,27 +121,28 @@ public void showBulkEventParticipantQuarantineOrderDocumentDialog(List { + (template, sample, pathogenTest, vaccination, extraProperties, shouldUploadGeneratedDoc) -> { QuarantineOrderFacade quarantineOrderFacade = FacadeProvider.getQuarantineOrderFacade(); Map generatedDocumentContents = quarantineOrderFacade.getGeneratedDocumentsForEventParticipants( - templateFile, + template.toReference(), referenceDtos, eventDisease, extraProperties, shouldUploadGeneratedDoc); - return generateZip(templateFile, shouldUploadGeneratedDoc, generatedDocumentContents); + return generateZip(template, shouldUploadGeneratedDoc, generatedDocumentContents); }, (templateFile) -> filename)); } private ByteArrayInputStream generateZip( - String templateFile, + DocumentTemplateDto template, Boolean shouldUploadGeneratedDoc, Map generatedDocumentContents) { long fileSizeLimitMB = FacadeProvider.getConfigFacade().getDocumentUploadSizeLimitMb(); @@ -149,7 +154,7 @@ private ByteArrayInputStream generateZip( for (Map.Entry referenceDocumentContent : generatedDocumentContents.entrySet()) { ReferenceDto referenceDto = referenceDocumentContent.getKey(); - ZipEntry entry = new ZipEntry(getDocumentFileName(referenceDto, templateFile)); + ZipEntry entry = new ZipEntry(getDocumentFileName(referenceDto, template)); zos.putNextEntry(entry); byte[] document = referenceDocumentContent.getValue(); @@ -206,16 +211,17 @@ private void buildDocumentUploadWarningWindow(List fileSizeLimitExceeded window.setWidth(1024, Sizeable.Unit.PIXELS); } - public void showEventDocumentDialog(EventReferenceDto eventReferenceDto, DocumentListComponent documentListComponent) { + public void showEventDocumentDialog(EventDto event, DocumentListComponent documentListComponent) { showDialog( new EventDocumentLayout( + event.getDisease(), documentListComponent, - (templateFileName) -> getDocumentFileName(eventReferenceDto, templateFileName), - (templateFile, properties, shouldUploadGeneratedDoc) -> { + (template) -> getDocumentFileName(event.toReference(), template), + (template, properties, shouldUploadGeneratedDoc) -> { EventDocumentFacade eventDocumentFacade = FacadeProvider.getEventDocumentFacade(); return new ByteArrayInputStream( - eventDocumentFacade.getGeneratedDocument(templateFile, eventReferenceDto, properties, shouldUploadGeneratedDoc) + eventDocumentFacade.getGeneratedDocument(template.toReference(), event.toReference(), properties, shouldUploadGeneratedDoc) .getBytes(StandardCharsets.UTF_8)); })); } @@ -223,13 +229,13 @@ public void showEventDocumentDialog(EventReferenceDto eventReferenceDto, Documen public void showEventDocumentDialog(List referenceDtos) { String filename = DownloadUtil.createFileNameWithCurrentDate(ExportEntityName.EVENTS, ".zip"); - showDialog(new EventDocumentLayout(null, (templateFile) -> filename, (templateFile, properties, shouldUploadGeneratedDoc) -> { + showDialog(new EventDocumentLayout(null, null, (templateFile) -> filename, (template, properties, shouldUploadGeneratedDoc) -> { EventDocumentFacade eventDocumentFacade = FacadeProvider.getEventDocumentFacade(); Map generatedDocumentContents = - eventDocumentFacade.getGeneratedDocuments(templateFile, referenceDtos, properties, shouldUploadGeneratedDoc); + eventDocumentFacade.getGeneratedDocuments(template.toReference(), referenceDtos, properties, shouldUploadGeneratedDoc); - return generateZip(templateFile, shouldUploadGeneratedDoc, generatedDocumentContents); + return generateZip(template, shouldUploadGeneratedDoc, generatedDocumentContents); })); } @@ -240,7 +246,7 @@ private void showDialog(AbstractDocgenerationLayout docgenerationLayout) { window.setCaption(I18nProperties.getCaption(docgenerationLayout.getWindowCaption())); } - private String getDocumentFileName(ReferenceDto eventReferenceDto, String templateFileName) { - return DataHelper.getShortUuid(eventReferenceDto) + '-' + templateFileName; + private String getDocumentFileName(ReferenceDto eventReferenceDto, DocumentTemplateDto template) { + return DataHelper.getShortUuid(eventReferenceDto) + '-' + template.getFileName(); } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentLayout.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentLayout.java index 5ed024ae1fc..2ff77315502 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentLayout.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentLayout.java @@ -27,8 +27,11 @@ import com.vaadin.server.StreamResource; import com.vaadin.ui.Notification; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; @@ -41,10 +44,16 @@ public class EventDocumentLayout extends AbstractDocgenerationLayout { private DocumentListComponent documentListComponent; public EventDocumentLayout( + Disease defaultDisease, DocumentListComponent documentListComponent, - Function fileNameFunction, + Function fileNameFunction, DocumentInputStreamSupplier documentInputStreamSupplier) { - super(I18nProperties.getCaption(Captions.DocumentTemplate_EventHandout), fileNameFunction, isNull(documentListComponent), false); + super( + defaultDisease, + I18nProperties.getCaption(Captions.DocumentTemplate_EventHandout), + fileNameFunction, + isNull(documentListComponent), + false); this.documentListComponent = documentListComponent; this.documentInputStreamSupplier = documentInputStreamSupplier; @@ -53,20 +62,20 @@ public EventDocumentLayout( } @Override - protected List getAvailableTemplates() { - return FacadeProvider.getEventDocumentFacade().getAvailableTemplates(); + protected List getAvailableTemplates(Disease disease) { + return FacadeProvider.getEventDocumentFacade().getAvailableTemplates(disease); } @Override - protected DocumentVariables getDocumentVariables(String templateFile) throws DocumentTemplateException { - return FacadeProvider.getEventDocumentFacade().getDocumentVariables(templateFile); + protected DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + return FacadeProvider.getEventDocumentFacade().getDocumentVariables(templateReference); } @Override - protected StreamResource createStreamResource(String templateFile, String filename) { + protected StreamResource createStreamResource(DocumentTemplateDto template, String filename) { return new StreamResource((StreamResource.StreamSource) () -> { try { - return documentInputStreamSupplier.get(templateFile, readAdditionalVariables(), shouldUploadGeneratedDocument()); + return documentInputStreamSupplier.get(template, readAdditionalVariables(), shouldUploadGeneratedDocument()); } catch (Exception e) { new Notification(I18nProperties.getString(Strings.errorProcessingTemplate), e.getMessage(), Notification.Type.ERROR_MESSAGE) .show(Page.getCurrent()); @@ -86,6 +95,6 @@ protected String getWindowCaption() { interface DocumentInputStreamSupplier { - InputStream get(String templateFile, Properties properties, Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException; + InputStream get(DocumentTemplateDto template, Properties properties, Boolean shouldUploadGeneratedDoc) throws DocumentTemplateException; } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentsComponent.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentsComponent.java index 334d1f1d3f3..1f474c4724a 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentsComponent.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/EventDocumentsComponent.java @@ -1,6 +1,6 @@ package de.symeda.sormas.ui.docgeneration; -import de.symeda.sormas.api.event.EventReferenceDto; +import de.symeda.sormas.api.event.EventDto; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; import de.symeda.sormas.ui.ControllerProvider; @@ -10,11 +10,11 @@ public class EventDocumentsComponent extends AbstractDocumentGenerationComponent public static final String DOCGENERATION_LOC = "docgeneration"; - public EventDocumentsComponent(EventReferenceDto eventReferenceDto, DocumentListComponent documentListComponent) { + public EventDocumentsComponent(EventDto event, DocumentListComponent documentListComponent) { super(); if (DocGenerationHelper.isDocGenerationAllowed()) { addDocumentBar( - () -> ControllerProvider.getDocGenerationController().showEventDocumentDialog(eventReferenceDto, documentListComponent), + () -> ControllerProvider.getDocGenerationController().showEventDocumentDialog(event, documentListComponent), I18nProperties.getCaption(Captions.DocumentTemplate_EventHandout_create)); } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderDocumentsComponent.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderDocumentsComponent.java index b67a41076de..6f1d830e937 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderDocumentsComponent.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderDocumentsComponent.java @@ -17,6 +17,7 @@ import static de.symeda.sormas.ui.docgeneration.DocGenerationHelper.isDocGenerationAllowed; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.ReferenceDto; import de.symeda.sormas.api.caze.CaseDataDto; import de.symeda.sormas.api.contact.ContactDto; @@ -33,18 +34,21 @@ public class QuarantineOrderDocumentsComponent extends AbstractDocumentGenerationComponent { + private static final long serialVersionUID = 85800761803338225L; + public static final String QUARANTINE_LOC = "quarantine"; - public QuarantineOrderDocumentsComponent( + private QuarantineOrderDocumentsComponent( RootEntityType rootEntityType, ReferenceDto referenceDto, DocumentWorkflow workflow, + Disease defaultDisease, SampleCriteria sampleCriteria, VaccinationCriteria vaccinationCriteria) { super(); addDocumentBar( () -> ControllerProvider.getDocGenerationController() - .showQuarantineOrderDocumentDialog(rootEntityType, referenceDto, workflow, sampleCriteria, vaccinationCriteria, null), + .showQuarantineOrderDocumentDialog(rootEntityType, referenceDto, workflow, defaultDisease, sampleCriteria, vaccinationCriteria, null), Captions.DocumentTemplate_QuarantineOrder); } @@ -56,12 +60,17 @@ public static void addComponentToLayout(LayoutWithSidePanel targetLayout, CaseDa RootEntityType.ROOT_CASE, caze.toReference(), DocumentWorkflow.QUARANTINE_ORDER_CASE, + caze.getDisease(), sampleCriteria, vaccinationCriteria, documentList); } - public static void addComponentToLayout(LayoutWithSidePanel targetLayout, ContactDto contact, DocumentListComponent documentList) { + public static void addComponentToLayout( + LayoutWithSidePanel targetLayout, + ContactDto contact, + Disease defaultDisease, + DocumentListComponent documentList) { VaccinationCriteria vaccinationCriteria = new VaccinationCriteria.Builder(contact.getPerson()).withDisease(contact.getDisease()).build(); SampleCriteria sampleCriteria = new SampleCriteria().contact(contact.toReference()); @@ -70,6 +79,7 @@ public static void addComponentToLayout(LayoutWithSidePanel targetLayout, Contac RootEntityType.ROOT_CONTACT, contact.toReference(), DocumentWorkflow.QUARANTINE_ORDER_CONTACT, + defaultDisease, sampleCriteria, vaccinationCriteria, documentList); @@ -81,6 +91,7 @@ public static void addComponentToLayout(LayoutWithSidePanel targetLayout, Travel RootEntityType.ROOT_TRAVEL_ENTRY, travelEntry.toReference(), DocumentWorkflow.QUARANTINE_ORDER_TRAVEL_ENTRY, + travelEntry.getDisease(), null, null, documentList); @@ -91,21 +102,23 @@ public static void addComponentToLayout( RootEntityType rootEntityType, ReferenceDto referenceDto, DocumentWorkflow workflow, + Disease defaultDisease, SampleCriteria sampleCriteria, VaccinationCriteria vaccinationCriteria) { if (isDocGenerationAllowed()) { QuarantineOrderDocumentsComponent docGenerationComponent = - new QuarantineOrderDocumentsComponent(rootEntityType, referenceDto, workflow, sampleCriteria, vaccinationCriteria); + new QuarantineOrderDocumentsComponent(rootEntityType, referenceDto, workflow, defaultDisease, sampleCriteria, vaccinationCriteria); docGenerationComponent.addStyleName(CssStyles.SIDE_COMPONENT); targetLayout.addSidePanelComponent(docGenerationComponent, QUARANTINE_LOC); } } - public static void addComponentToLayout( + private static void addComponentToLayout( LayoutWithSidePanel targetLayout, RootEntityType rootEntityType, ReferenceDto referenceDto, DocumentWorkflow workflow, + Disease defaultDisease, SampleCriteria sampleCriteria, VaccinationCriteria vaccinationCriteria, DocumentListComponent documentListComponent) { @@ -114,6 +127,7 @@ public static void addComponentToLayout( rootEntityType, referenceDto, workflow, + defaultDisease, sampleCriteria, vaccinationCriteria, documentListComponent); @@ -122,10 +136,11 @@ public static void addComponentToLayout( } } - public QuarantineOrderDocumentsComponent( + private QuarantineOrderDocumentsComponent( RootEntityType rootEntityType, ReferenceDto referenceDto, DocumentWorkflow workflow, + Disease defaultDisease, SampleCriteria sampleCriteria, VaccinationCriteria vaccinationCriteria, DocumentListComponent documentListComponent) { @@ -136,6 +151,7 @@ public QuarantineOrderDocumentsComponent( rootEntityType, referenceDto, workflow, + defaultDisease, sampleCriteria, vaccinationCriteria, documentListComponent), diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderLayout.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderLayout.java index b31f8320381..86119927bd9 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderLayout.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/docgeneration/QuarantineOrderLayout.java @@ -26,7 +26,6 @@ import javax.annotation.Nullable; -import de.symeda.sormas.ui.utils.CssStyles; import org.slf4j.LoggerFactory; import com.vaadin.server.Page; @@ -35,8 +34,11 @@ import com.vaadin.ui.ComboBox; import com.vaadin.ui.Notification; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentTemplateException; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentVariables; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.QuarantineOrderDocumentOptionsDto; @@ -56,6 +58,7 @@ import de.symeda.sormas.api.vaccination.VaccinationListEntryDto; import de.symeda.sormas.api.vaccination.VaccinationReferenceDto; import de.symeda.sormas.ui.document.DocumentListComponent; +import de.symeda.sormas.ui.utils.CssStyles; public class QuarantineOrderLayout extends AbstractDocgenerationLayout { @@ -71,12 +74,14 @@ public class QuarantineOrderLayout extends AbstractDocgenerationLayout { public QuarantineOrderLayout( DocumentWorkflow workflow, + Disease defaultDisease, @Nullable SampleCriteria sampleCriteria, @Nullable VaccinationCriteria vaccinationCriteria, DocumentListComponent documentListComponent, DocumentStreamSupplier documentStreamSupplier, - Function fileNameFunction) { + Function fileNameFunction) { super( + defaultDisease, I18nProperties.getCaption(Captions.DocumentTemplate_QuarantineOrder), fileNameFunction, isNull(sampleCriteria) && isNull(documentListComponent), @@ -99,7 +104,7 @@ public QuarantineOrderLayout( } public QuarantineOrderLayout(DocumentWorkflow workflow) { - super(I18nProperties.getCaption(Captions.DocumentTemplate_QuarantineOrder), null, true, true); + super(null, I18nProperties.getCaption(Captions.DocumentTemplate_QuarantineOrder), null, true, true); this.workflow = workflow; this.documentStreamSupplier = null; @@ -164,9 +169,9 @@ protected void createVaccinationSelector(VaccinationCriteria vaccinationCriteria } @Override - protected List getAvailableTemplates() { + protected List getAvailableTemplates(Disease disease) { try { - return FacadeProvider.getQuarantineOrderFacade().getAvailableTemplates(workflow); + return FacadeProvider.getQuarantineOrderFacade().getAvailableTemplates(workflow, disease); } catch (Exception e) { new Notification(I18nProperties.getString(Strings.errorProcessingTemplate), e.getMessage(), Notification.Type.ERROR_MESSAGE) .show(Page.getCurrent()); @@ -175,12 +180,12 @@ protected List getAvailableTemplates() { } @Override - protected DocumentVariables getDocumentVariables(String templateFile) throws DocumentTemplateException { - return FacadeProvider.getQuarantineOrderFacade().getDocumentVariables(workflow, templateFile); + protected DocumentVariables getDocumentVariables(DocumentTemplateReferenceDto templateReference) throws DocumentTemplateException { + return FacadeProvider.getQuarantineOrderFacade().getDocumentVariables(templateReference); } @Override - protected StreamResource createStreamResource(String templateFile, String filename) { + protected StreamResource createStreamResource(DocumentTemplateDto template, String filename) { return new StreamResource((StreamSource) () -> { SampleReferenceDto sampleReference = sampleSelector != null && sampleSelector.getValue() != null ? sampleSelector.getValue().toReference() : null; @@ -192,7 +197,7 @@ protected StreamResource createStreamResource(String templateFile, String filena try { InputStream stream = documentStreamSupplier.getStream( - templateFile, + template, sampleReference, pathogenTestReference, vaccinationReference, @@ -260,7 +265,7 @@ public QuarantineOrderDocumentOptionsDto getFieldValues() { options.setPathogenTest(new PathogenTestReferenceDto(pathogenTestSelector.getValue().getUuid())); } if (templateSelector != null) { - options.setTemplateFile(templateSelector.getValue()); + options.setTemplate(templateSelector.getValue().toReference()); } options.setShouldUploadGeneratedDoc(shouldUploadGeneratedDocument()); @@ -275,7 +280,7 @@ public QuarantineOrderDocumentOptionsDto getFieldValues() { public interface DocumentStreamSupplier { InputStream getStream( - String templateFile, + DocumentTemplateDto template, SampleReferenceDto sample, PathogenTestReferenceDto pathogenTest, VaccinationReferenceDto vaccinationReference, diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalBulkEmailOptionsForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalBulkEmailOptionsForm.java index d1021d75aa2..1cb156d7f63 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalBulkEmailOptionsForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalBulkEmailOptionsForm.java @@ -44,6 +44,7 @@ import de.symeda.sormas.api.DocumentHelper; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.docgeneneration.EmailAttachementDto; import de.symeda.sormas.api.docgeneneration.RootEntityType; @@ -113,7 +114,7 @@ protected String createHtmlLayout() { protected void addFields() { ComboBox templateCombo = addField(ExternalEmailOptionsWithAttachmentsDto.TEMPLATE_NAME, ComboBox.class); templateCombo.setRequired(true); - List templateNames = FacadeProvider.getExternalEmailFacade().getTemplateNames(documentWorkflow); + List templateNames = FacadeProvider.getExternalEmailFacade().getTemplates(documentWorkflow); FieldHelper.updateItems(templateCombo, templateNames); if (Arrays.asList(DocumentWorkflow.CASE_EMAIL, DocumentWorkflow.CONTACT_EMAIL, DocumentWorkflow.TRAVEL_ENTRY_EMAIL) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalEmailOptionsForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalEmailOptionsForm.java index 5a9787a8007..507bf0385b4 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalEmailOptionsForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/email/ExternalEmailOptionsForm.java @@ -18,6 +18,7 @@ import static de.symeda.sormas.ui.utils.LayoutUtil.fluidRowLocs; import java.util.List; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -27,6 +28,8 @@ import com.vaadin.v7.ui.ComboBox; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateReferenceDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.document.DocumentReferenceDto; import de.symeda.sormas.api.document.DocumentRelatedEntityType; @@ -43,28 +46,28 @@ public class ExternalEmailOptionsForm extends AbstractEditForm { - private static final String ATTACHMENT_NOT_AVAILABLE_INFO_LOC = "attachmentNotAvailableInfoLoc"; - private static final String HTML_LAYOUT = fluidRowLocs(ExternalEmailOptionsDto.TEMPLATE_NAME) - + fluidRowLocs(ExternalEmailOptionsDto.RECIPIENT_EMAIL) - + fluidRowLocs(ExternalEmailOptionsDto.ATTACHED_DOCUMENTS) - + fluidRowLocs(ATTACHMENT_NOT_AVAILABLE_INFO_LOC); + private static final String ATTACHMENT_NOT_AVAILABLE_INFO_LOC = "attachmentNotAvailableInfoLoc"; + private static final String HTML_LAYOUT = fluidRowLocs(ExternalEmailOptionsDto.TEMPLATE) + + fluidRowLocs(ExternalEmailOptionsDto.RECIPIENT_EMAIL) + + fluidRowLocs(ExternalEmailOptionsDto.ATTACHED_DOCUMENTS) + + fluidRowLocs(ATTACHMENT_NOT_AVAILABLE_INFO_LOC); private final DocumentWorkflow documentWorkflow; - private final DocumentRelatedEntityType documentRelatedEntityType; + private final DocumentRelatedEntityType documentRelatedEntityType; private final PersonDto person; - private final boolean isAttachmentAvailable; - private MultiSelect attachedDocumentsField; - - protected ExternalEmailOptionsForm( - DocumentWorkflow documentWorkflow, - DocumentRelatedEntityType documentRelatedEntityType, - PersonDto person, - boolean isAttachmentAvailable) { + private final boolean isAttachmentAvailable; + private MultiSelect attachedDocumentsField; + + protected ExternalEmailOptionsForm( + DocumentWorkflow documentWorkflow, + DocumentRelatedEntityType documentRelatedEntityType, + PersonDto person, + boolean isAttachmentAvailable) { super(ExternalEmailOptionsDto.class, ExternalEmailOptionsDto.I18N_PREFIX, false); this.documentWorkflow = documentWorkflow; - this.documentRelatedEntityType = documentRelatedEntityType; + this.documentRelatedEntityType = documentRelatedEntityType; this.person = person; - this.isAttachmentAvailable = isAttachmentAvailable; + this.isAttachmentAvailable = isAttachmentAvailable; addFields(); hideValidationUntilNextCommit(); @@ -77,10 +80,14 @@ protected String createHtmlLayout() { @Override protected void addFields() { - ComboBox templateCombo = addField(ExternalEmailOptionsDto.TEMPLATE_NAME, ComboBox.class); + ComboBox templateCombo = addField(ExternalEmailOptionsDto.TEMPLATE, ComboBox.class); templateCombo.setRequired(true); - List templateNames = FacadeProvider.getExternalEmailFacade().getTemplateNames(documentWorkflow); - FieldHelper.updateItems(templateCombo, templateNames); + List templates = FacadeProvider.getExternalEmailFacade() + .getTemplates(documentWorkflow) + .stream() + .map(DocumentTemplateDto::toReference) + .collect(Collectors.toList()); + FieldHelper.updateItems(templateCombo, templates); ComboBox recipientEmailCombo = addField(ExternalEmailOptionsDto.RECIPIENT_EMAIL, ComboBox.class); recipientEmailCombo.setRequired(true); @@ -89,30 +96,30 @@ protected void addFields() { String primaryEmailAddress = person.getEmailAddress(true); if (StringUtils.isNotBlank(primaryEmailAddress)) { recipientEmailCombo - .setItemCaption(primaryEmailAddress, primaryEmailAddress + " (" + I18nProperties.getCaption(Captions.primarySuffix) + ")"); + .setItemCaption(primaryEmailAddress, primaryEmailAddress + " (" + I18nProperties.getCaption(Captions.primarySuffix) + ")"); } - if (documentRelatedEntityType != null) { - attachedDocumentsField = addField(ExternalEmailOptionsDto.ATTACHED_DOCUMENTS, MultiSelect.class); - if (!isAttachmentAvailable) { - attachedDocumentsField.setEnabled(false); - - MultilineLabel attachmentUnavailableInfo = new MultilineLabel( - VaadinIcons.INFO_CIRCLE.getHtml() + " " + I18nProperties.getString(Strings.messageExternalEmailAttachmentNotAvailableInfo), - ContentMode.HTML); - attachmentUnavailableInfo.addStyleNames(CssStyles.VSPACE_2, CssStyles.VSPACE_TOP_4); - getContent().addComponent(attachmentUnavailableInfo, ATTACHMENT_NOT_AVAILABLE_INFO_LOC); - } - } - } - - @Override - public void setValue(ExternalEmailOptionsDto newFieldValue) throws ReadOnlyException, Converter.ConversionException { - super.setValue(newFieldValue); - - if (attachedDocumentsField != null) { - attachedDocumentsField.setItems( - FacadeProvider.getExternalEmailFacade().getAttachableDocuments(documentWorkflow, newFieldValue.getRootEntityReference().getUuid())); - } + if (documentRelatedEntityType != null) { + attachedDocumentsField = addField(ExternalEmailOptionsDto.ATTACHED_DOCUMENTS, MultiSelect.class); + if (!isAttachmentAvailable) { + attachedDocumentsField.setEnabled(false); + + MultilineLabel attachmentUnavailableInfo = new MultilineLabel( + VaadinIcons.INFO_CIRCLE.getHtml() + " " + I18nProperties.getString(Strings.messageExternalEmailAttachmentNotAvailableInfo), + ContentMode.HTML); + attachmentUnavailableInfo.addStyleNames(CssStyles.VSPACE_2, CssStyles.VSPACE_TOP_4); + getContent().addComponent(attachmentUnavailableInfo, ATTACHMENT_NOT_AVAILABLE_INFO_LOC); + } + } + } + + @Override + public void setValue(ExternalEmailOptionsDto newFieldValue) throws ReadOnlyException, Converter.ConversionException { + super.setValue(newFieldValue); + + if (attachedDocumentsField != null) { + attachedDocumentsField.setItems( + FacadeProvider.getExternalEmailFacade().getAttachableDocuments(documentWorkflow, newFieldValue.getRootEntityReference().getUuid())); + } } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataView.java index cb2733d9a47..46db076f7db 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventDataView.java @@ -140,7 +140,7 @@ protected void initView(String params) { layout.addSidePanelComponent(new SideComponentLayout(documentList), DOCUMENTS_LOC); } - EventDocumentsComponent eventDocuments = new EventDocumentsComponent(getEventRef(), documentList); + EventDocumentsComponent eventDocuments = new EventDocumentsComponent(event, documentList); eventDocuments.addStyleName(CssStyles.SIDE_COMPONENT); layout.addSidePanelComponent(eventDocuments, EventDocumentsComponent.DOCGENERATION_LOC); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventParticipantDataView.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventParticipantDataView.java index 0e067d6f6bc..f34e16f0225 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventParticipantDataView.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/events/EventParticipantDataView.java @@ -151,6 +151,7 @@ protected void initView(String params) { RootEntityType.ROOT_EVENT_PARTICIPANT, eventParticipantRef, DocumentWorkflow.QUARANTINE_ORDER_EVENT_PARTICIPANT, + event.getDisease(), sampleCriteria, vaccinationCriteria); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/importer/DocumentTemplateReceiver.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/importer/DocumentTemplateReceiver.java index b8336e890c4..dda954b2d26 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/importer/DocumentTemplateReceiver.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/importer/DocumentTemplateReceiver.java @@ -10,13 +10,16 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Date; +import java.util.function.Supplier; import com.vaadin.server.Page; import com.vaadin.ui.Label; import com.vaadin.ui.Notification; import com.vaadin.v7.ui.Upload.StartedEvent; +import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.FacadeProvider; +import de.symeda.sormas.api.docgeneneration.DocumentTemplateDto; import de.symeda.sormas.api.docgeneneration.DocumentWorkflow; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; @@ -35,9 +38,11 @@ public class DocumentTemplateReceiver private File file; private String fName; private final DocumentWorkflow documentWorkflow; + private final Supplier diseaseSupplier; - public DocumentTemplateReceiver(DocumentWorkflow documentWorkflow) { + public DocumentTemplateReceiver(DocumentWorkflow documentWorkflow, Supplier diseaseSupplier) { this.documentWorkflow = documentWorkflow; + this.diseaseSupplier = diseaseSupplier; } @Override @@ -89,7 +94,7 @@ public void uploadSucceeded(com.vaadin.v7.ui.Upload.SucceededEvent succeededEven } // Check for duplicate files - if (FacadeProvider.getDocumentTemplateFacade().isExistingTemplate(documentWorkflow, fName)) { + if (FacadeProvider.getDocumentTemplateFacade().isExistingTemplateFile(documentWorkflow, diseaseSupplier.get(), fName)) { VaadinUiUtil.showConfirmationPopup( I18nProperties.getString(Strings.headingFileExists), new Label(String.format(I18nProperties.getString(Strings.infoDocumentAlreadyExists), fName)), @@ -108,8 +113,9 @@ public void uploadSucceeded(com.vaadin.v7.ui.Upload.SucceededEvent succeededEven private void writeTemplateFile() { try { - byte[] filecontent = Files.readAllBytes(file.toPath()); - FacadeProvider.getDocumentTemplateFacade().writeDocumentTemplate(documentWorkflow, fName, filecontent); + byte[] fileContent = Files.readAllBytes(file.toPath()); + DocumentTemplateDto documentTemplateDto = DocumentTemplateDto.build(documentWorkflow, fName, diseaseSupplier.get()); + FacadeProvider.getDocumentTemplateFacade().saveDocumentTemplate(documentTemplateDto, fileContent); VaadinUiUtil.showSimplePopupWindow( I18nProperties.getString(Strings.headingUploadSuccess), I18nProperties.getString(Strings.messageUploadSuccessful)); diff --git a/sormas-ui/src/test/resources/META-INF/persistence.xml b/sormas-ui/src/test/resources/META-INF/persistence.xml index f0c6e5b197e..0242f19fa99 100644 --- a/sormas-ui/src/test/resources/META-INF/persistence.xml +++ b/sormas-ui/src/test/resources/META-INF/persistence.xml @@ -89,6 +89,7 @@ de.symeda.sormas.backend.environment.environmentsample.EnvironmentSample de.symeda.sormas.backend.specialcaseaccess.SpecialCaseAccess de.symeda.sormas.backend.selfreport.SelfReport + de.symeda.sormas.backend.docgeneration.DocumentTemplate true