From ee1633867815240640c1710ebecc7f23480bc207 Mon Sep 17 00:00:00 2001 From: Alicia de Dios Fuente <Alicia.DeDiosFuente@unige.ch> Date: Mon, 17 Feb 2025 12:13:12 +0100 Subject: [PATCH 1/3] feat: [DLCM-2812] send email to request creator when request is processed --- .../ch/dlcm/business/NotificationService.java | 68 +++++++++++++++---- .../business/OrganizationalUnitService.java | 4 ++ .../service/AdminEmailProcessingService.java | 46 +++++++++++-- .../service/NotificationsEmailService.java | 10 ++- .../dlcm/task/NotificationsTaskRunnable.java | 3 +- .../java/ch/dlcm/message/EmailMessage.java | 7 +- .../NotificationSpecification.java | 11 +++ .../main/resources/scripts/upgrade22to30.sql | 5 +- .../request_to_create_org_unit_accepted.html | 48 +++++++++++++ .../request_to_create_org_unit_refused.html | 44 ++++++++++++ .../request_to_dispose_archive_accepted.html | 38 +++++++++++ .../request_to_join_org_unit_accepted.html | 48 +++++++++++++ .../request_to_join_org_unit_refused.html | 50 ++++++++++++++ 13 files changed, 359 insertions(+), 23 deletions(-) create mode 100644 DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_accepted.html create mode 100644 DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_refused.html create mode 100644 DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_dispose_archive_accepted.html create mode 100644 DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_accepted.html create mode 100644 DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_refused.html diff --git a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java index 5cad874a9d..32f9a00a9a 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java @@ -51,6 +51,7 @@ import ch.unige.solidify.exception.SolidifyUnmodifiableException; import ch.unige.solidify.service.ResourceService; import ch.unige.solidify.util.StringTool; +import ch.dlcm.DLCMConstants; import ch.dlcm.controller.AdminController; import ch.dlcm.message.EmailMessage; import ch.dlcm.model.StatusHistory; @@ -283,19 +284,8 @@ public class NotificationService extends ResourceService<Notification> { @Override public Notification save(Notification item) { - // Send an email for notification of type access dataset request - if (item.getNotificationType().equals(NotificationType.ACCESS_DATASET_REQUEST)) { - EmailMessage emailMessage; - Map<String, Object> parameters = new HashMap<>(); - parameters.put(item.getObjectId(), null); - if (Objects.requireNonNull(item.getNotificationStatus()) == NotificationStatus.APPROVED) { - emailMessage = new EmailMessage(item.getEmitter().getEmail(), EmailMessage.EmailTemplate.REQUEST_TO_ACCESS_DATASET_ACCEPTED, parameters); - SolidifyEventPublisher.getPublisher().publishEvent(Objects.requireNonNull(emailMessage)); - } else if (item.getNotificationStatus() == NotificationStatus.REFUSED) { - emailMessage = new EmailMessage(item.getEmitter().getEmail(), EmailMessage.EmailTemplate.REQUEST_TO_ACCESS_DATASET_REFUSED, parameters); - SolidifyEventPublisher.getPublisher().publishEvent(Objects.requireNonNull(emailMessage)); - } - } + // Send an email to emitter for certain types of notification + this.sendEmailToEmitter(item); // Status is non-applicable if notification category is info Optional<NotificationType> notificationType = this.notificationTypeRepository.findById(item.getNotificationType().getResId()); @@ -307,6 +297,58 @@ public class NotificationService extends ResourceService<Notification> { return super.save(item); } + private void sendEmailToEmitter(Notification notification) { + if ((notification.getNotificationType().equals(NotificationType.ACCESS_DATASET_REQUEST) + || notification.getNotificationType().equals(NotificationType.JOIN_ORGUNIT_REQUEST) + || notification.getNotificationType().equals(NotificationType.CREATE_ORGUNIT_REQUEST) + || notification.getNotificationType().equals(NotificationType.APPROVE_DISPOSAL_REQUEST) + || notification.getNotificationType().equals(NotificationType.APPROVE_DISPOSAL_BY_ORGUNIT_REQUEST)) + && (notification.getNotificationStatus().equals(NotificationStatus.APPROVED) + || notification.getNotificationStatus().equals(NotificationStatus.REFUSED))) { + EmailMessage.EmailTemplate emailTemplate = this.getEmailTemplate(notification.getNotificationType(), notification.getNotificationStatus()); + if (emailTemplate != null) { + Map<String, Object> parameters = new HashMap(); + if (notification.getNotificationType().equals(NotificationType.JOIN_ORGUNIT_REQUEST)) { + parameters.put("objectId", notification.getNotifiedOrgUnit().getResId()); + } else { + parameters.put("objectId", notification.getObjectId()); + } + if (notification.getNotificationStatus().equals(NotificationStatus.REFUSED)) { + parameters.put(DLCMConstants.REASON, notification.getResponseMessage()); + } + EmailMessage emailMessage = new EmailMessage(notification.getEmitter().getEmail(), emailTemplate, parameters); + SolidifyEventPublisher.getPublisher().publishEvent(Objects.requireNonNull(emailMessage)); + } + } + } + + private EmailMessage.EmailTemplate getEmailTemplate(NotificationType notificationType, NotificationStatus notificationStatus) { + Map<String, Map<NotificationStatus, EmailMessage.EmailTemplate>> templateMap = Map.of( + NotificationType.ACCESS_DATASET_REQUEST.getResId(), Map.of( + NotificationStatus.APPROVED, EmailMessage.EmailTemplate.REQUEST_TO_ACCESS_DATASET_ACCEPTED, + NotificationStatus.REFUSED, EmailMessage.EmailTemplate.REQUEST_TO_ACCESS_DATASET_REFUSED + ), + NotificationType.JOIN_ORGUNIT_REQUEST.getResId(), Map.of( + NotificationStatus.APPROVED, EmailMessage.EmailTemplate.REQUEST_TO_JOIN_ORG_UNIT_ACCEPTED, + NotificationStatus.REFUSED, EmailMessage.EmailTemplate.REQUEST_TO_JOIN_ORG_UNIT_REFUSED + ), + NotificationType.CREATE_ORGUNIT_REQUEST.getResId(), Map.of( + NotificationStatus.APPROVED, EmailMessage.EmailTemplate.REQUEST_TO_CREATE_ORG_UNIT_ACCEPTED, + NotificationStatus.REFUSED, EmailMessage.EmailTemplate.REQUEST_TO_CREATE_ORG_UNIT_REFUSED + ), + NotificationType.APPROVE_DISPOSAL_REQUEST.getResId(), Map.of( + NotificationStatus.APPROVED, EmailMessage.EmailTemplate.REQUEST_TO_DISPOSE_ARCHIVE_ACCEPTED + ), + NotificationType.APPROVE_DISPOSAL_BY_ORGUNIT_REQUEST.getResId(), Map.of( + NotificationStatus.APPROVED, EmailMessage.EmailTemplate.REQUEST_TO_DISPOSE_ARCHIVE_ACCEPTED + ) + ); + + return Optional.ofNullable(templateMap.get(notificationType.getResId())) + .map(statusMap -> statusMap.get(notificationStatus)) + .orElse(null); + } + /* * Verify that each contributorsId correspond to an authenticated user and the type of notification is the type of * MY_INDIRECT_DEPOSIT_COMPLETED_INFO diff --git a/DLCM-Admin/src/main/java/ch/dlcm/business/OrganizationalUnitService.java b/DLCM-Admin/src/main/java/ch/dlcm/business/OrganizationalUnitService.java index 45ce92ef17..893827aaf9 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/business/OrganizationalUnitService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/business/OrganizationalUnitService.java @@ -80,6 +80,10 @@ public class OrganizationalUnitService extends CompositeResourceService<Organiza super.delete(id); } + public OrganizationalUnit findByName(String name) { + return ((OrganizationalUnitRepository)this.itemRepository).findByName(name); + } + @Override public void validateItemSpecificRules(OrganizationalUnit item, BindingResult errors) { diff --git a/DLCM-Admin/src/main/java/ch/dlcm/service/AdminEmailProcessingService.java b/DLCM-Admin/src/main/java/ch/dlcm/service/AdminEmailProcessingService.java index da77f72438..aa8e8ec6a1 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/service/AdminEmailProcessingService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/service/AdminEmailProcessingService.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 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 * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -23,6 +23,11 @@ package ch.dlcm.service; +import static ch.dlcm.message.EmailMessage.EmailTemplate.REQUEST_TO_ACCESS_DATASET_REFUSED; +import static ch.dlcm.message.EmailMessage.EmailTemplate.REQUEST_TO_CREATE_ORG_UNIT_REFUSED; +import static ch.dlcm.message.EmailMessage.EmailTemplate.REQUEST_TO_DISPOSE_ARCHIVE_ACCEPTED; +import static ch.dlcm.message.EmailMessage.EmailTemplate.REQUEST_TO_JOIN_ORG_UNIT_REFUSED; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -47,6 +52,7 @@ import ch.dlcm.config.DLCMProperties; import ch.dlcm.controller.AdminController; import ch.dlcm.message.EmailMessage; import ch.dlcm.model.oais.ArchivalInfoPackage; +import ch.dlcm.model.settings.OrganizationalUnit; import ch.dlcm.service.rest.trusted.TrustedArchivalInfoPackageRemoteResourceService; @Service @@ -96,11 +102,39 @@ public class AdminEmailProcessingService extends EmailProcessingService { parameterList.put(ORG_UNITS_CONSTANT, emailMessage.getParameters()); this.sendEmailWithOrgUnitsName(emailMessage); } - case REQUEST_TO_ACCESS_DATASET_ACCEPTED, REQUEST_TO_ACCESS_DATASET_REFUSED -> { + case REQUEST_TO_CREATE_ORG_UNIT_ACCEPTED, REQUEST_TO_CREATE_ORG_UNIT_REFUSED -> { + parameterList = this.getEmailDefaultParameters(); + OrganizationalUnit one = this.organizationalUnitService.findByName(emailMessage.getParameters().get("objectId").toString()); + parameterList.put("orgunit", one); + if (emailMessage.getTemplate().equals(REQUEST_TO_CREATE_ORG_UNIT_REFUSED)) { + parameterList.put(DLCMConstants.REASON, emailMessage.getParameters().get(DLCMConstants.REASON).toString()); + } + + EmailParameters emailParameters = new EmailParameters().setToList(Collections.singletonList(emailMessage.getTo())) + .setSubject(emailMessage.getTemplate().getSubject()).setTemplate(emailMessage.getTemplate().toString().toLowerCase()) + .setTemplateParameters(parameterList).addCc(null).addBcc(null); + this.emailService.sendEmailWithTemplate(emailParameters); + } + case REQUEST_TO_JOIN_ORG_UNIT_ACCEPTED, REQUEST_TO_JOIN_ORG_UNIT_REFUSED -> { + parameterList = this.getEmailDefaultParameters(); + OrganizationalUnit one = this.organizationalUnitService.findOne(emailMessage.getParameters().get("objectId").toString()); + parameterList.put("orgunit", one); + if (emailMessage.getTemplate().equals(REQUEST_TO_JOIN_ORG_UNIT_REFUSED)) { + parameterList.put(DLCMConstants.REASON, emailMessage.getParameters().get(DLCMConstants.REASON).toString()); + } + EmailParameters emailParameters = new EmailParameters().setToList(Collections.singletonList(emailMessage.getTo())) + .setSubject(emailMessage.getTemplate().getSubject()).setTemplate(emailMessage.getTemplate().toString().toLowerCase()) + .setTemplateParameters(parameterList).addCc(null).addBcc(null); + this.emailService.sendEmailWithTemplate(emailParameters); + } + case REQUEST_TO_ACCESS_DATASET_ACCEPTED, + REQUEST_TO_ACCESS_DATASET_REFUSED, + REQUEST_TO_DISPOSE_ARCHIVE_ACCEPTED-> { parameterList = this.getEmailDefaultParameters(); - for (String aip : emailMessage.getParameters().keySet()) { - ArchivalInfoPackage one = this.archivalInfoPackageRemoteResourceService.findOne(aip); - parameterList.put("aip", one); // we should have only one aip here + ArchivalInfoPackage one = this.archivalInfoPackageRemoteResourceService.findOne(emailMessage.getParameters().get("objectId").toString()); + parameterList.put("aip", one); + if (emailMessage.getTemplate().equals(REQUEST_TO_ACCESS_DATASET_REFUSED)) { + parameterList.put(DLCMConstants.REASON, emailMessage.getParameters().get(DLCMConstants.REASON).toString()); } EmailParameters emailParameters = new EmailParameters().setToList(Collections.singletonList(emailMessage.getTo())) .setSubject(emailMessage.getTemplate().getSubject()).setTemplate(emailMessage.getTemplate().toString().toLowerCase()) diff --git a/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java b/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java index 411cca8fec..c34f60da74 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java @@ -115,7 +115,8 @@ public class NotificationsEmailService extends DLCMService { for (Notification notificationToSend : notificationsToSend) { // keep the notification only if it has the specified filter status - if (notificationToSend.getNotificationStatus().equals(NotificationStatus.PENDING)) { + if (notificationToSend.getNotificationStatus().equals(NotificationStatus.PENDING) + || notificationToSend.getNotificationStatus().equals(NotificationStatus.NON_APPLICABLE)) { String recipientId = notificationToSend.getRecipient().getResId(); notificationsByPersonId.computeIfAbsent(recipientId, s -> new ArrayList<>()); notificationsByPersonId.get(recipientId).add(notificationToSend); @@ -185,6 +186,13 @@ public class NotificationsEmailService extends DLCMService { parameters.put(n.getEmitter().getPerson().getResId(), n.getObjectId()); } this.sendEmailAndMarkNotificationAsSent(notifications, emailTemplate, recipientUser, parameters); + } else { + // Need to mark notification as sent, otherwise, it is going to be check every time the job runs. + OffsetDateTime sentTime = OffsetDateTime.now(); + for (Notification notification : notifications) { + this.notificationService.updateSentTime(notification.getResId(), sentTime); + log.info("notification {} sentTime updated with value {}", notification.getResId(), sentTime); + } } } diff --git a/DLCM-Admin/src/main/java/ch/dlcm/task/NotificationsTaskRunnable.java b/DLCM-Admin/src/main/java/ch/dlcm/task/NotificationsTaskRunnable.java index 3e7211d0c9..474d29ab08 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/task/NotificationsTaskRunnable.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/task/NotificationsTaskRunnable.java @@ -80,9 +80,10 @@ public abstract class NotificationsTaskRunnable extends AbstractTaskRunnable<Sch //Get all notifications with PENDING status, sentTime is equal to NULL and Type is equal to notificationType Notification search = new Notification(); search.setNotificationType(notificationType); - search.setNotificationStatus(NotificationStatus.PENDING); NotificationSpecification notificationSpecification = new NotificationSpecification(search); notificationSpecification.setOnlyWithNullSentTime(true); + notificationSpecification.setNotificationStatusList(List.of(NotificationStatus.PENDING, NotificationStatus.NON_APPLICABLE)); + Pageable pageable = PageRequest.of(0, RestCollectionPage.MAX_SIZE_PAGE, Sort.by(ActionName.CREATION)); return this.notificationService.findAll(notificationSpecification, pageable).toList(); diff --git a/DLCM-Model/src/main/java/ch/dlcm/message/EmailMessage.java b/DLCM-Model/src/main/java/ch/dlcm/message/EmailMessage.java index f3cf9b020c..3fffe53c90 100644 --- a/DLCM-Model/src/main/java/ch/dlcm/message/EmailMessage.java +++ b/DLCM-Model/src/main/java/ch/dlcm/message/EmailMessage.java @@ -52,7 +52,12 @@ public class EmailMessage implements Serializable { NEW_MEMBER_ORG_UNIT_TO_APPROVE(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Nouveaux membres d'unités organisationnelles à approuver / New members of organizational units to approve"), NEW_REQUEST_TO_ACCESS_DATASET(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Nouvelles demandes d'autorisation d'accès aux archives / New requests to access archives archive"), REQUEST_TO_ACCESS_DATASET_ACCEPTED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande d'accès acceptée / Request to access granted"), - REQUEST_TO_ACCESS_DATASET_REFUSED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande d'accès refusée / Request to access refused"); + REQUEST_TO_ACCESS_DATASET_REFUSED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande d'accès refusée / Request to access refused"), + REQUEST_TO_CREATE_ORG_UNIT_ACCEPTED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande de creation d'unité organisationnelle acceptée / Request to create an organizational unit granted"), + REQUEST_TO_CREATE_ORG_UNIT_REFUSED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande de creation d'unité organisationnelle refusée / Request to create an organizational unit refused"), + REQUEST_TO_JOIN_ORG_UNIT_REFUSED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande d'adhésion à une unité organisationelle refusée / Request to add members to an organizational unit refused"), + REQUEST_TO_JOIN_ORG_UNIT_ACCEPTED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande d'adhésion à une unité organisationelle acceptée / Request to add members to an organizational unit accepted"), + REQUEST_TO_DISPOSE_ARCHIVE_ACCEPTED(DLCMConstants.NOTIFICATION_EMAIL_DLCM_PREFIX + " Demande de dispose un archive acceptée / Request to dispose of a file accepted"); private final String subject; diff --git a/DLCM-Model/src/main/java/ch/dlcm/specification/NotificationSpecification.java b/DLCM-Model/src/main/java/ch/dlcm/specification/NotificationSpecification.java index 5ed961be81..2338ec7780 100644 --- a/DLCM-Model/src/main/java/ch/dlcm/specification/NotificationSpecification.java +++ b/DLCM-Model/src/main/java/ch/dlcm/specification/NotificationSpecification.java @@ -36,6 +36,7 @@ import jakarta.persistence.criteria.Root; import ch.unige.solidify.specification.SolidifySpecification; import ch.dlcm.model.notification.Notification; +import ch.dlcm.model.notification.NotificationStatus; import ch.dlcm.model.notification.NotificationType; public class NotificationSpecification extends SolidifySpecification<Notification> { @@ -49,6 +50,8 @@ public class NotificationSpecification extends SolidifySpecification<Notificatio private boolean onlyWithNullReadTime = false; private String exceptResId = null; + private List<NotificationStatus> notificationStatusList; + public NotificationSpecification(Notification notification) { super(notification); } @@ -96,6 +99,10 @@ public class NotificationSpecification extends SolidifySpecification<Notificatio if (this.exceptResId != null) { predicatesList.add(builder.notEqual(root.get("resId"), this.exceptResId)); } + + if (this.criteria.getNotificationStatus() == null && this.notificationStatusList != null) { + predicatesList.add(builder.and(root.get("notificationStatus").in(this.notificationStatusList))); + } } private void setNotificationTypeCriteria(Root<Notification> root, CriteriaBuilder builder, List<Predicate> predicatesList, @@ -144,4 +151,8 @@ public class NotificationSpecification extends SolidifySpecification<Notificatio public void setExceptResId(String exceptResId) { this.exceptResId = exceptResId; } + + public void setNotificationStatusList(List<NotificationStatus> notificationStatusList) { + this.notificationStatusList = notificationStatusList; + } } diff --git a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql index 89dd0091ef..d602318ca7 100755 --- a/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql +++ b/DLCM-Model/src/main/resources/scripts/upgrade22to30.sql @@ -373,4 +373,7 @@ SET parameters = JSON_SET( '$.manifestPattern', '**/*.json' ) WHERE type IN ('IIIF') - AND parameters IS NOT NULL; \ No newline at end of file + AND parameters IS NOT NULL; + + +update notification set sent_time = NOW() where sent_time is null; \ No newline at end of file diff --git a/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_accepted.html b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_accepted.html new file mode 100644 index 0000000000..0228af87cb --- /dev/null +++ b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_accepted.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html><!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" + content="text/html; charset=UTF-8" + /> +</head> +<body> + +<p>[English below]</p> + +<p>Madame, Monsieur,</p> + +<p>Votre demande de creation d'unité organisationelle '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' a été acceptée.</p> + +<p>Avec nos cordiales salutations, + <br/> + <span th:text="${project}"> </span> +</p> + +<p>-------- + <br/> +</p> + +<p>Dear Sir or Madam,</p> + +<p>Your request to create an organizational unit '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' has been granted.</p> + +<p>Kind regards, + <br/> + <span th:text="${project}"> </span> +</p> + +</body> +</html> diff --git a/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_refused.html b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_refused.html new file mode 100644 index 0000000000..6daffff423 --- /dev/null +++ b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_create_org_unit_refused.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html><!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" + content="text/html; charset=UTF-8" + /> +</head> +<body> + +<p>[English below]</p> + +<p>Madame, Monsieur,</p> + +<p>Votre demande de creation d'unité organisationelle: '<span th:text="${orgunit.name}"></span>' a été refusée avec le motif: <span th:text="${reason}"></span>.</p> + +<p>Avec nos cordiales salutations, + <br/> + <span th:text="${project}"> </span> +</p> + +<p>-------- + <br/> +</p> + +<p>Dear Sir or Madam,</p> + +<p>Your request to create an organizational unit '<span th:text="${orgunit.name}"></span>' has been refused because: <span th:text="${reason}"></span>.</p> + +<p>Kind regards, + <br/> + <span th:text="${project}"> </span> +</p> + +</body> +</html> diff --git a/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_dispose_archive_accepted.html b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_dispose_archive_accepted.html new file mode 100644 index 0000000000..b344e1e6e7 --- /dev/null +++ b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_dispose_archive_accepted.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" + content="text/html; charset=UTF-8" + /> +</head> +<body> + +<p>[English below]</p> + +<p>Madame, Monsieur,</p> +<p>Votre demande de dispose de l'aip '<a th:href="${submissionPortalHomepage} + '/preservation-planning/aip/1/detail/' + ${aip.resId} +'/metadadata'" + th:text="${aip.info.name}" +></a>' a été acceptée.</p> + +<p>Avec nos cordiales salutations, + <br/> + <span th:text="${project}"> </span> +</p> + +<p>-------- + <br/> +</p> + +<p>Dear Sir or Madam,</p> + +<p>Your request to dispose d'aip '<a th:href="${submissionPortalHomepage} + '/preservation-planning/aip/1/detail/' + ${aip.resId} +'/metadadata'" + th:text="${aip.info.name}" +></a>' has been granted.</p> + +<p>Kind regards, + <br/> + <span th:text="${project}"> </span> +</p> + +</body> +</html> diff --git a/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_accepted.html b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_accepted.html new file mode 100644 index 0000000000..a1c5e98357 --- /dev/null +++ b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_accepted.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html><!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" + content="text/html; charset=UTF-8" + /> +</head> +<body> + +<p>[English below]</p> + +<p>Madame, Monsieur,</p> + +<p>Votre demande d'être membre de l'unité organisationelle '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' a été acceptée.</p> + +<p>Avec nos cordiales salutations, + <br/> + <span th:text="${project}"> </span> +</p> + +<p>-------- + <br/> +</p> + +<p>Dear Sir or Madam,</p> + +<p>Your request of being member of the organizational unit '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' has been granted.</p> + +<p>Kind regards, + <br/> + <span th:text="${project}"> </span> +</p> + +</body> +</html> diff --git a/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_refused.html b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_refused.html new file mode 100644 index 0000000000..b9992c6d73 --- /dev/null +++ b/DLCM-ResourceServerCommon/src/main/resources/email-templates/request_to_join_org_unit_refused.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html><!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" + content="text/html; charset=UTF-8" + /> +</head> +<body> + +<p>[English below]</p> + +<p>Madame, Monsieur,</p> + +<p>Votre demande d'être membre de l'unité organisationelle: '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' a été refusée avec le motif: <span th:text="${reason}"></span>.</p> + + +<p>Avec nos cordiales salutations, + <br/> + <span th:text="${project}"> </span> +</p> + +<p>-------- + <br/> +</p> + +<p>Dear Sir or Madam,</p> + +<p>Your request to be member of the organizational unit '<a th:href="${submissionPortalHomepage} + '/preservation-space/organizational-unit/detail/' + ${orgunit.resId}" + th:text="${orgunit.name}" +></a>' has been refused because: <span th:text="${reason}"></span>.</p> + + +<p>Kind regards, + <br/> + <span th:text="${project}"> </span> +</p> + +</body> +</html> -- GitLab From eee0b9042b2e03bed2755683efa55b2fcf268996 Mon Sep 17 00:00:00 2001 From: Alicia de Dios Fuente <Alicia.DeDiosFuente@unige.ch> Date: Mon, 17 Feb 2025 17:33:15 +0100 Subject: [PATCH 2/3] feat: unable to change notification status once approved/rejected --- .../ch/dlcm/business/NotificationService.java | 3 ++ .../test/admin/NotificationAsAdminIT.java | 11 ++-- .../dlcm/test/admin/NotificationAsUserIT.java | 53 ++++++++++++++----- .../dlcm/model/notification/Notification.java | 6 +++ 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java index 32f9a00a9a..7ef5d2b79b 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java @@ -382,6 +382,9 @@ public class NotificationService extends ResourceService<Notification> { NotificationStatus.REFUSED.equals(newStatus)) { throw new SolidifyUnmodifiableException("Notification of category INFO cannot be refused"); } + if (notification.getNotificationStatus().equals(NotificationStatus.APPROVED) || notification.getNotificationStatus().equals(NotificationStatus.REFUSED)) { + throw new SolidifyUnmodifiableException("Notification already approved or refused, cannot be changed"); + } notification.setNotificationStatus(newStatus); notification.setResponseMessage(responseMessage); this.save(notification); diff --git a/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsAdminIT.java b/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsAdminIT.java index 27433d5f1f..73c8bffcdf 100644 --- a/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsAdminIT.java +++ b/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsAdminIT.java @@ -86,10 +86,13 @@ class NotificationAsAdminIT extends AbstractNotificationIT { remoteNotification = this.notificationClientService.findOne(notificationId); assertEquals(NotificationStatus.APPROVED, remoteNotification.getNotificationStatus(), "Notification status should be approved"); - this.notificationClientService.setRefusedStatus(notificationId, REFUSAL_REASON); - remoteNotification = this.notificationClientService.findOne(notificationId); - assertEquals(NotificationStatus.REFUSED, remoteNotification.getNotificationStatus(), "Notification status should be refused"); - assertEquals(REFUSAL_REASON, remoteNotification.getResponseMessage()); + final Notification localNotification2 = this.createLocalNotification(NotificationType.JOIN_ORGUNIT_REQUEST, notifiedOrgUnit); + + Notification remoteNotification2 = this.createRemoteNotification(localNotification2); + this.notificationClientService.setRefusedStatus(remoteNotification2.getResId(), REFUSAL_REASON); + remoteNotification2 = this.notificationClientService.findOne(remoteNotification2.getResId()); + assertEquals(NotificationStatus.REFUSED, remoteNotification2.getNotificationStatus(), "Notification status should be refused"); + assertEquals(REFUSAL_REASON, remoteNotification2.getResponseMessage()); } @Test diff --git a/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsUserIT.java b/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsUserIT.java index 8ffdd4819c..e4daa61d4a 100644 --- a/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsUserIT.java +++ b/DLCM-IntegrationTests/src/test/java/ch/dlcm/test/admin/NotificationAsUserIT.java @@ -112,22 +112,25 @@ class NotificationAsUserIT extends AbstractNotificationIT { remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); assertEquals(NotificationStatus.APPROVED, remoteNotification.getNotificationStatus(), "Notification status should be APPROVED"); - this.notificationClientService.setRefusedStatus(remoteNotification.getResId(), REFUSAL_REASON); - remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); - assertEquals(NotificationStatus.REFUSED, remoteNotification.getNotificationStatus(), "Notification status should be REFUSED"); - assertEquals(REFUSAL_REASON, remoteNotification.getResponseMessage()); + final Notification localNotification2 = this.createLocalNotification(NotificationType.JOIN_ORGUNIT_REQUEST, organizationalUnit); - this.notificationClientService.setPendingStatus(remoteNotification.getResId()); - remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); - assertEquals(NotificationStatus.PENDING, remoteNotification.getNotificationStatus(), "Notification status should be PENDING"); + Notification remoteNotification2 = this.createRemoteNotification(localNotification2); + this.notificationClientService.setPendingStatus(remoteNotification2.getResId()); + remoteNotification2 = this.notificationClientService.getInboxNotification(remoteNotification2.getResId()); + assertEquals(NotificationStatus.PENDING, remoteNotification2.getNotificationStatus(), "Notification status should be PENDING"); - this.notificationClientService.setReadMark(remoteNotification.getResId()); - remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); - assertEquals(NotificationMark.READ, remoteNotification.getNotificationMark(), "Notification mark should be READ"); + this.notificationClientService.setRefusedStatus(remoteNotification2.getResId(), REFUSAL_REASON); + remoteNotification2 = this.notificationClientService.getInboxNotification(remoteNotification2.getResId()); + assertEquals(NotificationStatus.REFUSED, remoteNotification2.getNotificationStatus(), "Notification status should be REFUSED"); + assertEquals(REFUSAL_REASON, remoteNotification2.getResponseMessage()); - this.notificationClientService.setUnreadMark(remoteNotification.getResId()); - remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); - assertEquals(NotificationMark.UNREAD, remoteNotification.getNotificationMark(), "Notification mark should be UNREAD"); + this.notificationClientService.setReadMark(remoteNotification2.getResId()); + remoteNotification2 = this.notificationClientService.getInboxNotification(remoteNotification2.getResId()); + assertEquals(NotificationMark.READ, remoteNotification2.getNotificationMark(), "Notification mark should be READ"); + + this.notificationClientService.setUnreadMark(remoteNotification2.getResId()); + remoteNotification2 = this.notificationClientService.getInboxNotification(remoteNotification2.getResId()); + assertEquals(NotificationMark.UNREAD, remoteNotification2.getNotificationMark(), "Notification mark should be UNREAD"); } @Test @@ -156,4 +159,28 @@ class NotificationAsUserIT extends AbstractNotificationIT { this.checkUnableToCreateMultiplesNotificationsForSameUser(NotificationType.JOIN_ORGUNIT_REQUEST); } + @Test + void cannotChangeNotificationStatusAlreadyAcceptedOrRefused() { + final Person currentPerson = this.personITService.getLinkedPersonToCurrentUser(); + this.restClientTool.sudoAdmin(); + final OrganizationalUnit organizationalUnit = this.orgUnitITService.createTemporaryRemoteOrgUnit( + "Org unit for testing changes in notification status ", new ArrayList<>()); + this.orgUnitITService.enforcePersonHasRoleInOrgUnit(organizationalUnit, currentPerson, Role.MANAGER_ID); + this.restClientTool.exitSudo(); + + final Notification localNotification = this.createLocalNotification(NotificationType.JOIN_ORGUNIT_REQUEST, organizationalUnit); + + Notification remoteNotification = this.createRemoteNotification(localNotification); + assertEquals(NotificationStatus.PENDING, remoteNotification.getNotificationStatus(), "Initial notification status should be PENDING"); + assertEquals(NotificationMark.UNREAD, remoteNotification.getNotificationMark(), "Initial notification should be UNREAD"); + + this.notificationClientService.setProcessedStatus(remoteNotification.getResId()); + remoteNotification = this.notificationClientService.getInboxNotification(remoteNotification.getResId()); + assertEquals(NotificationStatus.APPROVED, remoteNotification.getNotificationStatus(), "Notification status should be APPROVED"); + + remoteNotification.setNotificationStatus(NotificationStatus.REFUSED); + final String notificationId = remoteNotification.getResId(); + final Notification updatedNotification = remoteNotification; + assertThrows(HttpClientErrorException.Forbidden.class, () -> this.notificationClientService.update(notificationId, updatedNotification)); + } } diff --git a/DLCM-Model/src/main/java/ch/dlcm/model/notification/Notification.java b/DLCM-Model/src/main/java/ch/dlcm/model/notification/Notification.java index 24bc3ecf39..9ea60235b1 100644 --- a/DLCM-Model/src/main/java/ch/dlcm/model/notification/Notification.java +++ b/DLCM-Model/src/main/java/ch/dlcm/model/notification/Notification.java @@ -285,4 +285,10 @@ public class Notification extends ResourceNormalized implements ResourceFileInte public void setResourceFile(ResourceFile resourceFile) { this.setSignedDuaFile((SignedDuaFile) resourceFile); } + + @Override + @JsonIgnore + public boolean isModifiable() { + return (this.getNotificationStatus() != NotificationStatus.REFUSED && this.getNotificationStatus() != NotificationStatus.APPROVED); + } } -- GitLab From 73065d66ef470c929df646f807ee048c2937279a Mon Sep 17 00:00:00 2001 From: Alicia de Dios Fuente <Alicia.DeDiosFuente@unige.ch> Date: Wed, 19 Feb 2025 15:21:22 +0100 Subject: [PATCH 3/3] fix: merge review --- .../src/main/java/ch/dlcm/business/NotificationService.java | 4 ++-- .../main/java/ch/dlcm/service/NotificationsEmailService.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java index 7ef5d2b79b..e659586a1f 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/business/NotificationService.java @@ -284,7 +284,7 @@ public class NotificationService extends ResourceService<Notification> { @Override public Notification save(Notification item) { - // Send an email to emitter for certain types of notification + // Email emitter for certain types of notification this.sendEmailToEmitter(item); // Status is non-applicable if notification category is info @@ -307,7 +307,7 @@ public class NotificationService extends ResourceService<Notification> { || notification.getNotificationStatus().equals(NotificationStatus.REFUSED))) { EmailMessage.EmailTemplate emailTemplate = this.getEmailTemplate(notification.getNotificationType(), notification.getNotificationStatus()); if (emailTemplate != null) { - Map<String, Object> parameters = new HashMap(); + Map<String, Object> parameters = new HashMap<>(); if (notification.getNotificationType().equals(NotificationType.JOIN_ORGUNIT_REQUEST)) { parameters.put("objectId", notification.getNotifiedOrgUnit().getResId()); } else { diff --git a/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java b/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java index c34f60da74..a4ee75d21b 100644 --- a/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java +++ b/DLCM-Admin/src/main/java/ch/dlcm/service/NotificationsEmailService.java @@ -187,7 +187,7 @@ public class NotificationsEmailService extends DLCMService { } this.sendEmailAndMarkNotificationAsSent(notifications, emailTemplate, recipientUser, parameters); } else { - // Need to mark notification as sent, otherwise, it is going to be check every time the job runs. + // Need to mark notification as sent, otherwise, it is going to be checked every time the job runs. OffsetDateTime sentTime = OffsetDateTime.now(); for (Notification notification : notifications) { this.notificationService.updateSentTime(notification.getResId(), sentTime); -- GitLab