diff --git a/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditForm.java b/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditForm.java index c365fa4f6d2023863ada43ad097943154d30f7ce..ed3aac5a5df2309e842ac454820a407def02d19c 100644 --- a/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditForm.java +++ b/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditForm.java @@ -174,6 +174,22 @@ public class IQEditForm extends FormBasicController { @Override protected void formOK(UserRequest ureq) { + modConfig.set(IQEditController.CONFIG_KEY_TEMPLATE, getTemplate()); + if (!isSurvey) { + modConfig.set(IQEditController.CONFIG_KEY_ATTEMPTS, getAttempts()); + modConfig.set(IQEditController.CONFIG_KEY_CUTVALUE, getCutValue()); + } + modConfig.set(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS, new Boolean(isShowResultsDateDependent())); + modConfig.set(IQEditController.CONFIG_KEY_RESULTS_START_DATE, getShowResultsStartDate()); + modConfig.set(IQEditController.CONFIG_KEY_RESULTS_END_DATE, getShowResultsEndDate()); + modConfig.set(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE, isShowResultsOnHomePage()); + //<OLATCE-982> + modConfig.set(IQEditController.CONFIG_KEY_ALLOW_SHOW_SOLUTION, allowShowSolution()); + //</OLATCE-982> + //<OLATCE-2009> + modConfig.set(IQEditController.CONFIG_KEY_ALLOW_SUSPENSION_ALLOWED, allowSuspension()); + //</OLATCE-2009> + fireEvent(ureq, Event.DONE_EVENT); } diff --git a/src/main/java/org/olat/course/CourseXStreamAliases.java b/src/main/java/org/olat/course/CourseXStreamAliases.java index 125f657f226de150fb540cde0c4fa2887c1a1f63..f5d712bc4dd6ae165ca05028b5d02fdfca04583a 100644 --- a/src/main/java/org/olat/course/CourseXStreamAliases.java +++ b/src/main/java/org/olat/course/CourseXStreamAliases.java @@ -97,6 +97,7 @@ public class CourseXStreamAliases { //start read configuration (mostly for compatibility with OLAT 7.3 and greater) readXstream.alias("CourseConfig", CourseConfig.class); readXstream.alias("com.frentix.olat.course.nodes.ViteroCourseNode", ViteroCourseNode.class); + readXstream.alias("org.olat.course.nodes.QTI21AssessmentCourseNode", IQTESTCourseNode.class); readXstream.alias("CourseEditorTreeModel", CourseEditorTreeModel.class); readXstream.alias("CourseEditorTreeNode", CourseEditorTreeNode.class); readXstream.alias("Structure", Structure.class); diff --git a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java index 3095ee1d26e66ec1b91b707f1aecbc7b5781b32a..8715ce585dc1c2fb685509c365c6885c9350dd8e 100644 --- a/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQSELFCourseNode.java @@ -47,9 +47,9 @@ import org.olat.course.assessment.AssessmentManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; +import org.olat.course.nodes.iq.CourseIQSecurityCallback; import org.olat.course.nodes.iq.IQEditController; import org.olat.course.nodes.iq.IQRunController; -import org.olat.course.nodes.iq.IQUIFactory; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; @@ -63,6 +63,7 @@ import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.modules.ModuleConfiguration; import org.olat.modules.iq.IQManager; +import org.olat.modules.iq.IQSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryImportExport; import org.olat.repository.RepositoryManager; @@ -70,6 +71,7 @@ import org.olat.repository.handlers.RepositoryHandler; import org.olat.repository.handlers.RepositoryHandlerFactory; import de.bps.onyx.plugin.OnyxModule; +import de.bps.onyx.plugin.run.OnyxRunController; /** * Initial Date: Feb 9, 2004 @@ -97,10 +99,8 @@ public class IQSELFCourseNode extends AbstractAccessableCourseNode implements Se */ @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { - - TabbableController childTabCntrllr = IQUIFactory.createIQSelftestEditController(ureq, wControl, stackPanel, course, this, course.getCourseEnvironment().getCourseGroupManager(), euce); + TabbableController childTabCntrllr = new IQEditController(ureq, wControl, stackPanel, course, this, euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); - return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, childTabCntrllr); } @@ -114,9 +114,18 @@ public class IQSELFCourseNode extends AbstractAccessableCourseNode implements Se public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { - Controller runController = IQUIFactory.createIQSelftestRunController(ureq, wControl, userCourseEnv, this); - Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, runController, this, "o_iqself_icon"); + Controller runController; + ModuleConfiguration config = getModuleConfiguration(); + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); + if (onyx) { + runController = new OnyxRunController(userCourseEnv, config, ureq, wControl, this); + } else { + IQSecurityCallback sec = new CourseIQSecurityCallback(this, am, ureq.getIdentity()); + runController = new IQRunController(userCourseEnv, getModuleConfiguration(), sec, ureq, wControl, this); + } + Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, runController, this, "o_iqself_icon"); return new NodeRunConstructionResult(ctrl); } diff --git a/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java b/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java index c920f3623d8faa898efa05774b8f0dbf598a1e86..1c7440b54d59c1be50a25e05d46807ca7dec9d82 100644 --- a/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQSURVCourseNode.java @@ -37,22 +37,26 @@ import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.translator.PackageTranslator; +import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.core.id.Roles; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Util; +import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.resource.OresHelper; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; +import org.olat.course.nodes.iq.CourseIQSecurityCallback; import org.olat.course.nodes.iq.IQEditController; import org.olat.course.nodes.iq.IQRunController; -import org.olat.course.nodes.iq.IQUIFactory; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; @@ -71,12 +75,15 @@ import org.olat.ims.qti.statistics.QTIStatisticSearchParams; import org.olat.ims.qti.statistics.QTIType; import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; import org.olat.modules.ModuleConfiguration; +import org.olat.modules.iq.IQSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryImportExport; import org.olat.repository.RepositoryManager; import org.olat.repository.handlers.RepositoryHandler; import org.olat.repository.handlers.RepositoryHandlerFactory; +import de.bps.onyx.plugin.run.OnyxRunController; + /** * Initial Date: Feb 9, 2004 * @author Mike Stock Comment: @@ -105,7 +112,7 @@ public class IQSURVCourseNode extends AbstractAccessableCourseNode implements QT */ @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { - TabbableController childTabCntrllr = IQUIFactory.createIQSurveyEditController(ureq, wControl, stackPanel, course, this, course.getCourseEnvironment().getCourseGroupManager(), euce); + TabbableController childTabCntrllr = new IQEditController(ureq, wControl, stackPanel, course, this, euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, childTabCntrllr); } @@ -119,7 +126,39 @@ public class IQSURVCourseNode extends AbstractAccessableCourseNode implements QT @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { - Controller controller = IQUIFactory.createIQSurveyRunController(ureq, wControl, userCourseEnv, this); + + Controller controller; + // Do not allow guests to start questionnaires + Roles roles = ureq.getUserSession().getRoles(); + if (roles.isGuestOnly()) { + Translator trans = Util.createPackageTranslator(IQSURVCourseNode.class, ureq.getLocale()); + String title = trans.translate("guestnoaccess.title"); + String message = trans.translate("guestnoaccess.message"); + controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); + } else { + ModuleConfiguration config = getModuleConfiguration(); + boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); + if (onyx) { + controller = new OnyxRunController(userCourseEnv, config, ureq, wControl, this); + } else { + RepositoryEntry repositoryEntry = getReferencedRepositoryEntry(); + OLATResourceable ores = repositoryEntry.getOlatResource(); + Long resId = ores.getResourceableId(); + SurveyFileResource fr = new SurveyFileResource(); + fr.overrideResourceableId(resId); + if(!CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(fr, null)) { + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + IQSecurityCallback sec = new CourseIQSecurityCallback(this, am, ureq.getIdentity()); + controller = new IQRunController(userCourseEnv, getModuleConfiguration(), sec, ureq, wControl, this); + } else { + Translator trans = Util.createPackageTranslator(IQSURVCourseNode.class, ureq.getLocale()); + String title = trans.translate("editor.lock.title"); + String message = trans.translate("editor.lock.message"); + controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); + } + } + } + Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, controller, this, "o_iqsurv_icon"); return new NodeRunConstructionResult(ctrl); } diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java index 0422a92067085903be62790591e8635b02882087..032f477c6656d48189b602def898055312368a1a 100644 --- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java +++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java @@ -37,15 +37,18 @@ import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.messages.MessageUIFactory; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.core.id.Roles; import org.olat.core.logging.DBRuntimeException; import org.olat.core.logging.KnownIssueException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.Util; +import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.resource.OresHelper; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; @@ -53,9 +56,12 @@ import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; +import org.olat.course.nodes.iq.CourseIQSecurityCallback; import org.olat.course.nodes.iq.IQEditController; +import org.olat.course.nodes.iq.IQPreviewController; import org.olat.course.nodes.iq.IQRunController; -import org.olat.course.nodes.iq.IQUIFactory; +import org.olat.course.nodes.iq.QTI21AssessmentDetailsController; +import org.olat.course.nodes.iq.QTI21AssessmentRunController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; @@ -64,6 +70,7 @@ import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.statistic.StatisticResourceOption; import org.olat.course.statistic.StatisticResourceResult; +import org.olat.fileresource.types.ImsQTI21Resource; import org.olat.ims.qti.QTI12ResultDetailsController; import org.olat.ims.qti.QTIResultManager; import org.olat.ims.qti.QTIResultSet; @@ -77,15 +84,18 @@ import org.olat.ims.qti.statistics.QTIStatisticSearchParams; import org.olat.ims.qti.statistics.QTIType; import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController; import org.olat.modules.ModuleConfiguration; +import org.olat.modules.iq.IQSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryImportExport; import org.olat.repository.RepositoryManager; import org.olat.repository.handlers.RepositoryHandler; import org.olat.repository.handlers.RepositoryHandlerFactory; +import org.olat.resource.OLATResource; import de.bps.ims.qti.QTIResultDetailsController; import de.bps.onyx.plugin.OnyxExportManager; import de.bps.onyx.plugin.OnyxModule; +import de.bps.onyx.plugin.run.OnyxRunController; /** * Initial Date: Feb 9, 2004 @@ -111,22 +121,51 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { updateModuleConfigDefaults(false); - TabbableController childTabCntrllr = IQUIFactory.createIQTestEditController(ureq, wControl, stackPanel, course, this, course.getCourseEnvironment().getCourseGroupManager(), euce); + TabbableController childTabCntrllr = new IQEditController(ureq, wControl, stackPanel, course, this, euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, childTabCntrllr); } - /** - * @see org.olat.course.nodes.CourseNode#createNodeRunConstructionResult(org.olat.core.gui.UserRequest, - * org.olat.core.gui.control.WindowControl, - * org.olat.course.run.userview.UserCourseEnvironment, - * org.olat.course.run.userview.NodeEvaluation) - */ @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { updateModuleConfigDefaults(false); - Controller controller = IQUIFactory.createIQTestRunController(ureq, wControl, userCourseEnv, this); + + Controller controller; + // Do not allow guests to start tests + Roles roles = ureq.getUserSession().getRoles(); + Translator trans = Util.createPackageTranslator(IQTESTCourseNode.class, ureq.getLocale()); + if (roles.isGuestOnly()) { + String title = trans.translate("guestnoaccess.title"); + String message = trans.translate("guestnoaccess.message"); + controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); + } else { + ModuleConfiguration config = getModuleConfiguration(); + boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); + if (onyx) { + controller = new OnyxRunController(userCourseEnv, config, ureq, wControl, this); + } else { + RepositoryEntry repositoryEntry = getReferencedRepositoryEntry(); + OLATResource ores = repositoryEntry.getOlatResource(); + if(ImsQTI21Resource.TYPE_NAME.equals(ores.getResourceableTypeName())) { + //QTI 2.1 + controller = new QTI21AssessmentRunController(ureq, wControl, userCourseEnv, this); + } else { + //QTI 1.2 + TestFileResource fr = new TestFileResource(); + fr.overrideResourceableId(ores.getResourceableId()); + if(!CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(fr, null)) { + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + IQSecurityCallback sec = new CourseIQSecurityCallback(this, am, ureq.getIdentity()); + controller = new IQRunController(userCourseEnv, getModuleConfiguration(), sec, ureq, wControl, this); + } else { + String title = trans.translate("editor.lock.title"); + String message = trans.translate("editor.lock.message"); + controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); + } + } + } + } Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, controller, this, "o_iqtest_icon"); return new NodeRunConstructionResult(ctrl); } @@ -139,7 +178,15 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As */ @Override public Controller createPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne) { - return IQUIFactory.createIQTestPreviewController(ureq, wControl, userCourseEnv, this); + Controller controller; + ModuleConfiguration config = getModuleConfiguration(); + boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); + if (onyx) { + controller = new OnyxRunController(ureq, wControl, this); + } else { + controller = new IQPreviewController(ureq, wControl, userCourseEnv, this); + } + return controller; } @Override @@ -159,9 +206,7 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As OLATResourceable courseOres = OresHelper.createOLATResourceableInstance("CourseModule", courseId); QTIStatisticSearchParams searchParams = new QTIStatisticSearchParams(courseOres.getResourceableId(), getIdent()); searchParams.setLimitToGroups(options.getParticipantsGroups()); - - QTIStatisticResourceResult result = new QTIStatisticResourceResult(courseOres, this, searchParams); - return result; + return new QTIStatisticResourceResult(courseOres, this, searchParams); } @Override @@ -174,7 +219,7 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As if(types.length == 0 || (types.length == 1 && types[0] == null)) return true; for(QTIType type:types) { - if(QTIType.test.equals(type) || QTIType.onyx.equals(type)) { + if(QTIType.test.equals(type) || QTIType.onyx.equals(type) || QTIType.qtiworks.equals(type)) { return true; } } @@ -557,15 +602,22 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As */ @Override public Controller getDetailsEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, UserCourseEnvironment userCourseEnvironment) { + Controller detailsCtrl = null; RepositoryEntry ref = getReferencedRepositoryEntry(); - Long courseResourceableId = userCourseEnvironment.getCourseEnvironment().getCourseResourceableId(); - Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); - boolean onyx = OnyxModule.isOnyxTest(ref.getOlatResource()); - if(onyx) { - return new QTIResultDetailsController(courseResourceableId, getIdent(), assessedIdentity, ref, AssessmentInstance.QMD_ENTRY_TYPE_ASSESS, ureq, wControl); - } else { - return new QTI12ResultDetailsController(ureq, wControl, courseResourceableId, getIdent(), assessedIdentity, ref, AssessmentInstance.QMD_ENTRY_TYPE_ASSESS); + if(ref != null) { + OLATResource resource = ref.getOlatResource(); + Long courseResourceableId = userCourseEnvironment.getCourseEnvironment().getCourseResourceableId(); + Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity(); + + if(ImsQTI21Resource.TYPE_NAME.equals(resource.getResourceableTypeName())) { + detailsCtrl = new QTI21AssessmentDetailsController(ureq, wControl, userCourseEnvironment, this); + } else if(OnyxModule.isOnyxTest(ref.getOlatResource())) { + detailsCtrl = new QTIResultDetailsController(courseResourceableId, getIdent(), assessedIdentity, ref, AssessmentInstance.QMD_ENTRY_TYPE_ASSESS, ureq, wControl); + } else { + detailsCtrl = new QTI12ResultDetailsController(ureq, wControl, courseResourceableId, getIdent(), assessedIdentity, ref, AssessmentInstance.QMD_ENTRY_TYPE_ASSESS); + } } + return detailsCtrl; } /** diff --git a/src/main/java/org/olat/course/nodes/QTI21AssessmentCourseNode.java b/src/main/java/org/olat/course/nodes/QTI21AssessmentCourseNode.java deleted file mode 100644 index ff6881d575a555b2e7ec30cbeece92919a36eb11..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/QTI21AssessmentCourseNode.java +++ /dev/null @@ -1,371 +0,0 @@ -/** - * <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; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.zip.ZipOutputStream; - -import org.olat.core.CoreSpringFactory; -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.BreadcrumbPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.generic.messages.MessageUIFactory; -import org.olat.core.gui.control.generic.tabbable.TabbableController; -import org.olat.core.gui.translator.Translator; -import org.olat.core.id.Identity; -import org.olat.core.id.Roles; -import org.olat.core.logging.OLATRuntimeException; -import org.olat.core.util.StringHelper; -import org.olat.core.util.Util; -import org.olat.course.ICourse; -import org.olat.course.assessment.AssessmentManager; -import org.olat.course.auditing.UserNodeAuditManager; -import org.olat.course.editor.CourseEditorEnv; -import org.olat.course.editor.NodeEditController; -import org.olat.course.editor.StatusDescription; -import org.olat.course.nodes.qti21.QTI21AssessmentDetailsController; -import org.olat.course.nodes.qti21.QTI21AssessmentRunController; -import org.olat.course.nodes.qti21.QTI21EditController; -import org.olat.course.run.navigation.NodeRunConstructionResult; -import org.olat.course.run.scoring.ScoreEvaluation; -import org.olat.course.run.userview.NodeEvaluation; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.modules.ModuleConfiguration; -import org.olat.repository.RepositoryEntry; -import org.olat.repository.RepositoryManager; - -/** - * - * Initial date: 19.05.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21AssessmentCourseNode extends AbstractAccessableCourseNode implements AssessableCourseNode { - - private static final long serialVersionUID = -3619170190576867622L; - private final static String PACKAGE_QTI21 = Util.getPackageName(QTI21EditController.class); - - public static final String CONFIG_KEY_REPOSITORY_SOFTKEY = "repoSoftkey"; - public static final String CONFIG_KEY_ENABLESCOREINFO = "enableScoreInfo"; - public static final String CONFIG_KEY_BLOCK_AFTER_SUCCESS = "blockAfterSuccess"; - public final static String CONFIG_FULLWINDOW = "fullwindow"; - - public static final String CONFIG_KEY_MINSCORE = "minscore"; - public static final String CONFIG_KEY_MAXSCORE = "maxscore"; - public static final String CONFIG_KEY_CUTVALUE = "cutvalue"; - public static final String CONFIG_KEY_ATTEMPTS = "attempts"; - - - public static final String TYPE = "qti21assessment"; - private static final int CURRENT_CONFIG_VERSION = 1; - - public QTI21AssessmentCourseNode() { - super(TYPE); - updateModuleConfigDefaults(true); - } - - @Override - public boolean needsReferenceToARepositoryEntry() { - return true; - } - - @Override - public RepositoryEntry getReferencedRepositoryEntry() { - String repoSoftkey = getModuleConfiguration().getStringValue(CONFIG_KEY_REPOSITORY_SOFTKEY); - if (repoSoftkey == null) { - return null; - } - return CoreSpringFactory.getImpl(RepositoryManager.class) - .lookupRepositoryEntryBySoftkey(repoSoftkey, false); - } - - @Override - public void updateModuleConfigDefaults(boolean isNewNode) { - ModuleConfiguration config = getModuleConfiguration(); - if(isNewNode) { - //setup default configuration: - //layout - config.set(QTI21AssessmentCourseNode.CONFIG_FULLWINDOW, Boolean.TRUE); - - //configure grading - config.set(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD, Boolean.FALSE); - config.set(MSCourseNode.CONFIG_KEY_SCORE_MIN, new Float(0)); - config.set(MSCourseNode.CONFIG_KEY_SCORE_MAX, new Float(0)); - config.set(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD, Boolean.TRUE); - - config.setConfigurationVersion(CURRENT_CONFIG_VERSION); - } else { - config.setConfigurationVersion(CURRENT_CONFIG_VERSION); - } - } - - @Override - public StatusDescription isConfigValid() { - if (oneClickStatusCache != null && oneClickStatusCache.length > 0) { - return oneClickStatusCache[0]; - } - - List<StatusDescription> statusDescs = validateInternalConfiguration(null); - if(statusDescs.isEmpty()) { - statusDescs.add(StatusDescription.NOERROR); - } - oneClickStatusCache = StatusDescriptionHelper.sort(statusDescs); - return oneClickStatusCache[0]; - } - - @Override - public StatusDescription[] isConfigValid(CourseEditorEnv cev) { - oneClickStatusCache = null;//delete the cache - - List<StatusDescription> sds = isConfigValidWithTranslator(cev, PACKAGE_QTI21, getConditionExpressions()); - if(oneClickStatusCache != null && oneClickStatusCache.length > 0) { - //isConfigValidWithTranslator add first - sds.remove(oneClickStatusCache[0]); - } - sds.addAll(validateInternalConfiguration(cev)); - oneClickStatusCache = StatusDescriptionHelper.sort(sds); - return oneClickStatusCache; - } - - private List<StatusDescription> validateInternalConfiguration(CourseEditorEnv cev) { - List<StatusDescription> sdList = new ArrayList<>(5); - - ModuleConfiguration config = getModuleConfiguration(); - - String repoEntrySoftKey = config.getStringValue(CONFIG_KEY_REPOSITORY_SOFTKEY); - if(!StringHelper.containsNonWhitespace(repoEntrySoftKey)) { - addStatusErrorDescription("error.missing.score.config", QTI21EditController.PANE_TAB_CONFIG_RE, sdList); - } - - return sdList; - } - - private void addStatusErrorDescription(String key, String pane, List<StatusDescription> status) { - String[] params = new String[] { getShortTitle() }; - StatusDescription sd = new StatusDescription(StatusDescription.ERROR, key, key, params, PACKAGE_QTI21); - sd.setDescriptionForUnit(getIdent()); - sd.setActivateableViewIdentifier(pane); - status.add(sd); - } - - @Override - public void exportNode(File fExportDirectory, ICourse course) { - super.exportNode(fExportDirectory, course); - } - - @Override - public void importNode(File importDirectory, ICourse course, Identity owner, Locale locale, boolean withReferences) { - super.importNode(importDirectory, course, owner, locale, withReferences); - } - - @Override - public CourseNode createInstanceForCopy(boolean isNewTitle, ICourse course) { - return super.createInstanceForCopy(isNewTitle, course); - } - - @Override - public boolean archiveNodeData(Locale locale, ICourse course, ArchiveOptions options, ZipOutputStream exportStream, String charset) { - return super.archiveNodeData(locale, course, options, exportStream, charset); - } - - @Override - public void cleanupOnDelete(ICourse course) { - super.cleanupOnDelete(course); - } - - @Override - public boolean hasStatusConfigured() { - return false; - } - - @Override - public Float getMaxScoreConfiguration() { - if (!hasScoreConfigured()) { - throw new OLATRuntimeException(QTI21AssessmentCourseNode.class, "getMaxScore not defined when hasScore set to false", null); - } - return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MAX); - } - - @Override - public Float getMinScoreConfiguration() { - return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_SCORE_MIN); - } - - @Override - public Float getCutValueConfiguration() { - return getModuleConfiguration().getFloatEntry(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE); - } - - @Override - public boolean hasScoreConfigured() { - return true; - } - - @Override - public boolean hasPassedConfigured() { - return true; - } - - @Override - public boolean hasCommentConfigured() { - return true; - } - - @Override - public boolean hasAttemptsConfigured() { - return true; - } - - @Override - public boolean hasDetails() { - return true; - } - - @Override - public boolean isEditableConfigured() { - return true; - } - - @Override - public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, - ICourse course, UserCourseEnvironment euce) { - QTI21EditController editCtrl = new QTI21EditController(ureq, wControl, stackPanel, this, course, euce); - CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); - return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, editCtrl); - } - - @Override - public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { - updateModuleConfigDefaults(false); - Controller controller; - // Do not allow guests to start tests - Roles roles = ureq.getUserSession().getRoles(); - Translator trans = Util.createPackageTranslator(QTI21AssessmentCourseNode.class, ureq.getLocale()); - if (roles.isGuestOnly()) { - String title = trans.translate("guestnoaccess.title"); - String message = trans.translate("guestnoaccess.message"); - controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); - } else { - controller = new QTI21AssessmentRunController(ureq, wControl, userCourseEnv, this); - } - Controller ctrl = TitledWrapperHelper.getWrapper(ureq, wControl, controller, this, "o_qtiassessment_icon"); - return new NodeRunConstructionResult(ctrl); - } - - @Override - public String getDetailsListViewHeaderKey() { - return "table.header.details.qti21test"; - } - - @Override - public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { - return ""; - } - - @Override - public Controller getDetailsEditController(UserRequest ureq, WindowControl wControl, - BreadcrumbPanel stackPanel, UserCourseEnvironment userCourseEnvironment) { - return new QTI21AssessmentDetailsController(ureq, wControl, userCourseEnvironment, this); - } - - @Override - public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) { - // read score from properties save score, passed and attempts information - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity(); - Boolean passed = am.getNodePassed(this, mySelf); - Float score = am.getNodeScore(this, mySelf); - Long assessmentID = am.getAssessmentID(this, mySelf); - Boolean fullyAssessed = am.getNodeFullyAssessed(this, mySelf); - return new ScoreEvaluation(score, passed, fullyAssessed, assessmentID); - } - - @Override - public String getUserUserComment(UserCourseEnvironment userCourseEnv) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - return am.getNodeComment(this, userCourseEnv.getIdentityEnvironment().getIdentity()); - } - - @Override - public String getUserCoachComment(UserCourseEnvironment userCourseEnv) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - return am.getNodeCoachComment(this, userCourseEnv.getIdentityEnvironment().getIdentity()); - } - - @Override - public String getUserLog(UserCourseEnvironment userCourseEnv) { - UserNodeAuditManager am = userCourseEnv.getCourseEnvironment().getAuditManager(); - return am.getUserNodeLog(this, userCourseEnv.getIdentityEnvironment().getIdentity()); - } - - @Override - public Integer getUserAttempts(UserCourseEnvironment userCourseEnv) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - return am.getNodeAttempts(this, assessedIdentity); - } - - @Override - public void updateUserScoreEvaluation(ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnv, - Identity coachingIdentity, boolean incrementAttempts) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - am.saveScoreEvaluation(this, coachingIdentity, assessedIdentity, new ScoreEvaluation(scoreEvaluation.getScore(), scoreEvaluation.getPassed()), userCourseEnv, incrementAttempts); - } - - @Override - public void updateUserUserComment(String userComment, UserCourseEnvironment userCourseEnv, Identity coachingIdentity) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - if (userComment != null) { - am.saveNodeComment(this, coachingIdentity, assessedIdentity, userComment); - } - } - - @Override - public void incrementUserAttempts(UserCourseEnvironment userCourseEnv) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - am.incrementNodeAttempts(this, assessedIdentity, userCourseEnv); - } - - @Override - public void updateUserAttempts(Integer userAttempts, UserCourseEnvironment userCourseEnv, Identity coachingIdentity) { - if (userAttempts != null) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - am.saveNodeAttempts(this, coachingIdentity, assessedIdentity, userAttempts); - } - } - - @Override - public void updateUserCoachComment(String coachComment, UserCourseEnvironment userCourseEnv) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity(); - if (coachComment != null) { - am.saveNodeCoachComment(this, assessedIdentity, coachComment); - } - } -} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/_spring/buildingblockContext.xml b/src/main/java/org/olat/course/nodes/_spring/buildingblockContext.xml index 34290197de561b2f024a65fa967e938cb1c07bea..c60b1cccb5877a091a40ff8cdc29e2ac634da60a 100644 --- a/src/main/java/org/olat/course/nodes/_spring/buildingblockContext.xml +++ b/src/main/java/org/olat/course/nodes/_spring/buildingblockContext.xml @@ -126,8 +126,4 @@ <property name="order" value="210" /> </bean> - <bean id="qti21test" class="org.olat.course.nodes.qti21.QTI21AssessmentCourseNodeConfiguration" scope="prototype" > - <property name="order" value="210" /> - </bean> - </beans> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQ12EditForm.java b/src/main/java/org/olat/course/nodes/iq/IQ12EditForm.java index 31c3d82aa1c6df62b514eb57479fc6b6fd5d16fd..ebdc371f2d05bcc3a3ee6be89b2211ece674e48c 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQ12EditForm.java +++ b/src/main/java/org/olat/course/nodes/iq/IQ12EditForm.java @@ -120,7 +120,6 @@ public class IQ12EditForm extends FormBasicController { } protected boolean validateFormLogic (UserRequest ureq) { - startDateElement.clearError(); endDateElement.clearError(); @@ -153,6 +152,42 @@ public class IQ12EditForm extends FormBasicController { @Override protected void formOK(UserRequest ureq) { + modConfig.set(IQEditController.CONFIG_KEY_DISPLAYMENU, new Boolean(isDisplayMenu())); + modConfig.set(IQEditController.CONFIG_FULLWINDOW, new Boolean(isFullWindow())); + + if (isDisplayMenu()) { + modConfig.set(IQEditController.CONFIG_KEY_RENDERMENUOPTION, isMenuRenderSectionsOnly()); + modConfig.set(IQEditController.CONFIG_KEY_ENABLEMENU, new Boolean(isEnableMenu())); + } else { + // set default values when menu is not displayed + modConfig.set(IQEditController.CONFIG_KEY_RENDERMENUOPTION, Boolean.FALSE); + modConfig.set(IQEditController.CONFIG_KEY_ENABLEMENU, Boolean.FALSE); + } + + modConfig.set(IQEditController.CONFIG_KEY_QUESTIONPROGRESS, new Boolean(isDisplayQuestionProgress())); + modConfig.set(IQEditController.CONFIG_KEY_SEQUENCE, getSequence()); + modConfig.set(IQEditController.CONFIG_KEY_ENABLECANCEL, new Boolean(isEnableCancel())); + modConfig.set(IQEditController.CONFIG_KEY_ENABLESUSPEND, new Boolean(isEnableSuspend())); + modConfig.set(IQEditController.CONFIG_KEY_QUESTIONTITLE, new Boolean(isDisplayQuestionTitle())); + modConfig.set(IQEditController.CONFIG_KEY_AUTOENUM_CHOICES, new Boolean(isAutoEnumChoices())); + modConfig.set(IQEditController.CONFIG_KEY_MEMO, new Boolean(isProvideMemoField())); + // Only tests and selftests have summaries and score progress + if (!isSurvey) { + modConfig.set(IQEditController.CONFIG_KEY_SUMMARY, getSummary()); + modConfig.set(IQEditController.CONFIG_KEY_SCOREPROGRESS, new Boolean(isDisplayScoreProgress())); + modConfig.set(IQEditController.CONFIG_KEY_ENABLESCOREINFO, new Boolean(isEnableScoreInfo())); + modConfig.set(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS, new Boolean(isShowResultsDateDependent())); + modConfig.set(IQEditController.CONFIG_KEY_RESULTS_START_DATE, getShowResultsStartDate()); + modConfig.set(IQEditController.CONFIG_KEY_RESULTS_END_DATE, getShowResultsEndDate()); + modConfig.set(IQEditController.CONFIG_KEY_RESULT_ON_FINISH, isShowResultsAfterFinishTest()); + modConfig.set(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE, isShowResultsOnHomePage()); + } + // Only tests have a limitation on number of attempts + if (isAssessment) { + modConfig.set(IQEditController.CONFIG_KEY_ATTEMPTS, getAttempts()); + modConfig.set(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS, new Boolean(isBlockAfterSuccess())); + } + fireEvent(ureq, Event.DONE_EVENT); } @@ -428,65 +463,72 @@ public class IQ12EditForm extends FormBasicController { flc.setDirty(true); } + private boolean isDisplayMenu() { + return displayMenu.isSelected(0); + } /** * @return true: menu is enabled */ - boolean isEnableMenu() { return enableMenu.isSelected(0); } - /** - * @return true: menu should be displayed - */ - boolean isDisplayMenu() { return displayMenu.isSelected(0); } + private boolean isEnableMenu() { + return enableMenu.isSelected(0); + } + /** * @return true: menu should be displayed */ - boolean isFullWindow() { return fullWindowEl.isSelected(0); } + private boolean isFullWindow() { + return fullWindowEl.isSelected(0); + } /** * @return true: score progress is enabled */ - boolean isDisplayScoreProgress() { return displayScoreProgress.isSelected(0); } + private boolean isDisplayScoreProgress() { + return displayScoreProgress.isSelected(0); + } + /** * @return true: score progress is enabled */ - boolean isDisplayQuestionProgress() { return displayQuestionProgress.isSelected(0); } + private boolean isDisplayQuestionProgress() { return displayQuestionProgress.isSelected(0); } /** * @return true: question title is enabled */ - boolean isDisplayQuestionTitle() { return displayQuestionTitle.isSelected(0); } + private boolean isDisplayQuestionTitle() { return displayQuestionTitle.isSelected(0); } /** * @return true: automatic enumeration of choice options enabled */ - boolean isAutoEnumChoices() { return autoEnumerateChoices.isSelected(0); } + private boolean isAutoEnumChoices() { return autoEnumerateChoices.isSelected(0); } /** * @return true: provide memo field */ - boolean isProvideMemoField() { return provideMemoField.isSelected(0); } + private boolean isProvideMemoField() { return provideMemoField.isSelected(0); } /** * @return sequence configuration: section or item */ - String getSequence() { return sequence.getSelectedKey(); } + private String getSequence() { return sequence.getSelectedKey(); } /** * @return true: cancel is enabled */ - boolean isEnableCancel() { return enableCancel.isSelected(0); } + private boolean isEnableCancel() { return enableCancel.isSelected(0); } /** * @return true: suspend is enabled */ - boolean isEnableSuspend() { return enableSuspend.isSelected(0); } + private boolean isEnableSuspend() { return enableSuspend.isSelected(0); } /** * @return summary type: compact or detailed */ - String getSummary() { return summary.getSelectedKey();} + private String getSummary() { return summary.getSelectedKey();} /** * @return number of max attempts */ - Integer getAttempts() { + private Integer getAttempts() { Integer a = attempts.getIntValue(); return a == 0 ? null : attempts.getIntValue(); } - boolean isBlockAfterSuccess() { + private boolean isBlockAfterSuccess() { return blockAfterSuccess.isSelected(0); } @@ -494,41 +536,41 @@ public class IQ12EditForm extends FormBasicController { * * @return true if only section title should be rendered */ - Boolean isMenuRenderSectionsOnly() { return Boolean.valueOf(menuRenderOptions.getSelectedKey());} + private Boolean isMenuRenderSectionsOnly() { return Boolean.valueOf(menuRenderOptions.getSelectedKey());} /** * @return true: score-info on start-page is enabled */ - boolean isEnableScoreInfo() { return scoreInfo.isSelected(0); } + private boolean isEnableScoreInfo() { return scoreInfo.isSelected(0); } /** * * @return true is the results are shown date dependent */ - boolean isShowResultsDateDependent() { return showResultsDateDependentButton.isSelected(0); } + private boolean isShowResultsDateDependent() { return showResultsDateDependentButton.isSelected(0); } /** * * @return Returns the start date for the result visibility. */ - Date getShowResultsStartDate() { return startDateElement.getDate(); } + private Date getShowResultsStartDate() { return startDateElement.getDate(); } /** * * @return Returns the end date for the result visibility. */ - Date getShowResultsEndDate() { return endDateElement.getDate(); } + private Date getShowResultsEndDate() { return endDateElement.getDate(); } /** * * @return Returns true if the results are shown after test finished. */ - boolean isShowResultsAfterFinishTest() { return showResultsAfterFinishTest.isSelected(0); } + private boolean isShowResultsAfterFinishTest() { return showResultsAfterFinishTest.isSelected(0); } /** * * @return Returns true if the results are shown on the test home page. */ - boolean isShowResultsOnHomePage() { return showResultsOnHomePage.isSelected(0); } + private boolean isShowResultsOnHomePage() { return showResultsOnHomePage.isSelected(0); } @Override diff --git a/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java new file mode 100644 index 0000000000000000000000000000000000000000..637664f2c14859ea37b56fc49d9ef7f172642107 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/IQConfigurationController.java @@ -0,0 +1,669 @@ +/** + * <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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.dom4j.Attribute; +import org.dom4j.Document; +import org.dom4j.Element; +import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.Constants; +import org.olat.commons.file.filechooser.FileChooseCreateEditController; +import org.olat.commons.file.filechooser.LinkChooseCreateEditController; +import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; +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.stack.BreadcrumbPanel; +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.Identity; +import org.olat.core.id.Roles; +import org.olat.core.logging.AssertException; +import org.olat.core.util.StringHelper; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.coordinate.LockResult; +import org.olat.core.util.vfs.LocalFileImpl; +import org.olat.core.util.vfs.LocalFolderImpl; +import org.olat.core.util.vfs.VFSConstants; +import org.olat.core.util.vfs.VFSContainer; +import org.olat.core.util.vfs.VFSItem; +import org.olat.core.util.vfs.VFSStatus; +import org.olat.course.ICourse; +import org.olat.course.editor.NodeEditController; +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.run.userview.UserCourseEnvironment; +import org.olat.course.tree.CourseInternalLinkTreeModel; +import org.olat.fileresource.FileResourceManager; +import org.olat.fileresource.types.ImsQTI21Resource; +import org.olat.ims.qti.QTIResult; +import org.olat.ims.qti.QTIResultManager; +import org.olat.ims.qti.editor.QTIEditorPackage; +import org.olat.ims.qti.editor.QTIEditorPackageImpl; +import org.olat.ims.qti.editor.beecom.objects.Assessment; +import org.olat.ims.qti.editor.beecom.objects.Item; +import org.olat.ims.qti.editor.beecom.objects.Section; +import org.olat.ims.qti.fileresource.SurveyFileResource; +import org.olat.ims.qti.fileresource.TestFileResource; +import org.olat.ims.qti.process.AssessmentInstance; +import org.olat.ims.qti.process.QTIHelper; +import org.olat.modules.ModuleConfiguration; +import org.olat.modules.iq.IQManager; +import org.olat.modules.iq.IQPreviewSecurityCallback; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.repository.controllers.ReferencableEntriesSearchController; +import org.olat.resource.OLATResource; +import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; + +import de.bps.onyx.plugin.OnyxModule; +import de.bps.onyx.plugin.course.nodes.iq.IQEditForm; +import de.bps.webservices.clients.onyxreporter.OnyxReporterConnector; +import de.bps.webservices.clients.onyxreporter.OnyxReporterException; + +/** + * + * Initial date: 26.06.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class IQConfigurationController extends BasicController { + + private static final String VC_CHOSENTEST = "chosentest"; + + private VelocityContainer myContent; + private final BreadcrumbPanel stackPanel; + + private Link previewLink, chooseTestButton, changeTestButton, editTestButton; + + private Controller previewLayoutCtr; + private CloseableModalController cmc; + private IQEditReplaceWizard replaceWizard; + private FileChooseCreateEditController fccecontr; + private ReferencableEntriesSearchController searchController; + + private IQEditForm modOnyxConfigForm; + private IQ12EditForm mod12ConfigForm; + private QTI21EditForm mod21ConfigForm; + + private String type; + private ICourse course; + private List<Identity> learners; + private Boolean allowRelativeLinks; + private ModuleConfiguration moduleConfiguration; + private AbstractAccessableCourseNode courseNode; + + @Autowired + private IQManager iqManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private RepositoryManager repositoryManager; + + /** + * + * @param ureq + * @param wControl + * @param stackPanel + * @param course + * @param courseNode + * @param euce + * @param type + */ + public IQConfigurationController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, + AbstractAccessableCourseNode courseNode, UserCourseEnvironment euce, String type) { + super(ureq, wControl); + this.stackPanel = stackPanel; + this.moduleConfiguration = courseNode.getModuleConfiguration(); + //o_clusterOk by guido: save to hold reference to course inside editor + this.course = course; + this.courseNode = courseNode; + this.type = type; + + + myContent = createVelocityContainer("edit"); + chooseTestButton = LinkFactory.createButtonSmall("command.chooseRepFile", myContent, this); + chooseTestButton.setElementCssClass("o_sel_test_choose_repofile"); + changeTestButton = LinkFactory.createButtonSmall("command.changeRepFile", myContent, this); + changeTestButton.setElementCssClass("o_sel_test_change_repofile"); + + // fetch repository entry + RepositoryEntry re = null; + String displayName = ""; + String repoSoftkey = (String)moduleConfiguration.get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); + if (repoSoftkey != null) { + re = getIQReference(moduleConfiguration, false); + displayName = StringHelper.escapeHtml(re.getDisplayname()); + } + + myContent.contextPut(VC_CHOSENTEST, re == null ? translate("no.file.chosen") : displayName); + if (re != null) { + myContent.contextPut("dontRenderRepositoryButton", new Boolean(true)); + // Put values to velocity container + + boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); + if(isOnyx) { + setOnyxVariables(re); + } else { + if (isEditable(ureq.getIdentity(), ureq.getUserSession().getRoles(), re)) { + editTestButton = LinkFactory.createButtonSmall("command.editRepFile", myContent, this); + } + myContent.contextPut("showOutcomes", Boolean.FALSE); + myContent.contextPut(IQEditController.CONFIG_KEY_MINSCORE, moduleConfiguration.get(IQEditController.CONFIG_KEY_MINSCORE)); + myContent.contextPut(IQEditController.CONFIG_KEY_MAXSCORE, moduleConfiguration.get(IQEditController.CONFIG_KEY_MAXSCORE)); + myContent.contextPut(IQEditController.CONFIG_KEY_CUTVALUE, moduleConfiguration.get(IQEditController.CONFIG_KEY_CUTVALUE)); + } + + previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayName, Link.NONTRANSLATED, myContent, this); + previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); + if(isOnyx) { + previewLink.setEnabled(false); + } else { + previewLink.setCustomEnabledLinkCSS("o_preview"); + previewLink.setTitle(translate("command.preview")); + } + } + + String disclaimer = (String) moduleConfiguration.get(IQEditController.CONFIG_KEY_DISCLAIMER); + String legend = translate("fieldset.chosecreateeditfile"); + + allowRelativeLinks = moduleConfiguration.getBooleanEntry(IQEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS); + if(allowRelativeLinks == null) { + allowRelativeLinks=Boolean.FALSE; + } + fccecontr = new LinkChooseCreateEditController(ureq, wControl, disclaimer, allowRelativeLinks, course.getCourseFolderContainer(), + type, legend, new CourseInternalLinkTreeModel(course.getEditorTreeModel())); + listenTo(fccecontr); + + Component fcContent = fccecontr.getInitialComponent(); + myContent.put("filechoosecreateedit", fcContent); + myContent.contextPut("type", type); + + putInitialPanel(myContent); + updateEditController(ureq); + + switch(type) { + case AssessmentInstance.QMD_ENTRY_TYPE_ASSESS: + myContent.contextPut("repEntryTitle", translate("choosenfile.test")); + break; + case AssessmentInstance.QMD_ENTRY_TYPE_SELF: + myContent.contextPut("repEntryTitle", translate("choosenfile.self")); + break; + case AssessmentInstance.QMD_ENTRY_TYPE_SURVEY: + myContent.contextPut("repEntryTitle", translate("choosenfile.surv")); + chooseTestButton.setCustomDisplayText(translate("command.createSurvey")); + break; + } + } + + private void updateEditController(UserRequest ureq) { + removeAsListenerAndDispose(mod12ConfigForm); + removeAsListenerAndDispose(mod21ConfigForm); + removeAsListenerAndDispose(modOnyxConfigForm); + + RepositoryEntry re = getIQReference(moduleConfiguration, false); + if(re == null) { + mod12ConfigForm = new IQ12EditForm(ureq, getWindowControl(), moduleConfiguration); + listenTo(mod12ConfigForm); + myContent.put("iqeditform", mod12ConfigForm.getInitialComponent()); + } else if(ImsQTI21Resource.TYPE_NAME.equals(re.getOlatResource().getResourceableTypeName())) { + mod21ConfigForm = new QTI21EditForm(ureq, getWindowControl(), moduleConfiguration); + listenTo(mod21ConfigForm); + myContent.put("iqeditform", mod21ConfigForm.getInitialComponent()); + } else if(OnyxModule.isOnyxTest(re.getOlatResource())) { + modOnyxConfigForm = new IQEditForm(ureq, getWindowControl(), moduleConfiguration, re); + listenTo(modOnyxConfigForm); + myContent.put("iqeditform", modOnyxConfigForm.getInitialComponent()); + } else { + mod12ConfigForm = new IQ12EditForm(ureq, getWindowControl(), moduleConfiguration); + listenTo(mod12ConfigForm); + myContent.put("iqeditform", mod12ConfigForm.getInitialComponent()); + } + } + + private void setOnyxVariables(RepositoryEntry entry) { + myContent.contextPut("onyxDisplayName", entry.getDisplayname()); + myContent.contextPut("showOutcomes", Boolean.TRUE); + Map<String, String> outcomes = new HashMap<String, String>(); + try { + OnyxReporterConnector onyxReporter = new OnyxReporterConnector(); + outcomes = onyxReporter.getPossibleOutcomeVariables(courseNode); + } catch (OnyxReporterException e) { + getWindowControl().setWarning(translate("reporter.unavailable")); + } + myContent.contextPut("outcomes", outcomes); + } + + /** + * @param identity + * @param repository entry + * @return + */ + private boolean isEditable(Identity identity, Roles roles, RepositoryEntry re) { + boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); + if (isOnyx) { + return false; + } + + return (securityManager.isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_ADMIN) + || repositoryManager.isOwnerOfRepositoryEntry(identity, re) + || repositoryManager.isInstitutionalRessourceManagerFor(identity, roles, re)); + } + + @Override + public void event(UserRequest ureq, Component source, Event event) { + if (previewLink == source){ + doPreview(ureq); + } else if (chooseTestButton == source){ + doChooseTestAndSurvey(ureq); + } else if (changeTestButton == source) { + RepositoryEntry re = courseNode.getReferencedRepositoryEntry(); + if(re == null) { + showError("error.test.undefined.long", courseNode.getShortTitle()); + } else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)) { + doChangeSelfTest(ureq); + } else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS) || type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { + doChangeTestAndSurvey(ureq, re); + } + } else if (editTestButton == source) { + CourseNodeFactory.getInstance().launchReferencedRepoEntryEditor(ureq, getWindowControl(), courseNode); + } + } + + @Override + public void event(UserRequest urequest, Controller source, Event event) { + if (source.equals(searchController)) { + if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { + // repository search controller done + cmc.deactivate(); + RepositoryEntry re = searchController.getSelectedEntry(); + doIQReference(urequest, re); + updateEditController(urequest); + checkEssay(re); + } + } else if (source == fccecontr) { + if (event == FileChooseCreateEditController.FILE_CHANGED_EVENT) { + String chosenFile = fccecontr.getChosenFile(); + if (chosenFile != null){ + moduleConfiguration.set(IQEditController.CONFIG_KEY_DISCLAIMER, fccecontr.getChosenFile()); + } else { + moduleConfiguration.remove(IQEditController.CONFIG_KEY_DISCLAIMER); + } + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } else if (event == FileChooseCreateEditController.ALLOW_RELATIVE_LINKS_CHANGED_EVENT) { + allowRelativeLinks = fccecontr.getAllowRelativeLinks(); + courseNode.getModuleConfiguration().setBooleanEntry(IQEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS, allowRelativeLinks.booleanValue()); + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } else if (source == replaceWizard) { + if(event == Event.CANCELLED_EVENT) { + cmc.deactivate(); + } else if(event == Event.DONE_EVENT) { + cmc.deactivate(); + String repositorySoftKey = (String) moduleConfiguration.get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); + Long repKey = repositoryManager.lookupRepositoryEntryBySoftkey(repositorySoftKey, true).getKey(); + QTIResultManager.getInstance().deleteAllResults(course.getResourceableId(), courseNode.getIdent(), repKey); + IQEditController.removeIQReference(moduleConfiguration); + VFSStatus isDeleted = iqManager.removeQtiSerFiles(course.getResourceableId(), courseNode.getIdent()); + if (!isDeleted.equals(VFSConstants.YES)) { + // couldn't removed qtiser files + logWarn("Couldn't removed course node folder! Course resourceable id: " + course.getResourceableId() + ", Course node ident: " + courseNode.getIdent(), null); + } + doIQReference(urequest, replaceWizard.getSelectedRepositoryEntry()); + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } else if (source == mod12ConfigForm) { + if (event == Event.DONE_EVENT) { + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } else if (source == mod21ConfigForm) { + if (event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } else if (source == modOnyxConfigForm) { + if (event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } + } + + private void doPreview(UserRequest ureq) { + removeAsListenerAndDispose(previewLayoutCtr); + + RepositoryEntry re = getIQReference(moduleConfiguration, false); + if(re != null && !OnyxModule.isOnyxTest(re.getOlatResource())) { + if(ImsQTI21Resource.TYPE_NAME.equals(re.getOlatResource().getResourceableTypeName())) { + //TODO + } else { + long courseResId = course.getResourceableId().longValue(); + Controller previewController = iqManager.createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), courseResId, courseNode.getIdent(), null); + previewLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), previewController); + stackPanel.pushController(translate("preview"), previewLayoutCtr); + } + } + } + + private void doChooseTestAndSurvey(UserRequest ureq) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(searchController); + + if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { + searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, + SurveyFileResource.TYPE_NAME, translate("command.chooseSurvey")); + } else { // test and selftest use same repository resource type + searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, + TestFileResource.TYPE_NAME, translate("command.chooseTest")); + } + listenTo(searchController); + cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent(), true, translate("command.chooseRepFile")); + cmc.activate(); + } + + private void doChangeSelfTest(UserRequest ureq) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(searchController); + + String[] types = new String[]{ TestFileResource.TYPE_NAME }; + searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseTest")); + listenTo(searchController); + + cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent()); + listenTo(searchController); + cmc.activate(); + } + + private void doChangeTestAndSurvey(UserRequest ureq, RepositoryEntry re) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(searchController); + + String[] types; + if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {//test + types = new String[]{ TestFileResource.TYPE_NAME, ImsQTI21Resource.TYPE_NAME }; + } else {//survey + types = new String[]{ SurveyFileResource.TYPE_NAME }; + } + + if (moduleConfiguration.get(IQEditController.CONFIG_KEY_TYPE_QTI) == null) { + updateQtiType(re); + } + + int onyxSuccess = 0; + if (moduleConfiguration.get(IQEditController.CONFIG_KEY_TYPE_QTI) != null && moduleConfiguration.get(IQEditController.CONFIG_KEY_TYPE_QTI).equals(IQEditController.CONFIG_VALUE_QTI2)) { + if (courseNode.getClass().equals(IQSURVCourseNode.class)) { + File surveyDir = new File(course.getCourseEnvironment().getCourseBaseContainer() + .getBasefile() + File.separator + courseNode.getIdent() + File.separator); + if (surveyDir != null && surveyDir.exists() && surveyDir.listFiles().length > 0) { + onyxSuccess = surveyDir.listFiles().length; + } + } else { + onyxSuccess = QTIResultManager.getInstance().countResults(course.getResourceableId(), courseNode.getIdent(), re.getKey()); + } + } + + + if (moduleConfiguration.get(IQEditController.CONFIG_KEY_TYPE_QTI) != null + && moduleConfiguration.get(IQEditController.CONFIG_KEY_TYPE_QTI).equals(IQEditController.CONFIG_VALUE_QTI2) + && onyxSuccess > 0) { + + replaceWizard = new IQEditReplaceWizard(ureq, getWindowControl(), course, courseNode, types, learners, null, onyxSuccess, true); + replaceWizard.addControllerListener(this); + + String title = replaceWizard.getAndRemoveWizardTitle(); + cmc = new CloseableModalController(getWindowControl(), translate("close"), replaceWizard.getInitialComponent(), true, title); + cmc.activate(); + } else { + + List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), re.getKey(), null, 1); + // test was passed from an user + boolean passed = (results != null && results.size() > 0) ? true : false; + // test was started and not passed + // it exists partly results for this test + List<Identity> identitiesWithQtiSerEntry = iqManager.getIdentitiesWithQtiSerEntry(course.getResourceableId(), courseNode.getIdent()); + if(passed || identitiesWithQtiSerEntry.size() > 0) { + learners = new ArrayList<Identity>(); + for(QTIResult result : results) { + Identity identity = result.getResultSet().getIdentity(); + if(identity != null && !learners.contains(identity)){ + learners.add(identity); + } + } + // add identities with qti.ser entry + for (Identity identity : identitiesWithQtiSerEntry) { + if(!learners.contains(identity)) { + learners.add(identity); + } + } + replaceWizard = new IQEditReplaceWizard(ureq, getWindowControl(), course, courseNode, types, learners, results, identitiesWithQtiSerEntry.size(), false); + replaceWizard.addControllerListener(this); + + cmc = new CloseableModalController(getWindowControl(), translate("close"), replaceWizard.getInitialComponent()); + cmc.activate(); + } else { + if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {//test + searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseTest")); + } else {//survey + searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseSurvey")); + } + listenTo(searchController); + + cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent()); + cmc.activate(); + } + } + } + + /** + * This method updates the QTI Type in the editortreemodel. + * + * @param re Needed to check if this Test is of QTI Type 2.1 + */ + private void updateQtiType(RepositoryEntry re) { + boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); + if (isOnyx) { + moduleConfiguration.set(IQEditController.CONFIG_KEY_TYPE_QTI, IQEditController.CONFIG_VALUE_QTI2); + } else { + moduleConfiguration.set(IQEditController.CONFIG_KEY_TYPE_QTI, IQEditController.CONFIG_VALUE_QTI1); + } + } + + private void checkEssay(RepositoryEntry re) { + if(OnyxModule.isOnyxTest(re.getOlatResource())) return; + if(courseNode instanceof IQSURVCourseNode || courseNode instanceof IQSELFCourseNode) return; + + TestFileResource fr = new TestFileResource(); + fr.overrideResourceableId(re.getOlatResource().getResourceableId()); + QTIEditorPackage qtiPackage = new QTIEditorPackageImpl(getIdentity(), fr, null, getTranslator()); + Assessment ass = qtiPackage.getQTIDocument().getAssessment(); + + //Sections with their Items + List<Section> sections = ass.getSections(); + for (Section section:sections) { + List<Item> items = section.getItems(); + for (Item item:items) { + String ident = item.getIdent(); + if(ident != null && ident.startsWith("QTIEDIT:ESSAY")) { + showWarning("warning.test.with.essay"); + break; + } + } + } + } + + private void doIQReference(UserRequest urequest, RepositoryEntry re) { + // repository search controller done + if (re != null) { + if (CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(re.getOlatResource(), null)) { + LockResult lockResult = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(re.getOlatResource(), urequest.getIdentity(), null); + String fullName = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(lockResult.getOwner()); + showError("error.entry.locked", fullName); + if(lockResult.isSuccess()) { + //improbable concurrency security + CoordinatorManager.getInstance().getCoordinator().getLocker().releaseLock(lockResult); + } + } else { + if(editTestButton != null) { + myContent.remove(editTestButton); + } + + IQEditController.setIQReference(re, moduleConfiguration); + String displayName = StringHelper.escapeHtml(re.getDisplayname()); + previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayName, Link.NONTRANSLATED, myContent, this); + previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); + previewLink.setCustomEnabledLinkCSS("o_preview"); + previewLink.setTitle(getTranslator().translate("command.preview")); + myContent.contextPut("dontRenderRepositoryButton", new Boolean(true)); + // If of type test, get min, max, cut - put in module config and push + // to velocity + + boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); + myContent.contextPut("isOnyx", new Boolean(isOnyx)); + if(isOnyx) { + myContent.contextPut("onyxDisplayName", displayName); + moduleConfiguration.set(IQEditController.CONFIG_KEY_TYPE_QTI, IQEditController.CONFIG_VALUE_QTI2); + setOnyxVariables(re); + } else { + myContent.contextPut("showOutcomes", Boolean.FALSE); + moduleConfiguration.set(IQEditController.CONFIG_KEY_TYPE_QTI, IQEditController.CONFIG_VALUE_QTI1); + if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { + updateModuleConfigFromQTIFile(re.getOlatResource()); + // Put values to velocity container + myContent.contextPut(IQEditController.CONFIG_KEY_MINSCORE, moduleConfiguration.get(IQEditController.CONFIG_KEY_MINSCORE)); + myContent.contextPut(IQEditController.CONFIG_KEY_MAXSCORE, moduleConfiguration.get(IQEditController.CONFIG_KEY_MAXSCORE)); + myContent.contextPut(IQEditController.CONFIG_KEY_CUTVALUE, moduleConfiguration.get(IQEditController.CONFIG_KEY_CUTVALUE)); + } + if (isEditable(urequest.getIdentity(), urequest.getUserSession().getRoles(), re)) { + editTestButton = LinkFactory.createButtonSmall("command.editRepFile", myContent, this); + } + } + fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); + } + } + } + + /** + * Ge the qti file soft key repository reference + * @param config + * @param strict + * @return RepositoryEntry + */ + private RepositoryEntry getIQReference(ModuleConfiguration config, boolean strict) { + if (config == null) { + if (strict) { + throw new AssertException("missing config in IQ"); + } else { + return null; + } + } + String repoSoftkey = (String) config.get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); + if (repoSoftkey == null) { + if (strict) { + throw new AssertException("invalid config when being asked for references"); + } else { + return null; + } + } + + return RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repoSoftkey, strict); + } + + /** + * Update the module configuration from the qti file: read min/max/cut values + * @param res + */ + private void updateModuleConfigFromQTIFile(OLATResource res) { + FileResourceManager frm = FileResourceManager.getInstance(); + File unzippedRoot = frm.unzipFileResource(res); + //with VFS FIXME:pb:c: remove casts to LocalFileImpl and LocalFolderImpl if no longer needed. + VFSContainer vfsUnzippedRoot = new LocalFolderImpl(unzippedRoot); + VFSItem vfsQTI = vfsUnzippedRoot.resolve("qti.xml"); + if (vfsQTI==null){ + throw new AssertException("qti file did not exist even it should be guaranteed by repositor check-in "); + } + //ensures that InputStream is closed in every case. + Document doc = QTIHelper.getDocument((LocalFileImpl)vfsQTI); + if(doc == null){ + //error reading qti file (existence check was made before) + throw new AssertException("qti file could not be read " + ((LocalFileImpl)vfsQTI).getBasefile().getAbsolutePath()); + } + // Extract min, max and cut value + Float minValue = null, maxValue = null, cutValue = null; + Element decvar = (Element) doc.selectSingleNode("questestinterop/assessment/outcomes_processing/outcomes/decvar"); + if (decvar != null) { + Attribute minval = decvar.attribute("minvalue"); + if (minval != null) { + String mv = minval.getValue(); + try { + minValue = new Float(Float.parseFloat(mv)); + } catch (NumberFormatException e1) { + // if not correct in qti file -> ignore + } + } + Attribute maxval = decvar.attribute("maxvalue"); + if (maxval != null) { + String mv = maxval.getValue(); + try { + maxValue = new Float(Float.parseFloat(mv)); + } catch (NumberFormatException e1) { + // if not correct in qti file -> ignore + } + } + Attribute cutval = decvar.attribute("cutvalue"); + if (cutval != null) { + String cv = cutval.getValue(); + try { + cutValue = new Float(Float.parseFloat(cv)); + } catch (NumberFormatException e1) { + // if not correct in qti file -> ignore + } + } + } + // Put values to module configuration + moduleConfiguration.set(IQEditController.CONFIG_KEY_MINSCORE, minValue); + moduleConfiguration.set(IQEditController.CONFIG_KEY_MAXSCORE, maxValue); + moduleConfiguration.set(IQEditController.CONFIG_KEY_CUTVALUE, cutValue); + } + + /** + * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) + */ + @Override + protected void doDispose() { + //child controllers registered with listenTo() get disposed in BasicController + if (previewLayoutCtr != null) { + previewLayoutCtr.dispose(); + previewLayoutCtr = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQControllerCreator.java b/src/main/java/org/olat/course/nodes/iq/IQControllerCreator.java deleted file mode 100644 index bf336a2d04ca607def421861cf4fe5d12bd20e08..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/iq/IQControllerCreator.java +++ /dev/null @@ -1,100 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <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 -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <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> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.course.nodes.iq; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.BreadcrumbPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.generic.tabbable.TabbableController; -import org.olat.course.ICourse; -import org.olat.course.groupsandrights.CourseGroupManager; -import org.olat.course.nodes.IQSELFCourseNode; -import org.olat.course.nodes.IQSURVCourseNode; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.userview.UserCourseEnvironment; - -public interface IQControllerCreator { - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQTestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQTESTCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce); - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQSelftestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQSELFCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce); - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQSurveyEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQSURVCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce); - - /** - * - * @param ureq - * @param wControl - * @param userCourseEnv - * @param ne - * @param courseNode - * @return - */ - public Controller createIQTestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQTESTCourseNode courseNode); - - public Controller createIQTestPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQTESTCourseNode courseNode); - - public Controller createIQSelftestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQSELFCourseNode courseNode); - - public Controller createIQSurveyRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQSURVCourseNode courseNode); -} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQControllerCreatorOlat.java b/src/main/java/org/olat/course/nodes/iq/IQControllerCreatorOlat.java deleted file mode 100644 index 84d29418f8c3b6e8544fbae4927bb81f0bcb68f2..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/iq/IQControllerCreatorOlat.java +++ /dev/null @@ -1,216 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <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 -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <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> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.course.nodes.iq; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.BreadcrumbPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.generic.messages.MessageUIFactory; -import org.olat.core.gui.control.generic.tabbable.TabbableController; -import org.olat.core.gui.translator.Translator; -import org.olat.core.id.OLATResourceable; -import org.olat.core.id.Roles; -import org.olat.core.util.Util; -import org.olat.core.util.coordinate.CoordinatorManager; -import org.olat.course.ICourse; -import org.olat.course.assessment.AssessmentManager; -import org.olat.course.groupsandrights.CourseGroupManager; -import org.olat.course.nodes.IQSELFCourseNode; -import org.olat.course.nodes.IQSURVCourseNode; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.ims.qti.fileresource.SurveyFileResource; -import org.olat.ims.qti.fileresource.TestFileResource; -import org.olat.modules.ModuleConfiguration; -import org.olat.modules.iq.IQSecurityCallback; -import org.olat.repository.RepositoryEntry; - -import de.bps.onyx.plugin.run.OnyxRunController; - -/** - * Description:<br> - * TODO: patrickb Class Description for IQControllerCreatorOlat - * - * <P> - * Initial Date: 18.06.2010 <br> - * @author patrickb - */ -public class IQControllerCreatorOlat implements IQControllerCreator { - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQTestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQTESTCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce){ - return new IQEditController(ureq, wControl, stackPanel, course, courseNode, euce); - } - - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQSelftestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSELFCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce){ - return new IQEditController(ureq, wControl, stackPanel, course, courseNode, euce); - } - - - /** - * The iq test edit screen in the course editor. - * @param ureq - * @param wControl - * @param course - * @param courseNode - * @param groupMgr - * @param euce - * @return - */ - public TabbableController createIQSurveyEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSURVCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce){ - return new IQEditController(ureq, wControl, stackPanel, course, courseNode, euce); - } - - /** - * - * @param ureq - * @param wControl - * @param userCourseEnv - * @param ne - * @param courseNode - * @return - */ - @Override - public Controller createIQTestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQTESTCourseNode courseNode) { - Controller controller; - // Do not allow guests to start tests - Roles roles = ureq.getUserSession().getRoles(); - Translator trans = Util.createPackageTranslator(IQTESTCourseNode.class, ureq.getLocale()); - if (roles.isGuestOnly()) { - String title = trans.translate("guestnoaccess.title"); - String message = trans.translate("guestnoaccess.message"); - controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); - } else { - ModuleConfiguration config = courseNode.getModuleConfiguration(); - boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); - if (onyx) { - controller = new OnyxRunController(userCourseEnv, config, ureq, wControl, courseNode); - } else { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - IQSecurityCallback sec = new CourseIQSecurityCallback(courseNode, am, ureq.getIdentity()); - RepositoryEntry repositoryEntry = courseNode.getReferencedRepositoryEntry(); - OLATResourceable ores = repositoryEntry.getOlatResource(); - Long resId = ores.getResourceableId(); - TestFileResource fr = new TestFileResource(); - fr.overrideResourceableId(resId); - if(!CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(fr, null)) { - //QTI1 - controller = new IQRunController(userCourseEnv, courseNode.getModuleConfiguration(), sec, ureq, wControl, courseNode); - } else { - String title = trans.translate("editor.lock.title"); - String message = trans.translate("editor.lock.message"); - controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); - } - } - } - return controller; - } - - @Override - public Controller createIQTestPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQTESTCourseNode courseNode) { - Controller controller; - ModuleConfiguration config = courseNode.getModuleConfiguration(); - boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); - if (onyx) { - controller = new OnyxRunController(ureq, wControl, courseNode); - } else { - controller = new IQPreviewController(ureq, wControl, userCourseEnv, courseNode); - } - return controller; - } - - @Override - public Controller createIQSelftestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQSELFCourseNode courseNode) { - Controller controller; - ModuleConfiguration config = courseNode.getModuleConfiguration(); - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); - if (onyx) { - controller = new OnyxRunController(userCourseEnv, config, ureq, wControl, courseNode); - } else { - IQSecurityCallback sec = new CourseIQSecurityCallback(courseNode, am, ureq.getIdentity()); - controller = new IQRunController(userCourseEnv, courseNode.getModuleConfiguration(), sec, ureq, wControl, courseNode); - } - return controller; - } - - @Override - public Controller createIQSurveyRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQSURVCourseNode courseNode) { - Controller controller; - - // Do not allow guests to start questionnaires - Roles roles = ureq.getUserSession().getRoles(); - if (roles.isGuestOnly()) { - Translator trans = Util.createPackageTranslator(IQSURVCourseNode.class, ureq.getLocale()); - String title = trans.translate("guestnoaccess.title"); - String message = trans.translate("guestnoaccess.message"); - controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); - } else { - ModuleConfiguration config = courseNode.getModuleConfiguration(); - boolean onyx = IQEditController.CONFIG_VALUE_QTI2.equals(config.get(IQEditController.CONFIG_KEY_TYPE_QTI)); - if (onyx) { - controller = new OnyxRunController(userCourseEnv, config, ureq, wControl, courseNode); - } else { - RepositoryEntry repositoryEntry = courseNode.getReferencedRepositoryEntry(); - OLATResourceable ores = repositoryEntry.getOlatResource(); - Long resId = ores.getResourceableId(); - SurveyFileResource fr = new SurveyFileResource(); - fr.overrideResourceableId(resId); - if(!CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(fr, null)) { - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - IQSecurityCallback sec = new CourseIQSecurityCallback(courseNode, am, ureq.getIdentity()); - controller = new IQRunController(userCourseEnv, courseNode.getModuleConfiguration(), sec, ureq, wControl, courseNode); - } else { - Translator trans = Util.createPackageTranslator(IQSURVCourseNode.class, ureq.getLocale()); - String title = trans.translate("editor.lock.title"); - String message = trans.translate("editor.lock.message"); - controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message); - } - } - } - return controller; - } -} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQEditController.java b/src/main/java/org/olat/course/nodes/iq/IQEditController.java index ee6a8238195e78e63faae87db4cfeb3b42fd2a28..04dd24ab2b318c103ea519afb59d81f136f9719f 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQEditController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQEditController.java @@ -25,88 +25,30 @@ package org.olat.course.nodes.iq; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.dom4j.Attribute; -import org.dom4j.Document; -import org.dom4j.Element; -import org.olat.basesecurity.BaseSecurityManager; -import org.olat.basesecurity.Constants; -import org.olat.commons.file.filechooser.FileChooseCreateEditController; -import org.olat.commons.file.filechooser.LinkChooseCreateEditController; -import org.olat.core.CoreSpringFactory; -import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; 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.panel.Panel; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.components.tabbedpane.TabbedPane; -import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.ControllerEventListener; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.control.generic.tabbable.ActivateableTabbableDefaultController; -import org.olat.core.id.Identity; -import org.olat.core.id.Roles; import org.olat.core.logging.AssertException; -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.core.util.StringHelper; -import org.olat.core.util.coordinate.CoordinatorManager; -import org.olat.core.util.coordinate.LockResult; -import org.olat.core.util.vfs.LocalFileImpl; -import org.olat.core.util.vfs.LocalFolderImpl; -import org.olat.core.util.vfs.VFSConstants; -import org.olat.core.util.vfs.VFSContainer; -import org.olat.core.util.vfs.VFSItem; -import org.olat.core.util.vfs.VFSStatus; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.condition.Condition; import org.olat.course.condition.ConditionEditController; import org.olat.course.editor.NodeEditController; 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.IQTESTCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.course.tree.CourseInternalLinkTreeModel; -import org.olat.fileresource.FileResourceManager; -import org.olat.ims.qti.QTIResult; -import org.olat.ims.qti.QTIResultManager; -import org.olat.ims.qti.editor.QTIEditorPackage; -import org.olat.ims.qti.editor.QTIEditorPackageImpl; -import org.olat.ims.qti.editor.beecom.objects.Assessment; -import org.olat.ims.qti.editor.beecom.objects.Item; -import org.olat.ims.qti.editor.beecom.objects.Section; -import org.olat.ims.qti.fileresource.SurveyFileResource; -import org.olat.ims.qti.fileresource.TestFileResource; import org.olat.ims.qti.process.AssessmentInstance; -import org.olat.ims.qti.process.QTIHelper; import org.olat.modules.ModuleConfiguration; -import org.olat.modules.iq.IQManager; -import org.olat.modules.iq.IQPreviewSecurityCallback; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; -import org.olat.repository.controllers.ReferencableEntriesSearchController; -import org.olat.repository.handlers.RepositoryHandler; -import org.olat.repository.handlers.RepositoryHandlerFactory; -import org.olat.resource.OLATResource; -import org.olat.user.UserManager; - -import de.bps.onyx.plugin.OnyxModule; -import de.bps.onyx.plugin.course.nodes.iq.IQEditForm; -import de.bps.webservices.clients.onyxreporter.OnyxReporterConnector; -import de.bps.webservices.clients.onyxreporter.OnyxReporterException; /** * Description:<BR/> @@ -123,8 +65,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl public static final String PANE_TAB_IQCONFIG_SELF = "pane.tab.iqconfig.self"; public static final String PANE_TAB_IQCONFIG_TEST = "pane.tab.iqconfig.test"; public static final String PANE_TAB_ACCESSIBILITY = "pane.tab.accessibility"; - private static final String VC_CHOSENTEST = "chosentest"; - private static final String ACTION_CORRECT ="correcttest"; + /** configuration key: repository sof key reference to qti file*/ public static final String CONFIG_KEY_REPOSITORY_SOFTKEY = "repoSoftkey"; /** configuration key: the disclaimer text*/ @@ -192,36 +133,23 @@ public class IQEditController extends ActivateableTabbableDefaultController impl public static final Object CONFIG_VALUE_QTI1 = "qti1"; public static final String CONFIG_KEY_IS_SURVEY = "issurv"; - private OLog log = Tracing.createLoggerFor(IQEditController.class); private final String[] paneKeys; - private ModuleConfiguration moduleConfiguration; - private Panel main; - private VelocityContainer myContent; - private IQEditForm modOnyxConfigForm; - private IQ12EditForm mod12ConfigForm; - private ReferencableEntriesSearchController searchController; private ICourse course; - private ConditionEditController accessibilityCondContr; - private AbstractAccessableCourseNode courseNode; + private String type; private UserCourseEnvironment euce; + private AbstractAccessableCourseNode courseNode; + private ModuleConfiguration moduleConfiguration; + private TabbedPane myTabbedPane; - private FileChooseCreateEditController fccecontr; - private Controller correctQTIcontroller; - private Boolean allowRelativeLinks; - private Link previewLink; - private Link chooseTestButton; - private Link changeTestButton; - private IQEditReplaceWizard replaceWizard; - private List<Identity> learners; private Controller previewLayoutCtr; - private CloseableModalController cmc; - private Link editTestButton; + private final BreadcrumbPanel stackPanel; - private final IQManager iqManager; + private ConditionEditController accessibilityCondContr; + private IQConfigurationController configurationCtrl; /** * Constructor for the IMS QTI edit controller for a test course node @@ -233,7 +161,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl * @param groupMgr * @param euce */ - IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQTESTCourseNode courseNode, UserCourseEnvironment euce) { + public IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQTESTCourseNode courseNode, UserCourseEnvironment euce) { super(ureq, wControl); this.stackPanel = stackPanel; this.moduleConfiguration = courseNode.getModuleConfiguration(); @@ -242,22 +170,22 @@ public class IQEditController extends ActivateableTabbableDefaultController impl this.courseNode = courseNode; this.euce = euce; - iqManager = CoreSpringFactory.getImpl(IQManager.class); type = AssessmentInstance.QMD_ENTRY_TYPE_ASSESS; this.PANE_TAB_IQCONFIG_XXX = PANE_TAB_IQCONFIG_TEST; paneKeys = new String[]{PANE_TAB_IQCONFIG_XXX,PANE_TAB_ACCESSIBILITY}; // put some default values - if (moduleConfiguration.get(CONFIG_KEY_ENABLECANCEL) == null) + if (moduleConfiguration.get(CONFIG_KEY_ENABLECANCEL) == null) { moduleConfiguration.set(CONFIG_KEY_ENABLECANCEL, Boolean.FALSE); - if (moduleConfiguration.get(CONFIG_KEY_ENABLESUSPEND) == null) + } + if (moduleConfiguration.get(CONFIG_KEY_ENABLESUSPEND) == null) { moduleConfiguration.set(CONFIG_KEY_ENABLESUSPEND, Boolean.FALSE); - if(moduleConfiguration.get(CONFIG_KEY_RENDERMENUOPTION) == null) + } + if(moduleConfiguration.get(CONFIG_KEY_RENDERMENUOPTION) == null) { moduleConfiguration.set(CONFIG_KEY_RENDERMENUOPTION, Boolean.FALSE); + } - init(ureq, wControl); - myContent.contextPut("repEntryTitle", translate("choosenfile.test")); - myContent.contextPut("type", type); + init(ureq); } /** @@ -270,26 +198,26 @@ public class IQEditController extends ActivateableTabbableDefaultController impl * @param groupMgr * @param euce */ - IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSELFCourseNode courseNode , UserCourseEnvironment euce) { + public IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSELFCourseNode courseNode , UserCourseEnvironment euce) { super(ureq, wControl); this.stackPanel = stackPanel; this.moduleConfiguration = courseNode.getModuleConfiguration(); this.course = course; this.courseNode = courseNode; this.euce = euce; - iqManager = CoreSpringFactory.getImpl(IQManager.class); + type = AssessmentInstance.QMD_ENTRY_TYPE_SELF; this.PANE_TAB_IQCONFIG_XXX = PANE_TAB_IQCONFIG_SELF; paneKeys = new String[]{PANE_TAB_IQCONFIG_XXX,PANE_TAB_ACCESSIBILITY}; // put some default values - if (moduleConfiguration.get(CONFIG_KEY_ENABLECANCEL) == null) + if (moduleConfiguration.get(CONFIG_KEY_ENABLECANCEL) == null) { moduleConfiguration.set(CONFIG_KEY_ENABLECANCEL, Boolean.TRUE); - if (moduleConfiguration.get(CONFIG_KEY_ENABLESUSPEND) == null) + } + if (moduleConfiguration.get(CONFIG_KEY_ENABLESUSPEND) == null) { moduleConfiguration.set(CONFIG_KEY_ENABLESUSPEND, Boolean.TRUE); + } - init(ureq, wControl); - myContent.contextPut("repEntryTitle", translate("choosenfile.self")); - myContent.contextPut("type", type); + init(ureq); } /** @@ -302,14 +230,14 @@ public class IQEditController extends ActivateableTabbableDefaultController impl * @param groupMgr * @param euce */ - IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSURVCourseNode courseNode, UserCourseEnvironment euce) { + public IQEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, IQSURVCourseNode courseNode, UserCourseEnvironment euce) { super(ureq, wControl); this.stackPanel = stackPanel; this.moduleConfiguration = courseNode.getModuleConfiguration(); this.course = course; this.courseNode = courseNode; this.euce = euce; - iqManager = CoreSpringFactory.getImpl(IQManager.class); + type = AssessmentInstance.QMD_ENTRY_TYPE_SURVEY; this.PANE_TAB_IQCONFIG_XXX = PANE_TAB_IQCONFIG_SURV; paneKeys = new String[]{PANE_TAB_IQCONFIG_XXX,PANE_TAB_ACCESSIBILITY}; @@ -322,468 +250,45 @@ public class IQEditController extends ActivateableTabbableDefaultController impl moduleConfiguration.setBooleanEntry(CONFIG_KEY_ALLOW_RELATIVE_LINKS,false); } - init(ureq, wControl); - myContent.contextPut("repEntryTitle", translate("choosenfile.surv")); - myContent.contextPut("type", type); - chooseTestButton.setCustomDisplayText(translate("command.createSurvey")); + init(ureq); } - private void init(UserRequest ureq, WindowControl wControl) { - main = new Panel("iqeditpanel"); - - myContent = createVelocityContainer("edit"); - chooseTestButton = LinkFactory.createButtonSmall("command.chooseRepFile", myContent, this); - chooseTestButton.setElementCssClass("o_sel_test_choose_repofile"); - changeTestButton = LinkFactory.createButtonSmall("command.changeRepFile", myContent, this); - changeTestButton.setElementCssClass("o_sel_test_change_repofile"); - - - // fetch repository entry - RepositoryEntry re = null; - String displayName = ""; - String repoSoftkey = (String) moduleConfiguration.get(CONFIG_KEY_REPOSITORY_SOFTKEY); - if (repoSoftkey != null) { - re = getIQReference(moduleConfiguration, false); - displayName = StringHelper.escapeHtml(re.getDisplayname()); - } - - myContent.contextPut(VC_CHOSENTEST, re == null ? translate("no.file.chosen") : displayName); - if (re != null) { - myContent.contextPut("dontRenderRepositoryButton", new Boolean(true)); - // Put values to velocity container + private void init(UserRequest ureq) { + configurationCtrl = new IQConfigurationController(ureq, getWindowControl(), this.stackPanel, course, courseNode, euce, type); + listenTo(configurationCtrl); - boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); - if(isOnyx) { - setOnyxVariables(re); - } else { - if (isEditable(ureq.getIdentity(), ureq.getUserSession().getRoles(), re)) { - editTestButton = LinkFactory.createButtonSmall("command.editRepFile", myContent, this); - } - myContent.contextPut("showOutcomes", Boolean.FALSE); - myContent.contextPut(CONFIG_KEY_MINSCORE, moduleConfiguration.get(CONFIG_KEY_MINSCORE)); - myContent.contextPut(CONFIG_KEY_MAXSCORE, moduleConfiguration.get(CONFIG_KEY_MAXSCORE)); - myContent.contextPut(CONFIG_KEY_CUTVALUE, moduleConfiguration.get(CONFIG_KEY_CUTVALUE)); - } - - previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayName, Link.NONTRANSLATED, myContent, this); - previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); - previewLink.setCustomEnabledLinkCSS("o_preview"); - previewLink.setTitle(getTranslator().translate("command.preview")); - } - - String disclaimer = (String) moduleConfiguration.get(CONFIG_KEY_DISCLAIMER); - //allowRelativeLinks = courseNode.getModuleConfiguration().getBooleanEntry(CONFIG_KEY_ALLOW_RELATIVE_LINKS); - - String legend = translate("fieldset.chosecreateeditfile"); - - allowRelativeLinks = moduleConfiguration.getBooleanEntry(CONFIG_KEY_ALLOW_RELATIVE_LINKS); - if(allowRelativeLinks==null){ - allowRelativeLinks=Boolean.FALSE; - } - fccecontr = new LinkChooseCreateEditController(ureq, wControl, disclaimer, allowRelativeLinks, course.getCourseFolderContainer(), type, legend, new CourseInternalLinkTreeModel(course.getEditorTreeModel()) ); - listenTo(fccecontr); - - Component fcContent = fccecontr.getInitialComponent(); - myContent.put("filechoosecreateedit", fcContent); - Condition accessCondition = courseNode.getPreConditionAccess(); accessibilityCondContr = new ConditionEditController(ureq, getWindowControl(), accessCondition, AssessmentHelper.getAssessableNodes(course.getEditorTreeModel(), courseNode),euce); listenTo(accessibilityCondContr); - - main.setContent(myContent); - - updateEditController(ureq); - } - - private void updateEditController(UserRequest ureq) { - removeAsListenerAndDispose(mod12ConfigForm); - removeAsListenerAndDispose(modOnyxConfigForm); - - RepositoryEntry re = getIQReference(moduleConfiguration, false); - if(re == null || !OnyxModule.isOnyxTest(re.getOlatResource())) { - mod12ConfigForm = new IQ12EditForm(ureq, getWindowControl(), moduleConfiguration); - listenTo(mod12ConfigForm); - myContent.put("iqeditform", mod12ConfigForm.getInitialComponent()); - } else { - modOnyxConfigForm = new IQEditForm(ureq, getWindowControl(), moduleConfiguration, re); - listenTo(modOnyxConfigForm); - myContent.put("iqeditform", modOnyxConfigForm.getInitialComponent()); - } - } - - private void setOnyxVariables(RepositoryEntry entry) { - myContent.contextPut("onyxDisplayName", entry.getDisplayname()); - myContent.contextPut("showOutcomes", Boolean.TRUE); - Map<String, String> outcomes = new HashMap<String, String>(); - try { - OnyxReporterConnector onyxReporter = new OnyxReporterConnector(); - outcomes = onyxReporter.getPossibleOutcomeVariables(courseNode); - } catch (OnyxReporterException e) { - getWindowControl().setWarning(translate("reporter.unavailable")); - } - myContent.contextPut("outcomes", outcomes); } - /** - * @param identity - * @param repository entry - * @return - */ - private boolean isEditable(Identity identity, Roles roles, RepositoryEntry re) { - boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); - if (isOnyx) { - return false; - } - - return (BaseSecurityManager.getInstance().isIdentityPermittedOnResourceable(identity, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_ADMIN) - || RepositoryManager.getInstance().isOwnerOfRepositoryEntry(identity, re) - || RepositoryManager.getInstance().isInstitutionalRessourceManagerFor(identity, roles, re)); - } - - /** - * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, - * org.olat.core.gui.components.Component, org.olat.core.gui.control.Event) - */ + @Override public void event(UserRequest ureq, Component source, Event event) { - if (source == myContent) { - if(event.getCommand().equals(ACTION_CORRECT)){ - /* FIXME:pb:remove this elseif code, as the use case "correct" is started from details view only - * - check if the test is in use at the moment - * - check if already results exist - * - check if test is referenced from other courses - */ - String repoSoftKey = (String)moduleConfiguration.get(CONFIG_KEY_REPOSITORY_SOFTKEY); - RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repoSoftKey,false); - if(re==null) { - //not found - } else { - RepositoryHandler typeToEdit = RepositoryHandlerFactory.getInstance().getRepositoryHandler(re); - correctQTIcontroller = typeToEdit.createEditorController(re, ureq, getWindowControl(), null); - getWindowControl().pushToMainArea(correctQTIcontroller.getInitialComponent()); - listenTo(correctQTIcontroller); - } - } - } else if (source == previewLink){ - removeAsListenerAndDispose(previewLayoutCtr); - // handle preview - long courseResId = course.getResourceableId().longValue(); - Controller previewController = iqManager.createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), courseResId, courseNode.getIdent(), null); - previewLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), previewController); - stackPanel.pushController(translate("preview"), previewLayoutCtr); - - } else if (source == chooseTestButton){// initiate search controller - if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, - SurveyFileResource.TYPE_NAME, translate("command.chooseSurvey")); - } else { // test and selftest use same repository resource type - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, - TestFileResource.TYPE_NAME, translate("command.chooseTest")); - } - listenTo(searchController); - cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent(), true, translate("command.chooseRepFile")); - cmc.activate(); - } else if (source == changeTestButton) {//change associated test - if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)) {//selftest - String[] types = new String[]{TestFileResource.TYPE_NAME}; - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseTest")); - cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent()); - listenTo(searchController); - } else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS) | type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) {//test, survey - String[] types; - if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {//test - types = new String[]{TestFileResource.TYPE_NAME}; - } else {//survey - types = new String[]{SurveyFileResource.TYPE_NAME}; - } - - RepositoryEntry re = courseNode.getReferencedRepositoryEntry(); - if(re == null) { - showError("error.test.undefined.long", courseNode.getShortTitle()); - return; - } - - if (moduleConfiguration.get(CONFIG_KEY_TYPE_QTI) == null) { - updateQtiType(re); - } - - int onyxSuccess = 0; - if (moduleConfiguration.get(CONFIG_KEY_TYPE_QTI) != null && moduleConfiguration.get(CONFIG_KEY_TYPE_QTI).equals(CONFIG_VALUE_QTI2)) { - if (courseNode.getClass().equals(IQSURVCourseNode.class)) { - File surveyDir = new File(course.getCourseEnvironment().getCourseBaseContainer() - .getBasefile() + File.separator + courseNode.getIdent() + File.separator); - if (surveyDir != null && surveyDir.exists() && surveyDir.listFiles().length > 0) { - onyxSuccess = surveyDir.listFiles().length; - } - } else { - onyxSuccess = QTIResultManager.getInstance().countResults(course.getResourceableId(), courseNode.getIdent(), re.getKey()); - } - } - if (moduleConfiguration.get(CONFIG_KEY_TYPE_QTI) != null - && moduleConfiguration.get(CONFIG_KEY_TYPE_QTI).equals(CONFIG_VALUE_QTI2) - && onyxSuccess > 0) { - replaceWizard = new IQEditReplaceWizard(ureq, getWindowControl(), course, courseNode, types, learners, null, onyxSuccess, true); - replaceWizard.addControllerListener(this); - String title = replaceWizard.getAndRemoveWizardTitle(); - cmc = new CloseableModalController(getWindowControl(), translate("close"), replaceWizard.getInitialComponent(), true, title); - } else { - List<QTIResult> results = QTIResultManager.getInstance().selectResults(course.getResourceableId(), courseNode.getIdent(), re.getKey(), null, 1); - // test was passed from an user - boolean passed = (results != null && results.size() > 0) ? true : false; - // test was started and not passed - // it exists partly results for this test - List<Identity> identitiesWithQtiSerEntry = iqManager.getIdentitiesWithQtiSerEntry(course.getResourceableId(), courseNode.getIdent()); - if(passed || identitiesWithQtiSerEntry.size() > 0) { - learners = new ArrayList<Identity>(); - for(QTIResult result : results) { - Identity identity = result.getResultSet().getIdentity(); - if(identity != null && !learners.contains(identity)) learners.add(identity); - } - // add identities with qti.ser entry - for (Identity identity : identitiesWithQtiSerEntry) { - if(!learners.contains(identity)) learners.add(identity); - } - replaceWizard = new IQEditReplaceWizard(ureq, getWindowControl(), course, courseNode, types, learners, results, identitiesWithQtiSerEntry.size(), false); - replaceWizard.addControllerListener(this); - cmc = new CloseableModalController(getWindowControl(), translate("close"), replaceWizard.getInitialComponent()); - } else { - if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {//test - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseTest")); - } else {//survey - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, types, translate("command.chooseSurvey")); - } - listenTo(searchController); - cmc = new CloseableModalController(getWindowControl(), translate("close"), searchController.getInitialComponent()); - } - } - } - cmc.activate(); - } - else if (source == editTestButton) { - CourseNodeFactory.getInstance().launchReferencedRepoEntryEditor(ureq, getWindowControl(), courseNode); - } + // } - /** - * This method updates the QTI Type in the editortreemodel. - * - * @param re Needed to check if this Test is of QTI Type 2.1 - */ - private void updateQtiType(RepositoryEntry re) { - boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); - if (isOnyx) { - moduleConfiguration.set(CONFIG_KEY_TYPE_QTI, CONFIG_VALUE_QTI2); - } else { - moduleConfiguration.set(CONFIG_KEY_TYPE_QTI, CONFIG_VALUE_QTI1); - } - } - - /** - * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) - */ @Override public void event(UserRequest urequest, Controller source, Event event) { - if (source.equals(searchController)) { - if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { - // repository search controller done - cmc.deactivate(); - RepositoryEntry re = searchController.getSelectedEntry(); - doIQReference(urequest, re); - updateEditController(urequest); - checkEssay(re); - } - } else if (source == accessibilityCondContr) { + if (source == accessibilityCondContr) { if (event == Event.CHANGED_EVENT) { Condition cond = accessibilityCondContr.getCondition(); courseNode.setPreConditionAccess(cond); fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); } - } else if (source == fccecontr) { - if (event == FileChooseCreateEditController.FILE_CHANGED_EVENT) { - String chosenFile = fccecontr.getChosenFile(); - if (chosenFile != null){ - moduleConfiguration.set(CONFIG_KEY_DISCLAIMER, fccecontr.getChosenFile()); - } - else { - moduleConfiguration.remove(CONFIG_KEY_DISCLAIMER); - } + } else if (source == configurationCtrl) { + if (event == NodeEditController.NODECONFIG_CHANGED_EVENT) { fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); - } else if (event == FileChooseCreateEditController.ALLOW_RELATIVE_LINKS_CHANGED_EVENT) { - allowRelativeLinks = fccecontr.getAllowRelativeLinks(); - courseNode.getModuleConfiguration().setBooleanEntry(CONFIG_KEY_ALLOW_RELATIVE_LINKS, allowRelativeLinks.booleanValue()); - fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } else if (source == correctQTIcontroller){ - if(event == Event.DONE_EVENT){ - //getWindowControl().pop(); } - } else if (source == replaceWizard) { - if(event == Event.CANCELLED_EVENT) { - cmc.deactivate(); - } else if(event == Event.DONE_EVENT) { - cmc.deactivate(); - String repositorySoftKey = (String) moduleConfiguration.get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); - Long repKey = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repositorySoftKey, true).getKey(); - QTIResultManager.getInstance().deleteAllResults(course.getResourceableId(), courseNode.getIdent(), repKey); - removeIQReference(moduleConfiguration); - VFSStatus isDeleted = iqManager.removeQtiSerFiles(course.getResourceableId(), courseNode.getIdent()); - if (!isDeleted.equals(VFSConstants.YES)) { - // couldn't removed qtiser files - log.warn("Couldn't removed course node folder! Course resourceable id: " + course.getResourceableId() + ", Course node ident: " + courseNode.getIdent()); - } - doIQReference(urequest, replaceWizard.getSelectedRepositoryEntry()); - } - } else if (source == mod12ConfigForm) { // config form action - if (event == Event.DONE_EVENT) { - doMod12ConfigForm(mod12ConfigForm); - fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } else if (source == modOnyxConfigForm) { - if (event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) { - doModOnyxConfigForm(modOnyxConfigForm); - fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } - } - - private void checkEssay(RepositoryEntry re) { - if(OnyxModule.isOnyxTest(re.getOlatResource())) return; - if(courseNode instanceof IQSURVCourseNode || courseNode instanceof IQSELFCourseNode) return; - - TestFileResource fr = new TestFileResource(); - fr.overrideResourceableId(re.getOlatResource().getResourceableId()); - QTIEditorPackage qtiPackage = new QTIEditorPackageImpl(getIdentity(), fr, null, getTranslator()); - Assessment ass = qtiPackage.getQTIDocument().getAssessment(); - - //Sections with their Items - List<Section> sections = ass.getSections(); - for (Section section:sections) { - List<Item> items = section.getItems(); - for (Item item:items) { - String ident = item.getIdent(); - if(ident != null && ident.startsWith("QTIEDIT:ESSAY")) { - showWarning("warning.test.with.essay"); - break; - } - } - } - } - - private void doModOnyxConfigForm(IQEditForm modConfigForm) { - moduleConfiguration.set(CONFIG_KEY_TEMPLATE, modConfigForm.getTemplate()); - if (!(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY))) { - moduleConfiguration.set(CONFIG_KEY_ATTEMPTS, modConfigForm.getAttempts()); - moduleConfiguration.set(CONFIG_KEY_CUTVALUE, modConfigForm.getCutValue()); - } - moduleConfiguration.set(CONFIG_KEY_DATE_DEPENDENT_RESULTS, new Boolean(modConfigForm.isShowResultsDateDependent())); - moduleConfiguration.set(CONFIG_KEY_RESULTS_START_DATE, modConfigForm.getShowResultsStartDate()); - moduleConfiguration.set(CONFIG_KEY_RESULTS_END_DATE, modConfigForm.getShowResultsEndDate()); - moduleConfiguration.set(CONFIG_KEY_RESULT_ON_HOME_PAGE, modConfigForm.isShowResultsOnHomePage()); - //<OLATCE-982> - moduleConfiguration.set(CONFIG_KEY_ALLOW_SHOW_SOLUTION, modConfigForm.allowShowSolution()); - //</OLATCE-982> - //<OLATCE-2009> - moduleConfiguration.set(CONFIG_KEY_ALLOW_SUSPENSION_ALLOWED, modConfigForm.allowSuspension()); - //</OLATCE-2009> - } - - private void doMod12ConfigForm(IQ12EditForm modConfigForm) { - moduleConfiguration.set(CONFIG_KEY_DISPLAYMENU, new Boolean(modConfigForm.isDisplayMenu())); - moduleConfiguration.set(CONFIG_FULLWINDOW, new Boolean(modConfigForm.isFullWindow())); - - if (modConfigForm.isDisplayMenu()) { - moduleConfiguration.set(CONFIG_KEY_RENDERMENUOPTION, modConfigForm.isMenuRenderSectionsOnly()); - moduleConfiguration.set(CONFIG_KEY_ENABLEMENU, new Boolean(modConfigForm.isEnableMenu())); - } else { - // set default values when menu is not displayed - moduleConfiguration.set(CONFIG_KEY_RENDERMENUOPTION, Boolean.FALSE); - moduleConfiguration.set(CONFIG_KEY_ENABLEMENU, Boolean.FALSE); - } - - moduleConfiguration.set(CONFIG_KEY_QUESTIONPROGRESS, new Boolean(modConfigForm.isDisplayQuestionProgress())); - moduleConfiguration.set(CONFIG_KEY_SEQUENCE, modConfigForm.getSequence()); - moduleConfiguration.set(CONFIG_KEY_ENABLECANCEL, new Boolean(modConfigForm.isEnableCancel())); - moduleConfiguration.set(CONFIG_KEY_ENABLESUSPEND, new Boolean(modConfigForm.isEnableSuspend())); - moduleConfiguration.set(CONFIG_KEY_QUESTIONTITLE, new Boolean(modConfigForm.isDisplayQuestionTitle())); - moduleConfiguration.set(CONFIG_KEY_AUTOENUM_CHOICES, new Boolean(modConfigForm.isAutoEnumChoices())); - moduleConfiguration.set(CONFIG_KEY_MEMO, new Boolean(modConfigForm.isProvideMemoField())); - // Only tests and selftests have summaries and score progress - if (!type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { - moduleConfiguration.set(CONFIG_KEY_SUMMARY, modConfigForm.getSummary()); - moduleConfiguration.set(CONFIG_KEY_SCOREPROGRESS, new Boolean(modConfigForm.isDisplayScoreProgress())); - moduleConfiguration.set(CONFIG_KEY_ENABLESCOREINFO, new Boolean(modConfigForm.isEnableScoreInfo())); - moduleConfiguration.set(CONFIG_KEY_DATE_DEPENDENT_RESULTS, new Boolean(modConfigForm.isShowResultsDateDependent())); - moduleConfiguration.set(CONFIG_KEY_RESULTS_START_DATE, modConfigForm.getShowResultsStartDate()); - moduleConfiguration.set(CONFIG_KEY_RESULTS_END_DATE, modConfigForm.getShowResultsEndDate()); - moduleConfiguration.set(CONFIG_KEY_RESULT_ON_FINISH, modConfigForm.isShowResultsAfterFinishTest()); - moduleConfiguration.set(CONFIG_KEY_RESULT_ON_HOME_PAGE, modConfigForm.isShowResultsOnHomePage()); - } - // Only tests have a limitation on number of attempts - if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { - moduleConfiguration.set(CONFIG_KEY_ATTEMPTS, modConfigForm.getAttempts()); - moduleConfiguration.set(CONFIG_KEY_BLOCK_AFTER_SUCCESS, new Boolean(modConfigForm.isBlockAfterSuccess())); - } + } } - private void doIQReference(UserRequest urequest, RepositoryEntry re) { - // repository search controller done - if (re != null) { - if (CoordinatorManager.getInstance().getCoordinator().getLocker().isLocked(re.getOlatResource(), null)) { - LockResult lockResult = CoordinatorManager.getInstance().getCoordinator().getLocker().acquireLock(re.getOlatResource(), urequest.getIdentity(), null); - String fullName = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(lockResult.getOwner()); - showError("error.entry.locked", fullName); - if(lockResult.isSuccess()) { - //improbable concurrency security - CoordinatorManager.getInstance().getCoordinator().getLocker().releaseLock(lockResult); - } - } else { - if(editTestButton != null) { - myContent.remove(editTestButton); - } - - setIQReference(re, moduleConfiguration); - String displayName = StringHelper.escapeHtml(re.getDisplayname()); - previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayName, Link.NONTRANSLATED, myContent, this); - previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); - previewLink.setCustomEnabledLinkCSS("o_preview"); - previewLink.setTitle(getTranslator().translate("command.preview")); - myContent.contextPut("dontRenderRepositoryButton", new Boolean(true)); - // If of type test, get min, max, cut - put in module config and push - // to velocity - - boolean isOnyx = OnyxModule.isOnyxTest(re.getOlatResource()); - myContent.contextPut("isOnyx", new Boolean(isOnyx)); - if(isOnyx) { - myContent.contextPut("onyxDisplayName", displayName); - moduleConfiguration.set(CONFIG_KEY_TYPE_QTI, CONFIG_VALUE_QTI2); - setOnyxVariables(re); - } else { - myContent.contextPut("showOutcomes", Boolean.FALSE); - moduleConfiguration.set(CONFIG_KEY_TYPE_QTI, CONFIG_VALUE_QTI1); - if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { - updateModuleConfigFromQTIFile(re.getOlatResource()); - // Put values to velocity container - myContent.contextPut(CONFIG_KEY_MINSCORE, moduleConfiguration.get(CONFIG_KEY_MINSCORE)); - myContent.contextPut(CONFIG_KEY_MAXSCORE, moduleConfiguration.get(CONFIG_KEY_MAXSCORE)); - myContent.contextPut(CONFIG_KEY_CUTVALUE, moduleConfiguration.get(CONFIG_KEY_CUTVALUE)); - } - if (isEditable(urequest.getIdentity(), urequest.getUserSession().getRoles(), re)) { - editTestButton = LinkFactory.createButtonSmall("command.editRepFile", myContent, this); - } - } - fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } - } - - /** - * @see org.olat.core.gui.control.generic.tabbable.TabbableDefaultController#addTabs(org.olat.core.gui.components.TabbedPane) - */ + @Override public void addTabs(TabbedPane tabbedPane) { myTabbedPane = tabbedPane; tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondContr.getWrappedDefaultAccessConditionVC(translate("condition.accessibility.title"))); //PANE_TAB_IQCONFIG_XXX is set during construction time - tabbedPane.addTab(translate(PANE_TAB_IQCONFIG_XXX), main); + tabbedPane.addTab(translate(PANE_TAB_IQCONFIG_XXX), configurationCtrl.getInitialComponent()); } /** @@ -831,66 +336,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl moduleConfiguration.remove(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY); } - /** - * Update the module configuration from the qti file: read min/max/cut values - * @param res - */ - private void updateModuleConfigFromQTIFile(OLATResource res) { - FileResourceManager frm = FileResourceManager.getInstance(); - File unzippedRoot = frm.unzipFileResource(res); - //with VFS FIXME:pb:c: remove casts to LocalFileImpl and LocalFolderImpl if no longer needed. - VFSContainer vfsUnzippedRoot = new LocalFolderImpl(unzippedRoot); - VFSItem vfsQTI = vfsUnzippedRoot.resolve("qti.xml"); - if (vfsQTI==null){ - throw new AssertException("qti file did not exist even it should be guaranteed by repositor check-in "); - } - //ensures that InputStream is closed in every case. - Document doc = QTIHelper.getDocument((LocalFileImpl)vfsQTI); - if(doc == null){ - //error reading qti file (existence check was made before) - throw new AssertException("qti file could not be read " + ((LocalFileImpl)vfsQTI).getBasefile().getAbsolutePath()); - } - // Extract min, max and cut value - Float minValue = null, maxValue = null, cutValue = null; - Element decvar = (Element) doc.selectSingleNode("questestinterop/assessment/outcomes_processing/outcomes/decvar"); - if (decvar != null) { - Attribute minval = decvar.attribute("minvalue"); - if (minval != null) { - String mv = minval.getValue(); - try { - minValue = new Float(Float.parseFloat(mv)); - } catch (NumberFormatException e1) { - // if not correct in qti file -> ignore - } - } - Attribute maxval = decvar.attribute("maxvalue"); - if (maxval != null) { - String mv = maxval.getValue(); - try { - maxValue = new Float(Float.parseFloat(mv)); - } catch (NumberFormatException e1) { - // if not correct in qti file -> ignore - } - } - Attribute cutval = decvar.attribute("cutvalue"); - if (cutval != null) { - String cv = cutval.getValue(); - try { - cutValue = new Float(Float.parseFloat(cv)); - } catch (NumberFormatException e1) { - // if not correct in qti file -> ignore - } - } - } - // Put values to module configuration - moduleConfiguration.set(CONFIG_KEY_MINSCORE, minValue); - moduleConfiguration.set(CONFIG_KEY_MAXSCORE, maxValue); - moduleConfiguration.set(CONFIG_KEY_CUTVALUE, cutValue); - } - - /** - * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) - */ + @Override protected void doDispose() { //child controllers registered with listenTo() get disposed in BasicController if (previewLayoutCtr != null) { @@ -899,13 +345,13 @@ public class IQEditController extends ActivateableTabbableDefaultController impl } } + @Override public String[] getPaneKeys() { return paneKeys; } + @Override public TabbedPane getTabbedPane() { return myTabbedPane; } - - } \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/iq/IQPreviewController.java b/src/main/java/org/olat/course/nodes/iq/IQPreviewController.java index 1eac23ac2146a3acd043f8b8e573437ea1f56561..e6f82f133f68736bafcc645e25b9ba126b4bf8a1 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQPreviewController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQPreviewController.java @@ -42,7 +42,7 @@ import org.olat.course.run.userview.UserCourseEnvironment; * * @author Felix Jost */ -class IQPreviewController extends BasicController { +public class IQPreviewController extends BasicController { private PreviewForm pf; private final UserCourseEnvironment userCourseEnv; @@ -55,7 +55,7 @@ class IQPreviewController extends BasicController { * @param cn * @param ne */ - IQPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQTESTCourseNode cn) { + public IQPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, IQTESTCourseNode cn) { super(ureq, wControl); this.userCourseEnv = userCourseEnv; @@ -65,9 +65,12 @@ class IQPreviewController extends BasicController { putInitialPanel(pf.getInitialComponent()); } + @Override public void event(UserRequest ureq, Component source, Event event) { // } + + @Override public void event(UserRequest ureq, Controller source, Event event) { if (source == pf) { if (event == Event.DONE_EVENT) { @@ -86,6 +89,7 @@ class IQPreviewController extends BasicController { /** * @see org.olat.core.gui.control.DefaultController#doDispose(boolean) */ + @Override protected void doDispose() { // nothing to dispose } diff --git a/src/main/java/org/olat/course/nodes/iq/IQRunController.java b/src/main/java/org/olat/course/nodes/iq/IQRunController.java index 3dccffb6e16d48444d8e2b83ea848dec92fac413..f716c37f0447566e798d16277bc0a995b48cebf0 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQRunController.java @@ -139,7 +139,7 @@ public class IQRunController extends BasicController implements GenericEventList * @param wControl * @param testCourseNode */ - IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQTESTCourseNode testCourseNode) { + public IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQTESTCourseNode testCourseNode) { super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale())); this.modConfig = moduleConfiguration; @@ -230,7 +230,7 @@ public class IQRunController extends BasicController implements GenericEventList * @param wControl * @param selftestCourseNode */ - IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQSELFCourseNode selftestCourseNode) { + public IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQSELFCourseNode selftestCourseNode) { super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale())); this.modConfig = moduleConfiguration; @@ -271,7 +271,7 @@ public class IQRunController extends BasicController implements GenericEventList * @param wControl * @param surveyCourseNode */ - IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQSURVCourseNode surveyCourseNode) { + public IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl, IQSURVCourseNode surveyCourseNode) { super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale())); this.modConfig = moduleConfiguration; diff --git a/src/main/java/org/olat/course/nodes/iq/IQUIFactory.java b/src/main/java/org/olat/course/nodes/iq/IQUIFactory.java deleted file mode 100644 index e6c086868382556f92f8745915e9e923939f53a2..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/iq/IQUIFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/** -* OLAT - Online Learning and Training<br> -* http://www.olat.org -* <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 -* <p> -* http://www.apache.org/licenses/LICENSE-2.0 -* <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> -* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> -* University of Zurich, Switzerland. -* <hr> -* <a href="http://www.openolat.org"> -* OpenOLAT - Online Learning and Training</a><br> -* This file has been modified by the OpenOLAT community. Changes are licensed -* under the Apache 2.0 license as the original file. -*/ -package org.olat.course.nodes.iq; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.stack.BreadcrumbPanel; -import org.olat.core.gui.control.Controller; -import org.olat.core.gui.control.WindowControl; -import org.olat.core.gui.control.generic.tabbable.TabbableController; -import org.olat.course.ICourse; -import org.olat.course.groupsandrights.CourseGroupManager; -import org.olat.course.nodes.IQSELFCourseNode; -import org.olat.course.nodes.IQSURVCourseNode; -import org.olat.course.nodes.IQTESTCourseNode; -import org.olat.course.run.userview.UserCourseEnvironment; - -/** - * Description:<br> - * TODO: patrickb Class Description for IQUIFactory - * - * <P> - * Initial Date: 18.06.2010 <br> - * - * @author patrickb - */ -public class IQUIFactory { - - /** - * [SPRING] builds the SpecificControllerCreator as argument in the - * constructor. This "extension point" was created during ONYX integration - * review. It delegates creation to the factory instead of using new - * XXXController(..) creation of different IQxyzRun / IQxyzEdit / - * IQxyzPreviewControllers within the IQxyzCourseNodes. - */ - private static IQControllerCreator iqControllerCreator = new IQControllerCreatorOlat(); - - public IQUIFactory() { - // - } - - public static TabbableController createIQTestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQTESTCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce) { - return IQUIFactory.iqControllerCreator.createIQTestEditController(ureq, wControl, stackPanel, course, courseNode, groupMgr, euce); - } - - public static TabbableController createIQSelftestEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQSELFCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce) { - return IQUIFactory.iqControllerCreator.createIQSelftestEditController(ureq, wControl, stackPanel, course, courseNode, groupMgr, euce); - } - - public static TabbableController createIQSurveyEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, - IQSURVCourseNode courseNode, CourseGroupManager groupMgr, UserCourseEnvironment euce) { - return IQUIFactory.iqControllerCreator.createIQSurveyEditController(ureq, wControl, stackPanel, course, courseNode, groupMgr, euce); - } - - public static Controller createIQTestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQTESTCourseNode courseNode) { - return IQUIFactory.iqControllerCreator.createIQTestRunController(ureq, wControl, userCourseEnv, courseNode); - } - - public static Controller createIQTestPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQTESTCourseNode courseNode) { - return IQUIFactory.iqControllerCreator.createIQTestPreviewController(ureq, wControl, userCourseEnv, courseNode); - } - - public static Controller createIQSelftestRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQSELFCourseNode courseNode) { - return IQUIFactory.iqControllerCreator.createIQSelftestRunController(ureq, wControl, userCourseEnv, courseNode); - } - - public static Controller createIQSurveyRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, - IQSURVCourseNode courseNode) { - return IQUIFactory.iqControllerCreator.createIQSurveyRunController(ureq, wControl, userCourseEnv, courseNode); - } -} diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java similarity index 94% rename from src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentDetailsController.java rename to src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java index 68c2617b2fe540c37593efdbb8f03ae4097bbe44..1711070cd9f1d277406c0e3937a76857f7ed6ec3 100644 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.qti21; +package org.olat.course.nodes.iq; import java.util.List; @@ -39,8 +39,8 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.id.Identity; -import org.olat.course.nodes.QTI21AssessmentCourseNode; -import org.olat.course.nodes.qti21.QTI21TestSessionTableModel.TSCols; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.nodes.iq.QTI21TestSessionTableModel.TSCols; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.UserTestSession; @@ -61,7 +61,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private Identity assessedIdentity; private RepositoryEntry courseEntry; - private QTI21AssessmentCourseNode courseNode; + private IQTESTCourseNode courseNode; private CloseableModalController cmc; private AssessmentResultController resultCtrl; @@ -70,7 +70,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private QTI21Service qtiService; public QTI21AssessmentDetailsController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment userCourseEnvironment, QTI21AssessmentCourseNode courseNode) { + UserCourseEnvironment userCourseEnvironment, IQTESTCourseNode courseNode) { super(ureq, wControl, "assessment_details"); this.courseNode = courseNode; diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentRunController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java similarity index 93% rename from src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentRunController.java rename to src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java index 95210ef329465ac909f13bb11bb8002485f2a198..6380df660fdbdd7d2fb817f919df49ca82221049 100644 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.qti21; +package org.olat.course.nodes.iq; import java.text.DateFormat; import java.util.Date; @@ -40,6 +40,7 @@ import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.UserSession; +import org.olat.core.util.Util; import org.olat.core.util.event.EventBus; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; @@ -49,9 +50,8 @@ import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.nodes.AssessableCourseNode; -import org.olat.course.nodes.QTI21AssessmentCourseNode; -import org.olat.course.nodes.iq.AssessmentEvent; -import org.olat.course.nodes.iq.IQEditController; +import org.olat.course.nodes.CourseNode; +import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti.process.AssessmentInstance; @@ -83,14 +83,14 @@ public class QTI21AssessmentRunController extends BasicController implements Gen private final UserSession userSession; private final ModuleConfiguration config; private final UserCourseEnvironment userCourseEnv; - private final QTI21AssessmentCourseNode courseNode; + private final IQTESTCourseNode courseNode; private AssessmentTestDisplayController displayCtrl; private LayoutMain3ColsController displayContainerController; public QTI21AssessmentRunController(UserRequest ureq, WindowControl wControl, - UserCourseEnvironment userCourseEnv, QTI21AssessmentCourseNode courseNode) { - super(ureq, wControl); + UserCourseEnvironment userCourseEnv, IQTESTCourseNode courseNode) { + super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale())); this.courseNode = courseNode; this.userCourseEnv = userCourseEnv; @@ -98,7 +98,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen config = courseNode.getModuleConfiguration(); singleUserEventCenter = ureq.getUserSession().getSingleUserEventCenter(); - mainVC = createVelocityContainer("run"); + mainVC = createVelocityContainer("assessment_run"); init(); initAssessment(ureq); putInitialPanel(mainVC); @@ -112,11 +112,11 @@ public class QTI21AssessmentRunController extends BasicController implements Gen private void initAssessment(UserRequest ureq) { // config : show score info - boolean enableScoreInfo= config.getBooleanSafe(QTI21AssessmentCourseNode.CONFIG_KEY_ENABLESCOREINFO); + boolean enableScoreInfo= config.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLESCOREINFO); mainVC.contextPut("enableScoreInfo", new Boolean(enableScoreInfo)); // configuration data - mainVC.contextPut("attemptsConfig", config.get(QTI21AssessmentCourseNode.CONFIG_KEY_ATTEMPTS)); + mainVC.contextPut("attemptsConfig", config.get(IQEditController.CONFIG_KEY_ATTEMPTS)); // user data if (!(courseNode instanceof AssessableCourseNode)) { throw new AssertException("exposeUserTestDataToVC can only be called for test nodes, not for selftest or questionnaire"); @@ -126,7 +126,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen ScoreEvaluation scoreEval = acn.getUserScoreEvaluation(userCourseEnv); //block if test passed (and config set to check it) - boolean blockAfterSuccess = config.getBooleanSafe(QTI21AssessmentCourseNode.CONFIG_KEY_BLOCK_AFTER_SUCCESS); + boolean blockAfterSuccess = config.getBooleanSafe(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS); Boolean blocked = Boolean.FALSE; if(blockAfterSuccess) { Boolean passed = scoreEval.getPassed(); @@ -253,7 +253,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen Controller disposedRestartController = new LayoutMain3ColsController(ureq, getWindowControl(), empty, courseCloser.getInitialComponent(), "disposed course while in assessment run " + courseResId); displayContainerController.setDisposedMessageController(disposedRestartController); - boolean fullWindow = config.getBooleanSafe(QTI21AssessmentCourseNode.CONFIG_FULLWINDOW); + boolean fullWindow = config.getBooleanSafe(IQEditController.CONFIG_FULLWINDOW); if(fullWindow) { displayContainerController.setAsFullscreen(ureq); } diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java b/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java new file mode 100644 index 0000000000000000000000000000000000000000..6c1187d84dea621a0ef043958216360069607279 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java @@ -0,0 +1,78 @@ +/** + * <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 org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.SelectionElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.modules.ModuleConfiguration; + +/** + * + * Initial date: 26.06.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21EditForm extends FormBasicController { + + + private SelectionElement fullWindowEl; + + private ModuleConfiguration modConfig; + + public QTI21EditForm(UserRequest ureq, WindowControl wControl, ModuleConfiguration modConfig) { + super(ureq, wControl); + + this.modConfig = modConfig; + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + boolean fullWindow = modConfig.getBooleanSafe(IQEditController.CONFIG_FULLWINDOW); + fullWindowEl = uifactory.addCheckboxesHorizontal("fullwindow", "qti.form.fullwindow", formLayout, new String[]{"x"}, new String[]{""}); + fullWindowEl.select("x", fullWindow); + + uifactory.addSpacerElement("submitSpacer", formLayout, true); + uifactory.addFormSubmitButton("submit", formLayout); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + modConfig.setBooleanEntry(IQEditController.CONFIG_FULLWINDOW, fullWindowEl.isSelected(0)); + + fireEvent(ureq, Event.DONE_EVENT); + } + + + + + +} diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21TestSessionTableModel.java b/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java similarity index 98% rename from src/main/java/org/olat/course/nodes/qti21/QTI21TestSessionTableModel.java rename to src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java index fee59918170ab8d4767c27f3042d5f5631926823..5f833c95f847951131aec9a5b07da3a95d607b29 100644 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21TestSessionTableModel.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.qti21; +package org.olat.course.nodes.iq; import java.util.Date; diff --git a/src/main/java/org/olat/course/nodes/qti21/_content/assessment_details.html b/src/main/java/org/olat/course/nodes/iq/_content/assessment_details.html similarity index 100% rename from src/main/java/org/olat/course/nodes/qti21/_content/assessment_details.html rename to src/main/java/org/olat/course/nodes/iq/_content/assessment_details.html diff --git a/src/main/java/org/olat/course/nodes/qti21/_content/run.html b/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html similarity index 98% rename from src/main/java/org/olat/course/nodes/qti21/_content/run.html rename to src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html index a308a1e59eeb0a20e794867462bf4b74929d33e3..522ebc77612459af89939bd364bac9be2b71c921 100644 --- a/src/main/java/org/olat/course/nodes/qti21/_content/run.html +++ b/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html @@ -115,7 +115,7 @@ #end <div class="o_statusinfo"> - <p>$r.translate("intro.assessment")</p> + <p>$r.translate("Intro.test")</p> <p>$r.translate("info.assessment")</p> #if ($hasChatWindowOpen) <p>$r.translate("close.chat.windows.for.test")</p> 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 181bb6a9505e6561fa9ce95b7a4749243b4613f5..f0bc80024a49b81ff9ddd8b572ff579acb52773d 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 @@ -197,4 +197,6 @@ showResults.detailed=Resultate showResults.title=Resultate showResults.visibility=Die Resultate werden von "{0}" bis "{1}" angezeigt. start=Start +table.header.lastModified=Last modified +table.header.results=Resultaten warning.test.with.essay=$org.olat.ims.qti.editor\:warning.test.with.essay \ No newline at end of file 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 96bad3020270ab1694f4addcf799f1e13b3792f3..13220c94554c0a4d79893d007fa0b295bfb273a8 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 @@ -197,4 +197,6 @@ showResults.detailed=Results showResults.title=Results showResults.visibility=Your results will be displayed from "{0}" until "{1}" start=Start +table.header.lastModified=Last modified +table.header.results=Results warning.test.with.essay=$org.olat.ims.qti.editor\:warning.test.with.essay diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentCourseNodeConfiguration.java b/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentCourseNodeConfiguration.java deleted file mode 100644 index b5dbd2a5559f6685d2b6ee90a85158dedb3ba721..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21AssessmentCourseNodeConfiguration.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * <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.qti21; - -import java.util.Locale; - -import org.olat.core.gui.translator.Translator; -import org.olat.core.util.Util; -import org.olat.course.nodes.AbstractCourseNodeConfiguration; -import org.olat.course.nodes.CourseNode; -import org.olat.course.nodes.CourseNodeConfiguration; -import org.olat.course.nodes.CourseNodeGroup; -import org.olat.course.nodes.QTI21AssessmentCourseNode; - -/** - * - * Initial date: 23.02.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21AssessmentCourseNodeConfiguration extends AbstractCourseNodeConfiguration { - - @Override - public CourseNode getInstance() { - return new QTI21AssessmentCourseNode(); - } - - @Override - public String getLinkText(Locale locale) { - Translator fallback = Util.createPackageTranslator(CourseNodeConfiguration.class, locale); - Translator translator = Util.createPackageTranslator(this.getClass(), locale, fallback); - return translator.translate("title_qti21assessment"); - } - - @Override - public String getIconCSSClass() { - return "o_qtiassessment_icon"; - } - - @Override - public String getAlias() { - return "qti21assessment"; - } - - @Override - public String getGroup() { - return CourseNodeGroup.assessment.name(); - } -} diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21EditController.java b/src/main/java/org/olat/course/nodes/qti21/QTI21EditController.java deleted file mode 100644 index 68662cd71d064c6a3118e80b5f45f1071e4fd862..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21EditController.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * <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.qti21; - -import org.olat.core.gui.UserRequest; -import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.stack.BreadcrumbPanel; -import org.olat.core.gui.components.tabbedpane.TabbedPane; -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.generic.tabbable.ActivateableTabbableDefaultController; -import org.olat.course.ICourse; -import org.olat.course.assessment.AssessmentHelper; -import org.olat.course.condition.Condition; -import org.olat.course.condition.ConditionEditController; -import org.olat.course.editor.NodeEditController; -import org.olat.course.nodes.QTI21AssessmentCourseNode; -import org.olat.course.run.userview.UserCourseEnvironment; -import org.olat.course.tree.CourseEditorTreeModel; - -/** - * - * Initial date: 19.05.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21EditController extends ActivateableTabbableDefaultController { - - - public static final String PANE_TAB_CONFIG_RE = "pane.tab.config.entry"; - public static final String PANE_TAB_ACCESSIBILITY = "pane.tab.accessibility"; - - public static final String[] paneKeys = { - PANE_TAB_ACCESSIBILITY, PANE_TAB_CONFIG_RE - }; - - private TabbedPane myTabbedPane; - private final QTI21AssessmentCourseNode qtiNode; - - private final ConditionEditController accessibilityCondCtrl; - private final QTI21ReferenceConfigurationController referenceCtrl; - - public QTI21EditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, - QTI21AssessmentCourseNode qtiNode, ICourse course, UserCourseEnvironment euce) { - super(ureq, wControl); - - this.qtiNode = qtiNode; - - // Accessibility precondition - Condition accessCondition = qtiNode.getPreConditionAccess(); - CourseEditorTreeModel editorModel = course.getEditorTreeModel(); - accessibilityCondCtrl = new ConditionEditController(ureq, getWindowControl(), - accessCondition, AssessmentHelper.getAssessableNodes(editorModel, qtiNode), euce); - listenTo(accessibilityCondCtrl); - - referenceCtrl = new QTI21ReferenceConfigurationController(ureq, getWindowControl(), stackPanel, qtiNode); - listenTo(referenceCtrl); - } - - @Override - protected void doDispose() { - // - } - - @Override - public void addTabs(TabbedPane tabbedPane) { - myTabbedPane = tabbedPane; - tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondCtrl.getWrappedDefaultAccessConditionVC(translate("condition.accessibility.title"))); - tabbedPane.addTab(translate(PANE_TAB_CONFIG_RE), referenceCtrl.getInitialComponent()); - } - - @Override - public String[] getPaneKeys() { - return paneKeys; - } - - @Override - public TabbedPane getTabbedPane() { - return myTabbedPane; - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - // - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if (source == accessibilityCondCtrl) { - if (event == Event.CHANGED_EVENT) { - Condition cond = accessibilityCondCtrl.getCondition(); - qtiNode.setPreConditionAccess(cond); - fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } else if(referenceCtrl == source) { - if (event == Event.CHANGED_EVENT) { - fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); - } - } - - super.event(ureq, source, event); - } -} diff --git a/src/main/java/org/olat/course/nodes/qti21/QTI21ReferenceConfigurationController.java b/src/main/java/org/olat/course/nodes/qti21/QTI21ReferenceConfigurationController.java deleted file mode 100644 index 4e12be4ae19e5482abf893fec99caa85542af94d..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/QTI21ReferenceConfigurationController.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * <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.qti21; - -import java.io.File; - -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.stack.BreadcrumbPanel; -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.Identity; -import org.olat.core.id.Roles; -import org.olat.core.util.StringHelper; -import org.olat.course.nodes.CourseNodeFactory; -import org.olat.course.nodes.MSCourseNode; -import org.olat.course.nodes.QTI21AssessmentCourseNode; -import org.olat.fileresource.FileResourceManager; -import org.olat.fileresource.types.ImsQTI21Resource; -import org.olat.ims.qti21.QTI21Constants; -import org.olat.ims.qti21.QTI21Service; -import org.olat.ims.qti21.ui.InMemoryOutcomesListener; -import org.olat.ims.qti21.ui.AssessmentTestDisplayController; -import org.olat.modules.ModuleConfiguration; -import org.olat.repository.RepositoryEntry; -import org.olat.repository.controllers.ReferencableEntriesSearchController; -import org.olat.resource.OLATResource; -import org.springframework.beans.factory.annotation.Autowired; - -import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration; -import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; -import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest; -import uk.ac.ed.ph.jqtiplus.value.NumberValue; -import uk.ac.ed.ph.jqtiplus.value.Value; - -/** - * - * Initial date: 19.05.2015<br> - * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * - */ -public class QTI21ReferenceConfigurationController extends BasicController { - - private Link previewLink, editLink; - private final VelocityContainer mainVC; - private final Link chooseButton, changeButton; - private final BreadcrumbPanel stackPanel; - - private Controller previewCtr; - private CloseableModalController cmc; - private ReferencableEntriesSearchController searchController; - - private QTI21AssessmentCourseNode courseNode; - private final ModuleConfiguration config; - - @Autowired - private QTI21Service qtiService; - - public QTI21ReferenceConfigurationController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, - QTI21AssessmentCourseNode courseNode) { - super(ureq, wControl); - - this.courseNode = courseNode; - this.stackPanel = stackPanel; - config = courseNode.getModuleConfiguration(); - mainVC = createVelocityContainer("reference"); - - chooseButton = LinkFactory.createButtonSmall("command.create", mainVC, this); - chooseButton.setElementCssClass("o_sel_qti21_choose_repofile"); - changeButton = LinkFactory.createButtonSmall("command.change", mainVC, this); - changeButton.setElementCssClass("o_sel_qti21_change_repofile"); - - if (config.get(QTI21AssessmentCourseNode.CONFIG_KEY_REPOSITORY_SOFTKEY) != null) { - // fetch repository entry to display the repository entry title of the chosen cp - RepositoryEntry re = courseNode.getReferencedRepositoryEntry(); - if (re == null) { // we cannot display the entries name, because the - // repository entry had been deleted between the time when it was chosen here, and now - showError("error.entry.missing"); - mainVC.contextPut("showPreviewButton", Boolean.FALSE); - mainVC.contextPut("chosen", translate("no.test.chosen")); - } else { - if (isEditable(ureq.getIdentity(), ureq.getUserSession().getRoles(), re)) { - editLink = LinkFactory.createButtonSmall("edit", mainVC, this); - } - mainVC.contextPut("showPreviewButton", Boolean.TRUE); - String displayname = StringHelper.escapeHtml(re.getDisplayname()); - previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayname, Link.NONTRANSLATED, mainVC, this); - previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview"); - previewLink.setTitle(getTranslator().translate("command.preview")); - updateModuleConfigFromQTIFile(re.getOlatResource()); - } - } else { - // no valid config yet - mainVC.contextPut("showPreviewButton", Boolean.FALSE); - mainVC.contextPut("chosen", translate("no.test.chosen")); - } - - putInitialPanel(mainVC); - } - - private boolean isEditable(Identity identity, Roles roles, RepositoryEntry entry) { - return identity != null && roles != null && entry != null; - } - - @Override - protected void doDispose() { - // - } - - @Override - protected void event(UserRequest ureq, Component source, Event event) { - if (source == chooseButton || source == changeButton) { - removeAsListenerAndDispose(searchController); - searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, - ImsQTI21Resource.TYPE_NAME, translate("choose")); - listenTo(searchController); - - removeAsListenerAndDispose(cmc); - cmc = new CloseableModalController(getWindowControl(), translate("close"), - searchController.getInitialComponent(), true, translate("choose.assessment")); - listenTo(cmc); - cmc.activate(); - } else if (source == previewLink){ - // Preview as modal dialogue only if the config is valid - RepositoryEntry re = courseNode.getReferencedRepositoryEntry(); - if (re == null) { // we cannot preview it, because the repository entry - // had been deleted between the time when it was chosen here, and now - showError("error.entry.missing"); - } else { - removeAsListenerAndDispose(previewCtr); - InMemoryOutcomesListener listener = new InMemoryOutcomesListener(); - previewCtr = new AssessmentTestDisplayController(ureq, getWindowControl(), listener, re, null, null); - stackPanel.pushController(translate("preview"), previewCtr); - } - } else if (source == editLink) { - CourseNodeFactory.getInstance().launchReferencedRepoEntryEditor(ureq, getWindowControl(), courseNode); - } - } - - @Override - public void event(UserRequest urequest, Controller source, Event event) { - if (source == searchController) { - if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { - // search controller done - // -> close closeable modal controller - cmc.deactivate(); - RepositoryEntry re = searchController.getSelectedEntry(); - if (re != null) { - config.setStringValue(QTI21AssessmentCourseNode.CONFIG_KEY_REPOSITORY_SOFTKEY, re.getSoftkey()); - updateModuleConfigFromQTIFile(re.getOlatResource()); - mainVC.contextPut("showPreviewButton", Boolean.TRUE); - - String displayname = StringHelper.escapeHtml(re.getDisplayname()); - previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayname, Link.NONTRANSLATED, mainVC, this); - previewLink.setCustomEnabledLinkCSS("o_preview"); - previewLink.setTitle(getTranslator().translate("command.preview")); - // remove existing edit link, add new one if user is allowed to edit this CP - if (editLink != null) { - mainVC.remove(editLink); - editLink = null; - } - if (isEditable(urequest.getIdentity(), urequest.getUserSession().getRoles(), re)) { - editLink = LinkFactory.createButtonSmall("edit", mainVC, this); - } - // fire event so the updated config is saved by the editormaincontroller - fireEvent(urequest, Event.CHANGED_EVENT); - } - } - } - } - - private void updateModuleConfigFromQTIFile(OLATResource res) { - FileResourceManager frm = FileResourceManager.getInstance(); - File fUnzippedDirRoot = frm.unzipFileResource(res); - - ResolvedAssessmentTest resolvedAssessmentTest = qtiService.loadAndResolveAssessmentObject(fUnzippedDirRoot); - AssessmentTest test = resolvedAssessmentTest.getTestLookup().getRootNodeHolder().getRootNode(); - - Float minValue = null, maxValue = null, cutValue = null; - for (OutcomeDeclaration declaration : test.getOutcomeDeclarations()) { - if(QTI21Constants.SCORE_IDENTIFIER.equals(declaration.getIdentifier())) { - minValue = extractValue(declaration); - } else if(QTI21Constants.MAXSCORE_IDENTIFIER.equals(declaration.getIdentifier())) { - maxValue = extractValue(declaration); - } - } - - // Put values to module configuration - config.set(MSCourseNode.CONFIG_KEY_SCORE_MIN, minValue); - config.set(MSCourseNode.CONFIG_KEY_SCORE_MAX, maxValue); - config.set(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE, cutValue); - } - - private Float extractValue(OutcomeDeclaration declaration) { - Float floatValue = null; - Value value = declaration.getDefaultValue().evaluate(); - if(value instanceof NumberValue) { - floatValue = (float)((NumberValue)value).doubleValue(); - } - return floatValue; - } -} diff --git a/src/main/java/org/olat/course/nodes/qti21/_content/reference.html b/src/main/java/org/olat/course/nodes/qti21/_content/reference.html deleted file mode 100644 index 326221574620cfbb58f62ef8012d8af85c846bce..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/_content/reference.html +++ /dev/null @@ -1,26 +0,0 @@ -<fieldset class="o_form form-horizontal "> - <legend>$r.translate("header")</legend> - - #if ($showPreviewButton) - <div class="form-group"> - <label class="control-label col-sm-3">$r.translate("chosen")</label> - <div class="col-sm-9"><p class="form-control-static">$r.render("command.preview")</p></div> - </div> - <div class="form-group"> - <div class="col-sm-offset-3 col-sm-9"> - $r.render("command.change") - #if($r.available("edit")) - $r.render("edit") - #end - </div> - </div> - #else - <div class="form-group"> - <label class="control-label col-sm-3">$r.translate("chosen")</label> - <div class="col-sm-9"><p class="form-control-static">$chosen</p></div> - </div> - <div class="form-group"> - <div class="col-sm-offset-3 col-sm-9">$r.render("command.create")</div> - </div> - #end -</fieldset> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_de.properties deleted file mode 100644 index e930ac2fe760ea2ccca45bb9cadbf59981e63ff2..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_de.properties +++ /dev/null @@ -1,24 +0,0 @@ -#Wed Feb 25 12:01:15 CET 2015 -condition.accessibility.title=Zugang -chosen=Gew\u00E4hlter QTI 2.1 Assessment -choose.assessment=QTI 2.1 Assessment ausw\u00E4hlen -command.preview=Vorschau anzeigen -command.change=QTI 2.1 Assessment auswechseln -command.create=QTI 2.1 Assessment w\u00E4hlen, erstellen oder importieren -command.choose=QTI 2.1 Assessment w\u00E4hlen -command.close=Ansicht schliessen -header=QTI 2.1 Assessment ausw\u00E4hlen - -no.test.chosen=<i>Kein QTI 2.1 Assessment ausgew\u00E4hlt</i> -error.entry.missing=Der Assessment, den Sie anzeigen m\u00F6chten, wurde in der Zwischenzeit in der Ablage der Lernressourcen gel\u00F6scht. -pane.tab.accessibility=Zugang -pane.tab.config.entry=Konfiguration - -qti.form.attempts.noLimit=$org.olat.course.nodes.iq\:qti.form.attempts.noLimit -qti.form.attempts=$org.olat.course.nodes.iq\:qti.form.attempts -score.noscoreinfoyet=$org.olat.course.nodes.iq\:score.noscoreinfoyet -intro.assessment=$org.olat.course.nodes.iq\:Intro.test -info.assessment=$org.olat.course.nodes.iq\:info.assessment - -table.header.lastModified=Last modified -table.header.results=Resultaten \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_en.properties deleted file mode 100644 index aa8e10ae6dd5151315a22293ccb79f814bd5a338..0000000000000000000000000000000000000000 --- a/src/main/java/org/olat/course/nodes/qti21/_i18n/LocalStrings_en.properties +++ /dev/null @@ -1,7 +0,0 @@ -#Wed Feb 25 12:08:35 CET 2015 -condition.accessibility.title=Access -pane.tab.accessibility=Access -pane.tab.config.entry=Configuration - -intro.assessment=$org.olat.course.nodes.iq\:Intro.test -info.assessment=$org.olat.course.nodes.iq\:info.assessment diff --git a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java index 49d1f64577fe17f489c5d189cc05f9fea00a22d8..efa1cc32aff0ea9e682a18513fc9ce1e7f5443d7 100644 --- a/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java +++ b/src/main/java/org/olat/ims/qti/statistics/QTIStatisticResourceResult.java @@ -42,6 +42,7 @@ import org.olat.course.nodes.QTICourseNode; import org.olat.course.nodes.TitledWrapperHelper; import org.olat.course.statistic.StatisticResourceNode; import org.olat.course.statistic.StatisticResourceResult; +import org.olat.fileresource.types.ImsQTI21Resource; import org.olat.ims.qti.editor.beecom.objects.Item; import org.olat.ims.qti.editor.beecom.objects.QTIDocument; import org.olat.ims.qti.editor.beecom.objects.Section; @@ -53,6 +54,7 @@ import org.olat.ims.qti.statistics.model.StatisticAssessment; import org.olat.ims.qti.statistics.ui.QTI12AssessmentStatisticsController; import org.olat.ims.qti.statistics.ui.QTI12ItemStatisticsController; import org.olat.ims.qti.statistics.ui.QTI21OnyxAssessmentStatisticsController; +import org.olat.ims.qti21.statistics.ui.QTI21AssessmentTestStatisticsController; import org.olat.repository.RepositoryEntry; import de.bps.onyx.plugin.OnyxModule; @@ -85,6 +87,8 @@ public class QTIStatisticResourceResult implements StatisticResourceResult { qtiRepositoryEntry = courseNode.getReferencedRepositoryEntry(); if(OnyxModule.isOnyxTest(qtiRepositoryEntry.getOlatResource())) { type = QTIType.onyx; + } else if(ImsQTI21Resource.TYPE_NAME.equals(qtiRepositoryEntry.getOlatResource().getResourceableTypeName())) { + type = QTIType.qtiworks; } else { resolver = new ImsRepositoryResolver(qtiRepositoryEntry); Document doc = resolver.getQTIDocument(); @@ -146,6 +150,10 @@ public class QTIStatisticResourceResult implements StatisticResourceResult { subTreeModel = new GenericTreeModel(); StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, this); subTreeModel.setRootNode(rootTreeNode); + } else if(type == QTIType.qtiworks) { + subTreeModel = new GenericTreeModel(); + StatisticResourceNode rootTreeNode = new StatisticResourceNode(courseNode, this); + subTreeModel.setRootNode(rootTreeNode); } else if(qtiDocument == null) { subTreeModel = null; } else { @@ -185,6 +193,8 @@ public class QTIStatisticResourceResult implements StatisticResourceResult { Controller ctrl; if (type == QTIType.onyx){ ctrl = new QTI21OnyxAssessmentStatisticsController(ureq, wControl, this, printMode); + } else if(type == QTIType.qtiworks) { + ctrl = new QTI21AssessmentTestStatisticsController(ureq, wControl, this, printMode); } else { ctrl = new QTI12AssessmentStatisticsController(ureq, wControl, stackPanel, this, printMode); } diff --git a/src/main/java/org/olat/ims/qti/statistics/QTIType.java b/src/main/java/org/olat/ims/qti/statistics/QTIType.java index 6c53e7603df97f664908d0c0b35254d70a3e08f0..2233db01a86e116c80c4aae1d7137ace148f2da1 100644 --- a/src/main/java/org/olat/ims/qti/statistics/QTIType.java +++ b/src/main/java/org/olat/ims/qti/statistics/QTIType.java @@ -28,6 +28,7 @@ public enum QTIType { test, survey, self, - onyx + onyx, + qtiworks } diff --git a/src/main/java/org/olat/ims/qti/statistics/ui/StatisticFormatter.java b/src/main/java/org/olat/ims/qti/statistics/ui/StatisticFormatter.java index 2901e1aaf208edfd269e2ea9dbd3b186e35dea1c..b90ce474f89180349a0b861f455413756b05656f 100644 --- a/src/main/java/org/olat/ims/qti/statistics/ui/StatisticFormatter.java +++ b/src/main/java/org/olat/ims/qti/statistics/ui/StatisticFormatter.java @@ -30,24 +30,24 @@ import java.util.concurrent.TimeUnit; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -class StatisticFormatter { +public class StatisticFormatter { private final static DecimalFormat numberFormat = new DecimalFormat("#0.#", new DecimalFormatSymbols(Locale.ENGLISH)); private final static DecimalFormat scoreFormat = new DecimalFormat("#0.##", new DecimalFormatSymbols(Locale.ENGLISH)); - protected static String getLabel(int i) { + public static String getLabel(int i) { String label = Character.toString((char)(65 + i)); return label; } - protected static String format(Float score) { + public static String format(Float score) { if(score == null) return ""; synchronized(numberFormat) { return numberFormat.format(score); } } - protected static String format(double score) { + public static String format(double score) { if(Double.isNaN(score)) { return ""; } @@ -57,7 +57,7 @@ class StatisticFormatter { } } - protected static String formatTwo(double score) { + public static String formatTwo(double score) { if(Double.isNaN(score)) { return ""; } @@ -67,7 +67,7 @@ class StatisticFormatter { } } - protected static String duration(long duration) { + public static String duration(long duration) { return String.format("%d min %d sec", TimeUnit.MILLISECONDS.toMinutes(duration), TimeUnit.MILLISECONDS.toSeconds(duration) - @@ -75,7 +75,7 @@ class StatisticFormatter { ); } - protected static String getModeString(List<Double> modes) { + public static String getModeString(List<Double> modes) { StringBuilder sb = new StringBuilder(); for(Double mode:modes) { if(sb.length() > 0) sb.append(", "); diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/QTI21AssessmentTestStatisticsController.java b/src/main/java/org/olat/ims/qti21/statistics/ui/QTI21AssessmentTestStatisticsController.java new file mode 100644 index 0000000000000000000000000000000000000000..ea39aa70f509bfd0ab00665520e1902d94cfac78 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/QTI21AssessmentTestStatisticsController.java @@ -0,0 +1,105 @@ +/** + * <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.statistics.ui; + +import static org.olat.ims.qti.statistics.ui.StatisticFormatter.duration; +import static org.olat.ims.qti.statistics.ui.StatisticFormatter.format; +import static org.olat.ims.qti.statistics.ui.StatisticFormatter.getModeString; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.chart.BarSeries; +import org.olat.core.gui.components.chart.StatisticsComponent; +import org.olat.core.gui.components.velocity.VelocityContainer; +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.ims.qti.statistics.QTIStatisticResourceResult; +import org.olat.ims.qti.statistics.model.StatisticAssessment; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class QTI21AssessmentTestStatisticsController extends BasicController { + + private final VelocityContainer mainVC; + + private final QTIStatisticResourceResult resourceResult; + + public QTI21AssessmentTestStatisticsController(UserRequest ureq, WindowControl wControl, + QTIStatisticResourceResult resourceResult, boolean printMode) { + super(ureq, wControl); + + this.resourceResult = resourceResult; + + mainVC = createVelocityContainer("statistics_assessment_test"); + mainVC.put("loadd3js", new StatisticsComponent("d3loader")); + mainVC.contextPut("printMode", new Boolean(printMode)); + putInitialPanel(mainVC); + + StatisticAssessment stats = resourceResult.getQTIStatisticAssessment(); + initScoreHistogram(stats); + initDurationHistogram(stats); + initCourseNodeInformation(stats); + } + + @Override + protected void doDispose() { + // + } + + private void initCourseNodeInformation(StatisticAssessment stats) { + mainVC.contextPut("numOfParticipants", stats.getNumOfParticipants()); + + mainVC.contextPut("type", resourceResult.getType()); + mainVC.contextPut("numOfPassed", stats.getNumOfPassed()); + mainVC.contextPut("numOfFailed", stats.getNumOfFailed()); + + mainVC.contextPut("average", format(stats.getAverage())); + mainVC.contextPut("range", format(stats.getRange())); + mainVC.contextPut("standardDeviation", format(stats.getStandardDeviation())); + mainVC.contextPut("mode", getModeString(stats.getMode())); + mainVC.contextPut("median", format(stats.getMedian())); + + String duration = duration(stats.getAverageDuration()); + mainVC.contextPut("averageDuration", duration); + } + + private void initScoreHistogram(StatisticAssessment stats) { + VelocityContainer scoreHistogramVC = createVelocityContainer("histogram_score"); + scoreHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getScores())); + mainVC.put("scoreHistogram", scoreHistogramVC); + } + + private void initDurationHistogram(StatisticAssessment stats) { + if(!BarSeries.hasNotNullDatas(stats.getDurations())) return; + + VelocityContainer durationHistogramVC = createVelocityContainer("histogram_duration"); + durationHistogramVC.contextPut("datas", BarSeries.datasToString(stats.getDurations())); + mainVC.put("durationHistogram", durationHistogramVC); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + // + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_duration.html b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_duration.html new file mode 100644 index 0000000000000000000000000000000000000000..35c29532a9ad9b2d5ca81eabbd82a4ac101a6205 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_duration.html @@ -0,0 +1,16 @@ +<div class="o_print_break_avoid"> + <h4>$r.translate("chart.duration.histogramm")</h4> + <div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:90%;height:300px'></div> + <script type='text/javascript'> + /* <![CDATA[ */ + jQuery(function () { + jQuery('#$r.getId("d3holder")').qtiStatistics('histogramDuration', { + values: [$datas], + xBottomLegend: '$r.translate("chart.duration.histogramm.legend")', + yLeftLegend: '$r.translate("chart.percent.participants")', + yRightLegend:'$r.translate("chart.percent.participants.num")' + }); + }); + /* ]]> */</script> + </div> +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_score.html b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_score.html new file mode 100644 index 0000000000000000000000000000000000000000..ae649ab3882f44d4b788d924324d7f55d4401806 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/histogram_score.html @@ -0,0 +1,17 @@ +<div class="o_print_break_avoid"> + <h4>$r.translate("chart.score.histogramm")</h4> + <div id="$r.getId('d3div')"><div id='$r.getId("d3holder")' class='d3chart' style='width:90%;height:300px'></div> + <script type='text/javascript'> + /* <![CDATA[ */ + jQuery(function () { + jQuery('#$r.getId("d3holder")').qtiStatistics('histogramScore', { + values: [$datas], + cut: #if($cutValue) $cutValue #else null #end, + xBottomLegend: '$r.translate("chart.points")', + yLeftLegend: '$r.translate("chart.percent.participants")', + yRightLegend:'$r.translate("chart.percent.participants.num")' + }); + }); + /* ]]> */</script> + </div> +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/_content/statistics_assessment_test.html b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/statistics_assessment_test.html new file mode 100644 index 0000000000000000000000000000000000000000..ce36d19f2a62b040887d8c8351213cdb3a1be6f1 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/_content/statistics_assessment_test.html @@ -0,0 +1,28 @@ +<div class="panel panel-default"> + <div class="panel-heading"><h4>$r.translate("fig.title")</h4></div> + <table class="table"><tbody> + <tr><th>$r.translate("fig.participants")</th> + <td>$numOfParticipants</td> + <th>$r.translate("fig.avg")</th> + <td>$average</td></tr> + <tr><th>$r.translate("fig.span")</th> + <td>$range</td> + <th>$r.translate("fig.stddev")</th> + <td>$standardDeviation</td></tr> + <tr><th>$r.translate("fig.mode")</th> + <td>$mode</td> + <th>$r.translate("fig.median")</th> + <td>$median</td></tr> + <tr><th>$r.translate("fig.averagedur")</th> + <td>$averageDuration</td> + <th></th><td></td> + </tr> + </tbody></table> +</div> + +#if($r.available("scoreHistogram")) + $r.render("scoreHistogram") +#end +#if($r.available("durationHistogram")) + $r.render("durationHistogram") +#end \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_de.properties new file mode 100644 index 0000000000000000000000000000000000000000..194e348eb41f401639be05c8d6a7cbb50ed0cb51 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_de.properties @@ -0,0 +1,10 @@ +#Tue Apr 15 11:18:52 CEST 2014 +fig.participants=$org.olat.ims.qti.statistics.ui\:fig.participants +fig.title=$org.olat.ims.qti.statistics.ui\:fig.title +fig.avg=$org.olat.ims.qti.statistics.ui\:fig.avg +fig.span=$org.olat.ims.qti.statistics.ui\:fig.span +fig.averagedur=$org.olat.ims.qti.statistics.ui\:fig.averagedur +fig.stddev=$org.olat.ims.qti.statistics.ui\:fig.stddev +fig.mode=$org.olat.ims.qti.statistics.ui\:fig.mode +fig.median=$org.olat.ims.qti.statistics.ui\:fig.median +chart.score.histogramm=$org.olat.ims.qti.statistics.ui\:chart.score.histogramm \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_en.properties new file mode 100644 index 0000000000000000000000000000000000000000..f1522eed4892055ce37922e5f9b8198df10aec9d --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/statistics/ui/_i18n/LocalStrings_en.properties @@ -0,0 +1,3 @@ +#Tue Apr 15 11:18:52 CEST 2014 +fig.participants=$org.olat.ims.qti.statistics.ui\:fig.participants +fig.title=$org.olat.ims.qti.statistics.ui\:fig.title \ No newline at end of file