From 2ce7f93e7f5c9db9fbffe58d1ab98cc46c4e1122 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Tue, 3 Sep 2019 10:14:48 +0200 Subject: [PATCH] OO-4150: refinement of panel for multi-lectures blocks per participant --- .../olat/modules/lecture/LectureService.java | 2 + .../lecture/manager/AbsenceNoticeDAO.java | 3 +- .../lecture/manager/LectureServiceImpl.java | 3 +- .../ui/SingleParticipantRollCallRow.java | 20 +++- .../SingleParticipantRollCallsController.java | 96 ++++++++++++++----- .../SingleParticipantRollCallsDataModel.java | 23 ++++- .../lecture/ui/TeacherRollCallController.java | 2 +- 7 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/olat/modules/lecture/LectureService.java b/src/main/java/org/olat/modules/lecture/LectureService.java index 214eeea05d3..249e79a6102 100644 --- a/src/main/java/org/olat/modules/lecture/LectureService.java +++ b/src/main/java/org/olat/modules/lecture/LectureService.java @@ -340,6 +340,8 @@ public interface LectureService { */ public AbsenceNotice getAbsenceNotice(AbsenceNoticeRef notice); + public AbsenceNotice getAbsenceNotice(IdentityRef identity, LectureBlock lectureBlock); + public List<AbsenceNotice> getAbsenceNoticeRelatedTo(LectureBlock block); public List<AbsenceNoticeInfos> searchAbsenceNotices(AbsenceNoticeSearchParameters searchParams); 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 14fb26c8632..6a478f8d803 100644 --- a/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java +++ b/src/main/java/org/olat/modules/lecture/manager/AbsenceNoticeDAO.java @@ -28,6 +28,7 @@ import javax.persistence.TemporalType; import javax.persistence.TypedQuery; import org.olat.basesecurity.GroupRoles; +import org.olat.basesecurity.IdentityRef; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.QueryBuilder; import org.olat.core.id.Identity; @@ -331,7 +332,7 @@ public class AbsenceNoticeDAO { * @param lectureBlock The lecture block (mandatory) * @return */ - public List<AbsenceNotice> getAbsenceNotices(Identity calleeIdentity, LectureBlock lectureBlock) { + public List<AbsenceNotice> getAbsenceNotices(IdentityRef calleeIdentity, LectureBlock lectureBlock) { QueryBuilder sb = new QueryBuilder(1024); sb.append("select notice from absencenotice as notice") .append(" inner join fetch notice.identity as aIdent") 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 d78539057d3..4fca83cad13 100644 --- a/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java +++ b/src/main/java/org/olat/modules/lecture/manager/LectureServiceImpl.java @@ -1185,7 +1185,8 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable, De auditLog(LectureBlockAuditLog.Action.saveLectureBlock, before, after, null, lectureBlock, null, lectureBlock.getEntry(), null, teacher); } - private AbsenceNotice getAbsenceNotice(Identity identity, LectureBlock lectureBlock) { + @Override + public AbsenceNotice getAbsenceNotice(IdentityRef identity, LectureBlock lectureBlock) { List<AbsenceNotice> notices = absenceNoticeDao.getAbsenceNotices(identity, lectureBlock); AbsenceNotice preferedNotice; if(notices.isEmpty()) { diff --git a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallRow.java b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallRow.java index 9d7317eaeb5..85da287aa15 100644 --- a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallRow.java +++ b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallRow.java @@ -19,10 +19,13 @@ */ package org.olat.modules.lecture.ui; +import java.util.List; + 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.elements.TextElement; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.id.Identity; import org.olat.modules.lecture.AbsenceNotice; import org.olat.modules.lecture.LectureBlock; import org.olat.modules.lecture.LectureBlockRollCall; @@ -41,8 +44,10 @@ public class SingleParticipantRollCallRow implements RollCallRow, RollCallItem { private final LectureBlock lectureBlock; private final AbsenceNotice absenceNotice; private LectureBlockRollCall rollCall; + private List<Identity> teachers; private FormLink allLink; + private FormLink noticeLink; private FormLink reasonLink; private TextElement commentEl; private MultipleSelectionElement[] checks; @@ -50,10 +55,11 @@ public class SingleParticipantRollCallRow implements RollCallRow, RollCallItem { private FormLayoutContainer authorizedAbsenceCont; private LectureBlockRollCallStatusItem rollCallStatusEl; - public SingleParticipantRollCallRow(LectureBlock lectureBlock, AbsenceNotice absenceNotice, int numOfLectures) { + public SingleParticipantRollCallRow(LectureBlock lectureBlock, AbsenceNotice absenceNotice, int numOfLectures, List<Identity> teachers) { this.lectureBlock = lectureBlock; this.absenceNotice = absenceNotice; this.numOfLectures = numOfLectures; + this.teachers = teachers; } public int getNumOfLectures() { @@ -77,6 +83,10 @@ public class SingleParticipantRollCallRow implements RollCallRow, RollCallItem { return absenceNotice; } + public List<Identity> getTeachers() { + return teachers; + } + @Override public LectureBlockRollCall getRollCall() { return rollCall; @@ -147,6 +157,14 @@ public class SingleParticipantRollCallRow implements RollCallRow, RollCallItem { this.reasonLink = reasonLink; } + public FormLink getNoticeLink() { + return noticeLink; + } + + public void setNoticeLink(FormLink noticeLink) { + this.noticeLink = noticeLink; + } + @Override public MultipleSelectionElement getAuthorizedAbsence() { return authorizedAbsence; diff --git a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsController.java b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsController.java index 77032d0bf75..dc23aeede9d 100644 --- a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsController.java +++ b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsController.java @@ -28,6 +28,7 @@ import java.util.Map; import org.olat.admin.user.UserShortDescription; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.EscapeMode; 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.FlexiTableElement; @@ -40,6 +41,7 @@ import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; +import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; @@ -57,11 +59,15 @@ import org.olat.modules.lecture.LectureBlockStatus; import org.olat.modules.lecture.LectureModule; import org.olat.modules.lecture.LectureService; import org.olat.modules.lecture.RollCallSecurityCallback; +import org.olat.modules.lecture.model.LectureBlockWithTeachers; +import org.olat.modules.lecture.model.LecturesBlockSearchParameters; import org.olat.modules.lecture.model.RollCallSecurityCallbackImpl; import org.olat.modules.lecture.ui.SingleParticipantRollCallsDataModel.RollCallsCols; +import org.olat.modules.lecture.ui.coach.AbsenceNoticeDetailsCalloutController; import org.olat.modules.lecture.ui.component.LectureBlockRollCallStatusItem; import org.olat.modules.lecture.ui.component.LectureBlockTimesCellRenderer; import org.olat.user.DisplayPortraitController; +import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; /** @@ -91,8 +97,12 @@ public class SingleParticipantRollCallsController extends FormBasicController { private final Map<LectureBlock,RollCallSecurityCallback> secCallbacks; private ReasonController reasonCtrl; - private CloseableCalloutWindowController reasonCalloutCtrl; + private CloseableCalloutWindowController noticeCalloutCtrl; + private CloseableCalloutWindowController reasonCalloutCtrl; + private AbsenceNoticeDetailsCalloutController noticeDetailsCtrl; + @Autowired + private UserManager userManager; @Autowired private LectureModule lectureModule; @Autowired @@ -162,8 +172,9 @@ public class SingleParticipantRollCallsController extends FormBasicController { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.entry)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.externalRef)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.lecturesBlock)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.times, new LectureBlockTimesCellRenderer(getLocale()))); - columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.teacher)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.teacher, new TextFlexiCellRenderer(EscapeMode.antisamy))); boolean canViewAuthorizedAbsences = secCallbacks.values() .stream().anyMatch(RollCallSecurityCallback::canViewAuthorizedAbsences); @@ -192,7 +203,7 @@ public class SingleParticipantRollCallsController extends FormBasicController { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(RollCallsCols.comment)); - tableModel = new SingleParticipantRollCallsDataModel(columnsModel); + tableModel = new SingleParticipantRollCallsDataModel(columnsModel, userManager); tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 24, false, getTranslator(), formLayout); @@ -201,6 +212,11 @@ public class SingleParticipantRollCallsController extends FormBasicController { } private void loadModel() { + LecturesBlockSearchParameters searchParams = new LecturesBlockSearchParameters(); + searchParams.setLectureBlocks(lectureBlocks); + List<LectureBlockWithTeachers> lectureBlocksWithTeachers = lectureService.getLectureBlocksWithTeachers(searchParams); + + List<SingleParticipantRollCallRow> rows = new ArrayList<>(lectureBlocks.size()); for(LectureBlock lectureBlock:lectureBlocks) { LectureBlockRollCall lectureCall = null; @@ -209,18 +225,32 @@ public class SingleParticipantRollCallsController extends FormBasicController { lectureCall = call; } } - AbsenceNotice notice = null;//TODO absences notice + + List<Identity> teachers = null; + for(LectureBlockWithTeachers lectureBlockWithTeachers:lectureBlocksWithTeachers) { + if(lectureBlockWithTeachers.getLectureBlock().equals(lectureBlock)) { + teachers = lectureBlockWithTeachers.getTeachers(); + } + } + + AbsenceNotice notice = null; + if(lectureCall != null) { + notice = lectureCall.getAbsenceNotice(); + } else { + notice = lectureService.getAbsenceNotice(calledIdentity, lectureBlock); + } RollCallSecurityCallback secCallback = secCallbacks.get(lectureBlock); - rows.add(forgeRow(lectureBlock, lectureCall, notice, secCallback)); + rows.add(forgeRow(lectureBlock, lectureCall, notice, teachers, secCallback)); } tableModel.setObjects(rows); tableEl.reset(true, true, true); } - private SingleParticipantRollCallRow forgeRow(LectureBlock lectureBlock, LectureBlockRollCall rollCall, AbsenceNotice notice, RollCallSecurityCallback secCallback) { + private SingleParticipantRollCallRow forgeRow(LectureBlock lectureBlock, LectureBlockRollCall rollCall, + AbsenceNotice notice, List<Identity> teachers, RollCallSecurityCallback secCallback) { int numOfLectures = numOfLectures(lectureBlock); - SingleParticipantRollCallRow row = new SingleParticipantRollCallRow(lectureBlock, notice, numOfLectures); + SingleParticipantRollCallRow row = new SingleParticipantRollCallRow(lectureBlock, notice, numOfLectures, teachers); int numOfChecks = lectureBlock.isCompulsory() ? numOfLectures : 0; MultipleSelectionElement[] checks = new MultipleSelectionElement[numOfChecks]; @@ -231,17 +261,16 @@ public class SingleParticipantRollCallsController extends FormBasicController { MultipleSelectionElement check = uifactory.addCheckboxesHorizontal(checkId, null, flc, onKeys, onValues); check.setDomReplacementWrapperRequired(false); check.addActionListener(FormEvent.ONCHANGE); - check.setEnabled(secCallback.canEditAbsences()); + check.setEnabled(secCallback.canEditAbsences() && notice == null); check.setUserObject(row); check.setAjaxOnly(true); - if(absences.contains(i)) { + if(absences.contains(i) || notice != null) { check.select(onKeys[0], true); } checks[i] = check; flc.add(check); } row.setChecks(checks); - LectureBlockRollCallStatusItem statusEl = new LectureBlockRollCallStatusItem("status_".concat(Integer.toString(++counter)), row, authorizedAbsenceEnabled, absenceDefaultAuthorized, getTranslator()); @@ -259,7 +288,7 @@ public class SingleParticipantRollCallsController extends FormBasicController { authorizedAbsencedEl.addActionListener(FormEvent.ONCHANGE); authorizedAbsencedEl.setUserObject(row); authorizedAbsencedEl.setAjaxOnly(true); - authorizedAbsencedEl.setEnabled(secCallback.canEdit() && secCallback.canEditAuthorizedAbsences()); + authorizedAbsencedEl.setEnabled(secCallback.canEdit() && secCallback.canEditAuthorizedAbsences() && notice == null); boolean hasAuthorization = rollCall != null && rollCall.getAbsenceAuthorized() != null && rollCall.getAbsenceAuthorized().booleanValue(); @@ -268,21 +297,31 @@ public class SingleParticipantRollCallsController extends FormBasicController { } row.setAuthorizedAbsence(authorizedAbsencedEl); flc.add(authorizedAbsencedEl); - - String reasonId = "abs_reason_".concat(Integer.toString(++counter)); - FormLink reasonLink = uifactory.addFormLink(reasonId, "", null, absenceCont, Link.BUTTON_XSMALL | Link.NONTRANSLATED); - reasonLink.setTitle(translate("reason")); - reasonLink.setDomReplacementWrapperRequired(false); - reasonLink.setIconLeftCSS("o_icon o_icon_notes"); - reasonLink.setVisible(hasAuthorization); - reasonLink.setUserObject(row); - row.setReasonLink(reasonLink); + + if(notice != null) { + String noticeId = "notice_".concat(Integer.toString(++counter)); + FormLink noticeLink = uifactory.addFormLink(noticeId, "", null, absenceCont, Link.LINK | Link.NONTRANSLATED); + noticeLink.setTitle(translate("reason")); + noticeLink.setDomReplacementWrapperRequired(false); + noticeLink.setIconLeftCSS("o_icon o_icon_info"); + noticeLink.setUserObject(row); + row.setNoticeLink(noticeLink); + } else { + String reasonId = "abs_reason_".concat(Integer.toString(++counter)); + FormLink reasonLink = uifactory.addFormLink(reasonId, "", null, absenceCont, Link.BUTTON_XSMALL | Link.NONTRANSLATED); + reasonLink.setTitle(translate("reason")); + reasonLink.setDomReplacementWrapperRequired(false); + reasonLink.setIconLeftCSS("o_icon o_icon_notes"); + reasonLink.setVisible(hasAuthorization); + reasonLink.setUserObject(row); + row.setReasonLink(reasonLink); + } row.setAuthorizedAbsenceCont(absenceCont); absenceCont.contextPut("row", row); } - if(secCallback.canEditAbsences()) { + if(secCallback.canEditAbsences() && notice == null) { FormLink allLink = uifactory.addFormLink("all_".concat(Integer.toString(++counter)), "all", null, flc, Link.LINK); allLink.setTitle("all.desc"); allLink.setDomReplacementWrapperRequired(false); @@ -327,7 +366,8 @@ public class SingleParticipantRollCallsController extends FormBasicController { //??? stop? } else if(!absenceDefaultAuthorized) { String reason = row.getRollCall().getAbsenceReason(); - if(row.getAuthorizedAbsence() != null && row.getAuthorizedAbsence().isAtLeastSelected(1) && !StringHelper.containsNonWhitespace(reason)) { + if(row.getAbsenceNotice() == null && row.getAuthorizedAbsence() != null + && row.getAuthorizedAbsence().isAtLeastSelected(1) && !StringHelper.containsNonWhitespace(reason)) { row.getAuthorizedAbsence().setErrorKey("error.reason.mandatory", null); allOk &= false; } @@ -380,6 +420,8 @@ public class SingleParticipantRollCallsController extends FormBasicController { doCalloutReasonAbsence(ureq, link.getFormDispatchId(), row); } else if(cmd.startsWith("all_")) { doCheckAllRow((SingleParticipantRollCallRow)link.getUserObject()); + }else if(cmd.startsWith("notice_")) { + doCalloutAbsenceNotice(ureq, link.getFormDispatchId(), (SingleParticipantRollCallRow)link.getUserObject()); } } } @@ -397,6 +439,16 @@ public class SingleParticipantRollCallsController extends FormBasicController { fireEvent(ureq, Event.CANCELLED_EVENT); } + private void doCalloutAbsenceNotice(UserRequest ureq, String elementId, SingleParticipantRollCallRow row) { + noticeDetailsCtrl = new AbsenceNoticeDetailsCalloutController(ureq, getWindowControl(), row.getAbsenceNotice()); + listenTo(noticeDetailsCtrl); + + noticeCalloutCtrl = new CloseableCalloutWindowController(ureq, getWindowControl(), + noticeDetailsCtrl.getInitialComponent(), elementId, "", true, ""); + listenTo(noticeCalloutCtrl); + noticeCalloutCtrl.activate(); + } + private void doReason(SingleParticipantRollCallRow row, String reason, AbsenceCategory category) { LectureBlockRollCall rollCall = row.getRollCall(); String before = lectureService.toAuditXml(rollCall); diff --git a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsDataModel.java b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsDataModel.java index 9570d3edd81..d0128285f17 100644 --- a/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsDataModel.java +++ b/src/main/java/org/olat/modules/lecture/ui/SingleParticipantRollCallsDataModel.java @@ -22,6 +22,8 @@ package org.olat.modules.lecture.ui; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.id.Identity; +import org.olat.user.UserManager; /** * @@ -30,9 +32,12 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable * */ public class SingleParticipantRollCallsDataModel extends DefaultFlexiTableDataModel<SingleParticipantRollCallRow> { + + private final UserManager userManager; - public SingleParticipantRollCallsDataModel(FlexiTableColumnModel columnModel) { + public SingleParticipantRollCallsDataModel(FlexiTableColumnModel columnModel, UserManager userManager) { super(columnModel); + this.userManager = userManager; } @Override @@ -42,8 +47,9 @@ public class SingleParticipantRollCallsDataModel extends DefaultFlexiTableDataMo switch(RollCallsCols.values()[col]) { case entry: return call.getEntryDisplayname(); case externalRef: return call.getEntryExternalRef(); + case lecturesBlock: return call.getLectureBlock().getTitle(); case times: return call.getLectureBlock(); - case teacher: return "TEACHER"; + case teacher: return getTeachers(call); case status: return call.getRollCallStatusEl(); case authorizedAbsence: return call.getAuthorizedAbsenceCont(); case comment: return call.getCommentEl(); @@ -54,15 +60,26 @@ public class SingleParticipantRollCallsDataModel extends DefaultFlexiTableDataMo int propPos = col - TeacherRollCallController.CHECKBOX_OFFSET; return call.getCheck(propPos); } + + private String getTeachers(SingleParticipantRollCallRow row) { + StringBuilder sb = new StringBuilder(1024); + for(Identity teacher:row.getTeachers()) { + sb.append("<i class='o_icon o_icon_user'> </i> ") + .append(userManager.getUserDisplayName(teacher)) + .append(" "); + } + return sb.toString(); + } @Override public DefaultFlexiTableDataModel<SingleParticipantRollCallRow> createCopyWithEmptyList() { - return new SingleParticipantRollCallsDataModel(getTableColumnModel()); + return new SingleParticipantRollCallsDataModel(getTableColumnModel(), userManager); } public enum RollCallsCols implements FlexiSortableColumnDef { entry("table.header.entry"), externalRef("table.header.external.ref"), + lecturesBlock("table.header.lecture.block"), times("table.header.times"), teacher("table.header.teachers"), status("table.header.status"), diff --git a/src/main/java/org/olat/modules/lecture/ui/TeacherRollCallController.java b/src/main/java/org/olat/modules/lecture/ui/TeacherRollCallController.java index 258aa82ac10..d59c13e26a3 100644 --- a/src/main/java/org/olat/modules/lecture/ui/TeacherRollCallController.java +++ b/src/main/java/org/olat/modules/lecture/ui/TeacherRollCallController.java @@ -393,7 +393,7 @@ public class TeacherRollCallController extends FormBasicController { absenceCont.contextPut("row", row); } - if(secCallback.canEditAbsences()) { + if(secCallback.canEditAbsences() && notice == null) { FormLink allLink = uifactory.addFormLink("all_".concat(Integer.toString(++counter)), "all", null, flc, Link.LINK); allLink.setTitle("all.desc"); allLink.setDomReplacementWrapperRequired(false); -- GitLab