From 1226349dc0b7b6050f22f649155cfc7a4e2ab00c Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Thu, 5 Sep 2019 14:58:50 +0200
Subject: [PATCH] OO-4150: notice doesn't override physically roll call

---
 .../manager/LectureBlockRollCallDAO.java      | 73 +++++++++++++------
 .../lecture/manager/LectureServiceImpl.java   | 48 ------------
 .../model/LectureBlockAndRollCall.java        | 37 ++++++++--
 .../model/LectureBlockRollCallAndCoach.java   |  9 ++-
 .../ui/AppealListRepositoryController.java    |  3 +-
 .../modules/lecture/ui/AppealRollCallRow.java |  3 +-
 .../ParticipantLectureBlocksController.java   |  4 +-
 .../ui/ParticipantLectureBlocksDataModel.java |  2 +-
 ...AbsenceNoticeDetailsCalloutController.java |  9 ++-
 .../ui/coach/LecturesCoachingController.java  |  2 +-
 .../ui/coach/LecturesSearchController.java    | 12 ++-
 ...ectureBlockRollCallStatusCellRenderer.java | 15 ++++
 12 files changed, 127 insertions(+), 90 deletions(-)

diff --git a/src/main/java/org/olat/modules/lecture/manager/LectureBlockRollCallDAO.java b/src/main/java/org/olat/modules/lecture/manager/LectureBlockRollCallDAO.java
index a3701044b38..38c3f124114 100644
--- a/src/main/java/org/olat/modules/lecture/manager/LectureBlockRollCallDAO.java
+++ b/src/main/java/org/olat/modules/lecture/manager/LectureBlockRollCallDAO.java
@@ -308,8 +308,9 @@ public class LectureBlockRollCallDAO {
 		List<LectureBlockRollCallAndCoach> blockAndRollCalls = new ArrayList<>(rollCalls.size());
 		for(LectureBlockRollCall rollCall:rollCalls) {
 			LectureBlock block = rollCall.getLectureBlock();
+			AbsenceNotice absenceNotice = rollCall.getAbsenceNotice();
 			String coach = coaches.get(block.getKey());
-			blockAndRollCalls.add(new LectureBlockRollCallAndCoach(coach, block, rollCall));
+			blockAndRollCalls.add(new LectureBlockRollCallAndCoach(coach, block, rollCall, absenceNotice));
 		}
 		return blockAndRollCalls;
 	}
@@ -502,7 +503,7 @@ public class LectureBlockRollCallDAO {
 	public List<LectureBlockAndRollCall> getParticipantLectureBlockAndRollCalls(RepositoryEntryRef entry, IdentityRef identity,
 			String teacherSeaparator) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select block, call, re.displayname")
+		sb.append("select block, call, notice, re.displayname")
 		  .append(" from lectureblock block")
 		  .append(" inner join block.entry re")
 		  .append(" inner join block.groups blockToGroup")
@@ -510,6 +511,7 @@ public class LectureBlockRollCallDAO {
 		  .append(" inner join bGroup.members membership")
 		  .append(" inner join lectureparticipantsummary as summary on (summary.identity.key=membership.identity.key and summary.entry.key=block.entry.key)")
 		  .append(" left join lectureblockrollcall as call on (call.identity.key=membership.identity.key and call.lectureBlock.key=block.key)")
+		  .append(" left join call.absenceNotice notice")
 		  .append(" where membership.identity.key=:identityKey and membership.role='").append(GroupRoles.participant.name()).append("'")
 		  .append(" and block.entry.key=:repoEntryKey and block.endDate>=summary.firstAdmissionDate");
 		
@@ -523,8 +525,9 @@ public class LectureBlockRollCallDAO {
 			int pos = 0;
 			LectureBlock block = (LectureBlock)objects[pos++];
 			LectureBlockRollCall rollCall = (LectureBlockRollCall)objects[pos++];
-			String displayname = (String)objects[pos++];
-			blockToRollCallMap.put(block.getKey(), new LectureBlockAndRollCall(displayname, block, rollCall));
+			AbsenceNotice notice = (AbsenceNotice)objects[pos++];
+			String displayname = (String)objects[pos];
+			blockToRollCallMap.put(block.getKey(), new LectureBlockAndRollCall(displayname, block, rollCall, notice));
 		}
 		
 		appendCoaches(entry, blockToRollCallMap, teacherSeaparator);
@@ -570,6 +573,7 @@ public class LectureBlockRollCallDAO {
 		  .append("  call.lecturesAttendedNumber as attendedLectures,")
 		  .append("  call.lecturesAbsentNumber as absentLectures,")
 		  .append("  call.absenceAuthorized as absenceAuthorized,")
+		  .append("  notice.key as absenceNoticeKey,")
 		  .append("  notice.absenceAuthorized as absenceNoticeAuthorized,")
 		  .append("  block.key as blockKey,")
 		  .append("  block.compulsory as compulsory,")
@@ -620,6 +624,7 @@ public class LectureBlockRollCallDAO {
 				absenceAuthorized = null;
 				pos++;
 			}
+			Long absenceNoticeKey = (Long)rawObject[pos++];
 			Boolean absenceNoticeAuthorized = (Boolean)rawObject[pos++];
 			
 			Long lectureBlockKey = (Long)rawObject[pos++];
@@ -655,8 +660,8 @@ public class LectureBlockRollCallDAO {
 			
 			appendStatistics(entryStatistics, compulsory, status,
 					rollCallEndDate, rollCallStatus,
-					lecturesAttended, lecturesAbsent,
-					absenceAuthorized, absenceNoticeAuthorized, absenceDefaultAuthorized,
+					lecturesAttended, lecturesAbsent, absenceAuthorized,
+					absenceNoticeKey, absenceNoticeAuthorized, absenceDefaultAuthorized,
 					plannedLecturesNumber, effectiveLecturesNumber,
 					firstAdmissionDate, now);
 		});
@@ -677,6 +682,7 @@ public class LectureBlockRollCallDAO {
 		  .append("  call.lecturesAttendedNumber as attendedLectures,")
 		  .append("  call.lecturesAbsentNumber as absentLectures,")
 		  .append("  call.absenceAuthorized as absenceAuthorized,")
+		  .append("  notice.key as absenceNoticeKey,")
 		  .append("  notice.absenceAuthorized as absenceNoticeAuthorized,")
 		  .append("  block.key as blockKey,")
 		  .append("  block.compulsory as compulsory,")
@@ -786,6 +792,7 @@ public class LectureBlockRollCallDAO {
 				absenceAuthorized = null;
 				pos++;
 			}
+			Long absenceNoticeKey = (Long)rawObject[pos++];
 			Boolean absenceNoticeAuthorized = (Boolean)rawObject[pos++];
 
 			Long lectureBlockKey = (Long)rawObject[pos++];
@@ -834,8 +841,8 @@ public class LectureBlockRollCallDAO {
 
 			appendStatistics(entryStatistics, compulsory, status,
 					rollCallEndDate, rollCallStatus,
-					lecturesAttended, lecturesAbsent,
-					absenceAuthorized, absenceNoticeAuthorized, absenceDefaultAuthorized,
+					lecturesAttended, lecturesAbsent, absenceAuthorized,
+					absenceNoticeKey, absenceNoticeAuthorized, absenceDefaultAuthorized,
 					plannedLecturesNumber, effectiveLecturesNumber,
 					firstAdmissionDate, now);
 		});
@@ -911,6 +918,7 @@ public class LectureBlockRollCallDAO {
 		  .append("  call.lecturesAttendedNumber as attendedLectures,")
 		  .append("  call.lecturesAbsentNumber as absentLectures,")
 		  .append("  call.absenceAuthorized as absenceAuthorized,")
+		  .append("  notice.key as absenceNoticeKey,")
 		  .append("  notice.absenceAuthorized as absenceNoticeAuthorized,")
 		  .append("  block.key as blockKey,")
 		  .append("  block.compulsory as compulsory,")
@@ -958,6 +966,7 @@ public class LectureBlockRollCallDAO {
 				absenceAuthorized = null;
 				pos++;
 			}
+			Long absenceNoticeKey = (Long)rawObject[pos++];
 			Boolean absenceNoticeAuthorized = (Boolean)rawObject[pos++];
 
 			Long lectureBlockKey = (Long)rawObject[pos++];
@@ -988,7 +997,7 @@ public class LectureBlockRollCallDAO {
 			appendStatistics(entryStatistics, compulsory, status,
 					rollCallEndDate, rollCallStatus,
 					lecturesAttended, lecturesAbsent,
-					absenceAuthorized, absenceNoticeAuthorized, absenceDefaultAuthorized,
+					absenceAuthorized, absenceNoticeKey, absenceNoticeAuthorized, absenceDefaultAuthorized,
 					plannedLecturesNumber, effectiveLecturesNumber,
 					firstAdmissionDate, now);
 		});
@@ -1073,8 +1082,8 @@ public class LectureBlockRollCallDAO {
 	
 	private void appendStatistics(LectureBlockStatistics statistics, boolean compulsory, String blockStatus,
 			Date rollCallEndDate, String rollCallStatus,
-			Long lecturesAttended, Long lecturesAbsent,
-			Boolean absenceAuthorized, Boolean absenceNoticeAuthorized, boolean absenceDefaultAuthorized,
+			Long lecturesAttended, Long lecturesAbsent, Boolean absenceAuthorized,
+			Long absenceNoticeKey, Boolean absenceNoticeAuthorized, boolean absenceDefaultAuthorized,
 			Long plannedLecturesNumber, Long effectiveLecturesNumber,
 			Date firstAdmissionDate, Date now) {
 		if(!compulsory) return;// not compulsory blocks are simply ignored
@@ -1087,23 +1096,45 @@ public class LectureBlockRollCallDAO {
 		if(rollCallEndDate != null && rollCallEndDate.before(now)
 				&& firstAdmissionDate != null && firstAdmissionDate.before(rollCallEndDate)
 				&& blockStatus != null && !LectureBlockStatus.cancelled.name().equals(blockStatus)
-				&& rollCallStatus != null  && (LectureRollCallStatus.closed.name().equals(rollCallStatus) || LectureRollCallStatus.autoclosed.name().equals(rollCallStatus))) {
+				&& rollCallStatus != null && (LectureRollCallStatus.closed.name().equals(rollCallStatus) || LectureRollCallStatus.autoclosed.name().equals(rollCallStatus))) {
 		
-			if(lecturesAbsent != null) {
-				if(absenceAuthorized != null) {
+			if(absenceNoticeKey != null) {
+				long numOfLectures = 0l;
+				if(effectiveLecturesNumber != null) {
+					numOfLectures = effectiveLecturesNumber.longValue();
+				} else if(plannedLecturesNumber != null) {
+					numOfLectures = plannedLecturesNumber.longValue();
+				}
+				
+				if(absenceNoticeAuthorized != null) {
 					if(absenceAuthorized.booleanValue()) {
-						statistics.addTotalAuthorizedAbsentLectures(lecturesAbsent.longValue());
+						statistics.addTotalAuthorizedAbsentLectures(numOfLectures);
 					} else {
-						statistics.addTotalAbsentLectures(lecturesAbsent.longValue());
+						statistics.addTotalAbsentLectures(numOfLectures);
 					}
 				} else if(absenceDefaultAuthorized) {
-					statistics.addTotalAuthorizedAbsentLectures(lecturesAbsent.longValue());
+					statistics.addTotalAuthorizedAbsentLectures(numOfLectures);
 				} else {
-					statistics.addTotalAbsentLectures(lecturesAbsent.longValue());
+					statistics.addTotalAbsentLectures(numOfLectures);
+				}
+			} else {
+			
+				if(lecturesAbsent != null) {
+					if(absenceAuthorized != null) {
+						if(absenceAuthorized.booleanValue()) {
+							statistics.addTotalAuthorizedAbsentLectures(lecturesAbsent.longValue());
+						} else {
+							statistics.addTotalAbsentLectures(lecturesAbsent.longValue());
+						}
+					} else if(absenceDefaultAuthorized) {
+						statistics.addTotalAuthorizedAbsentLectures(lecturesAbsent.longValue());
+					} else {
+						statistics.addTotalAbsentLectures(lecturesAbsent.longValue());
+					}
+				}
+				if(lecturesAttended != null) {
+					statistics.addTotalAttendedLectures(lecturesAttended.longValue());
 				}
-			}
-			if(lecturesAttended != null) {
-				statistics.addTotalAttendedLectures(lecturesAttended.longValue());
 			}
 
 			if(effectiveLecturesNumber != null) {
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 4fca83cad13..193d4f228ac 100644
--- a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java
+++ b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java
@@ -600,24 +600,7 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De
 		List<LectureBlockRollCall> rollCalls = absenceNoticeDao.getRollCalls(notice);
 		if(rollCalls != null && !rollCalls.isEmpty()) {
 			for(LectureBlockRollCall rollCall:rollCalls) {
-				String beforeRollCall = auditLogDao.toXml(rollCall);
-				
-				rollCall.setAbsenceAuthorized(null);
 				rollCall.setAbsenceNotice(null);
-				rollCall.setAbsenceNoticeLectures(null);
-
-				LectureBlock lectureBlock = rollCall.getLectureBlock();
-				List<Integer> absenceToRemove = new ArrayList<>();
-				for(int i=0; i<lectureBlock.getPlannedLecturesNumber(); i++) {
-					absenceToRemove.add(Integer.valueOf(i));
-				}
-				lectureBlockRollCallDao.removeLecture(rollCall.getLectureBlock(), rollCall, absenceToRemove);
-				rollCall = lectureBlockRollCallDao.update(rollCall);
-
-				String afterRollCall = auditLogDao.toXml(rollCall);
-
-				RepositoryEntry entry = lectureBlock.getEntry();
-				auditLog(Action.deleteAbsenceNotice, beforeRollCall, afterRollCall, null, lectureBlock, rollCall, entry, assessedIdentity, actingIdentity);
 			}
 		}
 		// delete
@@ -736,28 +719,13 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De
 			if(currentRollCallSet.contains(rollCall)) {
 				currentRollCallSet.remove(rollCall);
 			} else {
-				List<Integer> absences = new ArrayList<>();
-				LectureBlock lectureBlock = rollCall.getLectureBlock();
-				for(int i=0; i<lectureBlock.getPlannedLecturesNumber(); i++) {
-					absences.add(Integer.valueOf(i));
-				}
 				rollCall.setAbsenceNotice(notice);
-				rollCall.setAbsenceAuthorized(notice.getAbsenceAuthorized());
-				lectureBlockRollCallDao.addLecture(lectureBlock, rollCall, absences);
 				lectureBlockRollCallDao.update(rollCall);
 			}
 		}
 		
 		for(LectureBlockRollCall toUnlink: currentRollCallSet) {
 			toUnlink.setAbsenceNotice(null);
-			
-			LectureBlock lectureBlock = toUnlink.getLectureBlock();
-			List<Integer> absenceToRemove = new ArrayList<>();
-			for(int i=0; i<lectureBlock.getPlannedLecturesNumber(); i++) {
-				absenceToRemove.add(Integer.valueOf(i));
-			}
-			lectureBlockRollCallDao.removeLecture(lectureBlock, toUnlink, absenceToRemove);
-			toUnlink.setAbsenceAuthorized(null);
 			lectureBlockRollCallDao.update(toUnlink);
 		}
 		
@@ -777,22 +745,6 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De
 			((AbsenceNoticeImpl)absenceNotice).setAuthorizer(authorizer);
 			absenceNotice = absenceNoticeDao.updateAbsenceNotice(absenceNotice);
 			dbInstance.commit();
-			
-			List<LectureBlockRollCall> rollCalls = absenceNoticeDao.getRollCalls(absenceNotice);
-			for(LectureBlockRollCall rollCall:rollCalls) {
-				if((authorize == null && rollCall.getAbsenceAuthorized() != null)
-						|| (authorize != null && !authorize.equals(rollCall.getAbsenceAuthorized()))) {
-					String before = toAuditXml(rollCall);
-					rollCall.setAbsenceAuthorized(authorize);
-					rollCall = lectureBlockRollCallDao.update(rollCall);
-					String after = toAuditXml(rollCall);
-
-					LectureBlock lectureBlock = rollCall.getLectureBlock();
-					auditLogDao.auditLog(Action.updateRollCall, before, after, null,
-							lectureBlock, rollCall, lectureBlock.getEntry(), rollCall.getIdentity(), actingIdentity);
-				}
-			}
-			dbInstance.commit();
 
 			String afterNotice = toAuditXml(absenceNotice);
 			auditLog(Action.updateAbsenceNotice, beforeNotice, afterNotice, null, absenceNotice, absenceNotice.getIdentity(), actingIdentity);
diff --git a/src/main/java/org/olat/modules/lecture/model/LectureBlockAndRollCall.java b/src/main/java/org/olat/modules/lecture/model/LectureBlockAndRollCall.java
index de9cd771aa1..a1fa9d47724 100644
--- a/src/main/java/org/olat/modules/lecture/model/LectureBlockAndRollCall.java
+++ b/src/main/java/org/olat/modules/lecture/model/LectureBlockAndRollCall.java
@@ -21,6 +21,8 @@ package org.olat.modules.lecture.model;
 
 import java.util.Date;
 
+import org.olat.modules.lecture.AbsenceNotice;
+import org.olat.modules.lecture.AbsenceNoticeType;
 import org.olat.modules.lecture.LectureBlock;
 import org.olat.modules.lecture.LectureBlockAppealStatus;
 import org.olat.modules.lecture.LectureBlockRef;
@@ -52,15 +54,19 @@ public class LectureBlockAndRollCall {
 	private final int lecturesAbsentNumber;
 	private final int lecturesAttendedNumber;
 	private final Boolean lecturesAuthorizedAbsent;
+	private final boolean withAbsenceNotice;
+	private final AbsenceNoticeType absenceNoticeType;
 	
 	private final Date appealDate;
 	private final LectureBlockAppealStatus appealStatus;
 	
 	private String coach;
 	
-	public LectureBlockAndRollCall(String entryDisplayname, LectureBlock lectureBlock, LectureBlockRollCall rollCall) {
+	public LectureBlockAndRollCall(String entryDisplayname, LectureBlock lectureBlock, LectureBlockRollCall rollCall, AbsenceNotice absenceNotice) {
 		this.entryDisplayname = entryDisplayname;
-		
+		this.withAbsenceNotice = absenceNotice != null;
+		this.absenceNoticeType = absenceNotice == null ? null : absenceNotice.getNoticeType();
+
 		startDate = lectureBlock.getStartDate();
 		lectureBlockKey = lectureBlock.getKey();
 		lectureBlockTitle = lectureBlock.getTitle();
@@ -79,9 +85,20 @@ public class LectureBlockAndRollCall {
 			appealStatus = null;
 		} else {
 			rollCallKey = rollCall.getKey();
-			lecturesAttendedNumber = rollCall.getLecturesAttendedNumber();
-			lecturesAbsentNumber = rollCall.getLecturesAbsentNumber();
-			lecturesAuthorizedAbsent = rollCall.getAbsenceAuthorized();	
+			
+			if(absenceNotice != null) {
+				lecturesAttendedNumber = 0;
+				int numOfLectures = lectureBlock.getEffectiveLecturesNumber();
+				if(numOfLectures <= 0) {
+					numOfLectures = lectureBlock.getPlannedLecturesNumber();
+				}
+				lecturesAbsentNumber = numOfLectures;
+				lecturesAuthorizedAbsent = absenceNotice.getAbsenceAuthorized();
+			} else {
+				lecturesAttendedNumber = rollCall.getLecturesAttendedNumber();
+				lecturesAbsentNumber = rollCall.getLecturesAbsentNumber();
+				lecturesAuthorizedAbsent = rollCall.getAbsenceAuthorized();
+			}
 			appealDate = rollCall.getAppealDate();
 			appealStatus = rollCall.getAppealStatus();
 		}
@@ -143,6 +160,14 @@ public class LectureBlockAndRollCall {
 		return effectiveLectures;
 	}
 	
+	public boolean hasAbsenceNotice() {
+		return withAbsenceNotice;
+	}
+	
+	public AbsenceNoticeType getAbsenceNoticeType() {
+		return absenceNoticeType;
+	}
+
 	public String getCoach() {
 		return coach;
 	}
@@ -158,6 +183,4 @@ public class LectureBlockAndRollCall {
 	public LectureBlockAppealStatus getAppealStatus() {
 		return appealStatus;
 	}
-	
-	
 }
diff --git a/src/main/java/org/olat/modules/lecture/model/LectureBlockRollCallAndCoach.java b/src/main/java/org/olat/modules/lecture/model/LectureBlockRollCallAndCoach.java
index c83c1ec806b..10ae63a3b10 100644
--- a/src/main/java/org/olat/modules/lecture/model/LectureBlockRollCallAndCoach.java
+++ b/src/main/java/org/olat/modules/lecture/model/LectureBlockRollCallAndCoach.java
@@ -19,6 +19,7 @@
  */
 package org.olat.modules.lecture.model;
 
+import org.olat.modules.lecture.AbsenceNotice;
 import org.olat.modules.lecture.LectureBlock;
 import org.olat.modules.lecture.LectureBlockRollCall;
 
@@ -32,12 +33,14 @@ public class LectureBlockRollCallAndCoach {
 	
 	private String coach;
 	private final LectureBlock block;
+	private final AbsenceNotice absenceNotice;
 	private final LectureBlockRollCall rollCall;
 	
-	public LectureBlockRollCallAndCoach(String coach, LectureBlock block, LectureBlockRollCall rollCall) {
+	public LectureBlockRollCallAndCoach(String coach, LectureBlock block, LectureBlockRollCall rollCall, AbsenceNotice absenceNotice) {
 		this.coach = coach;
 		this.block = block;
 		this.rollCall = rollCall;
+		this.absenceNotice = absenceNotice;
 	}
 
 	public LectureBlock getLectureBlock() {
@@ -52,4 +55,8 @@ public class LectureBlockRollCallAndCoach {
 		return coach;
 	}
 	
+	public AbsenceNotice getAbsenceNotice() {
+		return absenceNotice;
+	}
+	
 }
diff --git a/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java b/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java
index f01f9b80890..b2940e61345 100644
--- a/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java
+++ b/src/main/java/org/olat/modules/lecture/ui/AppealListRepositoryController.java
@@ -221,8 +221,7 @@ public class AppealListRepositoryController extends FormBasicController {
 			status.add(LectureBlockAppealStatus.rejected);
 			searchParams.setAppealStatus(status);
 		}
-		
-		
+
 		List<LectureBlockRollCallAndCoach> rollCallsWithCoach = lectureService.getLectureBlockAndRollCalls(searchParams);
 		List<AppealRollCallRow> rows = new ArrayList<>(rollCallsWithCoach.size());
 		for(LectureBlockRollCallAndCoach rollCallWithCoach:rollCallsWithCoach) {
diff --git a/src/main/java/org/olat/modules/lecture/ui/AppealRollCallRow.java b/src/main/java/org/olat/modules/lecture/ui/AppealRollCallRow.java
index 796d0d1cbb4..d511f7fda18 100644
--- a/src/main/java/org/olat/modules/lecture/ui/AppealRollCallRow.java
+++ b/src/main/java/org/olat/modules/lecture/ui/AppealRollCallRow.java
@@ -42,7 +42,8 @@ public class AppealRollCallRow extends UserPropertiesRow {
 	public AppealRollCallRow(LectureBlockRollCallAndCoach rollCall, List<UserPropertyHandler> propertyHandlers, Locale locale) {
 		super(rollCall.getRollCall().getIdentity(), propertyHandlers, locale);
 		this.rollCall = rollCall;
-		lectureBlockAndRollCall = new LectureBlockAndRollCall("", rollCall.getLectureBlock(), rollCall.getRollCall());
+		lectureBlockAndRollCall = new LectureBlockAndRollCall("",
+				rollCall.getLectureBlock(), rollCall.getRollCall(), rollCall.getAbsenceNotice());
 	}
 	
 	public String getCoach() {
diff --git a/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksController.java b/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksController.java
index 107ce10b984..17edcc61748 100644
--- a/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksController.java
+++ b/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksController.java
@@ -215,8 +215,10 @@ public class ParticipantLectureBlocksController extends FormBasicController {
 				if(lectures <= 0) {
 					lectures = row.getRow().getPlannedLecturesNumber();
 				}
+				
+				boolean absenceNotice = rollCall.hasAbsenceNotice();
 				int attended = row.getRow().getLecturesAttendedNumber();
-				if(attended < lectures) {
+				if(!absenceNotice && attended < lectures) {
 					Date date = row.getRow().getDate();
 					Calendar cal = Calendar.getInstance();
 					cal.setTime(date);
diff --git a/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksDataModel.java b/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksDataModel.java
index 8e092db121a..8f695bcd35c 100644
--- a/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksDataModel.java
+++ b/src/main/java/org/olat/modules/lecture/ui/ParticipantLectureBlocksDataModel.java
@@ -107,7 +107,7 @@ implements SortableFlexiTableDataModel<LectureBlockAndRollCallRow>, FilterableFl
 					return null;
 				}
 				if(row.getRow().isCompulsory()) {
-					return row.getRow().getLecturesAttendedNumber() < 0 ? 0 : row.getRow().getLecturesAttendedNumber();
+					return positive(row.getRow().getLecturesAttendedNumber());
 				}
 				return null;
 			}
diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticeDetailsCalloutController.java b/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticeDetailsCalloutController.java
index e2cb06efd11..2f903786d9a 100644
--- a/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticeDetailsCalloutController.java
+++ b/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticeDetailsCalloutController.java
@@ -20,8 +20,10 @@
 package org.olat.modules.lecture.ui.coach;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.olat.core.gui.UserRequest;
@@ -72,10 +74,10 @@ public class AbsenceNoticeDetailsCalloutController extends BasicController {
 		mainVC = createVelocityContainer("notice_details");
 		
 		List<LectureBlockWithNotice> lectureBlocksWith = lectureService.getLectureBlocksWithAbsenceNotices(Collections.singletonList(notice));
-		List<RepositoryEntry> entries = lectureBlocksWith.stream()
+		Set<RepositoryEntry> entries = lectureBlocksWith.stream()
 				.filter(block -> block.getEntry() != null)
 				.map(LectureBlockWithNotice::getEntry)
-				.collect(Collectors.toList());
+				.collect(Collectors.toSet());
 		List<LectureBlock> lectureBlocks = lectureBlocksWith.stream()
 				.map(LectureBlockWithNotice::getLectureBlock)
 				.collect(Collectors.toList());
@@ -85,7 +87,7 @@ public class AbsenceNoticeDetailsCalloutController extends BasicController {
 		putInitialPanel(mainVC);
 	}
 	
-	private void init(AbsenceNotice notice, List<LectureBlock> lectureBlocks, List<RepositoryEntry> entries, List<Identity> teachers) {
+	private void init(AbsenceNotice notice, List<LectureBlock> lectureBlocks, Collection<RepositoryEntry> entries, List<Identity> teachers) {
 		// info absences, reason, status, type
 		AbsenceCategory category = notice.getAbsenceCategory();
 		if(category != null) {
@@ -108,6 +110,7 @@ public class AbsenceNoticeDetailsCalloutController extends BasicController {
 		for(RepositoryEntry entry:entries) {
 			entriesInfo.add(entry.getDisplayname());
 		}
+		Collections.sort(entriesInfo);
 		mainVC.contextPut("entries", entriesInfo);
 
 		// teachers
diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java
index 96f5e01c15a..1a9289c0ec2 100644
--- a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java
+++ b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesCoachingController.java
@@ -283,7 +283,7 @@ public class LecturesCoachingController extends BasicController implements Activ
 	private void doOpenReport(UserRequest ureq) {
 		if(reportController == null) {
 			WindowControl swControl = addToHistory(ureq, OresHelper.createOLATResourceableType("Report"), null);
-			reportController = new LecturesSearchController(ureq, swControl, stackPanel);
+			reportController = new LecturesSearchController(ureq, swControl);
 			listenTo(reportController);
 		}
 		addToHistory(ureq, reportController);
diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesSearchController.java b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesSearchController.java
index 91fb17e0a27..bdeeae6861e 100644
--- a/src/main/java/org/olat/modules/lecture/ui/coach/LecturesSearchController.java
+++ b/src/main/java/org/olat/modules/lecture/ui/coach/LecturesSearchController.java
@@ -58,13 +58,17 @@ public class LecturesSearchController extends BasicController implements Activat
 	@Autowired
 	private LectureService lectureService;
 	
-	public LecturesSearchController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel) {
+	public LecturesSearchController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl, Util.createPackageTranslator(LectureRepositoryAdminController.class, ureq.getLocale()));
-		this.stackPanel = stackPanel;
 
 		searchForm = new LecturesSearchFormController(ureq, getWindowControl());
 		listenTo(searchForm);
-		putInitialPanel(searchForm.getInitialComponent());
+		
+		stackPanel = new TooledStackedPanel("ca-lectures-search", getTranslator(), this);
+		stackPanel.setNeverDisposeRootController(true);
+		stackPanel.setToolbarAutoEnabled(true);
+		stackPanel.pushController(translate("search.curriculums"), searchForm);
+		putInitialPanel(stackPanel);
 	}
 
 	@Override
@@ -123,7 +127,7 @@ public class LecturesSearchController extends BasicController implements Activat
 			ctrl = multipleUsersCtrl;
 		}
 
-		stackPanel.popUpToRootController(ureq);
+		//stackPanel.popUpToRootController(ureq);
 		stackPanel.pushController(translate("results"), ctrl);
 	}
 }
diff --git a/src/main/java/org/olat/modules/lecture/ui/component/LectureBlockRollCallStatusCellRenderer.java b/src/main/java/org/olat/modules/lecture/ui/component/LectureBlockRollCallStatusCellRenderer.java
index b616a7b8159..b4b64e20b23 100644
--- a/src/main/java/org/olat/modules/lecture/ui/component/LectureBlockRollCallStatusCellRenderer.java
+++ b/src/main/java/org/olat/modules/lecture/ui/component/LectureBlockRollCallStatusCellRenderer.java
@@ -25,6 +25,7 @@ import org.olat.core.gui.render.Renderer;
 import org.olat.core.gui.render.StringOutput;
 import org.olat.core.gui.render.URLBuilder;
 import org.olat.core.gui.translator.Translator;
+import org.olat.modules.lecture.AbsenceNoticeType;
 import org.olat.modules.lecture.LectureBlockStatus;
 import org.olat.modules.lecture.LectureRollCallStatus;
 import org.olat.modules.lecture.model.LectureBlockAndRollCall;
@@ -109,6 +110,20 @@ public class LectureBlockRollCallStatusCellRenderer implements FlexiCellRenderer
 			String title = translator.translate("rollcall.tooltip.free");
 			target.append("<span title='").append(title).append("'><i class='o_icon o_icon-lg o_lectures_rollcall_free'> </i></span>");
 		}
+		
+		AbsenceNoticeType absenceNoticeType = rollCall.getAbsenceNoticeType();
+		if(absenceNoticeType  != null) {
+			String noticeTitleI18n;
+			switch(absenceNoticeType) {
+				case dispensation: noticeTitleI18n = "noticed.dispensation"; break;
+				case notified: noticeTitleI18n = "noticed.notice.absence"; break;
+				case absence:
+				default: noticeTitleI18n = "noticed.type.absence"; break;
+			}
+			
+			String title = translator.translate(noticeTitleI18n);
+			target.append(" <span title='").append(title).append("'><i class='o_icon o_icon-fw o_icon-lg o_filetype_html'> </i></span>");
+		}
 	}
 	
 	private void renderClosed(StringOutput target, LectureBlockAndRollCall rollCall, boolean textOnly) {
-- 
GitLab