diff --git a/src/main/java/org/olat/course/archiver/ArchiverMainController.java b/src/main/java/org/olat/course/archiver/ArchiverMainController.java index 5c08e6b0e9f6d616b603cb2da085a256ecd02fd0..ff3a653435c6d54953c32f1d3122d5e6e6360f4f 100644 --- a/src/main/java/org/olat/course/archiver/ArchiverMainController.java +++ b/src/main/java/org/olat/course/archiver/ArchiverMainController.java @@ -305,7 +305,7 @@ public class ArchiverMainController extends MainLayoutBasicController { contentCtr = new GenericArchiveController(ureq, getWindowControl(), ores, new IQSURVCourseNode()); main.setContent(contentCtr.getInitialComponent()); } else if (menuCommand.equals(CMD_QTITESTRESULTS)) { - contentCtr = new GenericArchiveController(ureq, getWindowControl(), ores, new IQTESTCourseNode(), new IQSELFCourseNode()); + contentCtr = new TestArchiveController(ureq, getWindowControl(), ores, new IQTESTCourseNode(), new IQSELFCourseNode()); main.setContent(contentCtr.getInitialComponent()); } else if (menuCommand.equals(CMD_SCOREACCOUNTING)) { contentCtr = new ScoreAccountingArchiveController(ureq, getWindowControl(), ores); diff --git a/src/main/java/org/olat/course/archiver/ExportOptionsController.java b/src/main/java/org/olat/course/archiver/ExportOptionsController.java new file mode 100644 index 0000000000000000000000000000000000000000..b9dcb5e1a7048a2de4e404e3da8c841fc5e032fa --- /dev/null +++ b/src/main/java/org/olat/course/archiver/ExportOptionsController.java @@ -0,0 +1,131 @@ +/** + * <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.archiver; + +import org.olat.core.gui.UserRequest; +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.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.Util; +import org.olat.core.util.prefs.Preferences; +import org.olat.course.nodes.CourseNode; +import org.olat.ims.qti.export.OptionsChooseForm; +import org.olat.ims.qti.export.QTIExportItemFormatConfig; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * user interface to determine export config + * + * Initial Date: 11.04.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class ExportOptionsController extends FormBasicController { + + private static final String ITEMCOLS = "itemcols"; + private static final String POSCOL = "poscol"; + private static final String POINTCOL = "pointcol"; + private static final String TIMECOLS = "timecols"; + + private MultipleSelectionElement downloadOptionsEl; + + private String[] optionKeys, optionVals; + + @Autowired + private FormatConfigHelper configHelper; + + + public ExportOptionsController(UserRequest ureq, WindowControl wControl, CourseNode courseNode) { + super(ureq, wControl); + Translator fallback = Util.createPackageTranslator(OptionsChooseForm.class, getLocale()); + setTranslator(Util.createPackageTranslator(getTranslator(), fallback, getLocale())); + + optionKeys = new String[]{ITEMCOLS, POSCOL, POINTCOL, TIMECOLS}; + optionVals = new String[] { + translate("form.itemcols"), + translate("form.poscol"), + translate("form.pointcol"), + translate("form.timecols") + }; + + initForm(ureq); + } + + @Override + protected void doDispose() { + + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + downloadOptionsEl = uifactory.addCheckboxesVertical("setting", "form.title", formLayout, optionKeys, optionVals, 1); + QTIExportItemFormatConfig c = configHelper.doLoadQTIExportFormatConfig(ureq); + downloadOptionsEl.select(ITEMCOLS, c.hasResponseCols()); + downloadOptionsEl.select(POSCOL, c.hasPositionsOfResponsesCol()); + downloadOptionsEl.select(POINTCOL, c.hasPointCol()); + downloadOptionsEl.select(TIMECOLS, c.hasTimeCols()); + + FormItemContainer buttonContainer = FormLayoutContainer.createButtonLayout("buttonLayout", getTranslator()); + formLayout.add(buttonContainer); + uifactory.addFormSubmitButton("save", buttonContainer); + uifactory.addFormCancelButton("cancel", buttonContainer, ureq, getWindowControl()); + } + + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + allOk &= downloadOptionsEl.isAtLeastSelected(1); + if(!allOk) { + downloadOptionsEl.setErrorKey("nodechoose.config.error", null); + } + return allOk &= super.validateFormLogic(ureq); + } + + + @Override + protected void formOK(UserRequest ureq) { + doUpdateMemberListConfig(ureq); + fireEvent(ureq, Event.DONE_EVENT); + } + + private void doUpdateMemberListConfig(UserRequest ureq) { + // save new config in GUI prefs + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + if (guiPrefs != null) { + boolean itemcols = downloadOptionsEl.isSelected(0); + boolean poscol = downloadOptionsEl.isSelected(1); + boolean pointcol = downloadOptionsEl.isSelected(2); + boolean timecols = downloadOptionsEl.isSelected(3); + configHelper.updateQTIExportFormatConfig(ureq, itemcols, poscol, pointcol, timecols); + } + } + +} diff --git a/src/main/java/org/olat/course/archiver/FormatConfigHelper.java b/src/main/java/org/olat/course/archiver/FormatConfigHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..864b676600169d8359b178a0858961217901fc25 --- /dev/null +++ b/src/main/java/org/olat/course/archiver/FormatConfigHelper.java @@ -0,0 +1,99 @@ +/** + * <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.archiver; + +import org.olat.core.gui.UserRequest; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.prefs.Preferences; +import org.olat.core.util.xml.XStreamHelper; +import org.olat.course.nodes.ArchiveOptions; +import org.olat.ims.qti.export.QTIExportItemFormatConfig; +import org.springframework.stereotype.Service; + +import com.thoughtworks.xstream.XStream; + +/** + * this class reads and writes XML serialized config data to personal gui prefs and retrieves them + * + * Initial Date: 21.04.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +@Service +public class FormatConfigHelper { + + private static final String QTI_EXPORT_ITEM_FORMAT_CONFIG = "QTIExportItemFormatConfig"; + private static final OLog log = Tracing.createLoggerFor(FormatConfigHelper.class); + + private static XStream configXstream = XStreamHelper.createXStreamInstance(); + static { + configXstream.alias(QTI_EXPORT_ITEM_FORMAT_CONFIG, QTIExportFormatConfig.class); + } + + + public QTIExportItemFormatConfig doLoadQTIExportFormatConfig(UserRequest ureq) { + QTIExportItemFormatConfig formatConfig = null; + if (ureq != null) { + try { + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + String formatConfigString = (String) guiPrefs.get(ExportOptionsController.class, QTI_EXPORT_ITEM_FORMAT_CONFIG); + Object formatObject = configXstream.fromXML(formatConfigString); + formatConfig = (QTIExportFormatConfig) formatObject; + } catch (Exception e) { + log.error("could not establish object from xml", e); + formatConfig = new QTIExportFormatConfig(true, true, true, true); + } + } + return formatConfig; + } + + public void updateQTIExportFormatConfig(UserRequest ureq, boolean itemcols, boolean poscol, boolean pointcol, boolean timecols) { + // save new config in GUI prefs + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + if (guiPrefs != null) { + QTIExportItemFormatConfig formatConfig = new QTIExportFormatConfig(itemcols, poscol, pointcol, timecols); + try { + String formatConfigString = configXstream.toXML(formatConfig); + guiPrefs.putAndSave(ExportOptionsController.class, QTI_EXPORT_ITEM_FORMAT_CONFIG, formatConfigString); + } catch (Exception e) { + log.error("",e); + } + } + } + + public ArchiveOptions getArchiveOptions(UserRequest ureq) { + ArchiveOptions options = new ArchiveOptions(); + QTIExportItemFormatConfig formatConfig = null; + if (ureq != null) { + try { + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + String formatConfigString = (String) guiPrefs.get(ExportOptionsController.class, QTI_EXPORT_ITEM_FORMAT_CONFIG); + Object formatObject = configXstream.fromXML(formatConfigString); + formatConfig = (QTIExportFormatConfig) formatObject; + } catch (Exception e) { + log.error("could not establish object from xml", e); + formatConfig = new QTIExportFormatConfig(true, true, true, true); + } + } + options.setQtiExportItemFormatConfig(formatConfig); + return options; + } + +} diff --git a/src/main/java/org/olat/course/archiver/GenericArchiveController.java b/src/main/java/org/olat/course/archiver/GenericArchiveController.java index 99f1ed97b4217857824ed2d8b4e30f606ef5a0d9..c67ca79432935238c7824859429c5e0de4466389 100644 --- a/src/main/java/org/olat/course/archiver/GenericArchiveController.java +++ b/src/main/java/org/olat/course/archiver/GenericArchiveController.java @@ -58,17 +58,22 @@ import org.olat.group.BusinessGroup; /** * @author schnider Comment: Archives the User selected wiki's to the personal * folder of this user. + * @author fkiefer */ public class GenericArchiveController extends BasicController { private static final String CMD_SELECT_NODE = "cmd.select.node"; private final Panel main; + private final VelocityContainer nodeChoose; private TableController nodeListCtr; private NodeTableDataModel nodeTableModel; private CloseableModalController cmc; private ChooseGroupController chooseGroupCtrl; + private boolean hideTitle; + private ArchiveOptions options; + private final CourseNode[] nodeTypes; private final OLATResourceable ores; @@ -86,8 +91,11 @@ public class GenericArchiveController extends BasicController { this.nodeTypes = nodeTypes; main = new Panel("main"); - VelocityContainer nodeChoose = createVelocityContainer("nodechoose"); + nodeChoose = createVelocityContainer("nodechoose"); nodeChoose.contextPut("nodeType", nodeTypes[0].getType()); + + options = new ArchiveOptions(); + doNodeChoose(ureq, nodeChoose); putInitialPanel(main); } @@ -241,7 +249,6 @@ public class GenericArchiveController extends BasicController { } private void archiveNode(UserRequest ureq, CourseNode node, BusinessGroup group) { - ArchiveOptions options = new ArchiveOptions(); options.setGroup(group); ArchiveResource aResource = new ArchiveResource(node, ores, options, getLocale()); ureq.getDispatchResult().setResultingMediaResource(aResource); @@ -253,4 +260,17 @@ public class GenericArchiveController extends BasicController { protected void doDispose() { // } + + public boolean isHideTitle() { + return hideTitle; + } + + public void setHideTitle(boolean hideTitle) { + this.hideTitle = hideTitle; + nodeChoose.contextPut("hideTitle", hideTitle); + } + + public void setOptions(ArchiveOptions options) { + this.options = options; + } } \ No newline at end of file diff --git a/src/main/java/org/olat/course/archiver/QTIExportFormatConfig.java b/src/main/java/org/olat/course/archiver/QTIExportFormatConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..833f67ef136db3b697add13d6728518dbc90781e --- /dev/null +++ b/src/main/java/org/olat/course/archiver/QTIExportFormatConfig.java @@ -0,0 +1,76 @@ +/** + * <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.archiver; + +import org.olat.ims.qti.export.QTIExportItemFormatConfig; + +/** + * data container to persist result export config + * + * Initial Date: 18.04.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class QTIExportFormatConfig implements QTIExportItemFormatConfig { + + private boolean responseCols; + private boolean positionsOfResponsesCol; + private boolean pointCol; + private boolean timeCols; + + public QTIExportFormatConfig(boolean resCols, boolean posOfResCol, boolean pointCol, boolean timeCols){ + this.responseCols = resCols; + this.positionsOfResponsesCol = posOfResCol; + this.pointCol = pointCol; + this.timeCols = timeCols; + } + + public boolean hasResponseCols() { + return responseCols; + } + + public boolean hasPositionsOfResponsesCol() { + return positionsOfResponsesCol; + } + + public boolean hasPointCol() { + return pointCol; + } + + public boolean hasTimeCols() { + return timeCols; + } + + public void setPointCol(boolean pointColConfigured) { + this.pointCol = pointColConfigured; + } + + public void setPositionsOfResponsesCol(boolean positionsOfResponsesColConfigured) { + this.positionsOfResponsesCol = positionsOfResponsesColConfigured; + } + + public void setResponseCols(boolean responseColsConfigured) { + this.responseCols = responseColsConfigured; + } + + public void setTimeCols(boolean timeColsConfigured) { + this.timeCols = timeColsConfigured; + } + +} diff --git a/src/main/java/org/olat/course/archiver/TestArchiveController.java b/src/main/java/org/olat/course/archiver/TestArchiveController.java new file mode 100644 index 0000000000000000000000000000000000000000..ae771ffb0128a087ea5607dab4e072d4124f2c7c --- /dev/null +++ b/src/main/java/org/olat/course/archiver/TestArchiveController.java @@ -0,0 +1,120 @@ +/** + * <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.archiver; + +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.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.closablewrapper.CloseableModalController; +import org.olat.core.id.OLATResourceable; +import org.olat.course.nodes.CourseNode; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * wrapper for GenericArchiveController to handle individual export configuration + * + * Initial Date: 19.04.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class TestArchiveController extends BasicController { + + private Link downloadOptionsEl; + + private GenericArchiveController genericArchiveController; + private CloseableModalController cmc; + private ExportOptionsController exportOptionsCtrl; + + private final CourseNode[] nodeTypes; + + @Autowired + private FormatConfigHelper configHelper; + + protected TestArchiveController(UserRequest ureq, WindowControl wControl, OLATResourceable ores, CourseNode... nodeTypes) { + super(ureq, wControl); + + this.nodeTypes = nodeTypes; + + VelocityContainer nodeChoose = createVelocityContainer("testarchive"); + + nodeChoose.contextPut("nodeType", nodeTypes[0].getType()); + + downloadOptionsEl = LinkFactory.createButton("download.options", nodeChoose, this); + downloadOptionsEl.setIconLeftCSS("o_icon o_icon_tools"); + + genericArchiveController = new GenericArchiveController(ureq, wControl, ores, nodeTypes); + genericArchiveController.setHideTitle(true); + genericArchiveController.setOptions(configHelper.getArchiveOptions(ureq)); + listenTo(genericArchiveController); + + nodeChoose.put("genericArchiveController", genericArchiveController.getInitialComponent()); + + putInitialPanel(nodeChoose); + + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + if (source == downloadOptionsEl) { + doOpenExportOptios(ureq); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == exportOptionsCtrl) { + if (event == Event.DONE_EVENT) { + genericArchiveController.setOptions(configHelper.getArchiveOptions(ureq)); + } + cmc.deactivate(); + cleanUpPopups(); + } + } + + /** + * Aggressive clean up all popup controllers + */ + protected void cleanUpPopups() { + removeAsListenerAndDispose(exportOptionsCtrl); + removeAsListenerAndDispose(cmc); + exportOptionsCtrl = null; + cmc = null; + } + + @Override + protected void doDispose() { + // nothing to dispose + } + + private void doOpenExportOptios(UserRequest ureq) { + exportOptionsCtrl = new ExportOptionsController(ureq, getWindowControl(), nodeTypes[0]); + listenTo(exportOptionsCtrl); + cmc = new CloseableModalController(getWindowControl(), translate("close"), exportOptionsCtrl.getInitialComponent(), + true, translate("download.options")); + cmc.activate(); + listenTo(cmc); + } + +} diff --git a/src/main/java/org/olat/course/archiver/_content/nodechoose.html b/src/main/java/org/olat/course/archiver/_content/nodechoose.html index aba832f48469103e53d0a5530e1b2202df25c610..bd41fea6970d1ca66007262bc656c62c44874c17 100644 --- a/src/main/java/org/olat/course/archiver/_content/nodechoose.html +++ b/src/main/java/org/olat/course/archiver/_content/nodechoose.html @@ -1,6 +1,10 @@ -<h4>$r.translate("$nodeType")</h4> +#if (!$hideTitle) + <h4>$r.translate("$nodeType")</h4> +#end #if($hasNodes) + #if (!$hideTitle) <p>$r.translate("nodechoose.intro.$nodeType")</p> + #end <p>$r.render("nodeTable")</p> #else <p>$r.translate("overview.nonodes.$nodeType")</p> diff --git a/src/main/java/org/olat/course/archiver/_content/testarchive.html b/src/main/java/org/olat/course/archiver/_content/testarchive.html new file mode 100644 index 0000000000000000000000000000000000000000..1defb9e79d5989404ffac75cb06f0453883174d2 --- /dev/null +++ b/src/main/java/org/olat/course/archiver/_content/testarchive.html @@ -0,0 +1,10 @@ +<div class="o_header_with_buttons"> + <h4>$r.translate("$nodeType")</h4> + #if ($r.available("download.options")) + <div class="pull-right"> + $r.render("download.options") + </div> + #end +</div> +<p>$r.translate("nodechoose.intro.$nodeType") $r.translate("nodechoose.intro.config")</p> +<p>$r.render("genericArchiveController")</p> diff --git a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties index 9127a15bd6bb079e329895c9b13732d1c6181d95..17e89f5a48b35d1c4215e87f540c520a4ca78446 100644 --- a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_de.properties @@ -33,6 +33,7 @@ course.res.feedback=Die Datei <b>{0}</b> mit den Resultaten liegt in Ihrem pers\ course.res.intro=Klicken Sie den untenstehenden 'Start'-Knopf um Endresultate von Tests, Bewertungen und Aufgaben zu archivieren. course.res.title=Kursresultate dialog=Dateidiskussionen +download.options=Download Optionen konfigurieren iqtest=Testergebnisse iqsurv=Ergebnisse des Fragebogens pf=Teilnehmer Ordner @@ -77,6 +78,8 @@ menu.scorm=SCORM Resultate menu.scorm.alt=SCORM Resultate menu.wikis=Wikis menu.wikis.alt=Wikis archivieren +nodechoose.intro.config=Das Archiv enth\u00E4lt: alle Benutzer, alle Fragen, alle Items. \u00E4ndern Sie die Download-Konfiguration mit der Schaltfl\u00E4che oben. +nodechoose.config.error=Bitte w\u00E4hlen Sie mindestens eine Konfiguration. nodechoose.intro.pf=W\u00E4hlen Sie einen Kursbaustein aus, um dessen Ordnerinhalte zu archivieren. nodechoose.intro.cl=W\u00E4hlen Sie im folgenden Dialog den Checklisten Baustein aus, der ausgewertet werden soll nodechoose.intro.iqtest=W\u00E4hlen Sie einen Kursbaustein aus, um dessen Testergebnisse zu archivieren. @@ -112,3 +115,4 @@ table.action.select=Ausw\u00E4hlen table.header.node=Kursbaustein tool.name=Datenarchivierung wiki=Wikis +form.title=Optionen einstellen: \ No newline at end of file diff --git a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties index ca83d6c4c6d34756131a89dd6c215ad638bad0e2..25132a299da4afa0f8a87312afb4ec0c2b139ab9 100644 --- a/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/archiver/_i18n/LocalStrings_en.properties @@ -34,6 +34,7 @@ course.res.intro=Click the 'Start' button below in order to archive final result course.res.title=Course results pf=Participant Folder dialog=File dialogs +download.options=Configure download options iqtest=Test and self-test results iqsurv=Questionnaire results fo=Forums @@ -77,6 +78,8 @@ menu.scorm=SCORM results menu.scorm.alt=SCORM results menu.wikis=Wikis menu.wikis.alt=Archive Wikis +nodechoose.intro.config=The archive will contain: all users, all questions, all items. Change download configuration using button above. +nodechoose.config.error=Please choose at least one configuration. nodechoose.intro.pf=Choose a course element to archive the content of the participant folder. nodechoose.intro.cl=Please select the checklist element you wish to archive. nodechoose.intro.iqtest=Select a course element to archive its test results. @@ -112,3 +115,4 @@ table.action.select=Select table.header.node=Course element tool.name=Archive tool wiki=Wikis +form.title=Setup options: \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/ArchiveOptions.java b/src/main/java/org/olat/course/nodes/ArchiveOptions.java index a380dbdf94b2f7056e2dffcd50f308414e96e20f..ee0a3a9bbc6b2dca90c9a6ff4b1b6ed0739eba2c 100644 --- a/src/main/java/org/olat/course/nodes/ArchiveOptions.java +++ b/src/main/java/org/olat/course/nodes/ArchiveOptions.java @@ -23,18 +23,21 @@ import java.util.List; import org.olat.core.id.Identity; import org.olat.group.BusinessGroup; +import org.olat.ims.qti.export.QTIExportItemFormatConfig; /** * * Initial date: 20.12.2013<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * + * @author fkiefer */ public class ArchiveOptions { private BusinessGroup group; private List<Identity> identities; + private QTIExportItemFormatConfig qtiExportItemFormatConfig; + public BusinessGroup getGroup() { return group; } @@ -50,4 +53,14 @@ public class ArchiveOptions { public void setIdentities(List<Identity> identities) { this.identities = identities; } + + public QTIExportItemFormatConfig getQtiExportItemFormatConfig() { + return qtiExportItemFormatConfig; + } + + public void setQtiExportItemFormatConfig(QTIExportItemFormatConfig qtiExportItemFormatConfig) { + this.qtiExportItemFormatConfig = qtiExportItemFormatConfig; + } + + } diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java index 626cda13688f8d86b8b178a2ee29d3bd503e375d..03f21f29f3d1b1ff8a084a6609522d9d116f533c 100644 --- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java @@ -28,8 +28,10 @@ package org.olat.course.nodes; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.zip.ZipOutputStream; import org.olat.admin.user.imp.TransientIdentity; @@ -79,9 +81,15 @@ import org.olat.fileresource.types.ImsQTI21Resource; import org.olat.ims.qti.QTI12ResultDetailsController; import org.olat.ims.qti.QTIResultManager; import org.olat.ims.qti.QTIResultSet; +import org.olat.ims.qti.export.QTIExportEssayItemFormatConfig; +import org.olat.ims.qti.export.QTIExportFIBItemFormatConfig; import org.olat.ims.qti.export.QTIExportFormatter; import org.olat.ims.qti.export.QTIExportFormatterCSVType1; +import org.olat.ims.qti.export.QTIExportItemFormatConfig; +import org.olat.ims.qti.export.QTIExportKPRIMItemFormatConfig; +import org.olat.ims.qti.export.QTIExportMCQItemFormatConfig; import org.olat.ims.qti.export.QTIExportManager; +import org.olat.ims.qti.export.QTIExportSCQItemFormatConfig; import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.ims.qti.process.FilePersister; @@ -676,6 +684,15 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements Pe String shortTitle = getShortTitle(); QTIExportManager qem = QTIExportManager.getInstance(); QTIExportFormatter qef = new QTIExportFormatterCSVType1(locale, "\t", "\"", "\r\n", false); + if (options.getQtiExportItemFormatConfig() != null) { + Map<Class<?>, QTIExportItemFormatConfig> itemConfigs = new HashMap<>(); + Class<?>[] itemTypes = new Class<?>[] {QTIExportSCQItemFormatConfig.class, QTIExportMCQItemFormatConfig.class, + QTIExportKPRIMItemFormatConfig.class, QTIExportFIBItemFormatConfig.class, QTIExportEssayItemFormatConfig.class}; + for (Class<?> itemClass : itemTypes) { + itemConfigs.put(itemClass, options.getQtiExportItemFormatConfig()); + } + qef.setMapWithExportItemConfigs(itemConfigs); + } return qem.selectAndExportResults(qef, courseResourceableId, shortTitle, getIdent(), re, exportStream, locale, ".xls"); } } catch (IOException e) { diff --git a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java index f6e21f8fccf60c464ce438a4cb3a581f226e700a..e04f420329a200beda118a8ed7123c5129d8dcdc 100644 --- a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java +++ b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java @@ -51,10 +51,12 @@ import org.olat.core.util.openxml.OpenXMLWorksheet.Row; import org.olat.core.util.openxml.workbookstyle.CellStyle; import org.olat.course.CourseFactory; import org.olat.course.ICourse; +import org.olat.course.archiver.QTIExportFormatConfig; import org.olat.course.nodes.CourseNode; import org.olat.fileresource.FileResourceManager; import org.olat.ims.qti.export.QTIArchiver; import org.olat.ims.qti.export.QTIExportFormatter; +import org.olat.ims.qti.export.QTIExportItemFormatConfig; import org.olat.ims.qti.export.helper.IdentityAnonymizerCallback; import org.olat.ims.qti21.AssessmentItemSession; import org.olat.ims.qti21.AssessmentResponse; @@ -134,6 +136,7 @@ public class QTI21ArchiveFormat { private IdentityAnonymizerCallback anonymizerCallback; private final QTI21StatisticSearchParams searchParams; + private QTIExportItemFormatConfig exportConfig; private List<ItemInfos> itemInfos; private final Map<String, InteractionArchive> interactionArchiveMap = new HashMap<>(); @@ -144,6 +147,10 @@ public class QTI21ArchiveFormat { public QTI21ArchiveFormat(Locale locale, QTI21StatisticSearchParams searchParams) { this.searchParams = searchParams; + this.exportConfig = searchParams.getArchiveOptions().getQtiExportItemFormatConfig(); + if (exportConfig == null) { + exportConfig = new QTIExportFormatConfig(true, true, true, true); + } userManager = CoreSpringFactory.getImpl(UserManager.class); qtiService = CoreSpringFactory.getImpl(QTI21ServiceImpl.class); @@ -295,14 +302,25 @@ public class QTI21ArchiveFormat { List<ItemInfos> infos = getItemInfos(); for(int i=0; i<infos.size(); i++) { + int delta = col; ItemInfos item = infos.get(i); - List<Interaction> interactions = item.getInteractions(); - for(int j=0; j<interactions.size(); j++) { - Interaction interaction = interactions.get(j); - col = interactionArchiveMap.get(interaction.getQtiClassName()) - .writeHeader1(item.getAssessmentItem(), interaction, i, j, header1Row, col, workbook); + if (exportConfig.hasResponseCols() || exportConfig.hasPointCol() || exportConfig.hasTimeCols()) { + List<Interaction> interactions = item.getInteractions(); + for(int j=0; j<interactions.size(); j++) { + Interaction interaction = interactions.get(j); + col = interactionArchiveMap.get(interaction.getQtiClassName()) + .writeHeader1(item.getAssessmentItem(), interaction, i, j, header1Row, col, workbook); + } + } + if (!exportConfig.hasResponseCols()) { + col -= col - delta; + } + if (exportConfig.hasPointCol()) { + col++; } - col += 3;//score, start, duration + if (exportConfig.hasTimeCols()) { + col += 2; + } } } @@ -337,16 +355,21 @@ public class QTI21ArchiveFormat { List<ItemInfos> infos = getItemInfos(); for(int i=0; i<infos.size(); i++) { ItemInfos info = infos.get(i); - List<Interaction> interactions = info.getInteractions(); - for(int j=0; j<interactions.size(); j++) { - Interaction interaction = interactions.get(j); - col = interactionArchiveMap.get(interaction.getQtiClassName()) - .writeHeader2(info.getAssessmentItem(), interaction, i, j, header2Row, col, workbook); + if (exportConfig.hasResponseCols()) { + List<Interaction> interactions = info.getInteractions(); + for(int j=0; j<interactions.size(); j++) { + Interaction interaction = interactions.get(j); + col = interactionArchiveMap.get(interaction.getQtiClassName()) + .writeHeader2(info.getAssessmentItem(), interaction, i, j, header2Row, col, workbook); + } + } + if (exportConfig.hasPointCol()) { + header2Row.addCell(col++, translator.translate("item.score"), headerStyle); + } + if (exportConfig.hasTimeCols()) { + header2Row.addCell(col++, translator.translate("item.start"), headerStyle); + header2Row.addCell(col++, translator.translate("item.duration"), headerStyle); } - - header2Row.addCell(col++, translator.translate("item.score"), headerStyle); - header2Row.addCell(col++, translator.translate("item.start"), headerStyle); - header2Row.addCell(col++, translator.translate("item.duration"), headerStyle); } } @@ -437,22 +460,33 @@ public class QTI21ArchiveFormat { String itemRefIdentifier = itemRef.getIdentifier().toString(); AssessmentItemSession itemSession = responses.getItemSession(itemRefIdentifier); - List<Interaction> interactions = info.getInteractions(); - for(int j=0; j<interactions.size(); j++) { - Interaction interaction = interactions.get(j); - AssessmentResponse response = responses - .getResponse(itemRefIdentifier, interaction.getResponseIdentifier()); - col = interactionArchiveMap.get(interaction.getQtiClassName()) - .writeInteractionData(info.getAssessmentItem(), response, interaction, j, dataRow, col, workbook); + if (exportConfig.hasResponseCols()) { + List<Interaction> interactions = info.getInteractions(); + for(int j=0; j<interactions.size(); j++) { + Interaction interaction = interactions.get(j); + AssessmentResponse response = responses + .getResponse(itemRefIdentifier, interaction.getResponseIdentifier()); + col = interactionArchiveMap.get(interaction.getQtiClassName()) + .writeInteractionData(info.getAssessmentItem(), response, interaction, j, dataRow, col, workbook); + } } //score, start, duration - if(itemSession == null) { - col += 3; + if (itemSession == null) { + if (exportConfig.hasPointCol()) { + col++; + } + if (exportConfig.hasTimeCols()) { + col += 2; + } } else { - dataRow.addCell(col++, itemSession.getScore(), null); - dataRow.addCell(col++, itemSession.getCreationDate(), workbook.getStyles().getTimeStyle()); - dataRow.addCell(col++, toDurationInMilliseconds(itemSession.getDuration()), null); + if (exportConfig.hasPointCol()) { + dataRow.addCell(col++, itemSession.getScore(), null); + } + if (exportConfig.hasTimeCols()) { + dataRow.addCell(col++, itemSession.getCreationDate(), workbook.getStyles().getTimeStyle()); + dataRow.addCell(col++, toDurationInMilliseconds(itemSession.getDuration()), null); + } } } } diff --git a/src/main/java/org/olat/ims/qti21/model/QTI21StatisticSearchParams.java b/src/main/java/org/olat/ims/qti21/model/QTI21StatisticSearchParams.java index 248832aef4fd512dd3e85371112c3443862c7685..488d1da43b68e23158964106e0edc9c5a43cc71f 100644 --- a/src/main/java/org/olat/ims/qti21/model/QTI21StatisticSearchParams.java +++ b/src/main/java/org/olat/ims/qti21/model/QTI21StatisticSearchParams.java @@ -42,6 +42,8 @@ public class QTI21StatisticSearchParams { private List<Group> limitToGroups; private List<Identity> limitToIdentities; + private ArchiveOptions archiveOptions; + private boolean viewAnonymUsers; private boolean viewAllUsers; private boolean viewNonMembers; @@ -50,6 +52,7 @@ public class QTI21StatisticSearchParams { this.testEntry = testEntry; this.courseEntry = courseEntry; this.nodeIdent = nodeIdent; + this.archiveOptions = options; if(options == null) { viewAnonymUsers = true; @@ -131,4 +134,14 @@ public class QTI21StatisticSearchParams { public void setViewAnonymUsers(boolean viewAnonymUsers) { this.viewAnonymUsers = viewAnonymUsers; } + + public ArchiveOptions getArchiveOptions() { + return archiveOptions; + } + + public void setArchiveOptions(ArchiveOptions archiveOptions) { + this.archiveOptions = archiveOptions; + } + + } diff --git a/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java b/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java index bb629458702edf5ee923e973ca81a15f2a64f90d..084413b898367a310592486d9939f9cc96985d69 100644 --- a/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java +++ b/src/main/java/org/olat/modules/forms/ui/EvaluationFormController.java @@ -493,7 +493,9 @@ public class EvaluationFormController extends FormBasicController implements Val dbInstance.commit(); loadResponses(); updateElements(); - saveAsDoneButton.setVisible(false); + if (saveAsDoneButton != null) { + saveAsDoneButton.setVisible(false); + } dbInstance.commit(); fireEvent(ureq, Event.DONE_EVENT); }