/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.course.nodes.gta.ui; import java.io.File; import java.util.ArrayList; import java.util.List; import org.olat.basesecurity.GroupRoles; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.text.TextFactory; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.io.SystemFilenameFilter; import org.olat.core.util.vfs.VFSContainer; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.nodes.GTACourseNode; import org.olat.course.nodes.gta.GTAManager; import org.olat.course.nodes.gta.GTAType; import org.olat.course.nodes.gta.Task; import org.olat.course.nodes.gta.TaskHelper; import org.olat.course.nodes.gta.TaskHelper.FilesLocked; import org.olat.course.nodes.gta.TaskProcess; import org.olat.course.nodes.gta.ui.events.SubmitEvent; import org.olat.course.nodes.gta.ui.events.TaskMultiUserEvent; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 11.03.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class GTACoachRevisionAndCorrectionsController extends BasicController { private final VelocityContainer mainVC; private Link returnToRevisionsButton, closeRevisionsButton, collectButton; private DirectoryController revisionsCtrl, correctionsCtrl; private SubmitDocumentsController uploadCorrectionsCtrl; private DialogBoxController confirmCloseRevisionProcessCtrl, confirmReturnToRevisionsCtrl, confirmCollectCtrl; private Task assignedTask; private final int currentIteration; private final GTACourseNode gtaNode; private final boolean businessGroupTask; private final Identity assessedIdentity; private final BusinessGroup assessedGroup; private final CourseEnvironment courseEnv; private final UserCourseEnvironment coachCourseEnv; private final OLATResourceable taskListEventResource; private final UserCourseEnvironment assessedUserCourseEnv; @Autowired private GTAManager gtaManager; @Autowired private UserManager userManager; @Autowired private BusinessGroupService businessGroupService; public GTACoachRevisionAndCorrectionsController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, Task assignedTask, GTACourseNode gtaNode, UserCourseEnvironment coachCourseEnv, BusinessGroup assessedGroup, Identity assessedIdentity, UserCourseEnvironment assessedUserCourseEnv, OLATResourceable taskListEventResource) { super(ureq, wControl); this.gtaNode = gtaNode; this.courseEnv = courseEnv; this.assignedTask = assignedTask; this.assessedGroup = assessedGroup; this.coachCourseEnv = coachCourseEnv; this.assessedIdentity = assessedIdentity; this.assessedUserCourseEnv = assessedUserCourseEnv; this.taskListEventResource = taskListEventResource; this.businessGroupTask = GTAType.group.name().equals(gtaNode.getModuleConfiguration().getStringValue(GTACourseNode.GTASK_TYPE)); currentIteration = assignedTask.getRevisionLoop(); mainVC = createVelocityContainer("coach_revisions"); putInitialPanel(mainVC); initRevisionProcess(ureq); } public Task getAssignedTask() { return assignedTask; } @Override protected void doDispose() { // } private void initRevisionProcess(UserRequest ureq) { List<String> revisionStepNames = new ArrayList<>(); mainVC.contextPut("previousRevisions", revisionStepNames); if(collectButton != null) { mainVC.remove(collectButton);//reset collect } if(assignedTask.getRevisionLoop() > 1) { for(int i=1 ; i<assignedTask.getRevisionLoop(); i++ ) { setRevisionIteration(ureq, i, revisionStepNames); } } TaskProcess status = assignedTask.getTaskStatus(); if(status == TaskProcess.revision) { //assessed user can return some revised documents setRevisions(ureq, "revisions", currentIteration); setCollectRevisions(); } else if(status == TaskProcess.correction) { //coach can return some corrections setRevisions(ureq, "revisions", currentIteration); setUploadCorrections(ureq, assignedTask, assignedTask.getRevisionLoop()); } else { int lastIteration = assignedTask.getRevisionLoop(); setRevisionIteration(ureq, lastIteration, revisionStepNames); } } private void setCollectRevisions() { collectButton = LinkFactory.createButton("coach.collect.revisions", mainVC, this); collectButton.setUserObject(assignedTask); collectButton.setVisible(!coachCourseEnv.isCourseReadOnly()); } private void setRevisionIteration(UserRequest ureq, int iteration, List<String> revisionStepNames) { //revisions String revCmpName = "revisions-" + iteration; if(setRevisions(ureq, revCmpName, iteration)) { revisionStepNames.add(revCmpName); } //corrections; String correctionCmpName = "corrections-" + iteration; if(setCorrections(ureq, correctionCmpName, iteration)) { revisionStepNames.add(correctionCmpName); } } private boolean setRevisions(UserRequest ureq, String cmpName, int iteration) { File documentsDir; VFSContainer documentsContainer = null; if(businessGroupTask) { documentsDir = gtaManager.getRevisedDocumentsDirectory(courseEnv, gtaNode, iteration, assessedGroup); documentsContainer = gtaManager.getRevisedDocumentsContainer(courseEnv, gtaNode, iteration, assessedGroup); } else { documentsDir = gtaManager.getRevisedDocumentsDirectory(courseEnv, gtaNode, iteration, assessedIdentity); documentsContainer = gtaManager.getRevisedDocumentsContainer(courseEnv, gtaNode, iteration, assessedIdentity); } boolean hasDocuments = TaskHelper.hasDocuments(documentsDir); if(hasDocuments) { revisionsCtrl = new DirectoryController(ureq, getWindowControl(), documentsDir, documentsContainer, "coach.revisions.description", "bulk.revisions", "revisions.zip"); listenTo(revisionsCtrl); mainVC.put(cmpName, revisionsCtrl.getInitialComponent()); } else if (assignedTask.getTaskStatus() == TaskProcess.revision) { String msg = "<i class='o_icon o_icon_error'> </i> " + translate("coach.corrections.rejected"); TextFactory.createTextComponentFromString(cmpName, msg, null, true, mainVC); } else { TextFactory.createTextComponentFromI18nKey(cmpName, "coach.revisions.nofiles", getTranslator(), null, true, mainVC); } return hasDocuments; } private boolean setCorrections(UserRequest ureq, String cmpName, int iteration) { File documentsDir; VFSContainer documentsContainer = null; if(businessGroupTask) { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedGroup); documentsContainer = gtaManager.getRevisedDocumentsCorrectionsContainer(courseEnv, gtaNode, iteration, assessedGroup); } else { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedIdentity); documentsContainer = gtaManager.getRevisedDocumentsCorrectionsContainer(courseEnv, gtaNode, iteration, assessedIdentity); } boolean hasDocuments = TaskHelper.hasDocuments(documentsDir); if(hasDocuments) { correctionsCtrl = new DirectoryController(ureq, getWindowControl(), documentsDir, documentsContainer, "run.coach.corrections.description", "bulk.review", "review"); listenTo(correctionsCtrl); mainVC.put(cmpName, correctionsCtrl.getInitialComponent()); } String msg = null; if (assignedTask.getTaskStatus() == TaskProcess.revision) { // message already displayed in setRevisions method for this case } else if (assignedTask.getTaskStatus() == TaskProcess.correction) { msg = "<i class='o_icon o_icon_info'> </i> " + translate("coach.corrections.waiting"); } else { msg = "<i class='o_icon o_icon_ok'> </i> " + translate("coach.corrections.closed"); } mainVC.contextPut("revisionMessage", msg); return hasDocuments; } private void setUploadCorrections(UserRequest ureq, Task task, int iteration) { File documentsDir; VFSContainer documentsContainer; if(businessGroupTask) { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedGroup); documentsContainer = gtaManager.getRevisedDocumentsCorrectionsContainer(courseEnv, gtaNode, iteration, assessedGroup); } else { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedIdentity); documentsContainer = gtaManager.getRevisedDocumentsCorrectionsContainer(courseEnv, gtaNode, iteration, assessedIdentity); } uploadCorrectionsCtrl = new SubmitDocumentsController(ureq, getWindowControl(), task, documentsDir, documentsContainer, -1, gtaNode, courseEnv, coachCourseEnv.isCourseReadOnly(), null, "coach.document"); listenTo(uploadCorrectionsCtrl); mainVC.put("uploadCorrections", uploadCorrectionsCtrl.getInitialComponent()); returnToRevisionsButton = LinkFactory.createCustomLink("coach.submit.corrections.to.revision.button", "submit", "coach.submit.corrections.to.revision.button", Link.BUTTON, mainVC, this); returnToRevisionsButton.setCustomEnabledLinkCSS("btn btn-primary"); returnToRevisionsButton.setIconLeftCSS("o_icon o_icon o_icon_rejected"); returnToRevisionsButton.setElementCssClass("o_sel_course_gta_return_revision"); returnToRevisionsButton.setVisible(!coachCourseEnv.isCourseReadOnly()); closeRevisionsButton = LinkFactory.createCustomLink("coach.close.revision.button", "close", "coach.close.revision.button", Link.BUTTON, mainVC, this); closeRevisionsButton.setCustomEnabledLinkCSS("btn btn-primary"); closeRevisionsButton.setIconLeftCSS("o_icon o_icon o_icon_accepted"); closeRevisionsButton.setElementCssClass("o_sel_course_gta_close_revision"); closeRevisionsButton.setVisible(!coachCourseEnv.isCourseReadOnly()); } @Override protected void event(UserRequest ureq, Controller source, Event event) { if(uploadCorrectionsCtrl == source) { if(event instanceof SubmitEvent) { Task aTask = uploadCorrectionsCtrl.getAssignedTask(); gtaManager.log("Corrections", (SubmitEvent)event, aTask, getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode); } } else if(confirmReturnToRevisionsCtrl == source) { if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { doReturnToRevisions(); fireEvent(ureq, Event.DONE_EVENT); } } else if(confirmCloseRevisionProcessCtrl == source) { if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { doCloseRevisionProcess(); fireEvent(ureq, Event.DONE_EVENT); } } else if(confirmCollectCtrl == source) { if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { doCollect(); fireEvent(ureq, Event.DONE_EVENT); } } super.event(ureq, source, event); } @Override protected void event(UserRequest ureq, Component source, Event event) { if(returnToRevisionsButton == source) { doConfirmReturnToRevisions(ureq); } else if(closeRevisionsButton == source) { doConfirmCloseRevisionProcess(ureq); } else if(collectButton == source) { doConfirmCollect(ureq); } } private void doConfirmReturnToRevisions(UserRequest ureq) { String title = translate("coach.revisions.confirm.title"); String text = translate("coach.revisions.confirm.text"); File documentsDir; int iteration = assignedTask.getRevisionLoop(); if(businessGroupTask) { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedGroup); } else { documentsDir = gtaManager.getRevisedDocumentsCorrectionsDirectory(courseEnv, gtaNode, iteration, assessedIdentity); } boolean hasDocument = TaskHelper.hasDocuments(documentsDir); if(!hasDocument) { String warning = translate("coach.revisions.confirm.text.warn"); text = "<div class='o_warning'>" + warning + "</div>" + text; } confirmReturnToRevisionsCtrl = activateOkCancelDialog(ureq, title, text, confirmReturnToRevisionsCtrl); listenTo(confirmReturnToRevisionsCtrl); } private void doConfirmCollect(UserRequest ureq) { String toName = null; if (assessedGroup != null) { toName = assessedGroup.getName(); } else if (assessedIdentity != null) { toName = userManager.getUserDisplayName(assessedIdentity); } File[] submittedDocuments; VFSContainer documentsContainer; int iteration = assignedTask.getRevisionLoop(); if(GTAType.group.name().equals(gtaNode.getModuleConfiguration().getStringValue(GTACourseNode.GTASK_TYPE))) { documentsContainer = gtaManager.getRevisedDocumentsContainer(courseEnv, gtaNode, iteration, assessedGroup); File documentsDir = gtaManager.getRevisedDocumentsDirectory(courseEnv, gtaNode, iteration, assessedGroup); submittedDocuments = documentsDir.listFiles(new SystemFilenameFilter(true, false)); } else { documentsContainer = gtaManager.getRevisedDocumentsContainer(courseEnv, gtaNode, iteration, getIdentity()); File documentsDir = gtaManager.getRevisedDocumentsDirectory(courseEnv, gtaNode, iteration, getIdentity()); submittedDocuments = documentsDir.listFiles(new SystemFilenameFilter(true, false)); } FilesLocked lockedBy = TaskHelper.getDocumentsLocked(documentsContainer, submittedDocuments); if(lockedBy != null) { showWarning("warning.submit.documents.edited", new String[]{ lockedBy.getLockedBy(), lockedBy.getLockedFiles() }); } else { String title = translate("coach.collect.revisions.confirm.title"); String text = translate("coach.collect.revisions.confirm.text", new String[]{ toName }); text = "<div class='o_warning'>" + text + "</div>"; confirmCollectCtrl = activateOkCancelDialog(ureq, title, text, confirmCollectCtrl); listenTo(confirmCollectCtrl); } } private void doCollect() { assignedTask = gtaManager.updateTask(assignedTask, TaskProcess.correction, gtaNode); gtaManager.log("Collect revision", "revision collected", assignedTask, getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode); ICourse course = CourseFactory.loadCourse(courseEnv.getCourseResourceableId()); if(businessGroupTask) { List<Identity> identities = businessGroupService.getMembers(assessedGroup, GroupRoles.participant.name()); for(Identity identity:identities) { UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(identity, course); gtaNode.incrementUserAttempts(userCourseEnv); } } else { gtaNode.incrementUserAttempts(assessedUserCourseEnv); } TaskMultiUserEvent event = new TaskMultiUserEvent(TaskMultiUserEvent.SUBMIT_REVISION, assessedGroup == null ? getIdentity() : null, assessedGroup, getIdentity()); CoordinatorManager.getInstance().getCoordinator().getEventBus() .fireEventToListenersOf(event, taskListEventResource); } private void doReturnToRevisions() { assignedTask = gtaManager.updateTask(assignedTask, TaskProcess.revision, currentIteration + 1, gtaNode); gtaManager.log("Revision", "need another revision", assignedTask, getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode); } private void doConfirmCloseRevisionProcess(UserRequest ureq) { String title = translate("coach.reviewed.confirm.title"); String text = translate("coach.reviewed.confirm.text"); confirmCloseRevisionProcessCtrl = activateOkCancelDialog(ureq, title, text, confirmCloseRevisionProcessCtrl); listenTo(confirmCloseRevisionProcessCtrl); } private void doCloseRevisionProcess() { TaskProcess nextStep = gtaManager.nextStep(TaskProcess.correction, gtaNode); assignedTask = gtaManager.updateTask(assignedTask, nextStep, gtaNode); gtaManager.log("Revision", "close revision", assignedTask, getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode); } }