From e1920bd7d92f2437cebb8b5932b93472f8932d63 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Thu, 5 Dec 2019 18:40:08 +0100 Subject: [PATCH] OO-4387: delete absence notice with a unique relation to deleted blocks --- .../olat/modules/lecture/LectureService.java | 4 +- .../AbsenceNoticeToLectureBlockDAO.java | 39 ++++ .../lecture/manager/LectureServiceImpl.java | 40 +++- .../restapi/LectureBlockWebService.java | 4 +- .../ConfirmDeleteLectureBlockController.java | 171 ++++++++++++++ .../ui/LectureListRepositoryController.java | 77 ++----- .../lecture/ui/_content/confirm_delete.html | 15 ++ .../ui/_i18n/LocalStrings_de.properties | 2 + .../ui/_i18n/LocalStrings_en.properties | 2 + .../AbsenceNoticeToLectureBlockDAOTest.java | 30 +++ .../lecture/manager/LectureServiceTest.java | 212 ++++++++++++++++++ 11 files changed, 537 insertions(+), 59 deletions(-) create mode 100644 src/main/java/org/olat/modules/lecture/ui/ConfirmDeleteLectureBlockController.java create mode 100644 src/main/java/org/olat/modules/lecture/ui/_content/confirm_delete.html diff --git a/src/main/java/org/olat/modules/lecture/LectureService.java b/src/main/java/org/olat/modules/lecture/LectureService.java index 249e79a6102..19e6ba480e6 100644 --- a/src/main/java/org/olat/modules/lecture/LectureService.java +++ b/src/main/java/org/olat/modules/lecture/LectureService.java @@ -220,7 +220,7 @@ public interface LectureService { * * @param block The block to delete. */ - public void deleteLectureBlock(LectureBlock block); + public void deleteLectureBlock(LectureBlock block, Identity doer); /** * Delete all the lecture blocks and configuration of the specified course. @@ -346,6 +346,8 @@ public interface LectureService { public List<AbsenceNoticeInfos> searchAbsenceNotices(AbsenceNoticeSearchParameters searchParams); + public List<AbsenceNotice> getAbsenceNoticeUniquelyRelatedTo(List<LectureBlock> blocks); + /** * Detect an absence notice for the specified identity. * diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java index ef8a58fa273..d58b7019e37 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAO.java @@ -19,8 +19,11 @@ */ package org.olat.modules.lecture.manager; +import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.QueryBuilder; @@ -80,6 +83,42 @@ public class AbsenceNoticeToLectureBlockDAO { .getResultList(); } + /** + * Return all relations of notices which has a relation with + * the specified lecture blocks. + * + * @param blocks A list of lecture blocks + * @return A list of absence notice to lecture block relationship + */ + public List<AbsenceNoticeToLectureBlock> getRelationsAStepFurther(List<LectureBlock> blocks) { + if(blocks == null || blocks.isEmpty()) return new ArrayList<>(); + + QueryBuilder sb = new QueryBuilder(255); + sb.append("select targetNoticeToBlock from absencenoticetolectureblock noticeToBlock") + .append(" inner join noticeToBlock.absenceNotice as notice") + .append(" inner join absencenoticetolectureblock as targetNoticeToBlock on (targetNoticeToBlock.absenceNotice.key=notice.key)") + .append(" inner join fetch targetNoticeToBlock.absenceNotice as targetNotice") + .append(" inner join fetch targetNoticeToBlock.lectureBlock as targetLectureBlock") + .append(" where noticeToBlock.lectureBlock.key in (:lectureBlockKeys)"); + + List<Long> lectureBlockKeys = blocks.stream() + .map(LectureBlock::getKey) + .collect(Collectors.toList()); + List<AbsenceNoticeToLectureBlock> relations= dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), AbsenceNoticeToLectureBlock.class) + .setParameter("lectureBlockKeys", lectureBlockKeys) + .getResultList(); + return new ArrayList<>(new HashSet<>(relations)); + } + + public int deleteRelations(LectureBlock lectureBlock) { + String query = "delete from absencenoticetolectureblock noticeToBlock where noticeToBlock.lectureBlock.key=:lectureBlockKey"; + return dbInstance.getCurrentEntityManager() + .createQuery(query) + .setParameter("lectureBlockKey", lectureBlock.getKey()) + .executeUpdate(); + } + public void deleteRelations(List<AbsenceNoticeToLectureBlock> relations) { for(AbsenceNoticeToLectureBlock relation:relations) { deleteRelation(relation); diff --git a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java index 193d4f228ac..761ae338f64 100644 --- a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java +++ b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java @@ -33,6 +33,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.apache.logging.log4j.Logger; @@ -430,7 +431,7 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De } @Override - public void deleteLectureBlock(LectureBlock lectureBlock) { + public void deleteLectureBlock(LectureBlock lectureBlock, Identity actingIdentity) { //first remove events LectureBlock reloadedBlock = lectureBlockDao.loadByKey(lectureBlock.getKey()); RepositoryEntry entry = reloadedBlock.getEntry(); @@ -442,7 +443,44 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De List<Identity> teachers = getTeachers(reloadedBlock); unsyncInternalCalendar(reloadedBlock, teachers); } + + List<AbsenceNotice> absenceNotices = getAbsenceNoticeUniquelyRelatedTo(Collections.singletonList(lectureBlock)); + for(AbsenceNotice absenceNotice:absenceNotices) { + deleteAbsenceNotice(absenceNotice, actingIdentity); + } + absenceNoticeToLectureBlockDao.deleteRelations(reloadedBlock); lectureBlockDao.delete(reloadedBlock); + dbInstance.commit();// make it quick + } + + @Override + public List<AbsenceNotice> getAbsenceNoticeUniquelyRelatedTo(List<LectureBlock> blocks) { + List<AbsenceNoticeToLectureBlock> relations = absenceNoticeToLectureBlockDao.getRelationsAStepFurther(blocks); + + Set<Long> lectureBlockKeys = blocks.stream() + .map(LectureBlock::getKey) + .collect(Collectors.toSet()); + + Map<AbsenceNotice,AtomicInteger> counters = new HashMap<>(); + for(AbsenceNoticeToLectureBlock relation:relations) { + AbsenceNotice notice = relation.getAbsenceNotice(); + LectureBlock lectureBlock = relation.getLectureBlock(); + + AtomicInteger counter = counters + .computeIfAbsent(notice, n -> new AtomicInteger(0)); + if(!lectureBlockKeys.contains(lectureBlock.getKey())) { + counter.incrementAndGet(); + } + } + + List<AbsenceNotice> uniquelyRelated = new ArrayList<>(); + for(Map.Entry<AbsenceNotice,AtomicInteger> counterEntry:counters.entrySet()) { + int count = counterEntry.getValue().intValue(); + if(count == 0) { + uniquelyRelated.add(counterEntry.getKey()); + } + } + return uniquelyRelated; } @Override diff --git a/src/main/java/org/olat/modules/lecture/restapi/LectureBlockWebService.java b/src/main/java/org/olat/modules/lecture/restapi/LectureBlockWebService.java index 9ddbb25d460..b02ff0f483f 100644 --- a/src/main/java/org/olat/modules/lecture/restapi/LectureBlockWebService.java +++ b/src/main/java/org/olat/modules/lecture/restapi/LectureBlockWebService.java @@ -125,8 +125,8 @@ public class LectureBlockWebService { */ @DELETE public Response deleteLectureBlock() { - lectureService.deleteLectureBlock(lectureBlock); - log.info(Tracing.M_AUDIT, "Lecture block deleted: " + lectureBlock); + lectureService.deleteLectureBlock(lectureBlock, null); + log.info(Tracing.M_AUDIT, "Lecture block deleted: {}", lectureBlock); return Response.ok().build(); } diff --git a/src/main/java/org/olat/modules/lecture/ui/ConfirmDeleteLectureBlockController.java b/src/main/java/org/olat/modules/lecture/ui/ConfirmDeleteLectureBlockController.java new file mode 100644 index 00000000000..540fadc7fdb --- /dev/null +++ b/src/main/java/org/olat/modules/lecture/ui/ConfirmDeleteLectureBlockController.java @@ -0,0 +1,171 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.modules.lecture.ui; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.id.Identity; +import org.olat.core.logging.activity.CoreLoggingResourceable; +import org.olat.core.logging.activity.LearningResourceLoggingAction; +import org.olat.core.logging.activity.OlatResourceableType; +import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; +import org.olat.core.util.StringHelper; +import org.olat.modules.lecture.AbsenceNotice; +import org.olat.modules.lecture.LectureBlock; +import org.olat.modules.lecture.LectureBlockManagedFlag; +import org.olat.modules.lecture.LectureService; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 5 déc. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ConfirmDeleteLectureBlockController extends FormBasicController { + + private static final String[] confirmKeys = new String[] { "confirm" }; + + private FormLink deleteButton; + private MultipleSelectionElement confirmEl; + + private final List<LectureBlock> blocks; + + @Autowired + private UserManager userManager; + @Autowired + private LectureService lectureService; + + public ConfirmDeleteLectureBlockController(UserRequest ureq, WindowControl wControl, List<LectureBlock> blocks) { + super(ureq, wControl, "confirm_delete"); + this.blocks = blocks; + initForm(ureq); + } + + public List<LectureBlock> getBlocks() { + return blocks; + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + if(formLayout instanceof FormLayoutContainer) { + StringBuilder titles = new StringBuilder(128); + for(LectureBlock block:blocks) { + if(titles.length() > 0) titles.append(", "); + titles.append(StringHelper.escapeHtml(block.getTitle())); + } + String text = translate("confirm.delete.lectures", new String[] { titles.toString() }); + ((FormLayoutContainer)formLayout).contextPut("msg", text); + } + + List<AbsenceNotice> notices = lectureService.getAbsenceNoticeUniquelyRelatedTo(blocks); + if(!notices.isEmpty()) { + // confirmation delete notices + Set<Identity> identities = new HashSet<>(); + for(AbsenceNotice notice:notices) { + identities.add(notice.getIdentity()); + } + + StringBuilder names = new StringBuilder(128); + for(Identity identity:identities) { + String name = userManager.getUserDisplayName(identity); + if(names.length() > 0) names.append(", "); + names.append(name); + } + + String text = translate("confirm.delete.lectures.notices", new String[] { Integer.toString(notices.size()), names.toString() }); + ((FormLayoutContainer)formLayout).contextPut("noticeMsg", text); + + String[] confirmationValues = new String[] {translate("confirm.delete.lectures.notices.confirmation") }; + confirmEl = uifactory.addCheckboxesHorizontal("confirm", "confirm.delete.lectures.notices.confirmation", formLayout, confirmKeys, confirmationValues); + } + deleteButton = uifactory.addFormLink("delete", formLayout, Link.BUTTON); + uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = super.validateFormLogic(ureq); + + if(confirmEl != null) { + confirmEl.clearError(); + if(!confirmEl.isAtLeastSelected(1)) { + confirmEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + } + + return allOk; + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(deleteButton == source) { + if(validateFormLogic(ureq)) { + doDeleteLectureBlocks(ureq); + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + private void doDeleteLectureBlocks(UserRequest ureq) { + for(LectureBlock block:blocks) { + if(LectureBlockManagedFlag.isManaged(block, LectureBlockManagedFlag.delete)) { + continue; + } + lectureService.deleteLectureBlock(block, getIdentity()); + logAudit("Lecture block deleted: " + block); + ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LECTURE_BLOCK_DELETED, getClass(), + CoreLoggingResourceable.wrap(block, OlatResourceableType.lectureBlock, block.getTitle())); + } + showInfo("lecture.deleted"); + fireEvent(ureq, Event.DONE_EVENT); + } +} diff --git a/src/main/java/org/olat/modules/lecture/ui/LectureListRepositoryController.java b/src/main/java/org/olat/modules/lecture/ui/LectureListRepositoryController.java index 06f0bdb6288..dd48a58b6de 100644 --- a/src/main/java/org/olat/modules/lecture/ui/LectureListRepositoryController.java +++ b/src/main/java/org/olat/modules/lecture/ui/LectureListRepositoryController.java @@ -53,8 +53,6 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowController; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.core.gui.control.generic.modal.DialogBoxController; -import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.gui.control.generic.wizard.Step; import org.olat.core.gui.control.generic.wizard.StepRunnerCallback; import org.olat.core.gui.control.generic.wizard.StepsMainRunController; @@ -63,7 +61,6 @@ import org.olat.core.logging.activity.CoreLoggingResourceable; import org.olat.core.logging.activity.LearningResourceLoggingAction; import org.olat.core.logging.activity.OlatResourceableType; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; -import org.olat.core.util.StringHelper; import org.olat.modules.lecture.LectureBlock; import org.olat.modules.lecture.LectureBlockAuditLog; import org.olat.modules.lecture.LectureBlockManagedFlag; @@ -101,11 +98,10 @@ public class LectureListRepositoryController extends FormBasicController { private ToolsController toolsCtrl; private CloseableModalController cmc; - private DialogBoxController deleteDialogCtrl; private StepsMainRunController importBlockWizard; - private DialogBoxController bulkDeleteDialogCtrl; private EditLectureBlockController editLectureCtrl; private CloseableCalloutWindowController toolsCalloutCtrl; + private ConfirmDeleteLectureBlockController deleteLectureBlocksCtrl; private int counter = 0; private final RepositoryEntry entry; @@ -269,34 +265,23 @@ public class LectureListRepositoryController extends FormBasicController { if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { loadModel(); } - } else if(deleteDialogCtrl == source) { - if (DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event)) { - LectureBlockRow row = (LectureBlockRow)deleteDialogCtrl.getUserObject(); - doDelete(row); + } else if(deleteLectureBlocksCtrl == source) { + if (event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { loadModel(); - cleanUp(); - } - } else if(bulkDeleteDialogCtrl == source) { - if (DialogBoxUIFactory.isYesEvent(event) || DialogBoxUIFactory.isOkEvent(event)) { - @SuppressWarnings("unchecked") - List<LectureBlock> blocks = (List<LectureBlock>)bulkDeleteDialogCtrl.getUserObject(); - doDelete(blocks); - loadModel(); - cleanUp(); } + cmc.deactivate(); + cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { - removeAsListenerAndDispose(bulkDeleteDialogCtrl); - removeAsListenerAndDispose(deleteDialogCtrl); + removeAsListenerAndDispose(deleteLectureBlocksCtrl); removeAsListenerAndDispose(editLectureCtrl); removeAsListenerAndDispose(toolsCalloutCtrl); removeAsListenerAndDispose(toolsCtrl); removeAsListenerAndDispose(cmc); - bulkDeleteDialogCtrl = null; - deleteDialogCtrl = null; + deleteLectureBlocksCtrl = null; toolsCalloutCtrl = null; editLectureCtrl = null; toolsCtrl = null; @@ -391,45 +376,27 @@ public class LectureListRepositoryController extends FormBasicController { if(blocks.isEmpty()) { showWarning("error.atleastone.lecture"); } else { - StringBuilder titles = new StringBuilder(); - for(LectureBlock block:blocks) { - if(titles.length() > 0) titles.append(", "); - titles.append(StringHelper.escapeHtml(block.getTitle())); - } - String text = translate("confirm.delete.lectures", new String[] { titles.toString() }); - bulkDeleteDialogCtrl = activateYesNoDialog(ureq, translate("delete.lectures.title"), text, bulkDeleteDialogCtrl); - bulkDeleteDialogCtrl.setUserObject(blocks); + deleteLectureBlocksCtrl = new ConfirmDeleteLectureBlockController(ureq, getWindowControl(), blocks); + listenTo(deleteLectureBlocksCtrl); + + String title = translate("delete.lectures.title"); + cmc = new CloseableModalController(getWindowControl(), "close", deleteLectureBlocksCtrl.getInitialComponent(), true, title); + listenTo(cmc); + cmc.activate(); } } private void doConfirmDelete(UserRequest ureq, LectureBlockRow row) { - String text = translate("confirm.delete.lectures", new String[] { row.getLectureBlock().getTitle() }); - deleteDialogCtrl = activateYesNoDialog(ureq, translate("delete.lectures.title"), text, deleteDialogCtrl); - deleteDialogCtrl.setUserObject(row); - } - - private void doDelete(LectureBlockRow row) { - if(LectureBlockManagedFlag.isManaged(row.getLectureBlock(), LectureBlockManagedFlag.delete)) return; + List<LectureBlock> blocks = Collections.singletonList(row.getLectureBlock()); + deleteLectureBlocksCtrl = new ConfirmDeleteLectureBlockController(ureq, getWindowControl(), blocks); + listenTo(deleteLectureBlocksCtrl); - LectureBlock lectureBlock = row.getLectureBlock(); - lectureService.deleteLectureBlock(lectureBlock); - showInfo("lecture.deleted"); - logAudit("Lecture block deleted: " + lectureBlock); - ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LECTURE_BLOCK_DELETED, getClass(), - CoreLoggingResourceable.wrap(lectureBlock, OlatResourceableType.lectureBlock, lectureBlock.getTitle())); - - } - - private void doDelete(List<LectureBlock> blocks) { - for(LectureBlock block:blocks) { - lectureService.deleteLectureBlock(block); - logAudit("Lecture block deleted: " + block); - ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LECTURE_BLOCK_DELETED, getClass(), - CoreLoggingResourceable.wrap(block, OlatResourceableType.lectureBlock, block.getTitle())); - } - showInfo("lecture.deleted"); + String title = translate("delete.lectures.title"); + cmc = new CloseableModalController(getWindowControl(), "close", deleteLectureBlocksCtrl.getInitialComponent(), true, title); + listenTo(cmc); + cmc.activate(); } - + private void doExportLog(UserRequest ureq, LectureBlockRow row) { LectureBlock lectureBlock = lectureService.getLectureBlock(row); List<LectureBlockAuditLog> auditLog = lectureService.getAuditLog(row); diff --git a/src/main/java/org/olat/modules/lecture/ui/_content/confirm_delete.html b/src/main/java/org/olat/modules/lecture/ui/_content/confirm_delete.html new file mode 100644 index 00000000000..82a35bdac8c --- /dev/null +++ b/src/main/java/org/olat/modules/lecture/ui/_content/confirm_delete.html @@ -0,0 +1,15 @@ +<div class="o_warning" role="alert">$msg +#if($r.isNotEmpty($noticeMsg)) + <br><i class="o_icon o_icon-lg o_icon_important"> </i> $noticeMsg +#end +</div> +#if($r.available("confirm")) + $r.render("confirm") + #if($f.hasError("confirm")) + $r.render("confirm_ERROR") + #end +#end +<div class="o_button_group"> + $r.render("cancel") + $r.render("delete") +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties index bbbb3ff301f..3df1c8ce7e0 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_de.properties @@ -99,6 +99,8 @@ confirm.delete.dispensation=Wollen Sie die Dispens von "{0}" l\u00F6schen? Die P confirm.delete.assessment.mode.text=Wollen Sie wirklich diesen Pr\u00FCfungsmodus von Lektionenblock "{0}" l\u00F6schen? confirm.delete.assessment.mode.title=Pr\u00FCfungsmodus l\u00F6schen confirm.delete.lectures=Wollen Sie wirklich diesen Lektionenblock "{0}" l\u00F6schen? +confirm.delete.lectures.notices=<strong>{0}</strong> Absenze(n) / Abmeldunge(n) f\u00FCr "<strong>{1}</strong>" die nur zu den Lektionbl\u00F6cke geh\u00F6ren werden mit gel\u00F6scht. +confirm.delete.lectures.notices.confirmation=Ich verstehe dass die Absenzen / Abmeldungen definitv gel\u00F6scht werden. confirm.delete.reason=Wollen Sie wirklich diese Begr\u00FCndung "{0}" l\u00F6schen? contact.teachers=Dozenten kontaktieren contact.teachers.list.name=Dozenten diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties index 9dfdfaf9cfc..17e573a88ea 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_en.properties @@ -99,6 +99,8 @@ confirm.delete.assessment.mode.text=Do you really want to delete the exam of thi confirm.delete.assessment.mode.title=Delete exam confirm.delete.dispensation=Do you really want to delete the dispensation of "{0}"? The person will be considered present. confirm.delete.lectures=Do you really want to delete these lectures "{0}"? +confirm.delete.lectures.notices=<strong>{0}</strong> absences / dispensation for "<strong>{1}</strong>" which only linked to the lecture blocks will be deleted. +confirm.delete.lectures.notices.confirmation=I understand that the absences / dispensation will be definitively deleted. confirm.delete.reason=Do you really want to delete this reason "{0}"? contact.teachers=Contact teachers contact.teachers.list.name=Teachers diff --git a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java index ca518b79c84..f1215ec75b5 100644 --- a/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java +++ b/src/test/java/org/olat/modules/lecture/manager/AbsenceNoticeToLectureBlockDAOTest.java @@ -19,6 +19,7 @@ */ package org.olat.modules.lecture.manager; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; @@ -194,6 +195,35 @@ public class AbsenceNoticeToLectureBlockDAOTest extends OlatTestCase { Assert.assertFalse(rollCallList.contains(oldRollCall1)); } + @Test + public void getRelationsAStepFurther() { + + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + LectureBlock lectureBlockEntry1 = createMinimalLectureBlock(entry, new Date(), new Date()); + LectureBlock lectureBlockEntry2 = createMinimalLectureBlock(entry, new Date(), new Date()); + List<LectureBlock> lectureBlockEntries = new ArrayList<>(); + lectureBlockEntries.add(lectureBlockEntry1); + lectureBlockEntries.add(lectureBlockEntry2); + + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + AbsenceNotice notice = lectureService.createAbsenceNotice(participant, AbsenceNoticeType.absence, AbsenceNoticeTarget.entries, + CalendarUtils.startOfDay(new Date()), CalendarUtils.endOfDay(new Date()), + null, null, Boolean.TRUE, null, lectureBlockEntries, null); + dbInstance.commitAndCloseSession(); + + List<AbsenceNoticeToLectureBlock> relations = absenceNoticeToLectureBlockDao + .getRelationsAStepFurther(Collections.singletonList(lectureBlockEntry1)); + dbInstance.commitAndCloseSession(); + + Assert.assertNotNull(relations); + Assert.assertEquals(2, relations.size()); + + for(AbsenceNoticeToLectureBlock relation:relations) { + Assert.assertEquals(notice, relation.getAbsenceNotice()); + Assert.assertTrue(lectureBlockEntries.remove(relation.getLectureBlock())); + } + } + private LectureBlock createMinimalLectureBlock(RepositoryEntry entry, Date start, Date end) { LectureBlock lectureBlock = lectureBlockDao.createLectureBlock(entry); lectureBlock.setStartDate(start); diff --git a/src/test/java/org/olat/modules/lecture/manager/LectureServiceTest.java b/src/test/java/org/olat/modules/lecture/manager/LectureServiceTest.java index b1cecbe2a5a..38093ba9a39 100644 --- a/src/test/java/org/olat/modules/lecture/manager/LectureServiceTest.java +++ b/src/test/java/org/olat/modules/lecture/manager/LectureServiceTest.java @@ -501,6 +501,218 @@ public class LectureServiceTest extends OlatTestCase { Assert.assertNull(reloadedRollCall3.getAbsenceNotice()); } + @Test + public void getAbsenceNoticeUniquelyRelatedTo() { + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-2"); + RepositoryEntry entry1 = JunitTestHelper.createAndPersistRepositoryEntry(); + RepositoryEntry entry2 = JunitTestHelper.createAndPersistRepositoryEntry(); + + LectureBlock block1 = createMinimalLectureBlock(entry1); + LectureBlock block2 = createMinimalLectureBlock(entry2); + LectureBlock block3 = createMinimalLectureBlock(entry2); + LectureBlock block4 = createMinimalLectureBlock(entry2); + dbInstance.commit(); + + List<LectureBlock> lectureBlocks = new ArrayList<>(); + lectureBlocks.add(block1); + lectureBlocks.add(block2); + + AbsenceNotice notice1_2 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, lectureBlocks, null); + AbsenceNotice notice1_2_alt = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, lectureBlocks, null); + AbsenceNotice notice3 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, Collections.singletonList(block3), null); + AbsenceNotice notice3b = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, Collections.singletonList(block3), null); + + List<LectureBlock> lectureSecondBlocks = new ArrayList<>(); + lectureSecondBlocks.add(block1); + lectureSecondBlocks.add(block2); + lectureSecondBlocks.add(block4); + + AbsenceNotice notice4 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, lectureSecondBlocks, null); + dbInstance.commit(); + Assert.assertNotNull(notice4); + Assert.assertNotNull(notice1_2); + Assert.assertNotNull(notice1_2_alt); + + List<LectureBlock> allLectureBlocks = new ArrayList<>(); + allLectureBlocks.add(block1); + allLectureBlocks.add(block3); + + // check block 1 + 3 + List<AbsenceNotice> uniquelyRelatedNotices = lectureService.getAbsenceNoticeUniquelyRelatedTo(allLectureBlocks); + Assert.assertNotNull(uniquelyRelatedNotices); + Assert.assertEquals(2, uniquelyRelatedNotices.size()); + Assert.assertTrue(uniquelyRelatedNotices.contains(notice3)); + Assert.assertTrue(uniquelyRelatedNotices.contains(notice3b)); + + List<AbsenceNotice> uniquelyRelatedNoticesTo4 = lectureService.getAbsenceNoticeUniquelyRelatedTo(Collections.singletonList(block4)); + Assert.assertNotNull(uniquelyRelatedNoticesTo4); + Assert.assertTrue(uniquelyRelatedNoticesTo4.isEmpty()); + + // dummy check + List<AbsenceNotice> emptyList = lectureService.getAbsenceNoticeUniquelyRelatedTo(Collections.emptyList()); + Assert.assertNotNull(emptyList); + Assert.assertTrue(emptyList.isEmpty()); + } + + @Test + public void getAbsenceNoticeUniquelyRelatedTo_minimal() { + Identity participant = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-3"); + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + + LectureBlock block = createMinimalLectureBlock(entry); + dbInstance.commit(); + + AbsenceNotice noticeEntries = lectureService.createAbsenceNotice(participant, AbsenceNoticeType.notified, AbsenceNoticeTarget.allentries, + new Date(), new Date(), null, null, null, null, null, null); + AbsenceNotice noticeLectures = lectureService.createAbsenceNotice(participant, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, null, null, null, Collections.singletonList(block), null); + + // check + List<AbsenceNotice> uniquelyRelatedNotices = lectureService.getAbsenceNoticeUniquelyRelatedTo(Collections.singletonList(block)); + Assert.assertNotNull(uniquelyRelatedNotices); + Assert.assertEquals(1, uniquelyRelatedNotices.size()); + Assert.assertTrue(uniquelyRelatedNotices.contains(noticeLectures)); + Assert.assertFalse(uniquelyRelatedNotices.contains(noticeEntries)); + } + + /** + * Lengthy test to check the method with more than a few notices + * and cross check different cases. + * + */ + @Test + public void getAbsenceNoticeUniquelyRelatedTo_variant() { + Identity participant1 = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-1"); + Identity participant2 = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-2"); + Identity participant3 = JunitTestHelper.createAndPersistIdentityAsRndUser("noticee-3"); + RepositoryEntry entry1 = JunitTestHelper.createAndPersistRepositoryEntry(); + RepositoryEntry entry2 = JunitTestHelper.createAndPersistRepositoryEntry(); + + LectureBlock block1 = createMinimalLectureBlock(entry1); + LectureBlock block2 = createMinimalLectureBlock(entry1); + LectureBlock block3 = createMinimalLectureBlock(entry1); + LectureBlock block4 = createMinimalLectureBlock(entry2); + LectureBlock block5 = createMinimalLectureBlock(entry2); + LectureBlock block6 = createMinimalLectureBlock(entry2); + dbInstance.commit(); + + List<LectureBlock> lectureBlocks1_2 = new ArrayList<>(); + lectureBlocks1_2.add(block1); + lectureBlocks1_2.add(block2); + + AbsenceNotice notice1_2 = lectureService.createAbsenceNotice(participant1, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "1 2", null, null, lectureBlocks1_2, null); + + List<LectureBlock> lectureBlocks2_3_4 = new ArrayList<>(); + lectureBlocks2_3_4.add(block2); + lectureBlocks2_3_4.add(block3); + lectureBlocks2_3_4.add(block4); + AbsenceNotice notice2_3_4 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "2 3 4", null, null, lectureBlocks2_3_4, null); + + List<LectureBlock> lectureBlocks1_3_4 = new ArrayList<>(); + lectureBlocks1_3_4.add(block1); + lectureBlocks1_3_4.add(block3); + lectureBlocks1_3_4.add(block4); + AbsenceNotice notice1_3_4 = lectureService.createAbsenceNotice(participant3, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "1 3 4", null, null, lectureBlocks1_3_4, null); + + AbsenceNotice notice4 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "4", null, null, Collections.singletonList(block4), null); + + List<LectureBlock> lectureSecondBlocks4_5 = new ArrayList<>(); + lectureSecondBlocks4_5.add(block4); + lectureSecondBlocks4_5.add(block5); + AbsenceNotice notice4_5 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "4 5", null, null, lectureSecondBlocks4_5, null); + + List<LectureBlock> lectureBlocks4_5_6 = new ArrayList<>(); + lectureBlocks4_5_6.add(block4); + lectureBlocks4_5_6.add(block5); + lectureBlocks4_5_6.add(block6); + AbsenceNotice notice4_5_6 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "4 5 6", null, null, lectureBlocks4_5_6, null); + + AbsenceNotice notice6 = lectureService.createAbsenceNotice(participant2, AbsenceNoticeType.notified, AbsenceNoticeTarget.lectureblocks, + new Date(), new Date(), null, "6", null, null, Collections.singletonList(block6), null); + dbInstance.commit(); + + + // check blocks 4, 5, 6 + List<AbsenceNotice> uniquelyRelatedNotices4_5_6 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks4_5_6); + Assert.assertNotNull(uniquelyRelatedNotices4_5_6); + Assert.assertEquals(4, uniquelyRelatedNotices4_5_6.size()); + Assert.assertTrue(uniquelyRelatedNotices4_5_6.contains(notice4)); + Assert.assertTrue(uniquelyRelatedNotices4_5_6.contains(notice4_5)); + Assert.assertTrue(uniquelyRelatedNotices4_5_6.contains(notice4_5_6)); + Assert.assertTrue(uniquelyRelatedNotices4_5_6.contains(notice6)); + + // check blocks 1 5 + List<LectureBlock> lectureBlocks1_5 = new ArrayList<>(); + lectureBlocks1_5.add(block1); + lectureBlocks1_5.add(block3); + List<AbsenceNotice> uniquelyRelatedNotices1_5 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks1_5); + Assert.assertNotNull(uniquelyRelatedNotices1_5); + Assert.assertTrue(uniquelyRelatedNotices1_5.isEmpty()); + + // check blocks 1 2 + lectureBlocks1_2.add(block1);// part of the test + lectureBlocks1_2.add(block2); + List<AbsenceNotice> uniquelyRelatedNotices1_2 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks1_2); + Assert.assertNotNull(uniquelyRelatedNotices1_2); + Assert.assertEquals(1, uniquelyRelatedNotices1_2.size()); + Assert.assertTrue(uniquelyRelatedNotices1_2.contains(notice1_2)); + + // check blocks 4 + List<LectureBlock> lectureBlocks4 = new ArrayList<>(); + lectureBlocks4.add(block4); + List<AbsenceNotice> uniquelyRelatedNotices4 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks4); + Assert.assertNotNull(uniquelyRelatedNotices4); + Assert.assertEquals(1, uniquelyRelatedNotices4.size()); + Assert.assertTrue(uniquelyRelatedNotices4.contains(notice4)); + + // check blocks 2, 3, 4, 5, 6 + List<LectureBlock> lectureBlocks2_3_4_5_6 = new ArrayList<>(); + lectureBlocks2_3_4_5_6.add(block2); + lectureBlocks2_3_4_5_6.add(block3); + lectureBlocks2_3_4_5_6.add(block4); + lectureBlocks2_3_4_5_6.add(block5); + lectureBlocks2_3_4_5_6.add(block6); + List<AbsenceNotice> uniquelyRelatedNotices2_3_4_5_6 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks2_3_4_5_6); + Assert.assertNotNull(uniquelyRelatedNotices2_3_4_5_6); + Assert.assertEquals(5, uniquelyRelatedNotices2_3_4_5_6.size()); + Assert.assertTrue(uniquelyRelatedNotices2_3_4_5_6.contains(notice2_3_4)); + Assert.assertTrue(uniquelyRelatedNotices2_3_4_5_6.contains(notice4)); + Assert.assertTrue(uniquelyRelatedNotices2_3_4_5_6.contains(notice4_5)); + Assert.assertTrue(uniquelyRelatedNotices2_3_4_5_6.contains(notice4_5_6)); + Assert.assertTrue(uniquelyRelatedNotices2_3_4_5_6.contains(notice6)); + + // check all blocks + List<LectureBlock> lectureBlocks1_2_3_4_5_6 = new ArrayList<>(); + lectureBlocks1_2_3_4_5_6.add(block1); + lectureBlocks1_2_3_4_5_6.add(block2); + lectureBlocks1_2_3_4_5_6.add(block3); + lectureBlocks1_2_3_4_5_6.add(block4); + lectureBlocks1_2_3_4_5_6.add(block5); + lectureBlocks1_2_3_4_5_6.add(block6); + List<AbsenceNotice> uniquelyRelatedNotices1_2_3_4_5_6 = lectureService.getAbsenceNoticeUniquelyRelatedTo(lectureBlocks1_2_3_4_5_6); + Assert.assertNotNull(uniquelyRelatedNotices1_2_3_4_5_6); + Assert.assertEquals(7, uniquelyRelatedNotices1_2_3_4_5_6.size()); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice1_2)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice1_3_4)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice2_3_4)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice4)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice4_5)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice4_5_6)); + Assert.assertTrue(uniquelyRelatedNotices1_2_3_4_5_6.contains(notice6)); + } + private LectureBlock createMinimalLectureBlock(RepositoryEntry entry) { LectureBlock lectureBlock = lectureService.createLectureBlock(entry); lectureBlock.setStartDate(new Date()); -- GitLab