/**
* 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.modules.iq;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.dom4j.Document;
import org.olat.core.CoreSpringFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.htmlheader.jscss.JSAndCSSComponent;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.progressbar.ProgressBar;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.DefaultController;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
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.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.logging.activity.LearningResourceLoggingAction;
import org.olat.core.logging.activity.StringResourceableType;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.assessment.AssessmentModeNotificationEvent;
import org.olat.course.nodes.iq.IQEditController;
import org.olat.course.nodes.iq.IQEvent;
import org.olat.ims.qti.QTIConstants;
import org.olat.ims.qti.container.AssessmentContext;
import org.olat.ims.qti.container.ItemsInput;
import org.olat.ims.qti.container.SectionContext;
import org.olat.ims.qti.navigator.Navigator;
import org.olat.ims.qti.navigator.NavigatorDelegate;
import org.olat.ims.qti.process.AssessmentFactory;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.FilePersister;
import org.olat.ims.qti.process.ImsRepositoryResolver;
import org.olat.ims.qti.process.Persister;
import org.olat.ims.qti.process.Resolver;
import org.olat.modules.ModuleConfiguration;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.util.logging.activity.LoggingResourceable;
/**
* @author Felix Jost
*/
public class IQDisplayController extends DefaultController implements GenericEventListener, Activateable2, NavigatorDelegate {
private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(IQDisplayController.class);
private static OLog log = Tracing.createLoggerFor(IQDisplayController.class);
private VelocityContainer myContent;
private Translator translator;
private String repositorySoftkey = null;
private Resolver resolver = null;
private Persister persister = null;
private final Locale locale;
private final Identity assessedIdentity;
private volatile boolean stoppedFlag = false;
private volatile boolean retrievedFlag = false;
private NavigatorDelegate delegate;
private ProgressBar qtiscoreprogress, qtiquestionprogress;
private IQComponent qticomp;
private IQStatus qtistatus;
private IQManager iqm;
private IQSecurityCallback iqsec;
private ModuleConfiguration modConfig;
private long courseResId = 0;
private String courseNodeIdent = "";
private boolean ready;
private Link closeButton;
private OLATResourceable retrieveListenerOres;
/**
* IMS QTI Display Controller used by the course nodes
*
* concurrency protection is solved on IQManager.
* -> do not make constructor public
* -> create controller only via IQManager
*
* @param moduleConfiguration
* @param secCallback
* @param ureq
* @param wControl
* @param callingResId
* @param callingResDetail
*/
IQDisplayController(ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq,
WindowControl wControl, long courseResId, String courseNodeIdent, NavigatorDelegate delegate) {
super(wControl);
assessedIdentity = ureq.getIdentity();
locale = ureq.getLocale();
this.delegate = delegate;
ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_OPEN, getClass());
this.modConfig = moduleConfiguration;
this.courseResId = courseResId;
this.courseNodeIdent = courseNodeIdent;
this.repositorySoftkey = (String) moduleConfiguration.get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY);
init(secCallback, ureq);
CoordinatorManager.getInstance().getCoordinator().getEventBus()
.registerFor(this, assessedIdentity, AssessmentModeNotificationEvent.ASSESSMENT_MODE_NOTIFICATION);
}
/**
* IMS QTI Display Controller used by QTI Editor for preview.
*
* concurrency protection is solved on IQManager.
* -> do not make constructor public
* -> create controller only via IQManager
*
* @param resolver
* @param type
* @param secCallback
* @param ureq
* @param wControl
*/
IQDisplayController(Resolver resolver, String type, IQSecurityCallback secCallback, UserRequest ureq, WindowControl wControl) {
super(wControl);
ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_OPEN, getClass());
this.assessedIdentity = ureq.getIdentity();
this.locale = ureq.getLocale();
this.modConfig = new ModuleConfiguration();
modConfig.set(IQEditController.CONFIG_KEY_ENABLEMENU, Boolean.TRUE);
modConfig.set(IQEditController.CONFIG_KEY_TYPE, type);
modConfig.set(IQEditController.CONFIG_KEY_SEQUENCE, AssessmentInstance.QMD_ENTRY_SEQUENCE_ITEM);
modConfig.set(IQEditController.CONFIG_KEY_SCOREPROGRESS, Boolean.TRUE);
modConfig.set(IQEditController.CONFIG_KEY_QUESTIONPROGRESS, Boolean.FALSE);
modConfig.set(IQEditController.CONFIG_KEY_ENABLECANCEL, Boolean.TRUE);
modConfig.set(IQEditController.CONFIG_KEY_ENABLESUSPEND, Boolean.FALSE);
modConfig.set(IQEditController.CONFIG_KEY_SUMMARY, AssessmentInstance.QMD_ENTRY_SUMMARY_DETAILED);
modConfig.set(IQEditController.CONFIG_KEY_RENDERMENUOPTION, Boolean.FALSE);
this.resolver = resolver;
this.persister = null;
init(secCallback, ureq);
}
private void init(IQSecurityCallback secCallback, UserRequest ureq) {
this.iqsec = secCallback;
this.translator = Util.createPackageTranslator(IQDisplayController.class, ureq.getLocale());
this.ready = false;
retrieveListenerOres = new IQRetrievedEvent(ureq.getIdentity(), courseResId, courseNodeIdent);
CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), retrieveListenerOres);
iqm = CoreSpringFactory.getImpl(IQManager.class);
myContent = new VelocityContainer("olatmodiqrun", VELOCITY_ROOT + "/qti.html", translator, this);
// Check if fibautocompl.js and fibautocompl.css exists for enhance FIB autocomplete feature
Resolver autcompResolver = null;
if (resolver == null){
RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntryBySoftkey(repositorySoftkey, true);
autcompResolver = new ImsRepositoryResolver(re);
} else {
autcompResolver = this.resolver;
}
if (autcompResolver != null && autcompResolver.hasAutocompleteFiles()) {
// Add Autocomplte JS and CSS file to header
StringBuilder sb = new StringBuilder();
// must be like <script type="text/javascript" src="/olat/secstatic/qti/74579818809617/_unzipped_/fibautocompl.js"></script>
sb.append("<script type=\"text/javascript\" src=\"").append(autcompResolver.getStaticsBaseURI()).append("/").append(ImsRepositoryResolver.QTI_FIB_AUTOCOMPLETE_JS_FILE).append("\"></script>\n");
// must be like <link rel="StyleSheet" href="/olat/secstatic/qti/74579818809617/_unzipped_/fibautocompl.css" type="text/css" media="screen, print">
sb.append("<link rel=\"StyleSheet\" href=\"").append(autcompResolver.getStaticsBaseURI()).append("/").append(ImsRepositoryResolver.QTI_FIB_AUTOCOMPLETE_CSS_FILE).append("\" type=\"text/css\" media=\"screen\" >\n");
JSAndCSSComponent autoCompleteJsCss = new JSAndCSSComponent("auto_complete_js_css", this.getClass(), true, sb.toString());
myContent.put("autoCompleteJsCss", autoCompleteJsCss);
}
closeButton = LinkFactory.createButton("close", myContent, this);
closeButton.setPrimary(true);
qtiscoreprogress = new ProgressBar("qtiscoreprogress", 150, 0, 0, "");
myContent.put("qtiscoreprogress", qtiscoreprogress);
Boolean displayScoreProgress = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_SCOREPROGRESS);
if (displayScoreProgress == null) displayScoreProgress = Boolean.TRUE; // migration,
// display
// menu
if (!displayScoreProgress.booleanValue()) qtiscoreprogress.setVisible(false);
myContent.contextPut("displayScoreProgress", displayScoreProgress);
qtiquestionprogress = new ProgressBar("qtiquestionprogress", 150, 0, 0, "");
myContent.put("qtiquestionprogress", qtiquestionprogress);
Boolean displayQuestionProgress = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_QUESTIONPROGRESS);
if (displayQuestionProgress == null) displayQuestionProgress = Boolean.FALSE; // migration,
// don't
// display
// progress
if (!displayQuestionProgress.booleanValue()) qtiquestionprogress.setVisible(false);
myContent.contextPut("displayQuestionProgress", displayQuestionProgress);
Boolean displayMenu = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_DISPLAYMENU);
if (displayMenu == null) displayMenu = Boolean.TRUE; // migration
myContent.contextPut("displayMenu", displayMenu);
Boolean enableCancel = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_ENABLECANCEL);
if (enableCancel == null) {
if (modConfig.get(IQEditController.CONFIG_KEY_TYPE).equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) enableCancel = Boolean.FALSE; // migration:
// disable
// cancel
// for
// assessments
else enableCancel = Boolean.TRUE; // migration: enable otherwise
}
myContent.contextPut("enableCancel", enableCancel);
Boolean enableSuspend = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_ENABLESUSPEND);
if (enableSuspend == null) enableSuspend = Boolean.FALSE; // migration
myContent.contextPut("enableSuspend", enableSuspend);
qtistatus = new IQStatus(translator);
qtistatus.setPreview(iqsec.isPreview());
myContent.contextPut("qtistatus", qtistatus);
setInitialComponent(myContent);
// get the assessment
AssessmentInstance ai = null;
if (repositorySoftkey != null) { // instantiate from repository
// build path information which will be used to store tempory qti file
String resourcePathInfo = courseResId + File.separator + courseNodeIdent;
ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(),
modConfig, iqsec.isPreview(), courseResId, courseNodeIdent, resourcePathInfo, this);
} else if (resolver != null) { // instantiate from given resolver
ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(),
courseResId, courseNodeIdent, resolver, persister, modConfig, this);
}
// check for null instance or instance with no items
if (ai == null || ai.getAssessmentContext().getSectionContext(0).getItemContextCount() == 0) throw new AssertException(
"Assessment Instance was null or no sections/items found.");
if (!iqsec.isAllowed(ai)) { // security check
getWindowControl().setError(translator.translate("status.notallowed"));
return;
}
if (iqsec.attemptsLeft(ai) < 1) { // security check
// note: important: do not check on == 0 since the nr of attempts can be
// republished for the same test with a smaller number as the latest time.
getWindowControl().setInfo(translator.translate(ai.isSurvey() ? "status.survey.nomoreattempts" : "status.assess.nomoreattempts"));
return;
}
if (ai.isResuming()) {
getWindowControl().setInfo(translator.translate(ai.isSurvey() ? "status.survey.resumed" : "status.assess.resumed"));
}
ai.setPreview(iqsec.isPreview());
/*
* menu render option: render only section titles or titles and questions.
*/
Object tmp = modConfig.get(IQEditController.CONFIG_KEY_RENDERMENUOPTION);
Boolean renderSectionsOnly;
if (tmp == null) {
// migration
modConfig.set(IQEditController.CONFIG_KEY_RENDERMENUOPTION, Boolean.FALSE);
renderSectionsOnly = Boolean.FALSE;
}else {
renderSectionsOnly = (Boolean)tmp;
}
boolean enabledMenu = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_ENABLEMENU).booleanValue();
boolean itemPageSequence = ((String)modConfig.get(IQEditController.CONFIG_KEY_SEQUENCE)).equals(AssessmentInstance.QMD_ENTRY_SEQUENCE_ITEM);
IQMenuDisplayConf mdc = new IQMenuDisplayConf(renderSectionsOnly.booleanValue(), enabledMenu, itemPageSequence);
Boolean tmpMemo = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_MEMO);
boolean memo = tmpMemo == null ? false : tmpMemo.booleanValue();
qticomp = new IQComponent("qticomponent", translator, ai, mdc, memo);
qticomp.addListener(this);
myContent.put("qticomp", qticomp);
if (!ai.isResuming()) {
Navigator navigator = ai.getNavigator();
navigator.startAssessment();
} else {
//fxdiff BAKS-7 Resume function
AssessmentContext act = ai.getAssessmentContext();
if (act.getCurrentSectionContextPos() >= 0) {
int sectionPos = act.getCurrentSectionContextPos();
OLATResourceable sres = OresHelper.createOLATResourceableInstance("gse", new Long(sectionPos));
WindowControl bwControl = addToHistory(ureq, sres, null, getWindowControl(), false);
if(!ai.isSectionPage()) {
SectionContext sct = act.getCurrentSectionContext();
int itemPos = sct.getCurrentItemContextPos();
if(itemPos >= 0) {
OLATResourceable ires = OresHelper.createOLATResourceableInstance("git", new Long(itemPos));
addToHistory(ureq, ires, null, bwControl, true);
}
}
}
}
qtistatus.update(ai);
if (!qtistatus.isSurvey()) {
qtiscoreprogress.setMax(ai.getAssessmentContext().getMaxScore());
qtiscoreprogress.setActual(ai.getAssessmentContext().getScore());
}
qtiquestionprogress.setMax(Integer.parseInt(qtistatus.getMaxQuestions()));
updateQuestionProgressDisplay (ai);
ready = true;
}
/**
* Wether the qti is ready to be launched.
*
* @return boolean
*/
public boolean isReady() {
return ready;
}
public boolean isClosed() {
if(qticomp == null) return true;
AssessmentInstance ai = qticomp.getAssessmentInstance();
return ai.isClosed();
}
private void updateQuestionProgressDisplay (AssessmentInstance ai) {
int answered = ai.getAssessmentContext().getItemsAnsweredCount();
qtiquestionprogress.setActual(answered);
qtistatus.setQuestionProgressLabel(
translator.translate(
"question.progress.answered", new String[] {
""+answered,
qtistatus.getMaxQuestions()
}
)
);
// tell velocity if all questions are answered or there are unanswered questions
int maxQuestions = Integer.parseInt(qtistatus.getMaxQuestions());
myContent.contextPut("allQuestionsAnswered", answered == maxQuestions);
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
}
@Override
public void event(Event event) {
if(event instanceof IQRetrievedEvent) {
IQRetrievedEvent e = (IQRetrievedEvent)event;
if(e.isConcerned(assessedIdentity, courseResId, courseNodeIdent)) {
//it's me -> it's finished
retrievedFlag = true;
}
} else if (event instanceof AssessmentModeNotificationEvent) {
try {
processAssessmentModeNotificationEvent((AssessmentModeNotificationEvent)event);
} catch (Exception e) {
log.error("", e);
}
}
}
private void processAssessmentModeNotificationEvent(AssessmentModeNotificationEvent event) {
if(event.getAssessementMode().getResource().getResourceableId().equals(courseResId)) {
String cmd = event.getCommand();
if(cmd.equals(AssessmentModeNotificationEvent.STOP_ASSESSMENT) || cmd.equals(AssessmentModeNotificationEvent.END)) {
stoppedFlag = true;
}
}
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(retrievedFlag || stoppedFlag) {
fireEvent(ureq, new IQEvent(IQEvent.TEST_PULLED));
} else if(stoppedFlag) {
fireEvent(ureq, new IQEvent(IQEvent.TEST_STOPPED));
} else {
super.event(ureq, source, event);
}
}
@Override
public void event(UserRequest ureq, Component source, Event event) {
if(retrievedFlag) {
fireEvent(ureq, new IQEvent(IQEvent.TEST_PULLED));
return;
} else if(retrievedFlag || stoppedFlag) {
fireEvent(ureq, new IQEvent(IQEvent.TEST_STOPPED));
return;
}
if (source == myContent || source == qticomp) { // those must be links
String wfCommand = event.getCommand();
// process workflow
AssessmentInstance ai = qticomp.getAssessmentInstance();
if (qticomp == null || ai == null) { throw new RuntimeException("AssessmentInstance not valid."); }
Navigator navig = ai.getNavigator();
if (wfCommand.equals("mark")) {
ai.mark(ureq.getParameter("id"), "true".equals(ureq.getParameter("p")));
ai.persist();
return;
}
if (wfCommand.equals("memo")) {
try {
String memo = java.net.URLDecoder.decode(ureq.getParameter("p"), "UTF-8");
ai.setMemo(ureq.getParameter("id"), memo);
ai.persist();
return;
} catch (UnsupportedEncodingException ex) {
log.info("Could not decode memo text " + ureq.getParameter("p"));
}
}
logAudit(ureq);
if (wfCommand.equals("sitse")) { // submitItemorSection
ItemsInput iInp = iqm.getItemsInput(ureq);
if(iInp.getItemCount() > 0) {
navig.submitItems(iInp);
}
if (ai.isClosed()) { // do all the finishing stuff
if(navig.getInfo().isFeedback()) {
//render the feedback
} else {
event(ureq, source, new Event(QTIConstants.QTI_WF_SUBMIT));
return;
}
}
} else if (wfCommand.equals("sitsec")) { // submit
if (ai.isClosed()) { // do all the finishing stuff
if (!qtistatus.isSurvey()) {
// for test and self-assessment, generate detailed results
generateDetailsResults(ureq, ai);
} else {
// Send also finished event in case of survey
fireEvent(ureq, new IQSubmittedEvent());
}
return;
}
} else if (wfCommand.equals("sflash")) { // submit flash answer
navig.submitItems(iqm.getItemsInput(ureq)); //
if (ai.isClosed()) { // do all the finishing stuff
event(ureq, source, new Event(QTIConstants.QTI_WF_SUBMIT));
return;
}
} else if (wfCommand.equals("git")) { // goToItem
String seid = ureq.getParameter("seid");
String itid = ureq.getParameter("itid");
if (seid!=null && seid.length()!=0 && itid!=null && itid.length()!=0) {
int sectionPos = Integer.parseInt(seid);
int itemPos = Integer.parseInt(itid);
navig.goToItem(sectionPos, itemPos);
//fxdiff BAKS-7 Resume function
OLATResourceable sres = OresHelper.createOLATResourceableInstance("gse", new Long(sectionPos));
WindowControl bwControl = addToHistory(ureq, sres, null, getWindowControl(), false);
OLATResourceable ires = OresHelper.createOLATResourceableInstance("git", new Long(itemPos));
addToHistory(ureq, ires, null, bwControl, true);
}
} else if (wfCommand.equals("gse")) { // goToSection
String seid = ureq.getParameter("seid");
if (seid!=null && seid.length()!=0) {
int sectionPos = Integer.parseInt(seid);
navig.goToSection(sectionPos);
//fxdiff BAKS-7 Resume function
OLATResourceable sres = OresHelper.createOLATResourceableInstance("gse", new Long(sectionPos));
addToHistory(ureq, sres, null);
}
} else if (wfCommand.equals(QTIConstants.QTI_WF_SUBMIT)) { // submit
// Assessment
navig.submitAssessment();
postSubmitAssessment(ureq, ai);
} else if (wfCommand.equals(QTIConstants.QTI_WF_CANCEL)) { // cancel
// assessment
navig.cancelAssessment();
} else if (wfCommand.equals(QTIConstants.QTI_WF_SUSPEND)) { // suspend
// assessment
// just close the controller
fireEvent(ureq, Event.DONE_EVENT);
return;
} else if (wfCommand.equals("close")) {
qtistatus.update(null);
// Parent controller need to pop, if they pushed previously
fireEvent(ureq, Event.DONE_EVENT);
return;
}
qtistatus.update(ai);
if (!qtistatus.isSurvey()) qtiscoreprogress.setActual(ai.getAssessmentContext().getScore());
updateQuestionProgressDisplay (ai);
} else if (source == closeButton){ // close component
qtistatus.update(null);
// Parent controller need to pop, if they pushed previously
fireEvent(ureq, Event.DONE_EVENT);
return;
}
}
@Override
public void submitAssessment(AssessmentInstance ai) {
if (!qtistatus.isPreview()) {
//iqm.persistResults(ai, callingResId, callingResDetail, ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr());
getWindowControl().setInfo(translator.translate("status.results.saved"));
} else {
getWindowControl().setInfo(translator.translate("status.results.notsaved"));
}
if (!qtistatus.isSurvey() && !iqsec.isPreview()) {
// for test and self-assessment, generate detailed results
Document docResReporting = iqm.getResultsReporting(ai, assessedIdentity, locale);
FilePersister.createResultsReporting(docResReporting, assessedIdentity, ai.getFormattedType(), ai.getAssessID());
}
if(delegate != null) {
delegate.submitAssessment(ai);
}
}
@Override
public void cancelAssessment(AssessmentInstance ai) {
//
}
/**
* Persist data in all cases: test, selftest, surveys except previews
* In case of survey, data will be anonymized when reading from the
* table (using the archiver)
*/
protected void postSubmitAssessment(UserRequest ureq, AssessmentInstance ai) {
if (qtistatus.isSurvey()) {
// Send also finished event in case of survey
fireEvent(ureq, new IQSubmittedEvent());
} else {
// for test and self-assessment, generate detailed results
generateDetailsResults(ureq, ai);
}
}
protected void generateDetailsResults(UserRequest ureq, AssessmentInstance ai) {
if (!iqsec.isPreview()) {
fireEvent(ureq, new IQSubmittedEvent());
}
Boolean showResultsOnFinishObj = modConfig.getBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_FINISH);
boolean showResultsOnFinish = showResultsOnFinishObj==null || showResultsOnFinishObj!=null && showResultsOnFinishObj.booleanValue();
if (ai.getSummaryType() == AssessmentInstance.SUMMARY_NONE || !showResultsOnFinish) {
// do not display results reporting
myContent.contextPut("displayreporting", Boolean.FALSE);
} else { // display results reporting
Document docResReporting = iqm.getResultsReporting(ai, ureq.getIdentity(), ureq.getLocale());
String resReporting = iqm.transformResultsReporting(docResReporting, ureq.getLocale(), ai.getSummaryType() );
myContent.contextPut("resreporting", resReporting);
myContent.contextPut("displayreporting", Boolean.TRUE);
}
myContent.setPage(VELOCITY_ROOT + "/result.html");
}
/**
* @param ureq
*/
private void logAudit(UserRequest ureq) {
Set<String> params = ureq.getParameterSet();
StringBuilder sb = new StringBuilder();
for (Iterator<String> iter = params.iterator(); iter.hasNext();) {
String paramName = iter.next();
sb.append("|");
sb.append(paramName);
sb.append("=");
sb.append(ureq.getParameter(paramName));
}
log.audit("QTI audit logging: hreq=" + ureq.getHttpReq().getRequestURL() + ", params=" + sb.toString());
String command = ureq.getParameter("cid");
String qtiDetails = LoggingResourceable.restrictStringLength("cid="+command+StringHelper.stripLineBreaks(sb.toString()), LoggingResourceable.MAX_NAME_LEN);
ThreadLocalUserActivityLogger.log(QTILoggingAction.QTI_AUDIT, getClass(),
LoggingResourceable.wrapNonOlatResource(StringResourceableType.qtiParams, "", qtiDetails));
}
@Override
protected void doDispose() {
CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, retrieveListenerOres);
CoordinatorManager.getInstance().getCoordinator().getEventBus()
.deregisterFor(this, AssessmentModeNotificationEvent.ASSESSMENT_MODE_NOTIFICATION);
}
}