diff --git a/src/main/java/org/olat/modules/lecture/LectureBlockAuditLog.java b/src/main/java/org/olat/modules/lecture/LectureBlockAuditLog.java index b589753a0537556641ee11ce41fe70beada180ff..e038204664396f5d123b1a5a39bdff00ba17f28e 100644 --- a/src/main/java/org/olat/modules/lecture/LectureBlockAuditLog.java +++ b/src/main/java/org/olat/modules/lecture/LectureBlockAuditLog.java @@ -44,6 +44,8 @@ public interface LectureBlockAuditLog { public Long getRollCallKey(); public Long getEntryKey(); + + public Long getAbsenceNoticeKey(); public Long getIdentityKey(); @@ -68,7 +70,13 @@ public interface LectureBlockAuditLog { updateSummary, removeCustomRate, - sendAppeal + sendAppeal, + + createAbsenceNotice, + updateAbsenceNotice, + createAbsenceNoticeRelations, + updateAbsenceNoticeRelations, + } } diff --git a/src/main/java/org/olat/modules/lecture/LectureService.java b/src/main/java/org/olat/modules/lecture/LectureService.java index 615cf9792ad5e856577a802fdf4b6dd87dd1aafc..aa64c0b6ff928ee9531726c451b3e0606c3aec80 100644 --- a/src/main/java/org/olat/modules/lecture/LectureService.java +++ b/src/main/java/org/olat/modules/lecture/LectureService.java @@ -140,6 +140,10 @@ public interface LectureService { public String toAuditXml(LectureParticipantSummary summary); + public String toAuditXml(AbsenceNotice absenceNotice); + + public AbsenceNotice toAuditAbsenceNotice(String xml); + public LectureParticipantSummary toAuditLectureParticipantSummary(String xml); @@ -153,6 +157,19 @@ public interface LectureService { LectureBlockRef lectureBlock, LectureBlockRollCall rollCall, RepositoryEntryRef entry, IdentityRef assessedIdentity, IdentityRef author); + /** + * + * @param action + * @param before + * @param after + * @param message + * @param absenceNotice + * @param assessedIdentity + * @param author + */ + public void auditLog(LectureBlockAuditLog.Action action, String before, String after, String message, + AbsenceNoticeRef absenceNotice, IdentityRef assessedIdentity, IdentityRef author); + public List<LectureBlockAuditLog> getAuditLog(LectureBlockRef lectureBlock); /** @@ -299,7 +316,7 @@ public interface LectureService { * @return A refreshed absence notice */ public AbsenceNotice updateAbsenceNotice(AbsenceNotice absenceNotice, Identity authorizer, - List<RepositoryEntry> entries, List<LectureBlock> lectureBlocks); + List<RepositoryEntry> entries, List<LectureBlock> lectureBlocks, Identity actingIdentity); public AbsenceNotice updateAbsenceNoticeAttachments(AbsenceNotice absenceNotice, List<VFSItem> newFiles, List<VFSItem> filesToDelete); diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java index b508120255e71c84e938c8ba36cf7c832f6444ff..6f3f2866347637b6ae5cfca0e23c5250d906892c 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java @@ -90,6 +90,7 @@ public class AbsenceNoticeDAO { QueryBuilder sb = new QueryBuilder(); sb.append("select rollCall from lectureblockrollcall rollCall") .append(" inner join fetch rollCall.absenceNotice as notice") + .append(" left join fetch rollCall.lectureBlock as lectureBlock") .append(" where notice.key=:noticeKey"); return dbInstance.getCurrentEntityManager() @@ -120,16 +121,12 @@ public class AbsenceNoticeDAO { .append(" inner join bGroup.members participants on (participants.role='").append(GroupRoles.participant.name()).append("')") .append(" inner join absencenotice notice on (participants.identity.key=notice.identity.key)") .append(" where notice.key in (:noticeKeys)"); + } + + if(target == AbsenceNoticeTarget.entries || target == AbsenceNoticeTarget.allentries) { // date constraints - sb.append(" and (") - .append("(notice.startDate<=block.startDate and notice.endDate>=block.endDate)") - .append(" or ") - .append("(notice.startDate>=block.startDate and notice.endDate<=block.startDate)") - .append(" or ") - .append("(notice.startDate>=block.endDate and notice.endDate<=block.endDate)") - .append(" or ") - .append("(notice.startDate is null and notice.endDate is null)") - .append(")"); + sb.append(" and "); + noticeBlockDates(sb); } List<Long> noticeKeys = notices.stream() @@ -150,6 +147,19 @@ public class AbsenceNoticeDAO { return blockWithNotices; } + protected static QueryBuilder noticeBlockDates(QueryBuilder sb) { + sb.append("(") + .append("(notice.startDate<=block.startDate and notice.endDate>=block.endDate)") + .append(" or ") + .append("(notice.startDate>=block.startDate and notice.endDate<=block.startDate)") + .append(" or ") + .append("(notice.startDate>=block.endDate and notice.endDate<=block.endDate)") + .append(" or ") + .append("(notice.startDate is null and notice.endDate is null)") + .append(")"); + return sb; + } + public AbsenceNotice loadAbsenceNotice(Long noticeKey) { QueryBuilder sb = new QueryBuilder(512); sb.append("select notice from absencenotice as notice") @@ -335,7 +345,7 @@ public class AbsenceNoticeDAO { .append(" and noticeToEntry.absenceNotice.key=notice.key and noticeToEntry.entry.key=:entryKey") .append(" ) or (notice.target ").in(AbsenceNoticeTarget.allentries) .append(" and notice.startDate<=:startDate and notice.endDate>=:endDate") - .append(" )") + .append(" )")//TODO absences date for entries and all entries .append(")"); } diff --git a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java index d9f6a8114916e382e91da040cda04c8dad15b1ea..931ab2b22b8b4f9d8ba4eb001a174dd1b1746785 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeToRepositoryEntryDAO.java @@ -77,14 +77,21 @@ public class AbsenceNoticeToRepositoryEntryDAO { dbInstance.getCurrentEntityManager().remove(relation); } + /** + * List of roll call with the suitable dates and repository entries + * @param notice + * @return + */ public List<LectureBlockRollCall> getRollCallsByRepositoryEntry(AbsenceNotice notice) { QueryBuilder sb = new QueryBuilder(); sb.append("select rollCall from absencenoticetoentry noticeToEntry") .append(" inner join noticeToEntry.entry as entry") + .append(" inner join noticeToEntry.absenceNotice as notice") .append(" inner join lectureblock as block on (block.entry.key=entry.key)") .append(" inner join lectureblockrollcall as rollCall on (rollCall.lectureBlock.key=block.key)") .append(" inner join fetch rollCall.absenceNotice as currentNotice") - .append(" where noticeToEntry.absenceNotice.key=:noticeKey"); + .append(" where notice.key=:noticeKey and "); + AbsenceNoticeDAO.noticeBlockDates(sb); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), LectureBlockRollCall.class) @@ -92,13 +99,24 @@ public class AbsenceNoticeToRepositoryEntryDAO { .getResultList(); } + /** + * + * @param notice + * @return + */ public List<LectureBlockRollCall> getRollCallsOfAllEntries(AbsenceNotice notice) { QueryBuilder sb = new QueryBuilder(); sb.append("select rollCall from lectureblock block") .append(" inner join lectureblockrollcall as rollCall on (rollCall.lectureBlock.key=block.key)") .append(" inner join fetch rollCall.absenceNotice as currentNotice") .append(" where rollCall.identity.key=:identityKey") - .append(" and block.startDate>=:startDate and block.endDate<=:endDate"); + .append(" and (") + .append(" (block.startDate>=:startDate and block.endDate<=:endDate)") + .append(" or ") + .append(" (block.startDate<=:startDate and block.startDate>=:endDate)") + .append(" or ") + .append(" (block.endDate<=:startDate and block.endDate>=:endDate)") + .append(" )"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), LectureBlockRollCall.class) diff --git a/src/main/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAO.java b/src/main/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAO.java index e9d402367646e0e51bd699fc890bbca51b05e00c..f15113253570433e54de0f21c6782f297fe60ab3 100644 --- a/src/main/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAO.java @@ -22,22 +22,31 @@ package org.olat.modules.lecture.manager; import java.util.Date; import java.util.List; +import org.apache.logging.log4j.Logger; +import org.olat.basesecurity.IdentityImpl; import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.xml.XStreamHelper; +import org.olat.modules.lecture.AbsenceNotice; +import org.olat.modules.lecture.AbsenceNoticeRef; +import org.olat.modules.lecture.AbsenceNoticeToRepositoryEntry; import org.olat.modules.lecture.LectureBlock; import org.olat.modules.lecture.LectureBlockAuditLog; import org.olat.modules.lecture.LectureBlockRef; import org.olat.modules.lecture.LectureBlockRollCall; import org.olat.modules.lecture.LectureParticipantSummary; +import org.olat.modules.lecture.model.AbsenceCategoryImpl; +import org.olat.modules.lecture.model.AbsenceNoticeImpl; +import org.olat.modules.lecture.model.AbsenceNoticeRelationsAuditImpl; +import org.olat.modules.lecture.model.AbsenceNoticeToLectureBlockImpl; import org.olat.modules.lecture.model.LectureBlockAuditLogImpl; import org.olat.modules.lecture.model.LectureBlockImpl; import org.olat.modules.lecture.model.LectureBlockRollCallImpl; import org.olat.modules.lecture.model.LectureParticipantSummaryImpl; import org.olat.modules.lecture.model.ReasonImpl; +import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -90,6 +99,62 @@ public class LectureBlockAuditLogDAO { summaryXStream.omitField(LectureParticipantSummaryImpl.class, "identity"); summaryXStream.omitField(LectureParticipantSummaryImpl.class, "entry"); } + + private static final XStream absenceNoticeXStream = XStreamHelper.createXStreamInstanceForDBObjects(); + static { + absenceNoticeXStream.alias("absenceNotice", AbsenceNoticeImpl.class); + absenceNoticeXStream.ignoreUnknownElements(); + absenceNoticeXStream.omitField(LectureParticipantSummaryImpl.class, "identity"); + absenceNoticeXStream.omitField(LectureParticipantSummaryImpl.class, "entry"); + absenceNoticeXStream.alias("identity", IdentityImpl.class); + absenceNoticeXStream.omitField(IdentityImpl.class, "user"); + absenceNoticeXStream.alias("absenceCategory", AbsenceCategoryImpl.class); + } + + private static final XStream absenceNoticeRelationsXStream = XStreamHelper.createXStreamInstanceForDBObjects(); + static { + absenceNoticeRelationsXStream.alias("absenceNoticeRelations", AbsenceNoticeRelationsAuditImpl.class); + absenceNoticeRelationsXStream.ignoreUnknownElements(); + + absenceNoticeRelationsXStream.omitField(AbsenceNoticeToLectureBlockImpl.class, "lectureToBlock"); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeToRepositoryEntry.class, "lectureToEntry"); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeToLectureBlockImpl.class, "absenceNotice"); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeToRepositoryEntry.class, "absenceNotice"); + + absenceNoticeRelationsXStream.alias("absenceNotice", AbsenceNoticeImpl.class); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeImpl.class, "identity"); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeImpl.class, "notifier"); + absenceNoticeRelationsXStream.omitField(AbsenceNoticeImpl.class, "authorizer"); + + absenceNoticeRelationsXStream.alias("lectureBlock", LectureBlockImpl.class); + absenceNoticeRelationsXStream.omitField(LectureBlockImpl.class, "entry"); + absenceNoticeRelationsXStream.omitField(LectureBlockImpl.class, "teacherGroup"); + absenceNoticeRelationsXStream.omitField(LectureBlockImpl.class, "groups"); + absenceNoticeRelationsXStream.omitField(LectureBlockImpl.class, "lastModified"); + absenceNoticeRelationsXStream.omitField(LectureBlockImpl.class, "taxonomyLevels"); + + absenceNoticeRelationsXStream.alias("repositoryEntry", RepositoryEntry.class); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "olatResource"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "groups"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "organisations"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "taxonomyLevels"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "description"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "objectives"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "requirements"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "location"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "credits"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "expenditureOfWork"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "lifecycle"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "deletedBy"); + absenceNoticeRelationsXStream.omitField(RepositoryEntry.class, "statistics"); + + absenceNoticeRelationsXStream.alias("rollcall", LectureBlockRollCallImpl.class); + absenceNoticeRelationsXStream.ignoreUnknownElements(); + absenceNoticeRelationsXStream.omitField(LectureBlockRollCallImpl.class, "identity"); + absenceNoticeRelationsXStream.omitField(LectureBlockRollCallImpl.class, "lastModified"); + + absenceNoticeRelationsXStream.alias("absenceCategory", AbsenceCategoryImpl.class); + } public void auditLog(LectureBlockAuditLog.Action action, String before, String after, String message, LectureBlockRef lectureBlock, LectureBlockRollCall rollCall, @@ -118,6 +183,26 @@ public class LectureBlockAuditLogDAO { dbInstance.getCurrentEntityManager().persist(auditLog); } + public void auditLog(LectureBlockAuditLog.Action action, String before, String after, String message, + AbsenceNoticeRef absenceNotice, IdentityRef assessedIdentity, IdentityRef author) { + LectureBlockAuditLogImpl auditLog = new LectureBlockAuditLogImpl(); + auditLog.setCreationDate(new Date()); + auditLog.setAction(action.name()); + auditLog.setBefore(before); + auditLog.setAfter(after); + auditLog.setMessage(message); + if(absenceNotice != null) { + auditLog.setAbsenceNoticeKey(absenceNotice.getKey()); + } + if(assessedIdentity != null) { + auditLog.setIdentityKey(assessedIdentity.getKey()); + } + if(author != null) { + auditLog.setAuthorKey(author.getKey()); + } + dbInstance.getCurrentEntityManager().persist(auditLog); + } + public List<LectureBlockAuditLog> getAuditLog(LectureBlockRef lectureBlock) { StringBuilder sb = new StringBuilder(128); sb.append("select log from lectureblockauditlog log where log.lectureBlockKey=:lectureBlockKey"); @@ -211,7 +296,7 @@ public class LectureBlockAuditLogDAO { if(summary == null) return null; return summaryXStream.toXML(summary); } - + public LectureParticipantSummary summaryFromXml(String xml) { if(StringHelper.containsNonWhitespace(xml)) { try { @@ -226,4 +311,30 @@ public class LectureBlockAuditLogDAO { } return null; } + + public String toXml(AbsenceNotice absenceNotice) { + if(absenceNotice == null) return null; + return absenceNoticeXStream.toXML(absenceNotice); + } + + public AbsenceNotice absenceNoticeFromXml(String xml) { + if(StringHelper.containsNonWhitespace(xml)) { + try { + Object obj = absenceNoticeXStream.fromXML(xml); + if(obj instanceof AbsenceNotice) { + return (AbsenceNotice)obj; + } + } catch (Exception e) { + log.error("", e); + return null; + } + } + return null; + } + + public String toXml(AbsenceNoticeRelationsAuditImpl absenceNoticeAudit) { + if(absenceNoticeAudit == null) return null; + return absenceNoticeRelationsXStream.toXML(absenceNoticeAudit); + } + } 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 b8a3d8035758301469abde9a73e5f0668d517c07..3d58ecdce90b4015086dccc50a0334f3b2ff8cd8 100644 --- a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java +++ b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java @@ -95,6 +95,7 @@ import org.olat.modules.lecture.Reason; import org.olat.modules.lecture.RepositoryEntryLectureConfiguration; import org.olat.modules.lecture.model.AbsenceNoticeImpl; import org.olat.modules.lecture.model.AbsenceNoticeInfos; +import org.olat.modules.lecture.model.AbsenceNoticeRelationsAuditImpl; import org.olat.modules.lecture.model.AggregatedLectureBlocksStatistics; import org.olat.modules.lecture.model.IdentityRateWarning; import org.olat.modules.lecture.model.LectureBlockAndRollCall; @@ -321,12 +322,28 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De return auditLogDao.summaryFromXml(xml); } + @Override + public String toAuditXml(AbsenceNotice absenceNotice) { + return auditLogDao.toXml(absenceNotice); + } + + @Override + public AbsenceNotice toAuditAbsenceNotice(String xml) { + return auditLogDao.absenceNoticeFromXml(xml); + } + @Override public void auditLog(LectureBlockAuditLog.Action action, String before, String after, String message, LectureBlockRef lectureBlock, LectureBlockRollCall rollCall, RepositoryEntryRef entry, IdentityRef assessedIdentity, IdentityRef author) { auditLogDao.auditLog(action, before, after, message, lectureBlock, rollCall, entry, assessedIdentity, author); } + + @Override + public void auditLog(LectureBlockAuditLog.Action action, String before, String after, String message, + AbsenceNoticeRef absenceNotice, IdentityRef assessedIdentity, IdentityRef author) { + auditLogDao.auditLog(action, before, after, message, absenceNotice, assessedIdentity, author); + } @Override public List<LectureBlockAuditLog> getAuditLog(LectureBlockRef lectureBlock) { @@ -529,28 +546,40 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De if(authorized != null && authorized.booleanValue()) { authorizer = actingIdentity; } - + + AbsenceNoticeRelationsAuditImpl after = new AbsenceNoticeRelationsAuditImpl(); AbsenceNotice notice = absenceNoticeDao.createAbsenceNotice(absentIdentity, type, target, start, end, category, absenceRason, authorized, authorizer, actingIdentity); if(entries != null && !entries.isEmpty()) { + List<AbsenceNoticeToRepositoryEntry> relations = new ArrayList<>(); for(RepositoryEntry entry:entries) { - absenceNoticeToRepositoryEntryDao.createRelation(notice, entry); + AbsenceNoticeToRepositoryEntry noticeToEntry = absenceNoticeToRepositoryEntryDao.createRelation(notice, entry); + relations.add(noticeToEntry); } + after.setNoticeToEntries(relations); } if(lectureBlocks != null && !lectureBlocks.isEmpty()) { + List<AbsenceNoticeToLectureBlock> relations = new ArrayList<>(); for(LectureBlock lectureBlock:lectureBlocks) { - absenceNoticeToLectureBlockDao.createRelation(notice, lectureBlock); + AbsenceNoticeToLectureBlock noticeToBlock = absenceNoticeToLectureBlockDao.createRelation(notice, lectureBlock); + relations.add(noticeToBlock); } + after.setNoticeToBlocks(relations); } dbInstance.commit(); + // calculate roll calls - calculateAbsencesOfRollcall(notice); + calculateAbsencesOfRollcall(notice, null, after); + dbInstance.commit(); + + String afterXml = auditLogDao.toXml(after); + auditLog(Action.createAbsenceNoticeRelations, null, afterXml, null, notice, absentIdentity, actingIdentity); return notice; } @Override public AbsenceNotice updateAbsenceNotice(AbsenceNotice absenceNotice, Identity authorizer, - List<RepositoryEntry> entries, List<LectureBlock> lectureBlocks) { + List<RepositoryEntry> entries, List<LectureBlock> lectureBlocks, Identity actingIdentity) { if(authorizer != null && absenceNotice.getAuthorizer() == null) { ((AbsenceNoticeImpl)absenceNotice).setAuthorizer(authorizer); @@ -559,6 +588,12 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De List<AbsenceNoticeToLectureBlock> currentNoticeToBlocks = absenceNoticeToLectureBlockDao.getRelations(absenceNotice); List<AbsenceNoticeToRepositoryEntry> currentNoticeToEntries = absenceNoticeToRepositoryEntryDao.getRelations(absenceNotice); + AbsenceNoticeRelationsAuditImpl before = new AbsenceNoticeRelationsAuditImpl(); + before.setNoticeToBlocks(currentNoticeToBlocks); + before.setNoticeToEntries(currentNoticeToEntries); + + AbsenceNoticeRelationsAuditImpl after = new AbsenceNoticeRelationsAuditImpl(); + if(notice.getNoticeTarget() == AbsenceNoticeTarget.allentries) { absenceNoticeToLectureBlockDao.deleteRelations(currentNoticeToBlocks); absenceNoticeToRepositoryEntryDao.deleteRelations(currentNoticeToEntries); @@ -566,47 +601,65 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De absenceNoticeToLectureBlockDao.deleteRelations(currentNoticeToBlocks); if(entries != null) { List<RepositoryEntry> currentEntries = new ArrayList<>(currentNoticeToEntries.size()); + List<AbsenceNoticeToRepositoryEntry> relations = new ArrayList<>(currentNoticeToEntries); for(AbsenceNoticeToRepositoryEntry currentNoticeToEntry:currentNoticeToEntries) { currentEntries.add(currentNoticeToEntry.getEntry()); - if(!entries.contains(currentNoticeToEntry.getEntry())) { + if(entries.contains(currentNoticeToEntry.getEntry())) { + relations.add(currentNoticeToEntry); + } else { absenceNoticeToRepositoryEntryDao.deleteRelation(currentNoticeToEntry); } } for(RepositoryEntry entry:entries) { if(!currentEntries.contains(entry)) { - absenceNoticeToRepositoryEntryDao.createRelation(notice, entry); + AbsenceNoticeToRepositoryEntry noticeToEntry = absenceNoticeToRepositoryEntryDao.createRelation(notice, entry); + relations.add(noticeToEntry); } } + after.setNoticeToEntries(relations); } } else if(notice.getNoticeTarget() == AbsenceNoticeTarget.lectureblocks) { absenceNoticeToRepositoryEntryDao.deleteRelations(currentNoticeToEntries); if(lectureBlocks != null) { List<LectureBlock> currentBlocks = new ArrayList<>(); + List<AbsenceNoticeToLectureBlock> relations = new ArrayList<>(); for(AbsenceNoticeToLectureBlock currentNoticeToBlock:currentNoticeToBlocks) { currentBlocks.add(currentNoticeToBlock.getLectureBlock()); - if(!lectureBlocks.contains(currentNoticeToBlock.getLectureBlock())) { + if(lectureBlocks.contains(currentNoticeToBlock.getLectureBlock())) { + relations.add(currentNoticeToBlock); + } else { absenceNoticeToLectureBlockDao.deleteRelation(currentNoticeToBlock); } } for(LectureBlock lectureBlock:lectureBlocks) { if(!currentBlocks.contains(lectureBlock)) { - absenceNoticeToLectureBlockDao.createRelation(notice, lectureBlock); + AbsenceNoticeToLectureBlock noticeToBlock = absenceNoticeToLectureBlockDao.createRelation(notice, lectureBlock); + relations.add(noticeToBlock); } } + after.setNoticeToBlocks(relations); } } dbInstance.commit(); // calculate roll calls - calculateAbsencesOfRollcall(notice); + calculateAbsencesOfRollcall(notice, before, after); + dbInstance.commit(); + + String beforeXml = auditLogDao.toXml(before); + String afterXml = auditLogDao.toXml(after); + auditLog(Action.updateAbsenceNoticeRelations, beforeXml, afterXml, null, absenceNotice, absenceNotice.getIdentity(), actingIdentity); return notice; } - private void calculateAbsencesOfRollcall(AbsenceNotice notice) { + private void calculateAbsencesOfRollcall(AbsenceNotice notice, AbsenceNoticeRelationsAuditImpl before, AbsenceNoticeRelationsAuditImpl after) { List<LectureBlockRollCall> currentRollCalls = absenceNoticeDao.getRollCalls(notice); + if(before != null) { + before.setRollCalls(currentRollCalls); + } Set<LectureBlockRollCall> currentRollCallSet = new HashSet<>(currentRollCalls); List<LectureBlockRollCall> rollCalls; @@ -621,15 +674,19 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De if(currentRollCallSet.contains(rollCall)) { currentRollCallSet.remove(rollCall); } else { - rollCall.setAbsenceNotice(notice);//TODO absences log + rollCall.setAbsenceNotice(notice); lectureBlockRollCallDao.update(rollCall); } } for(LectureBlockRollCall toUnlink: currentRollCallSet) { - toUnlink.setAbsenceNotice(null);//TODO absences log + toUnlink.setAbsenceNotice(null); lectureBlockRollCallDao.update(toUnlink); } + + if(after != null) { + after.setRollCalls(rollCalls); + } } @Override diff --git a/src/main/java/org/olat/modules/lecture/model/AbsenceNoticeRelationsAuditImpl.java b/src/main/java/org/olat/modules/lecture/model/AbsenceNoticeRelationsAuditImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4f5bc5130bf547715948d84a6612ffb4ccb33369 --- /dev/null +++ b/src/main/java/org/olat/modules/lecture/model/AbsenceNoticeRelationsAuditImpl.java @@ -0,0 +1,47 @@ +package org.olat.modules.lecture.model; + +import java.util.ArrayList; +import java.util.List; + +import org.olat.modules.lecture.AbsenceNoticeToLectureBlock; +import org.olat.modules.lecture.AbsenceNoticeToRepositoryEntry; +import org.olat.modules.lecture.LectureBlockRollCall; + +/** + * + * Initial date: 20 août 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AbsenceNoticeRelationsAuditImpl { + + private List<LectureBlockRollCall> rollCalls; + private List<AbsenceNoticeToLectureBlock> noticeToBlocks; + private List<AbsenceNoticeToRepositoryEntry> noticeToEntries; + + public List<AbsenceNoticeToLectureBlock> getNoticeToBlocks() { + return noticeToBlocks; + } + + public void setNoticeToBlocks(List<AbsenceNoticeToLectureBlock> noticeToBlocks) { + this.noticeToBlocks = noticeToBlocks == null ? new ArrayList<>(1) : new ArrayList<>(noticeToBlocks); + } + + public List<AbsenceNoticeToRepositoryEntry> getNoticeToEntries() { + return noticeToEntries; + } + + public void setNoticeToEntries(List<AbsenceNoticeToRepositoryEntry> noticeToEntries) { + this.noticeToEntries = noticeToEntries == null ? new ArrayList<>(1) : new ArrayList<>(noticeToEntries); + } + + public List<LectureBlockRollCall> getRollCalls() { + return rollCalls; + } + + public void setRollCalls(List<LectureBlockRollCall> rollCalls) { + this.rollCalls = rollCalls == null ? new ArrayList<>(1) : new ArrayList<>(rollCalls); + } + + +} diff --git a/src/main/java/org/olat/modules/lecture/model/LectureBlockAuditLogImpl.java b/src/main/java/org/olat/modules/lecture/model/LectureBlockAuditLogImpl.java index 7b322b2db84bac1d3f8183af1955a69379ad66d4..041a2a2bf06609bff6415419e9b4cf6989270df1 100644 --- a/src/main/java/org/olat/modules/lecture/model/LectureBlockAuditLogImpl.java +++ b/src/main/java/org/olat/modules/lecture/model/LectureBlockAuditLogImpl.java @@ -67,6 +67,8 @@ public class LectureBlockAuditLogImpl implements LectureBlockAuditLog, Persistab private Long lectureBlockKey; @Column(name="fk_roll_call", nullable=true, insertable=true, updatable=false) private Long rollCallKey; + @Column(name="fk_absence_notice", nullable=true, insertable=true, updatable=false) + private Long absenceNoticeKey; @Column(name="fk_entry", nullable=true, insertable=true, updatable=false) private Long entryKey; @@ -152,6 +154,15 @@ public class LectureBlockAuditLogImpl implements LectureBlockAuditLog, Persistab this.entryKey = entryKey; } + @Override + public Long getAbsenceNoticeKey() { + return absenceNoticeKey; + } + + public void setAbsenceNoticeKey(Long absenceNoticeKey) { + this.absenceNoticeKey = absenceNoticeKey; + } + @Override public Long getIdentityKey() { return identityKey; diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/EditNoticeController.java b/src/main/java/org/olat/modules/lecture/ui/coach/EditNoticeController.java index 40f394e8cc9310f1b642e5e8f938d3689335e954..6f3687fcb4bdf42cbfd50c0c280e044218dfa814 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/EditNoticeController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/EditNoticeController.java @@ -37,6 +37,7 @@ import org.olat.modules.lecture.AbsenceNoticeTarget; import org.olat.modules.lecture.AbsenceNoticeToLectureBlock; import org.olat.modules.lecture.AbsenceNoticeToRepositoryEntry; import org.olat.modules.lecture.LectureBlock; +import org.olat.modules.lecture.LectureBlockAuditLog.Action; import org.olat.modules.lecture.LectureService; import org.olat.modules.lecture.model.EditAbsenceNoticeWrapper; import org.olat.modules.lecture.ui.LecturesSecurityCallback; @@ -107,6 +108,8 @@ public class EditNoticeController extends FormBasicController { datesAndLecturesCtrl.formOK(ureq); absenceNotice = lectureService.getAbsenceNotice(absenceNotice); + String before = lectureService.toAuditXml(absenceNotice); + Boolean authorized = absenceNotice.getAbsenceAuthorized(); absenceNotice.setStartDate(noticeWrapper.getStartDate()); @@ -130,13 +133,16 @@ public class EditNoticeController extends FormBasicController { authorizer = getIdentity(); } - absenceNotice = lectureService.updateAbsenceNotice(absenceNotice, authorizer, entries, lectureBlocks); + absenceNotice = lectureService.updateAbsenceNotice(absenceNotice, authorizer, entries, lectureBlocks, getIdentity()); List<VFSItem> newFiles = new ArrayList<>(); if(noticeWrapper.getTempUploadFolder() != null) { newFiles.addAll(noticeWrapper.getTempUploadFolder().getItems(new VFSSystemItemFilter())); } - lectureService.updateAbsenceNoticeAttachments(absenceNotice, newFiles, noticeWrapper.getAttachmentsToDelete()); + absenceNotice = lectureService.updateAbsenceNoticeAttachments(absenceNotice, newFiles, noticeWrapper.getAttachmentsToDelete()); fireEvent(ureq, Event.CHANGED_EVENT); + + String after = lectureService.toAuditXml(absenceNotice); + lectureService.auditLog(Action.updateAbsenceNotice, before, after, null, absenceNotice, noticeWrapper.getIdentity(), getIdentity()); } @Override diff --git a/src/main/java/org/olat/modules/lecture/ui/export/AbstractLectureBlockAuditLogExport.java b/src/main/java/org/olat/modules/lecture/ui/export/AbstractLectureBlockAuditLogExport.java index bf25cdb0a1b369fcff92ab68614ef29e2045821e..353ab791016fb353efb374fba388f4b44ce71d2f 100644 --- a/src/main/java/org/olat/modules/lecture/ui/export/AbstractLectureBlockAuditLogExport.java +++ b/src/main/java/org/olat/modules/lecture/ui/export/AbstractLectureBlockAuditLogExport.java @@ -37,6 +37,7 @@ import org.olat.core.util.openxml.OpenXMLWorkbook; import org.olat.core.util.openxml.OpenXMLWorkbookResource; import org.olat.core.util.openxml.OpenXMLWorksheet; import org.olat.core.util.openxml.OpenXMLWorksheet.Row; +import org.olat.modules.lecture.AbsenceNotice; import org.olat.modules.lecture.LectureBlock; import org.olat.modules.lecture.LectureBlockAuditLog; import org.olat.modules.lecture.LectureBlockRef; @@ -157,6 +158,7 @@ public abstract class AbstractLectureBlockAuditLogExport extends OpenXMLWorkbook LectureBlock auditBlock = null; LectureBlockRollCall auditRollCall = null; LectureParticipantSummary auditSummary = null; + AbsenceNotice absenceNotice = null; if(logEntry.getRollCallKey() != null) { auditRollCall = getAuditRollCall(logEntry.getAfter()); } @@ -167,6 +169,9 @@ public abstract class AbstractLectureBlockAuditLogExport extends OpenXMLWorkbook auditSummary = getAuditLectureParticipantSummary(logEntry.getAfter()); } } + if(logEntry.getAbsenceNoticeKey() != null) { + absenceNotice = getAuditAbsenceNotice(logEntry.getAfter()); + } if(auditBlock != null) { if(auditBlock.getStatus() == null) { @@ -181,7 +186,25 @@ public abstract class AbstractLectureBlockAuditLogExport extends OpenXMLWorkbook pos += 4; } - if(auditRollCall != null) { + if(absenceNotice != null) { + Long assessedIdentityKey = logEntry.getIdentityKey(); + String fullname = userManager.getUserDisplayName(assessedIdentityKey); + row.addCell(pos++, fullname); + pos += 2;// attendedNumber, absentNumber + if(authorizedAbsenceEnabled) { + if(absenceNotice.getAbsenceAuthorized() != null && absenceNotice.getAbsenceAuthorized().booleanValue()) { + row.addCell(pos++, "x"); + } else { + pos++; + } + row.addCell(pos++, absenceNotice.getAbsenceReason(), null); + } + if(absenceNotice.getAbsenceCategory() != null) { + row.addCell(pos++, absenceNotice.getAbsenceCategory().getTitle(), null); + } else { + pos++; + } + } else if(auditRollCall != null) { Long assessedIdentityKey = logEntry.getIdentityKey(); String fullname = userManager.getUserDisplayName(assessedIdentityKey); row.addCell(pos++, fullname); @@ -267,6 +290,10 @@ public abstract class AbstractLectureBlockAuditLogExport extends OpenXMLWorkbook return lectureService.toAuditLectureParticipantSummary(xml); } + private AbsenceNotice getAuditAbsenceNotice(String xml) { + return lectureService.toAuditAbsenceNotice(xml); + } + protected void cacheRepositoryEntry(RepositoryEntry entry) { if(entry != null) { displayNames.put(entry.getKey(), entry.getDisplayname()); @@ -274,6 +301,7 @@ public abstract class AbstractLectureBlockAuditLogExport extends OpenXMLWorkbook } private String getRepositoryEntryDisplayName(Long entryKey) { + if(entryKey == null) return null; String displayName = displayNames.get(entryKey); if(displayName == null) { diff --git a/src/main/java/org/olat/modules/lecture/ui/wizard/AbsenceNoticeFinishStepCallback.java b/src/main/java/org/olat/modules/lecture/ui/wizard/AbsenceNoticeFinishStepCallback.java index 9fc274c0dfa94d8229bf9334980fa32f0d6b8ebe..9314224c116dc8bb2460e0066818e871ed53e689 100644 --- a/src/main/java/org/olat/modules/lecture/ui/wizard/AbsenceNoticeFinishStepCallback.java +++ b/src/main/java/org/olat/modules/lecture/ui/wizard/AbsenceNoticeFinishStepCallback.java @@ -1,4 +1,5 @@ /** + * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> @@ -46,6 +47,7 @@ import org.olat.core.util.mail.MailerResult; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.filters.VFSSystemItemFilter; import org.olat.modules.lecture.AbsenceNotice; +import org.olat.modules.lecture.LectureBlockAuditLog.Action; import org.olat.modules.lecture.LectureService; import org.olat.modules.lecture.model.EditAbsenceNoticeWrapper; import org.springframework.beans.factory.annotation.Autowired; @@ -87,8 +89,10 @@ public class AbsenceNoticeFinishStepCallback implements StepRunnerCallback { if(noticeWrapper.getTempUploadFolder() != null) { newFiles.addAll(noticeWrapper.getTempUploadFolder().getItems(new VFSSystemItemFilter())); } - lectureService.updateAbsenceNoticeAttachments(notice, newFiles, noticeWrapper.getAttachmentsToDelete()); - + notice = lectureService.updateAbsenceNoticeAttachments(notice, newFiles, noticeWrapper.getAttachmentsToDelete()); + + String after = lectureService.toAuditXml(notice); + lectureService.auditLog(Action.createAbsenceNotice, null, after, null, notice, absentIdentity, ureq.getIdentity()); } if(noticeWrapper.getTempUploadFolder() != null) { diff --git a/src/main/resources/database/mysql/alter_14_0_x_to_14_1_0.sql b/src/main/resources/database/mysql/alter_14_0_x_to_14_1_0.sql index 1db781375c508edb971b60007ff4351d86a70797..0da4ecf2dcc690a158056b442acd2cfe7780a784 100644 --- a/src/main/resources/database/mysql/alter_14_0_x_to_14_1_0.sql +++ b/src/main/resources/database/mysql/alter_14_0_x_to_14_1_0.sql @@ -69,4 +69,8 @@ alter table o_lecture_block_roll_call add column l_absence_notice_lectures varch alter table o_lecture_block_roll_call add column fk_absence_notice bigint default null; alter table o_lecture_block_roll_call add constraint rollcall_to_notice_idx foreign key (fk_absence_notice) references o_lecture_absence_notice (id); +alter table o_lecture_block_audit_log add column fk_absence_notice bigint default null; + + + diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 8c52f79056431a2287521f71ea15066d930130aa..fbcfcfc2fcb757a2f05f0f30b630aaa3ebe1aa9b 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -2579,6 +2579,7 @@ create table o_lecture_block_audit_log ( l_message mediumtext, fk_lecture_block bigint, fk_roll_call bigint, + fk_absence_notice bigint, fk_entry bigint, fk_identity bigint, fk_author bigint, diff --git a/src/main/resources/database/oracle/alter_14_0_x_to_14_1_0.sql b/src/main/resources/database/oracle/alter_14_0_x_to_14_1_0.sql index ec75e74a1a2a3007e14584b0dea7d335b882c0f4..2576296860ae117dc5dd7e619ff7b5ca3608606f 100644 --- a/src/main/resources/database/oracle/alter_14_0_x_to_14_1_0.sql +++ b/src/main/resources/database/oracle/alter_14_0_x_to_14_1_0.sql @@ -72,3 +72,7 @@ alter table o_lecture_block_roll_call add l_absence_notice_lectures varchar(128) alter table o_lecture_block_roll_call add fk_absence_notice number(20) default null; alter table o_lecture_block_roll_call add constraint rollcall_to_notice_idx foreign key (fk_absence_notice) references o_lecture_absence_notice (id); create index idx_rollcall_to_notice_idx on o_lecture_block_roll_call (fk_absence_notice); + +alter table o_lecture_block_audit_log add fk_absence_notice number(20) default null; + + diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index ce9919598f89b341b84e9328967cbbe20c43f808..8729b39c2d43630deab1b40c07741f0d5c3eb593 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -2658,6 +2658,7 @@ create table o_lecture_block_audit_log ( l_message CLOB, fk_lecture_block number(20), fk_roll_call number(20), + fk_absence_notice number(20), fk_entry number(20), fk_identity number(20), fk_author number(20), diff --git a/src/main/resources/database/postgresql/alter_14_0_x_to_14_1_0.sql b/src/main/resources/database/postgresql/alter_14_0_x_to_14_1_0.sql index 6b8296cb60a00809d02e7b1409fccc6ae2f479f6..bf29ff13482c0f86385dd260bb5e2f3c6b94e3b0 100644 --- a/src/main/resources/database/postgresql/alter_14_0_x_to_14_1_0.sql +++ b/src/main/resources/database/postgresql/alter_14_0_x_to_14_1_0.sql @@ -72,6 +72,8 @@ alter table o_lecture_block_roll_call add column fk_absence_notice bigint defaul alter table o_lecture_block_roll_call add constraint rollcall_to_notice_idx foreign key (fk_absence_notice) references o_lecture_absence_notice (id); create index idx_rollcall_to_notice_idx on o_lecture_block_roll_call (fk_absence_notice); +alter table o_lecture_block_audit_log add column fk_absence_notice int8 default null; + diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 03d9188d3a8872a6128e158113526a47b44d2ff0..cd3853e2b9391b07f0a837caad24a3220766f94a 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -2605,6 +2605,7 @@ create table o_lecture_block_audit_log ( l_message text, fk_lecture_block int8, fk_roll_call int8, + fk_absence_notice int8, fk_entry int8, fk_identity int8, fk_author int8, diff --git a/src/test/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAOTest.java b/src/test/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAOTest.java index 4b56dfb5517cf958b4998a0adb7a546e7d215bc3..5cccd095f18d271726ba5a78765382d67c50a970 100644 --- a/src/test/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAOTest.java +++ b/src/test/java/org/olat/modules/lecture/manager/LectureBlockAuditLogDAOTest.java @@ -21,11 +21,17 @@ package org.olat.modules.lecture.manager; import java.util.Date; import java.util.List; +import java.util.UUID; import org.junit.Assert; import org.junit.Test; +import org.olat.commons.calendar.CalendarUtils; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Identity; +import org.olat.modules.lecture.AbsenceCategory; +import org.olat.modules.lecture.AbsenceNotice; +import org.olat.modules.lecture.AbsenceNoticeTarget; +import org.olat.modules.lecture.AbsenceNoticeType; import org.olat.modules.lecture.LectureBlock; import org.olat.modules.lecture.LectureBlockAuditLog; import org.olat.repository.RepositoryEntry; @@ -46,6 +52,10 @@ public class LectureBlockAuditLogDAOTest extends OlatTestCase { @Autowired private LectureBlockDAO lectureBlockDao; @Autowired + private AbsenceNoticeDAO absenceNoticeDao; + @Autowired + private AbsenceCategoryDAO absenceCategoryDao; + @Autowired private LectureBlockAuditLogDAO lectureBlockAuditLogDao; @Test @@ -139,4 +149,27 @@ public class LectureBlockAuditLogDAOTest extends OlatTestCase { String xml = lectureBlockAuditLogDao.toXml(lectureBlock); Assert.assertNotNull(xml); } + + @Test + public void xmlAuditLog_absenceNotice() { + String title = UUID.randomUUID().toString(); + String description = "Long absence"; + AbsenceCategory absenceCategory = absenceCategoryDao.createAbsenceCategory(title, description); + dbInstance.commitAndCloseSession(); + + Identity identity = JunitTestHelper.createAndPersistIdentityAsRndUser("absent-1"); + Identity notifier = JunitTestHelper.createAndPersistIdentityAsRndUser("notifier-1"); + Identity authorizer = JunitTestHelper.createAndPersistIdentityAsRndUser("authorizer-1"); + + Date start = CalendarUtils.startOfDay(new Date()); + Date end = CalendarUtils.endOfDay(new Date()); + + AbsenceNotice notice = absenceNoticeDao.createAbsenceNotice(identity, AbsenceNoticeType.absence, AbsenceNoticeTarget.lectureblocks, + start, end, absenceCategory, "A very good reason", Boolean.TRUE, authorizer, notifier); + dbInstance.commitAndCloseSession(); + + String xml = lectureBlockAuditLogDao.toXml(notice); + System.out.println(xml); + Assert.assertNotNull(xml); + } }