diff --git a/src/main/java/org/olat/course/nodes/iq/ConfirmChangeResourceController.java b/src/main/java/org/olat/course/nodes/iq/ConfirmChangeResourceController.java new file mode 100644 index 0000000000000000000000000000000000000000..a6b971a0430cffad2db7bf491a57eec1e7702228 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/ConfirmChangeResourceController.java @@ -0,0 +1,147 @@ +/** + * <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.iq; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Date; +import java.util.zip.ZipOutputStream; + +import org.olat.core.gui.UserRequest; +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.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.link.Link; +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.media.FileMediaResource; +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.nodes.QTICourseNode; +import org.olat.repository.RepositoryEntry; + +/** + * This controller will make an archive of the current test entry results + * and propose some informations. + * + * + * Initial date: 24 mai 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ConfirmChangeResourceController extends FormBasicController { + + private FormLink downloadButton; + + private final ICourse course; + private final File downloadArchiveFile; + private final QTICourseNode courseNode; + private final RepositoryEntry newTestEntry; + private final RepositoryEntry currentTestEntry; + private final int numOfAssessedIdentities; + + public ConfirmChangeResourceController(UserRequest ureq, WindowControl wControl, ICourse course, QTICourseNode courseNode, + RepositoryEntry newTestEntry, RepositoryEntry currentTestEntry, int numOfAssessedIdentities) { + super(ureq, wControl, "confirm_change"); + this.course = course; + this.courseNode = courseNode; + this.newTestEntry = newTestEntry; + this.currentTestEntry = currentTestEntry; + this.numOfAssessedIdentities = numOfAssessedIdentities; + downloadArchiveFile = prepareArchive(ureq); + initForm(ureq); + } + + public RepositoryEntry getNewTestEntry() { + return newTestEntry; + } + + public RepositoryEntry getCurrentTestEntry() { + return currentTestEntry; + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("infos1", translate("confirmation.change.warning.1", new String[]{ Integer.toString(numOfAssessedIdentities) })); + layoutCont.contextPut("infos2", translate("confirmation.change.warning.2")); + String[] archiveArgs = new String[] { downloadArchiveFile.getParentFile().getName(), downloadArchiveFile.getName() }; + layoutCont.contextPut("infos3", translate("confirmation.change.warning.3", archiveArgs)); + } + + downloadButton = uifactory.addFormLink("download", downloadArchiveFile.getName(), null, formLayout, Link.LINK | Link.NONTRANSLATED); + downloadButton.setIconLeftCSS("o_icon o_icon_downloads"); + uifactory.addFormCancelButton("cancel", formLayout, ureq, getWindowControl()); + uifactory.addFormSubmitButton("ok", formLayout); + } + + private File prepareArchive(UserRequest ureq) { + File exportDir = CourseFactory.getOrCreateDataExportDirectory(ureq.getIdentity(), course.getCourseTitle()); + String label = StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + + "_" + Formatter.formatDatetimeWithMinutes(new Date()) + ".zip"; + String urlEncodedLabel = StringHelper.urlEncodeUTF8(label); + File archiveFile = new File(exportDir, urlEncodedLabel); + + try(OutputStream out= new FileOutputStream(archiveFile); + ZipOutputStream exportStream = new ZipOutputStream(out)) { + courseNode.archiveNodeData(getLocale(), course, null, exportStream, "UTF8"); + return archiveFile; + } catch(Exception e) { + logError("", e); + return null; + } + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + fireEvent(ureq, Event.DONE_EVENT); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(downloadButton == source) { + doDownload(ureq); + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + private void doDownload(UserRequest ureq) { + ureq.getDispatchResult() + .setResultingMediaResource(new FileMediaResource(downloadArchiveFile, true)); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java index 9547cd6c40e2ef38752fbed725193dfe0e70ecc0..dbb3d2aa3b00600059152348589abb26b678d0a2 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java @@ -21,7 +21,9 @@ package org.olat.course.nodes.iq; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.Constants; @@ -52,6 +54,7 @@ import org.olat.course.nodes.AbstractAccessableCourseNode; import org.olat.course.nodes.CourseNodeFactory; import org.olat.course.nodes.IQSELFCourseNode; import org.olat.course.nodes.IQSURVCourseNode; +import org.olat.course.nodes.QTICourseNode; import org.olat.fileresource.types.ImsQTI21Resource; import org.olat.ims.qti.QTIResult; import org.olat.ims.qti.QTIResultManager; @@ -101,6 +104,7 @@ public class IQConfigurationController extends BasicController { private IQEditReplaceWizard replaceWizard; private AssessmentTestDisplayController previewQTI21Ctrl; private ReferencableEntriesSearchController searchController; + private ConfirmChangeResourceController confirmChangeResourceCtrl; private IQEditForm modOnyxConfigForm; private IQ12EditForm mod12ConfigForm; @@ -287,20 +291,19 @@ public class IQConfigurationController extends BasicController { @Override public void event(UserRequest urequest, Controller source, Event event) { - if (source.equals(searchController)) { + if (source == searchController) { if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { // repository search controller done cmc.deactivate(); RepositoryEntry re = searchController.getSelectedEntry(); - try { - boolean needManualCorrection = checkManualCorrectionNeeded(re); - doIQReference(urequest, re, needManualCorrection); - updateEditController(urequest, true); - } catch (Exception e) { - logError("", e); - showError("error.resource.corrupted"); - } + doConfirmChangeTestAndSurvey(urequest, re); + } + } else if(source == confirmChangeResourceCtrl) { + if(event == Event.DONE_EVENT) { + RepositoryEntry newEntry = confirmChangeResourceCtrl.getNewTestEntry(); + doChangeResource(urequest, newEntry); } + cmc.deactivate(); } else if (source == replaceWizard) { if(event == Event.CANCELLED_EVENT) { cmc.deactivate(); @@ -336,6 +339,58 @@ public class IQConfigurationController extends BasicController { } } + /** + * This check if there is some QTI 2.1 results for the current selected test. + * + * @param ureq + * @param newEntry + */ + private void doConfirmChangeTestAndSurvey(UserRequest ureq, RepositoryEntry newEntry) { + try { + RepositoryEntry currentEntry = courseNode.getReferencedRepositoryEntry(); + RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); + + int numOfAssessedIdentities = 0; + if(currentEntry != null) { + List<AssessmentTestSession> assessmentTestSessions = qti21service.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), currentEntry); + Set<Identity> assessedIdentities = new HashSet<>(); + for(AssessmentTestSession assessmentTestSession:assessmentTestSessions) { + if(StringHelper.containsNonWhitespace(assessmentTestSession.getAnonymousIdentifier())) { + numOfAssessedIdentities++; + } else if(assessmentTestSession.getIdentity() != null) { + assessedIdentities.add(assessmentTestSession.getIdentity()); + } + } + numOfAssessedIdentities += assessedIdentities.size(); + } + + if(numOfAssessedIdentities > 0) { + confirmChangeResourceCtrl = new ConfirmChangeResourceController(ureq, getWindowControl(), + course, (QTICourseNode)courseNode, newEntry, currentEntry, numOfAssessedIdentities); + listenTo(confirmChangeResourceCtrl); + cmc = new CloseableModalController(getWindowControl(), translate("close"), confirmChangeResourceCtrl.getInitialComponent()); + listenTo(cmc); + cmc.activate(); + } else { + doChangeResource(ureq, newEntry); + } + } catch (Exception e) { + logError("", e); + showError("error.resource.corrupted"); + } + } + + private void doChangeResource(UserRequest ureq, RepositoryEntry newEntry) { + try { + boolean needManualCorrection = checkManualCorrectionNeeded(newEntry); + doIQReference(ureq, newEntry, needManualCorrection); + updateEditController(ureq, true); + } catch (Exception e) { + logError("", e); + showError("error.resource.corrupted"); + } + } + private void doPreview(UserRequest ureq) { removeAsListenerAndDispose(previewLayoutCtr); diff --git a/src/main/java/org/olat/course/nodes/iq/_content/confirm_change.html b/src/main/java/org/olat/course/nodes/iq/_content/confirm_change.html new file mode 100644 index 0000000000000000000000000000000000000000..5c11219038bd3d7d5b8b4b34bb6d16da8a295207 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/_content/confirm_change.html @@ -0,0 +1,7 @@ +<div class="o_warning"> + <strong>$r.infos1</strong><br>$infos2<br>$r.render("download")<br>$infos3 +</div> +<div class="o_button_group"> + $r.render("cancel") + $r.render("ok") +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties index b754761f16b11ba62f82705aba1b42cf2eeea462..501c5c84118b651aba303cea9417cd0245d7c2c5 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_de.properties @@ -22,6 +22,9 @@ command.preview=Vorschau anzeigen command.showResults=Resultate anzeigen comment.yourcomment=Kommentar vom Betreuer condition.accessibility.title=Zugang +confirmation.change.warning.1=Dieser Test/Fragebogen wurde bereits von {0} Teilnehmer(n) gestartet und zum Teil auch beendet\! +confirmation.change.warning.2=Die Ergebnisse der beendeten Tests/Frageb\u00F6gen werden archiviert. Die archivierten Ergebnisse k\u00F6nnen Sie hier herunterladen\: +confirmation.change.warning.3=Eine Kopie dieser Ergebnis\u00FCbersicht finden Sie in Ihrem pers\u00F6nlichen Ordner unter\:<br><i>private/archive/{0}/{1}</i>. correction.auto=Auto correction.manual=Manuell correction.mode=Korrektur diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties index b5f1c3247b9938280316bf4a87c656b1d6f38692..4c72024a4d468a8ffa27373e8bd94f23038cdab6 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_en.properties @@ -22,6 +22,9 @@ command.preview=Show preview command.showResults=Show results comment.yourcomment=Comment from your tutor condition.accessibility.title=Access +confirmation.change.warning.1=This test/questionnaire has already been launched by {0} participant(s) and finished in part\! +confirmation.change.warning.2=All results of tests/questionnaires already finished will be archived. The archived data can be downloaded here\: +confirmation.change.warning.3=A copy of this overview can be found in your personal folder:<br><i>private/archive/{0}/{1}</i>. correction.auto=Auto correction.manual=Manual correction.mode=Correction diff --git a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties index 395b0d38187a21e6812b43e613fb4541728dde02..9c89e38465df03870fcdd34da206e7c8811919e5 100644 --- a/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/nodes/iq/_i18n/LocalStrings_fr.properties @@ -22,6 +22,9 @@ command.preview=Afficher aper\u00E7u command.showResults=Afficher r\u00E9sultat comment.yourcomment=Commentaire du tuteur condition.accessibility.title=Acc\u00E8s +confirmation.change.warning.1=Ce test/questionnaire a d\u00E9j\u00E0 \u00E9t\u00E9 lanc\u00E9 par {0} participant/s et partiellement termin\u00E9\! +confirmation.change.warning.2=Les r\u00E9sultats archiv\u00E9s peuvent \u00EAtre t\u00E9l\u00E9charg\u00E9s ici\: +confirmation.change.warning.3=Une copie de cette vue d'ensemble des r\u00E9sultats se trouve dans votre dossier personnel sous\:<br><i>private/archive/{0}/{1}</i>. correction.auto=Automatique correction.manual=Manuelle correction.mode=Correction