From 62a7e2b4b1fb1d665706a6cedb12d73b7bde5d1d Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Tue, 3 Jan 2012 15:18:38 +0100
Subject: [PATCH] OO-30: show the feedback after the last question of a test

---
 .../ims/qti/navigator/DefaultNavigator.java   |  14 ++-
 .../olat/modules/iq/IQComponentRenderer.java  |  61 ++++++----
 .../olat/modules/iq/IQDisplayController.java  | 108 +++++++++++-------
 .../java/org/olat/modules/iq/IQStatus.java    |   4 +
 .../org/olat/modules/iq/_content/qti.html     |   6 +
 5 files changed, 130 insertions(+), 63 deletions(-)

diff --git a/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java b/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java
index 2d95158e067..1b68770c63f 100644
--- a/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java
+++ b/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java
@@ -142,14 +142,26 @@ public class DefaultNavigator implements Serializable {
 	 * @see org.olat.qti.process.Navigator#submitAssessment()
 	 */
 	public void submitAssessment() {
+		Output pendingOutput = null;
+		boolean pendingFeedback = getInfo().isFeedback();
+		if(pendingFeedback && getAssessmentInstance().getAssessmentContext().getCurrentSectionContext() != null) {
+			ItemContext itc = getAssessmentInstance().getAssessmentContext().getCurrentSectionContext().getCurrentItemContext();
+			pendingOutput = itc.getOutput();
+		}
+
 		getAssessmentInstance().close();
 		AssessmentContext ac = getAssessmentContext();
+		
+		info.clear();
 		if (ac.isFeedbackavailable()) {
 			Output outp = ac.getOutput();
 			getInfo().setCurrentOutput(outp);
 			getInfo().setFeedback(true);
+		} else if (pendingFeedback) {
+			getInfo().setCurrentOutput(pendingOutput);
+			getInfo().setFeedback(true);
 		}
-		info.clear();
+		//info.clear();
 		info.setMessage(QTIConstants.MESSAGE_ASSESSMENT_SUBMITTED);
 		info.setStatus(QTIConstants.ASSESSMENT_FINISHED);
 		info.setRenderItems(false);
diff --git a/src/main/java/org/olat/modules/iq/IQComponentRenderer.java b/src/main/java/org/olat/modules/iq/IQComponentRenderer.java
index 8bc709b1445..44d302fd4b9 100644
--- a/src/main/java/org/olat/modules/iq/IQComponentRenderer.java
+++ b/src/main/java/org/olat/modules/iq/IQComponentRenderer.java
@@ -25,7 +25,6 @@
 
 package org.olat.modules.iq;
 
-import java.io.UnsupportedEncodingException;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -116,23 +115,8 @@ public class IQComponentRenderer implements ComponentRenderer {
 						displayFeedback(sb, el_solution, ai, translator.getLocale());
 					}
 					// item fb?
-					if (info.isFeedback()) {
-						if (info.getCurrentOutput().hasItem_Responses()) {
-							int fbcount = info.getCurrentOutput().getFeedbackCount();
-							int i=0;
-							while (i < fbcount) {
-								Element el_anschosen = info.getCurrentOutput().getItemAnswerChosen(i);
-								if (el_anschosen != null) {
-									sb.append("<br /><br /><i>");
-									displayFeedback(sb, new Material(el_anschosen), ai, translator.getLocale());
-									sb.append("</i>");
-								}
-								Element el_resp= info.getCurrentOutput().getItemFeedback(i);
-								displayFeedback(sb, new ItemFeedback(el_resp), ai, translator.getLocale());
-								i++;
-							}
-						}
-					}
+					renderFeedback(info, sb, ai, translator);
+					
 					if(!comp.getMenuDisplayConf().isEnabledMenu() && comp.getMenuDisplayConf().isItemPageSequence() && !info.isRenderItems()) {
 						//if item was submitted and sequence is pageSequence and menu not enabled and isRenderItems returns false show section info
 					  SectionContext sc = ai.getAssessmentContext().getCurrentSectionContext();
@@ -146,7 +130,11 @@ public class IQComponentRenderer implements ComponentRenderer {
 					if (info.isFeedback()) {
 						Output outp = info.getCurrentOutput();
 						GenericQTIElement el_feedback = outp.getEl_response();
-						if (el_feedback != null) displayFeedback(sb, el_feedback, ai, translator.getLocale());
+						if (el_feedback != null) {
+							displayFeedback(sb, el_feedback, ai, translator.getLocale());
+						} else {
+							renderFeedback(info, sb, ai, translator);
+						}
 					}
 					if(!comp.getMenuDisplayConf().isEnabledMenu() && !comp.getMenuDisplayConf().isItemPageSequence()) {
 					  SectionContext sc = ai.getAssessmentContext().getCurrentSectionContext();
@@ -275,11 +263,44 @@ public class IQComponentRenderer implements ComponentRenderer {
 			if (info.isFeedback()) {
 				Output outp = info.getCurrentOutput();
 				GenericQTIElement el_feedback = outp.getEl_response();
-				if (el_feedback != null) displayFeedback(sb, el_feedback, ai, null);
+				if (el_feedback != null) {
+					displayFeedback(sb, el_feedback, ai, null);
+				} else {
+					renderFeedback(info, sb, ai, translator);
+					
+					//add the next button
+					sb.append("<a class=\"b_button\" onclick=\"return o2cl()\" href=\"");
+					ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "sitsec" });
+					String title = translator.translate("next"); 
+					sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(title) + "\">");
+					sb.append("<span>").append(title).append("</title>");
+					sb.append("</a>");
+				}
 			}
 		}
 		return sb;
 	}
+	
+	protected void renderFeedback(Info info, StringOutput sb, AssessmentInstance ai, Translator translator) {
+		if (info.isFeedback()) {
+			if (info.getCurrentOutput().hasItem_Responses()) {
+				int fbcount = info.getCurrentOutput().getFeedbackCount();
+				int i=0;
+				while (i < fbcount) {
+					Element el_anschosen = info.getCurrentOutput().getItemAnswerChosen(i);
+					if (el_anschosen != null) {
+						sb.append("<br /><br /><i>");
+						displayFeedback(sb, new Material(el_anschosen), ai, translator.getLocale());
+						sb.append("</i>");
+					}
+					Element el_resp= info.getCurrentOutput().getItemFeedback(i);
+					displayFeedback(sb, new ItemFeedback(el_resp), ai, translator.getLocale());
+					i++;
+				}
+			}
+		}
+		
+	}
 
 	protected static String getFormattedLimit(long millis) {
 		long sSec = millis / 1000;
diff --git a/src/main/java/org/olat/modules/iq/IQDisplayController.java b/src/main/java/org/olat/modules/iq/IQDisplayController.java
index 016f8284d81..00efa1e0302 100644
--- a/src/main/java/org/olat/modules/iq/IQDisplayController.java
+++ b/src/main/java/org/olat/modules/iq/IQDisplayController.java
@@ -82,9 +82,6 @@ public class IQDisplayController extends DefaultController implements Activateab
 
 	private static Logger log = Logger.getLogger(IQDisplayController.class.getName());
 
-	// used for logging
-	private static final String IMSQTI = "IMSQTI";
-
 	private VelocityContainer myContent;
 
 	private Translator translator;
@@ -403,7 +400,22 @@ public class IQDisplayController extends DefaultController implements Activateab
 					navig.submitItems(iInp);
 				}
 				if (ai.isClosed()) { // do all the finishing stuff
-					event(ureq, source, new Event(QTIConstants.QTI_WF_SUBMIT));
+					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
@@ -439,44 +451,7 @@ public class IQDisplayController extends DefaultController implements Activateab
 			} else if (wfCommand.equals(QTIConstants.QTI_WF_SUBMIT)) { // submit
 																																	// Assessment
 				navig.submitAssessment();
-				// 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)
-				if (!qtistatus.isPreview()) {
-					iqm.persistResults(ai, callingResId, callingResDetail, ureq);
-					getWindowControl().setInfo(translator.translate("status.results.saved"));
-				} else {
-					getWindowControl().setInfo(translator.translate("status.results.notsaved"));
-				}
-
-				if (!qtistatus.isSurvey()) { // for test and self-assessment, generate
-																			// detailed results
-					Document docResReporting = iqm.getResultsReporting(ai, ureq);
-					if (!iqsec.isPreview()) {
-						FilePersister.createResultsReporting(docResReporting, ureq.getIdentity(), ai.getFormattedType(), ai.getAssessID());
-						// Send score and passed to parent controller. Maybe it is necessary
-						// to save some data there
-						// Do this now and not later, maybe user will never click on
-						// 'close'...
-						AssessmentContext ac = ai.getAssessmentContext();
-						fireEvent(ureq, new IQSubmittedEvent(ac.getScore(), ac.isPassed(), ai.getAssessID()));
-					}
-					
-					Boolean showResultsOnFinishObj = (Boolean)modConfig.get(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
-						String resReporting = iqm.transformResultsReporting(docResReporting, ureq.getLocale(), ai.getSummaryType() );
-						myContent.contextPut("resreporting", resReporting);
-						myContent.contextPut("displayreporting", Boolean.TRUE);
-					} 
-					myContent.setPage(VELOCITY_ROOT + "/result.html");
-				} else {
-					// Send also finished event in case of survey
-					fireEvent(ureq, new IQSubmittedEvent());
-				}
+				postSubmitAssessment(ureq, ai);
 			} else if (wfCommand.equals(QTIConstants.QTI_WF_CANCEL)) { // cancel
 																																	// assessment
 				navig.cancelAssessment();
@@ -503,6 +478,55 @@ public class IQDisplayController extends DefaultController implements Activateab
 			return;
 		}
 	}
+	
+	/**
+	 * 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.isPreview()) {
+			iqm.persistResults(ai, callingResId, callingResDetail, ureq);
+			getWindowControl().setInfo(translator.translate("status.results.saved"));
+		} else {
+			getWindowControl().setInfo(translator.translate("status.results.notsaved"));
+		}
+
+		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());
+		}
+	}
+	
+	protected void generateDetailsResults(UserRequest ureq, AssessmentInstance ai) {
+		Document docResReporting = iqm.getResultsReporting(ai, ureq);
+		if (!iqsec.isPreview()) {
+			FilePersister.createResultsReporting(docResReporting, ureq.getIdentity(), ai.getFormattedType(), ai.getAssessID());
+			// Send score and passed to parent controller. Maybe it is necessary
+			// to save some data there
+			// Do this now and not later, maybe user will never click on
+			// 'close'...
+			AssessmentContext ac = ai.getAssessmentContext();
+			fireEvent(ureq, new IQSubmittedEvent(ac.getScore(), ac.isPassed(), ai.getAssessID()));
+		}
+		
+		Boolean showResultsOnFinishObj = (Boolean)modConfig.get(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
+			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
diff --git a/src/main/java/org/olat/modules/iq/IQStatus.java b/src/main/java/org/olat/modules/iq/IQStatus.java
index 0a84536d6ed..b65dff4a828 100644
--- a/src/main/java/org/olat/modules/iq/IQStatus.java
+++ b/src/main/java/org/olat/modules/iq/IQStatus.java
@@ -236,6 +236,10 @@ public class IQStatus {
 	 * @return true if assessment instance is not closed
 	 */
 	public boolean isOpen() { return isOpen; }
+	/**
+	 * @return true if assessment instance is not closed
+	 */
+	public boolean isClosed() { return !isOpen; }
 	/**
 	 * @return true if of type survey (questionnaire)
 	 */
diff --git a/src/main/java/org/olat/modules/iq/_content/qti.html b/src/main/java/org/olat/modules/iq/_content/qti.html
index b1832459b32..f24af15b995 100644
--- a/src/main/java/org/olat/modules/iq/_content/qti.html
+++ b/src/main/java/org/olat/modules/iq/_content/qti.html
@@ -187,6 +187,12 @@ function confirmSuspend() {
 					
 				</div>
 			</div>
+			#elseif($qtistatus.isClosed())
+			<div class="b_subcr">			
+				<div id="o_qti_run_content_inner" class="b_floatscrollbox">
+						$r.render("qticomp", "qtiform")
+				</div>
+			</div>
 			#end
 		</div>
 	</div>
-- 
GitLab