/**
* 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 java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.dom4j.Document;
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.panel.StackedPanel;
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.dtabs.Activateable2;
import org.olat.core.gui.control.generic.iframe.IFrameDisplayController;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.Roles;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLATRuntimeException;
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;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.DisposedCourseRestartController;
import org.olat.course.ICourse;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.assessment.AssessmentManager;
import org.olat.course.assessment.manager.AssessmentNotificationsHandler;
import org.olat.course.auditing.UserNodeAuditManager;
import org.olat.course.highscore.ui.HighScoreRunController;
import org.olat.course.nodes.AssessableCourseNode;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.IQSELFCourseNode;
import org.olat.course.nodes.IQSURVCourseNode;
import org.olat.course.nodes.IQTESTCourseNode;
import org.olat.course.nodes.ObjectivesHelper;
import org.olat.course.nodes.PersistentAssessableCourseNode;
import org.olat.course.nodes.SelfAssessableCourseNode;
import org.olat.course.run.scoring.ScoreEvaluation;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.ims.qti.QTIChangeLogMessage;
import org.olat.ims.qti.container.AssessmentContext;
import org.olat.ims.qti.navigator.NavigatorDelegate;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.ImsRepositoryResolver;
import org.olat.instantMessaging.InstantMessagingService;
import org.olat.modules.ModuleConfiguration;
import org.olat.modules.assessment.AssessmentEntry;
import org.olat.modules.assessment.model.AssessmentEntryStatus;
import org.olat.modules.iq.IQDisplayController;
import org.olat.modules.iq.IQManager;
import org.olat.modules.iq.IQSecurityCallback;
import org.olat.modules.iq.IQSubmittedEvent;
import org.olat.repository.RepositoryEntry;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Description:<BR>
* Run controller for the qti test, selftest and survey course node.
* Call assessmentStopped if test is finished, closed or at dispose (e.g. course tab gets closed).
*
* Initial Date: Oct 13, 2004
* @author Felix Jost
*/
public class IQRunController extends BasicController implements GenericEventListener, Activateable2, NavigatorDelegate {
private VelocityContainer myContent;
private IQSecurityCallback secCallback;
private ModuleConfiguration modConfig;
private LayoutMain3ColsController displayContainerController;
private IQDisplayController displayController;
private CourseNode courseNode;
private String type;
private UserCourseEnvironment userCourseEnv;
private Link startButton;
private Link showResultsButton;
private Link hideResultsButton;
private IFrameDisplayController iFrameCtr;
private StackedPanel mainPanel;
private boolean assessmentStopped = true; //default: true
private UserSession userSession;
private final EventBus singleUserEventCenter;
private final OLATResourceable assessmentEventOres = OresHelper.createOLATResourceableType(AssessmentEvent.class);
private final OLATResourceable assessmentInstanceOres = OresHelper.createOLATResourceableType(AssessmentInstance.class);
private final RepositoryEntry referenceTestEntry;
@Autowired
private IQManager iqManager;
@Autowired
private AssessmentNotificationsHandler assessmentNotificationsHandler;
@Autowired
private CourseModule courseModule;
/**
* Constructor for a test run controller
* @param userCourseEnv
* @param moduleConfiguration
* @param secCallback
* @param ureq
* @param wControl
* @param testCourseNode
*/
public IQRunController(UserCourseEnvironment userCourseEnv, ModuleConfiguration moduleConfiguration,
IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl,
IQTESTCourseNode testCourseNode, RepositoryEntry testEntry) {
super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale()));
this.modConfig = moduleConfiguration;
this.secCallback = secCallback;
this.userCourseEnv = userCourseEnv;
this.courseNode = testCourseNode;
this.type = AssessmentInstance.QMD_ENTRY_TYPE_ASSESS;
this.singleUserEventCenter = ureq.getUserSession().getSingleUserEventCenter();
this.referenceTestEntry = testEntry;
this.userSession = ureq.getUserSession();
addLoggingResourceable(LoggingResourceable.wrap(courseNode));
myContent = createVelocityContainer("testrun");
mainPanel = putInitialPanel(myContent);
if (!modConfig.get(IQEditController.CONFIG_KEY_TYPE).equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
throw new OLATRuntimeException("IQRunController launched with Test constructor but module configuration not configured as test" ,null);
}
HighScoreRunController highScoreCtr = new HighScoreRunController(ureq, wControl, userCourseEnv, courseNode);
if (highScoreCtr.isViewHighscore()) {
Component highScoreComponent = highScoreCtr.getInitialComponent();
myContent.put("highScore", highScoreComponent);
}
init(ureq);
exposeUserTestDataToVC(ureq);
StringBuilder qtiChangelog = createChangelogMsg(ureq);
// decide about changelog in VC
if(qtiChangelog.length()>0){
//there is some message
myContent.contextPut("changeLog", qtiChangelog);
}
//if show results on test home page configured - show log
Boolean showResultOnHomePage = testCourseNode.getModuleConfiguration().getBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE);
myContent.contextPut("showChangelog", showResultOnHomePage);
//admin setting whether to show change log and info box or not
myContent.contextPut("infobox", courseModule.isDisplayInfoBox());
myContent.contextPut("changelogconfig", courseModule.isDisplayChangeLog());
}
/**
* @param ureq
* @return
*/
private StringBuilder createChangelogMsg(UserRequest ureq) {
/*
* TODO:pb:is ImsRepositoryResolver the right place for getting the change log?
*/
//re could be null, but if we are here it should not be null!
Roles userRoles = ureq.getUserSession().getRoles();
boolean showAll = userRoles.isAuthor() || userRoles.isOLATAdmin();
//get changelog
Formatter formatter = Formatter.getInstance(ureq.getLocale());
ImsRepositoryResolver resolver = new ImsRepositoryResolver(referenceTestEntry);
QTIChangeLogMessage[] qtiChangeLog = resolver.getDocumentChangeLog();
StringBuilder qtiChangelog = new StringBuilder();
if(qtiChangeLog.length>0){
//there are resource changes
Arrays.sort(qtiChangeLog);
for (int i = qtiChangeLog.length-1; i >= 0 ; i--) {
//show latest change first
if(!showAll && qtiChangeLog[i].isPublic()){
//logged in person is a normal user, hence public messages only
Date msgDate = new Date(qtiChangeLog[i].getTimestmp());
qtiChangelog.append("\nChange date: ").append(formatter.formatDateAndTime(msgDate)).append("\n");
String msg = StringHelper.escapeHtml(qtiChangeLog[i].getLogMessage());
qtiChangelog.append(msg);
qtiChangelog.append("\n********************************\n");
}else if (showAll){
//logged in person is an author, olat admin, owner, show all messages
Date msgDate = new Date(qtiChangeLog[i].getTimestmp());
qtiChangelog.append("\nChange date: ").append(formatter.formatDateAndTime(msgDate)).append("\n");
String msg = StringHelper.escapeHtml(qtiChangeLog[i].getLogMessage());
qtiChangelog.append(msg);
qtiChangelog.append("\n********************************\n");
}//else non public messages are not shown to normal user
}
}
return qtiChangelog;
}
/**
* Constructor for a self-test run controller
* @param userCourseEnv
* @param moduleConfiguration
* @param secCallback
* @param ureq
* @param wControl
* @param 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;
this.secCallback = secCallback;
this.userCourseEnv = userCourseEnv;
this.courseNode = selftestCourseNode;
this.type = AssessmentInstance.QMD_ENTRY_TYPE_SELF;
this.referenceTestEntry = selftestCourseNode.getReferencedRepositoryEntry();
this.singleUserEventCenter = ureq.getUserSession().getSingleUserEventCenter();
iqManager = CoreSpringFactory.getImpl(IQManager.class);
addLoggingResourceable(LoggingResourceable.wrap(courseNode));
myContent = createVelocityContainer("selftestrun");
mainPanel = putInitialPanel(myContent);
if (!modConfig.get(IQEditController.CONFIG_KEY_TYPE).equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)) {
throw new OLATRuntimeException("IQRunController launched with Selftest constructor but module configuration not configured as selftest" ,null);
}
init(ureq);
exposeUserSelfTestDataToVC(ureq);
StringBuilder qtiChangelog = createChangelogMsg(ureq);
// decide about changelog in VC
if(qtiChangelog.length()>0){
//there is some message
myContent.contextPut("changeLog", qtiChangelog);
}
//per default change log is not open
myContent.contextPut("showChangelog", Boolean.FALSE);
//admin setting whether to show change log and info box or not
myContent.contextPut("infobox", courseModule.isDisplayInfoBox());
myContent.contextPut("changelogconfig", courseModule.isDisplayChangeLog());
}
/**
* Constructor for a survey run controller
* @param userCourseEnv
* @param moduleConfiguration
* @param secCallback
* @param ureq
* @param wControl
* @param 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;
this.secCallback = secCallback;
this.userCourseEnv = userCourseEnv;
this.courseNode = surveyCourseNode;
this.referenceTestEntry = surveyCourseNode.getReferencedRepositoryEntry();
this.type = AssessmentInstance.QMD_ENTRY_TYPE_SURVEY;
this.singleUserEventCenter = ureq.getUserSession().getSingleUserEventCenter();
iqManager = CoreSpringFactory.getImpl(IQManager.class);
addLoggingResourceable(LoggingResourceable.wrap(courseNode));
myContent = createVelocityContainer("surveyrun");
mainPanel = putInitialPanel(myContent);
if (!modConfig.get(IQEditController.CONFIG_KEY_TYPE).equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) {
throw new OLATRuntimeException("IQRunController launched with Survey constructor but module configuration not configured as survey" ,null);
}
init(ureq);
exposeUserQuestionnaireDataToVC();
StringBuilder qtiChangelog = createChangelogMsg(ureq);
// decide about changelog in VC
if(qtiChangelog.length()>0){
//there is some message
myContent.contextPut("changeLog", qtiChangelog);
}
//per default change log is not open
myContent.contextPut("showChangelog", Boolean.FALSE);
//admin setting whether to show change log and info box or not
myContent.contextPut("infobox", courseModule.isDisplayInfoBox());
myContent.contextPut("changelogconfig", courseModule.isDisplayChangeLog());
}
private void init(UserRequest ureq) {
startButton = LinkFactory.createButton("start", myContent, this);
startButton.setElementCssClass("o_sel_start_qti12_test");
startButton.setPrimary(true);
startButton.setVisible(!userCourseEnv.isCourseReadOnly());
// fetch disclaimer file
String sDisclaimer = (String)modConfig.get(IQEditController.CONFIG_KEY_DISCLAIMER);
if (sDisclaimer != null) {
VFSContainer baseContainer = userCourseEnv.getCourseEnvironment().getCourseFolderContainer();
int lastSlash = sDisclaimer.lastIndexOf('/');
if (lastSlash != -1) {
baseContainer = (VFSContainer)baseContainer.resolve(sDisclaimer.substring(0, lastSlash));
sDisclaimer = sDisclaimer.substring(lastSlash);
// first check if disclaimer exists on filesystem
if (baseContainer == null || baseContainer.resolve(sDisclaimer) == null) {
showWarning("disclaimer.file.invalid", sDisclaimer);
} else {
//screenreader do not like iframes, display inline
iFrameCtr = new IFrameDisplayController(ureq, getWindowControl(), baseContainer);
listenTo(iFrameCtr);//dispose automatically
myContent.put("disc", iFrameCtr.getInitialComponent());
iFrameCtr.setCurrentURI(sDisclaimer);
myContent.contextPut("hasDisc", Boolean.TRUE);
}
}
}
// push title and learning objectives, only visible on intro page
myContent.contextPut("menuTitle", courseNode.getShortTitle());
myContent.contextPut("displayTitle", courseNode.getLongTitle());
// Adding learning objectives
String learningObj = courseNode.getLearningObjectives();
if (learningObj != null) {
Component learningObjectives = ObjectivesHelper.createLearningObjectivesComponent(learningObj, ureq);
myContent.put("learningObjectives", learningObjectives);
myContent.contextPut("hasObjectives", learningObj); // dummy value, just an exists operator
}
if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
checkChats(ureq);
singleUserEventCenter.registerFor(this, getIdentity(), InstantMessagingService.TOWER_EVENT_ORES);
}
}
private void checkChats (UserRequest ureq) {
List<?> allChats = null;
if (ureq != null) {
allChats = ureq.getUserSession().getChats();
}
if (allChats == null || allChats.size() == 0) {
startButton.setEnabled (true);
myContent.contextPut("hasChatWindowOpen", false);
} else {
startButton.setEnabled (false);
myContent.contextPut("hasChatWindowOpen", true);
}
}
@Override
public void event(Event event) {
if (type == AssessmentInstance.QMD_ENTRY_TYPE_ASSESS) {
if (event.getCommand().startsWith("ChatWindow")) {
checkChats(null);
}
}
}
/**
* @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)
*/
public void event(UserRequest ureq, Component source, Event event) {
if (source == startButton && startButton.isEnabled() && startButton.isVisible()){
long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId().longValue();
String courseNodeIdent = courseNode.getIdent();
removeAsListenerAndDispose(displayController);
OLATResourceable ores = OresHelper.createOLATResourceableTypeWithoutCheck("test");
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = addToHistory(ureq, ores, null);
Controller returnController = iqManager.createIQDisplayController(modConfig, secCallback, ureq, bwControl, courseResId, courseNodeIdent, this);
/*
* either returnController is a MessageController or it is a IQDisplayController
* this should not serve as pattern to be copy&pasted.
* FIXME:2008-11-21:pb INTRODUCED because of read/write QTI Lock solution for scalability II, 6.1.x Release
*/
if(returnController instanceof IQDisplayController){
displayController = (IQDisplayController)returnController;
listenTo(displayController);
if(displayController.isClosed()) {
//do nothing
} else if (displayController.isReady()) {
// in case displayController was unable to initialize, a message was set by displayController
// this is the case if no more attempts or security check was unsuccessfull
displayContainerController = new LayoutMain3ColsController(ureq, getWindowControl(), displayController);
listenTo(displayContainerController); // autodispose
//need to wrap a course restart controller again, because IQDisplay
//runs on top of GUIStack
ICourse course = CourseFactory.loadCourse(courseResId);
RepositoryEntry courseRepositoryEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
Panel empty = new Panel("empty");//empty panel set as "menu" and "tool"
Controller courseCloser = new DisposedCourseRestartController(ureq, getWindowControl(), courseRepositoryEntry);
Controller disposedRestartController = new LayoutMain3ColsController(ureq, getWindowControl(), empty, courseCloser.getInitialComponent(), "disposed course whily in iqRun" + courseResId);
displayContainerController.setDisposedMessageController(disposedRestartController);
final boolean fullWindow = modConfig.getBooleanSafe(IQEditController.CONFIG_FULLWINDOW, true);
if(fullWindow) {
displayContainerController.setAsFullscreen(ureq);
}
displayContainerController.activate();
if (modConfig.get(IQEditController.CONFIG_KEY_TYPE).equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
assessmentStopped = false;
singleUserEventCenter.registerFor(this, getIdentity(), assessmentInstanceOres);
singleUserEventCenter.fireEventToListenersOf(new AssessmentEvent(AssessmentEvent.TYPE.STARTED, ureq.getUserSession()), assessmentEventOres);
}
}//endif isReady
}else{
// -> qti file was locked -> show info message
// user must click again on course node to activate
mainPanel.pushContent(returnController.getInitialComponent());
}
} else if(source == showResultsButton) {
AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
Long assessmentID = am.getAssessmentID(courseNode, ureq.getIdentity());
if(assessmentID==null) {
//fallback solution: if the assessmentID is not available via AssessmentManager than try to get it via IQManager
long callingResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId().longValue();
String callingResDetail = courseNode.getIdent();
assessmentID = iqManager.getLastAssessmentID(ureq.getIdentity(), callingResId, callingResDetail);
}
if(assessmentID!=null && !assessmentID.equals("")) {
Document doc = iqManager.getResultsReportingFromFile(ureq.getIdentity(), type, assessmentID);
//StringBuilder resultsHTML = LocalizedXSLTransformer.getInstance(ureq.getLocale()).renderResults(doc);
String summaryConfig = (String)modConfig.get(IQEditController.CONFIG_KEY_SUMMARY);
int summaryType = AssessmentInstance.getSummaryType(summaryConfig);
String resultsHTML = iqManager.transformResultsReporting(doc, ureq.getLocale(), summaryType);
myContent.contextPut("displayreporting", resultsHTML);
myContent.contextPut("resreporting", resultsHTML);
myContent.contextPut("showResults", Boolean.TRUE);
}
} else if (source == hideResultsButton) {
myContent.contextPut("showResults", Boolean.FALSE);
}
}
@Override
public void submitAssessment(AssessmentInstance ai) {
if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
AssessmentContext ac = ai.getAssessmentContext();
AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
Boolean fullyAssed = am.getNodeFullyAssessed(courseNode, getIdentity());
String correctionMode = courseNode.getModuleConfiguration().getStringValue(IQEditController.CONFIG_CORRECTION_MODE);
Boolean userVisibility;
AssessmentEntryStatus assessmentStatus;
if(IQEditController.CORRECTION_MANUAL.equals(correctionMode)) {
assessmentStatus = AssessmentEntryStatus.inReview;
userVisibility = Boolean.FALSE;
} else {
assessmentStatus = AssessmentEntryStatus.done;
userVisibility = Boolean.TRUE;
}
ScoreEvaluation sceval = new ScoreEvaluation(ac.getScore(), ac.isPassed(), assessmentStatus, userVisibility, fullyAssed, ai.getAssessID());
AssessableCourseNode acn = (AssessableCourseNode)courseNode; // assessment nodes are assessable
acn.updateUserScoreEvaluation(sceval, userCourseEnv, getIdentity(), true);
// Mark publisher for notifications
Long courseId = userCourseEnv.getCourseEnvironment().getCourseResourceableId();
assessmentNotificationsHandler.markPublisherNews(getIdentity(), courseId);
if(!assessmentStopped) {
assessmentStopped = true;
AssessmentEvent assessmentStoppedEvent = new AssessmentEvent(AssessmentEvent.TYPE.STOPPED, userSession);
singleUserEventCenter.deregisterFor(this, assessmentInstanceOres);
singleUserEventCenter.fireEventToListenersOf(assessmentStoppedEvent, assessmentEventOres);
}
} else if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) {
// save number of attempts
// although this is not an assessable node we still use the assessment
// manager since this one uses caching
AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
am.incrementNodeAttempts(courseNode, getIdentity(), userCourseEnv);
} else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)){
AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
am.incrementNodeAttempts(courseNode, getIdentity(), userCourseEnv);
}
}
@Override
public void cancelAssessment(AssessmentInstance ai) {
//
}
/**
* @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)
*/
public void event(UserRequest urequest, Controller source, Event event) {
if (source == displayController) {
if (event instanceof IQSubmittedEvent) {
// Save results in case of test
if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
exposeUserTestDataToVC(urequest);
}
// Save results in case of questionnaire
else if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) {
exposeUserQuestionnaireDataToVC();
if(displayContainerController != null) {
displayContainerController.deactivate(urequest);
} else {
getWindowControl().pop();
}
OLATResourceable ores = OresHelper.createOLATResourceableInstance("test", -1l);
addToHistory(urequest, ores, null);
}
// Don't save results in case of self-test
// but do safe attempts !
else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)){
//am.incrementNodeAttempts(courseNode, urequest.getIdentity(), userCourseEnv);
}
} else if (event.equals(Event.DONE_EVENT)) {
stopAssessment(urequest, event);
} else if (IQEvent.TEST_PULLED.equals(event.getCommand())) {
stopAssessment(urequest, event);
showWarning("error.assessment.pulled");
} else if (IQEvent.TEST_STOPPED.equals(event.getCommand())) {
stopAssessment(urequest, event);
showWarning("error.assessment.stopped");
}
}
}
private void stopAssessment(UserRequest ureq, Event event) {
if(displayContainerController != null) {
displayContainerController.deactivate(ureq);
} else {
getWindowControl().pop();
}
removeHistory(ureq);
OLATResourceable ores = OresHelper.createOLATResourceableInstance("test", -1l);
addToHistory(ureq, ores, null);
if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS) && !assessmentStopped ) {
assessmentStopped = true;
AssessmentEvent assessmentStoppedEvent = new AssessmentEvent(AssessmentEvent.TYPE.STOPPED, userSession);
singleUserEventCenter.deregisterFor(this, assessmentInstanceOres);
singleUserEventCenter.fireEventToListenersOf(assessmentStoppedEvent, assessmentEventOres);
}
fireEvent(ureq, event);
}
private void exposeUserTestDataToVC(UserRequest ureq) {
// config : show score info
Object enableScoreInfoObject = modConfig.get(IQEditController.CONFIG_KEY_ENABLESCOREINFO);
if (enableScoreInfoObject != null) {
myContent.contextPut("enableScoreInfo", enableScoreInfoObject );
} else {
myContent.contextPut("enableScoreInfo", Boolean.TRUE );
}
// configuration data
myContent.contextPut("attemptsConfig", modConfig.get(IQEditController.CONFIG_KEY_ATTEMPTS));
// user data
Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();
if(courseNode instanceof PersistentAssessableCourseNode) {
PersistentAssessableCourseNode acn = (PersistentAssessableCourseNode)courseNode;
AssessmentEntry assessmentEntry = acn.getUserAssessmentEntry(userCourseEnv);
if(assessmentEntry == null) {
myContent.contextPut("blockAfterSuccess", Boolean.FALSE);
myContent.contextPut("score", null);
myContent.contextPut("hasPassedValue", Boolean.FALSE);
myContent.contextPut("passed", Boolean.FALSE);
myContent.contextPut("comment", null);
myContent.contextPut("attempts", 0);
} else {
//block if test passed (and config set to check it)
Boolean blockAfterSuccess = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS);
Boolean blocked = Boolean.FALSE;
if(blockAfterSuccess != null && blockAfterSuccess.booleanValue()) {
Boolean passed = assessmentEntry.getPassed();
if(passed != null && passed.booleanValue()) {
blocked = Boolean.TRUE;
}
}
myContent.contextPut("blockAfterSuccess", blocked);
boolean resultsVisible = assessmentEntry.getUserVisibility() == null || assessmentEntry.getUserVisibility().booleanValue();
myContent.contextPut("resultsVisible", resultsVisible);
myContent.contextPut("score", AssessmentHelper.getRoundedScore(assessmentEntry.getScore()));
myContent.contextPut("hasPassedValue", (assessmentEntry.getPassed() == null ? Boolean.FALSE : Boolean.TRUE));
myContent.contextPut("passed", assessmentEntry.getPassed());
if(resultsVisible) {
StringBuilder comment = Formatter.stripTabsAndReturns(assessmentEntry.getComment());
myContent.contextPut("comment", StringHelper.xssScan(comment));
}
myContent.contextPut("attempts", assessmentEntry.getAttempts() == null ? 0 : assessmentEntry.getAttempts());
}
}
UserNodeAuditManager am = userCourseEnv.getCourseEnvironment().getAuditManager();
myContent.contextPut("log", am.getUserNodeLog(courseNode, identity));
exposeResults(ureq);
}
/**
* Provides the self test score and results, if any, to the velocity container.
* @param ureq
*/
private void exposeUserSelfTestDataToVC(UserRequest ureq) {
if (!(courseNode instanceof SelfAssessableCourseNode)) {
throw new AssertException("exposeUserSelfTestDataToVC can only be called for selftest nodes, not for test or questionnaire");
}
// config : show score info
Object enableScoreInfoObject = modConfig.get(IQEditController.CONFIG_KEY_ENABLESCOREINFO);
if (enableScoreInfoObject != null) {
myContent.contextPut("enableScoreInfo", enableScoreInfoObject );
} else {
myContent.contextPut("enableScoreInfo", Boolean.TRUE );
}
SelfAssessableCourseNode acn = (SelfAssessableCourseNode)courseNode;
ScoreEvaluation scoreEval = acn.getUserScoreEvaluation(userCourseEnv);
if (scoreEval != null) {
myContent.contextPut("hasResults", Boolean.TRUE);
myContent.contextPut("resultsVisible", Boolean.TRUE);
myContent.contextPut("score", AssessmentHelper.getRoundedScore(scoreEval.getScore()));
myContent.contextPut("hasPassedValue", (scoreEval.getPassed() == null ? Boolean.FALSE : Boolean.TRUE));
myContent.contextPut("passed", scoreEval.getPassed());
myContent.contextPut("attempts", new Integer(1)); //at least one attempt
exposeResults(ureq);
}
}
/**
* Provides the show results button if results available or a message with the visibility period.
* @param ureq
*/
private void exposeResults(UserRequest ureq) {
//migration: check if old tests have no summary configured
String configuredSummary = (String) modConfig.get(IQEditController.CONFIG_KEY_SUMMARY);
boolean noSummary = configuredSummary==null || (configuredSummary!=null && configuredSummary.equals(AssessmentInstance.QMD_ENTRY_SUMMARY_NONE));
if(!noSummary) {
Boolean showResultsObj = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE);
boolean showResultsOnHomePage = (showResultsObj!=null && showResultsObj.booleanValue());
myContent.contextPut("showResultsOnHomePage",new Boolean(showResultsOnHomePage));
boolean dateRelatedVisibility = AssessmentHelper.isResultVisible(modConfig);
if(showResultsOnHomePage && dateRelatedVisibility) {
myContent.contextPut("showResultsVisible",Boolean.TRUE);
showResultsButton = LinkFactory.createButton("command.showResults", myContent, this);
hideResultsButton = LinkFactory.createButton("command.hideResults", myContent, this);
} else if(showResultsOnHomePage) {
Date startDate = (Date)modConfig.get(IQEditController.CONFIG_KEY_RESULTS_START_DATE);
Date endDate = (Date)modConfig.get(IQEditController.CONFIG_KEY_RESULTS_END_DATE);
String visibilityStartDate = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, ureq.getLocale()).format(startDate);
String visibilityEndDate = "-";
if(endDate!=null) {
visibilityEndDate = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, ureq.getLocale()).format(endDate);
}
String visibilityPeriod = getTranslator().translate("showResults.visibility", new String[] { visibilityStartDate, visibilityEndDate});
myContent.contextPut("visibilityPeriod",visibilityPeriod);
myContent.contextPut("showResultsVisible",Boolean.FALSE);
}
}
}
private void exposeUserQuestionnaireDataToVC() {
AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();
// although this is not an assessable node we still use the assessment
// manager since this one uses caching
myContent.contextPut("attempts", am.getNodeAttempts(courseNode, identity));
}
/**
*
* @see org.olat.core.gui.control.DefaultController#doDisspose(boolean)
*/
protected void doDispose() {
// child controllers disposed by basic controller
if (!type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) {
return;
}
singleUserEventCenter.deregisterFor(this, assessmentInstanceOres);
singleUserEventCenter.deregisterFor(this, InstantMessagingService.TOWER_EVENT_ORES);
if (!assessmentStopped) {
AssessmentEvent assessmentStoppedEvent = new AssessmentEvent(AssessmentEvent.TYPE.STOPPED, userSession);
singleUserEventCenter.fireEventToListenersOf(assessmentStoppedEvent, assessmentEventOres);
}
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
ContextEntry ce = entries.remove(0);
if("test".equals(ce.getOLATResourceable().getResourceableTypeName())) {
Long resourceId = ce.getOLATResourceable().getResourceableId();
if(resourceId != null && resourceId.longValue() >= 0) {
//event(ureq, startButton, Event.CHANGED_EVENT);
}
}
}
}