/** * <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.ims.qti21.ui; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; import java.util.List; import java.util.zip.ZipOutputStream; import org.olat.basesecurity.GroupRoles; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; 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.closablewrapper.CloseableModalController; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.Roles; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.archiver.ScoreAccountingHelper; import org.olat.course.nodes.ArchiveOptions; import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.nodes.QTICourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.group.BusinessGroupService; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.manager.archive.QTI21ArchiveFormat; import org.olat.ims.qti21.model.QTI21StatisticSearchParams; import org.olat.modules.assessment.AssessmentToolOptions; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 08.08.2016<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class QTI21ResetToolController extends BasicController { private final Roles studentRoles = new Roles(false, false, false, false, false, false, false, false); private Link resetButton; private CloseableModalController cmc; private ConfirmResetController confirmResetCtrl; private QTICourseNode courseNode; private CourseEnvironment courseEnv; private RepositoryEntry assessedEntry; private final AssessmentToolOptions asOptions; @Autowired private QTI21Service qtiService; @Autowired private BusinessGroupService businessGroupService; @Autowired private RepositoryService repositoryService; public QTI21ResetToolController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, AssessmentToolOptions asOptions, QTICourseNode courseNode) { super(ureq, wControl); this.courseNode = courseNode; this.courseEnv = courseEnv; this.asOptions = asOptions; initButton(); } public QTI21ResetToolController(UserRequest ureq, WindowControl wControl, RepositoryEntry assessedEntry, AssessmentToolOptions asOptions) { super(ureq, wControl); this.assessedEntry = assessedEntry; this.asOptions = asOptions; initButton(); } private void initButton() { resetButton = LinkFactory.createButton("reset.test.data.title", null, this); resetButton.setIconLeftCSS("o_icon o_icon_delete_item"); resetButton.setTranslator(getTranslator()); putInitialPanel(resetButton); getInitialComponent().setSpanAsDomReplaceable(true); // override to wrap panel as span to not break link layout } @Override protected void doDispose() { // } @Override protected void event(UserRequest ureq, Component source, Event event) { if(resetButton == source) { doConfirmReset(ureq); } } @Override protected void event(UserRequest ureq, Controller source, Event event) { if(confirmResetCtrl == source) { if(event == Event.DONE_EVENT) { doReset(ureq, confirmResetCtrl.getOptions(), confirmResetCtrl.getIdentities()); fireEvent(ureq, Event.DONE_EVENT); } cmc.deactivate(); cleanUp(); } else if (cmc == source) { cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { removeAsListenerAndDispose(confirmResetCtrl); removeAsListenerAndDispose(cmc); confirmResetCtrl = null; cmc = null; } private void doConfirmReset(UserRequest ureq) { if(confirmResetCtrl != null) return; ArchiveOptions options = new ArchiveOptions(); List<Identity> identities = null; if(asOptions.getGroup() == null && asOptions.getIdentities() == null) { if(courseEnv != null) { identities = ScoreAccountingHelper.loadUsers(courseEnv); options.setIdentities(identities); } else { identities = repositoryService.getMembers(assessedEntry, GroupRoles.participant.name()); options.setIdentities(identities); } } else if (asOptions.getIdentities() != null) { identities = asOptions.getIdentities(); options.setIdentities(identities); } else { identities = businessGroupService.getMembers(asOptions.getGroup()); options.setGroup(asOptions.getGroup()); } if(identities == null || identities.isEmpty()) { showWarning("warning.reset.test.data.nobody"); } else { confirmResetCtrl = new ConfirmResetController(ureq, getWindowControl(), options, identities); listenTo(confirmResetCtrl); String title = translate("reset.test.data.title"); cmc = new CloseableModalController(getWindowControl(), null, confirmResetCtrl.getInitialComponent(), true, title, true); listenTo(cmc); cmc.activate(); } } private void doReset(UserRequest ureq, ArchiveOptions options, List<Identity> identities) { if(courseNode instanceof IQTESTCourseNode) { IQTESTCourseNode testCourseNode = (IQTESTCourseNode)courseNode; RepositoryEntry testEntry = courseNode.getReferencedRepositoryEntry(); RepositoryEntry courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); ICourse course = CourseFactory.loadCourse(courseEntry); archiveData(course, options); qtiService.deleteAssessmentTestSession(identities, testEntry, courseEntry, courseNode.getIdent()); for(Identity identity:identities) { ScoreEvaluation scoreEval = new ScoreEvaluation(null, null); IdentityEnvironment ienv = new IdentityEnvironment(identity, studentRoles); UserCourseEnvironment uce = new UserCourseEnvironmentImpl(ienv, courseEnv); testCourseNode.updateUserScoreEvaluation(scoreEval, uce, getIdentity(), false); } } else if(assessedEntry != null) { archiveData(assessedEntry); qtiService.deleteAssessmentTestSession(identities, assessedEntry, null, null); } fireEvent(ureq, Event.CHANGED_EVENT); } private void archiveData(ICourse course, ArchiveOptions options) { File exportDirectory = CourseFactory.getOrCreateDataExportDirectory(getIdentity(), course.getCourseTitle()); String archiveName = courseNode.getType() + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".zip"; File exportFile = new File(exportDirectory, archiveName); try(FileOutputStream fileStream = new FileOutputStream(exportFile); ZipOutputStream exportStream = new ZipOutputStream(fileStream)) { courseNode.archiveNodeData(getLocale(), course, options, exportStream, "UTF-8"); } catch (IOException e) { logError("", e); } } private void archiveData(RepositoryEntry testEntry) { //backup String archiveName = "qti21test_" + StringHelper.transformDisplayNameToFileSystemName(testEntry.getDisplayname()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + ".zip"; Path exportPath = Paths.get(FolderConfig.getCanonicalRoot(), FolderConfig.getUserHomes(), getIdentity().getName(), "private", "archive", StringHelper.transformDisplayNameToFileSystemName(testEntry.getDisplayname()), archiveName); File exportFile = exportPath.toFile(); exportFile.getParentFile().mkdirs(); try(FileOutputStream fileStream = new FileOutputStream(exportFile); ZipOutputStream exportStream = new ZipOutputStream(fileStream)) { //author can do this, also they can archive all users and anonyme users QTI21StatisticSearchParams searchParams = new QTI21StatisticSearchParams(testEntry, null, null, true, true); new QTI21ArchiveFormat(getLocale(), searchParams).exportResource(exportStream); } catch (IOException e) { logError("", e); } } private class ConfirmResetController extends FormBasicController { private final String[] onKeys = new String[]{ "on" }; private MultipleSelectionElement acknowledgeEl; private final ArchiveOptions options; private final List<Identity> identities; public ConfirmResetController(UserRequest ureq, WindowControl wControl, ArchiveOptions options, List<Identity> identities) { super(ureq, wControl, "confirm_reset_data"); this.options = options; this.identities = identities; initForm(ureq); } public ArchiveOptions getOptions() { return options; } public List<Identity> getIdentities() { return identities; } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; String[] args = new String[]{ Integer.toString(identities.size()) }; String msg = translate("reset.test.data.text", args); layoutCont.contextPut("msg", msg); } FormLayoutContainer confirmCont = FormLayoutContainer.createDefaultFormLayout("confirm", getTranslator()); formLayout.add("confirm", confirmCont); confirmCont.setRootForm(mainForm); String[] onValues = new String[]{ translate("reset.test.data.acknowledge") }; acknowledgeEl = uifactory.addCheckboxesHorizontal("acknowledge", "confirmation", confirmCont, onKeys, onValues); FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); buttonsCont.setRootForm(mainForm); confirmCont.add(buttonsCont); uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); uifactory.addFormSubmitButton("reset.data", buttonsCont); } @Override protected void doDispose() { // } @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; acknowledgeEl.clearError(); if(!acknowledgeEl.isAtLeastSelected(1)) { acknowledgeEl.setErrorKey("form.legende.mandatory", null); allOk &= false; } return allOk & super.validateFormLogic(ureq); } @Override protected void formOK(UserRequest ureq) { fireEvent(ureq, Event.DONE_EVENT); } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } } }