Package org.olat.modules.iq

Source Code of org.olat.modules.iq.IQComponentRenderer

/**
* 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.
* <p>
*/

package org.olat.modules.iq;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.StringEscapeUtils;
import org.dom4j.Element;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.ComponentRenderer;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.render.RenderResult;
import org.olat.core.gui.render.Renderer;
import org.olat.core.gui.render.RenderingState;
import org.olat.core.gui.render.StringOutput;
import org.olat.core.gui.render.URLBuilder;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.Formatter;
import org.olat.ims.qti.QTIConstants;
import org.olat.ims.qti.container.AssessmentContext;
import org.olat.ims.qti.container.ItemContext;
import org.olat.ims.qti.container.Output;
import org.olat.ims.qti.container.SectionContext;
import org.olat.ims.qti.container.qtielements.GenericQTIElement;
import org.olat.ims.qti.container.qtielements.Hint;
import org.olat.ims.qti.container.qtielements.Item;
import org.olat.ims.qti.container.qtielements.ItemFeedback;
import org.olat.ims.qti.container.qtielements.Material;
import org.olat.ims.qti.container.qtielements.Objectives;
import org.olat.ims.qti.container.qtielements.RenderInstructions;
import org.olat.ims.qti.container.qtielements.Solution;
import org.olat.ims.qti.navigator.Info;
import org.olat.ims.qti.process.AssessmentInstance;
import org.olat.ims.qti.process.Resolver;

/**
*
* @author Felix Jost
*/
public class IQComponentRenderer implements ComponentRenderer {
 
  /**
   * Default constructor
   */
  public IQComponentRenderer() {
    super();
  }
 
  /**
   * Render the QTI form
   * @param comp
   * @param translator
   * @param renderer
   * @return rendered form
   */
  public StringOutput buildForm(IQComponent comp, Translator translator, Renderer renderer, URLBuilder ubu) {
    StringOutput sb = new StringOutput();
    Info info = comp.getAssessmentInstance().getNavigator().getInfo();
    AssessmentInstance ai = comp.getAssessmentInstance();
    int status = info.getStatus();
    int message = info.getMessage();
    boolean renderItems = info.isRenderItems();
    AssessmentContext act = ai.getAssessmentContext();

    // first treat messages and errors
    if (info.containsMessage()) {
      switch (message) {
        case QTIConstants.MESSAGE_ITEM_SUBMITTED :         
          //item hints?
          if (info.isHint()) {
            Hint el_hint = info.getCurrentOutput().getHint();
            if (el_hint.getFeedbackstyle() == Hint.FEEDBACKSTYLE_INCREMENTAL) {
              // increase the hint level so we know which hint to display
              ItemContext itc = act.getCurrentSectionContext().getCurrentItemContext();
              int nLevel = itc.getHintLevel() + 1;
              int numofhints = el_hint.getChildCount();
              if (nLevel > numofhints) nLevel = numofhints;
              itc.setHintLevel(nLevel);
              //<!ELEMENT hint (qticomment? , hintmaterial+)>
             
              displayFeedback(sb, (GenericQTIElement)el_hint.getChildAt(nLevel-1), ai, translator.getLocale());
            } else {
              displayFeedback(sb, el_hint, ai, translator.getLocale());
            }
          }
          //item solution?
          if (info.isSolution()) {
            Solution el_solution = info.getCurrentOutput().getSolution();
            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++;
              }
            }
          }
          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();
            displaySectionInfo(sb, sc, ai, comp, ubu, translator);
          }
          break;

        case QTIConstants.MESSAGE_SECTION_SUBMITTED :
          //  provide section feedback if enabled and existing
          //SectionContext sc = act.getCurrentSectionContext();         
          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(!comp.getMenuDisplayConf().isEnabledMenu() && !comp.getMenuDisplayConf().isItemPageSequence()) {
            SectionContext sc = ai.getAssessmentContext().getCurrentSectionContext();
            displaySectionInfo(sb, sc, ai, comp, ubu, translator);
          }
          break;

        case QTIConstants.MESSAGE_ASSESSMENT_SUBMITTED :
          //  provide assessment feedback if enabled and existing
          if (info.isFeedback()) {
            Output outp = info.getCurrentOutput();
            GenericQTIElement el_feedback = outp.getEl_response();
            if (el_feedback != null) displayFeedback(sb, el_feedback, ai, translator.getLocale());
          }
          break;

        case QTIConstants.MESSAGE_SECTION_INFODEMANDED : // for menu item navigator
          // provide some stats maybe
          SectionContext sc = ai.getAssessmentContext().getCurrentSectionContext();
          displaySectionInfo(sb, sc, ai, comp, ubu, translator);
          break;

        case QTIConstants.MESSAGE_ASSESSMENT_INFODEMANDED : // at the start of the test
          displayAssessmentInfo(sb, act, ai, comp, ubu, translator);
          break;
      }
    }

    if (renderItems) {
      boolean displayForm = true;
      // First check wether we need to render a form.
      // No form is needed if the current item has a matapplet object to be displayed.
      // Matapplets will send their response back directly.
      SectionContext sct = act.getCurrentSectionContext();
      ItemContext itc = null;
      if (sct != null && !ai.isSectionPage()) {
        itc = sct.getCurrentItemContext();
        if (itc != null) {
          Item item = itc.getQtiItem();
          if (item.getQTIIdent().startsWith("QTIEDIT:FLA:")) displayForm = false;
        }
      }
     
      sb.append("<form action=\"");
      ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "sitse" });
     
      sb.append("\" id=\"ofo_iq_item\" method=\"post\">");

      if (!ai.isSectionPage()) {
        if (itc != null) displayItem(sb, renderer, ubu, itc, ai);
      } else {
        if (sct != null && sct.getItemContextCount() != 0)
          displayItems(sb, renderer, ubu, sct, ai);
      }
     
      sb.append("<div class=\"b_button_group\"><input class=\"b_button\" type=\"submit\" name=\"olat_fosm\" value=\"");
      if (ai.isSectionPage())
        sb.append(StringEscapeUtils.escapeHtml(translator.translate("submitMultiAnswers")));
      else
        sb.append(StringEscapeUtils.escapeHtml(translator.translate("submitSingleAnswer")));
      sb.append("\"");
      if (!displayForm) sb.append(" style=\"display: none;\"");
      sb.append(" /></div></form>");
    }

    if (status == QTIConstants.ASSESSMENT_FINISHED) {
      if (info.isFeedback()) {
        Output outp = info.getCurrentOutput();
        GenericQTIElement el_feedback = outp.getEl_response();
        if (el_feedback != null) displayFeedback(sb, el_feedback, ai, null);
      }
    }
    return sb;
  }

  protected static String getFormattedLimit(long millis) {
    long sSec = millis / 1000;
    long sMin = sSec / 60;
    sSec = sSec - (sMin * 60);
    StringOutput sb = new StringOutput();
    sb.append(sMin);
    sb.append("'&nbsp;");
    sb.append(sSec);
    sb.append("\"");
    return sb.toString();
  }
 
  private StringOutput addItemLink(Renderer r, URLBuilder ubu ,Formatter formatter, ItemContext itc, int sectionPos, int itemPos,
   boolean clickable, boolean active, boolean info) {
    StringOutput sb = new StringOutput();

    String title = itc.getEl_item().attributeValue("title", "no title");
    String titleShort = Formatter.truncate(title, 27);
    long maxdur = itc.getDurationLimit();
    long start = itc.getTimeOfStart();
    long due = start + maxdur;
    boolean started = (start != -1);
    boolean timelimit = (maxdur != -1);
    String fdue = (started && timelimit ? formatter.formatTimeShort(new Date(due)) : null);
    sb.append("<div class=\"o_qti_menu_item\">");
    if (clickable) {
      sb.append("<a onclick=\"return o2cl()\" href=\"");
      ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "git" });
      sb.append("?itid="  + itemPos  + "&seid=" + sectionPos);
      sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(title) + "\">");
    }
    sb.append("<b>" + (sectionPos + 1) + "." + (itemPos + 1) + ".</b>&nbsp;");
    if (active) {
      sb.append("<strong>" + titleShort + "</strong>");
    } else {
      sb.append(titleShort);
    }
    if (clickable) {
      sb.append("</a>");
    }
    sb.append("</div>");

   
    if (!itc.isOpen()) {
      // add lock image
      sb.append("<div class=\"b_small_icon o_qti_closed_icon\" title=\"Closed\" ></div>");
    } else if (info) {
      // max duration info
      if (maxdur != -1) {
          sb.append("<div class=\"b_small_icon o_qti_timelimit_icon\" title=\"");
          if (!itc.isStarted()) {
            sb.append(StringEscapeUtils.escapeHtml(r.getTranslator().translate("timelimit.initial", new String[] {getFormattedLimit(maxdur)})));
          } else  {
            sb.append(StringEscapeUtils.escapeHtml(r.getTranslator().translate("timelimit.running", new String[] {fdue})));
          }
          sb.append("\" ></div>");
      }
     
      // attempts info
      int maxa = itc.getMaxAttempts();
      int attempts = itc.getTimesAnswered();
      if (maxa != -1) { // only limited times of answers
        sb.append("<div class=\"b_small_icon o_qti_attemptslimit_icon\" title=\"");
        sb.append(StringEscapeUtils.escapeHtml(r.getTranslator().translate("attemptsleft", new String[] {"" + (maxa - attempts)})));
        sb.append("\" ></div>");
      }
    }

    return sb;
  }

  // menu stuff
  private StringOutput addSectionLink(Renderer r, URLBuilder ubu, Formatter formatter, SectionContext sc, int sectionPos, boolean clickable, boolean active) {
    StringOutput sb = new StringOutput();

    // section link
    String title = Formatter.truncate(sc.getTitle(), 30);
    long maxdur = sc.getDurationLimit();
    long start = sc.getTimeOfStart();
    long due = start + maxdur;
    boolean started = (start != -1);
    boolean timelimit = (maxdur != -1);
    String fdue = (started && timelimit ? formatter.formatTimeShort(new Date(due)) : null);
    sb.append("<div class=\"o_qti_menu_section\">");

    if (!sc.isOpen()) clickable = false;
    if (clickable) {
      sb.append("<a onclick=\"return o2cl()\" href=\"");
      ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "gse" });
      sb.append("?seid=" + sectionPos);
      sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(sc.getTitle()) + "\">");
    }
    sb.append("<b>" + (sectionPos + 1) + ".</b>&nbsp;");
    if (active) {
      sb.append("<strong>" + title + "</strong>");
    } else {
      sb.append(title);
    }
    if (clickable) {
      sb.append("</a>");
    }
    sb.append("</div>");


    if (!sc.isOpen()) {
      // add lock image
      sb.append("<div class=\"b_small_icon o_qti_closed_icon\" title=\"Section is closed.\" ></div>");
    } else {
      // max duration info
      if (maxdur != -1) {
          sb.append("<div class=\"b_small_icon o_qti_timelimit_icon\" title=\"");
          if (!sc.isStarted()) {
            sb.append(StringEscapeUtils.escapeHtml(r.getTranslator().translate("timelimit.initial", new String[] {getFormattedLimit(maxdur)})));
          } else  {
            sb.append(StringEscapeUtils.escapeHtml(r.getTranslator().translate("timelimit.running", new String[] {fdue})));
          }
          sb.append("\" ></div>");
      }
    }
    return sb;
  }

  /**
   * Method buildMenu.
   *
   * @return DOCUMENT ME!
   */
  private StringOutput buildMenu(IQComponent comp, Translator translator, Renderer r, URLBuilder ubu) {
    StringOutput sb = new StringOutput();
    AssessmentInstance ai = comp.getAssessmentInstance();
    AssessmentContext ac = ai.getAssessmentContext();
    boolean renderSectionTitlesOnly = comp.getMenuDisplayConf().isRenderSectionsOnly();

    sb.append("<h4>");
    sb.append(ac.getTitle());
    sb.append("</h4>");

    // append assessment navigation
    Formatter formatter = Formatter.getInstance(translator.getLocale());
    int scnt = ac.getSectionContextCount();
    for (int i = 0; i < scnt; i++) {
      SectionContext sc = ac.getSectionContext(i);
      boolean clickable = (ai.isSectionPage() && sc.isOpen()) || (!ai.isSectionPage());
      clickable = clickable && !ai.isClosed();
      clickable = clickable && ai.isMenu();
      sb.append("<ul><li class=\"o_qti_menu_section\">");
      sb.append(addSectionLink(r, ubu, formatter, sc, i, clickable, ac.getCurrentSectionContextPos() == i));
      sb.append("</li>");
     
      if (!renderSectionTitlesOnly) {
        //not only sections, but render questions to
        int icnt = sc.getItemContextCount();
        for (int j = 0; j < icnt; j++) {
          ItemContext itc = sc.getItemContext(j);
          clickable = !ai.isSectionPage() && sc.isOpen() && itc.isOpen();
          clickable = clickable && !ai.isClosed();
          clickable = clickable && ai.isMenu();
          sb.append("<li class=\"o_qti_menu_item\">");
          sb.append(addItemLink(r, ubu, formatter, itc, i, j, clickable,
              (ac.getCurrentSectionContextPos() == i && sc.getCurrentItemContextPos() == j), !ai.isSurvey()));
          sb.append("</li>");
        }
      }
      sb.append("</ul>");
    }
    return sb;
  }

  private void displayItems(StringOutput sb, Renderer renderer, URLBuilder ubu, SectionContext sc, AssessmentInstance ai) {
    // display the whole current section on one page
    List items = sc.getItemContextsToRender();
    for (Iterator iter= items.iterator(); iter.hasNext();) {
      ItemContext itc  = (ItemContext) iter.next();
      if (itc.isOpen()) {
        displayItem(sb, renderer, ubu, itc, ai);
      } else {
        displayItemClosed(sb,renderer,itc);
      }
    }
  }

  /**
   * Display message : Item is closed, could not be displayed.
   * @param sb
   * @param renderer
   * @param itc
   */
  private void displayItemClosed(StringOutput sb, Renderer renderer,ItemContext itc) {
    StringBuilder buffer = new StringBuilder(100);   
    buffer.append("<div class=\"b_warning\"><strong>").append(renderer.getTranslator().translate("couldNotDisplayItem")).append("</strong></div>");
    sb.append(buffer);
  }
 
  private void displayItem(StringOutput sb, Renderer renderer, URLBuilder ubu, ItemContext itc, AssessmentInstance ai) {
    StringBuilder buffer = new StringBuilder(1000);
    Resolver resolver = ai.getResolver();
    RenderInstructions ri = new RenderInstructions();
    ri.put(RenderInstructions.KEY_STATICS_PATH, resolver.getStaticsBaseURI() + "/");
    ri.put(RenderInstructions.KEY_LOCALE, renderer.getTranslator().getLocale());
    StringOutput soCommandURI = new StringOutput(50);
    ubu.buildURI(soCommandURI, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "sflash" });
    ri.put(RenderInstructions.KEY_APPLET_SUBMIT_URI, soCommandURI.toString());
    if (itc.getItemInput() != null)
      ri.put(RenderInstructions.KEY_ITEM_INPUT, itc.getItemInput());
    ri.put(RenderInstructions.KEY_RENDER_TITLE, Boolean.valueOf(ai.isDisplayTitles()));
    itc.getQtiItem().render(buffer, ri);
    sb.append(buffer);
  }
 
  private void displaySectionInfo(StringOutput sb, SectionContext sc, AssessmentInstance ai, IQComponent comp, URLBuilder ubu, Translator translator) {
    // display the sectionInfo
    if (sc == null) return;
    if (ai.isDisplayTitles())
      sb.append("<h3>" + sc.getTitle() + "</h3>");
    Objectives objectives = sc.getObjectives();
    if (objectives != null) {
      StringBuilder sbTmp = new StringBuilder();
      Resolver resolver = ai.getResolver();
      RenderInstructions ri = new RenderInstructions();
      ri.put(RenderInstructions.KEY_STATICS_PATH, resolver.getStaticsBaseURI() + "/");
      objectives.render(sbTmp, ri);
      sb.append(sbTmp);
    }
    // if Menu not visible, or if visible but not selectable, and itemPage sequence (one question per page) 
    // show button to navigate to the first question of the current section     
    IQMenuDisplayConf menuDisplayConfig = comp.getMenuDisplayConf();
    if (!menuDisplayConfig.isEnabledMenu() && menuDisplayConfig.isItemPageSequence()) {
      sb.append("<a onclick=\"return o2cl()\" href=\"");
      ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "git" });
      AssessmentContext ac = ai.getAssessmentContext();
      int sectionPos = ac.getCurrentSectionContextPos();
      sb.append("?itid=" + 0 + "&seid=" + sectionPos);
      String title = translator.translate("next");
      sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(title) + "\">");
      sb.append(title);
      sb.append("</a>");
    }   
  }

  private void displayAssessmentInfo(StringOutput sb, AssessmentContext ac, AssessmentInstance ai, IQComponent comp, URLBuilder ubu, Translator translator) {
    Objectives objectives = ac.getObjectives();
    if (objectives != null) {
      StringBuilder sbTmp = new StringBuilder();
      Resolver resolver = ai.getResolver();
      RenderInstructions ri = new RenderInstructions();
      ri.put(RenderInstructions.KEY_STATICS_PATH, resolver.getStaticsBaseURI() + "/");
      objectives.render(sbTmp, ri);
      sb.append(sbTmp);
    }
    //if Menu not visible, or if visible but not selectable show button to navigate to the first section panel     
    IQMenuDisplayConf menuDisplayConfig = comp.getMenuDisplayConf();
    if (!menuDisplayConfig.isEnabledMenu()) {
      sb.append("<a onclick=\"return o2cl()\" href=\"");
      ubu.buildURI(sb, new String[] { VelocityContainer.COMMAND_ID }, new String[] { "gse" });
      sb.append("?seid=" + 0);       
      String title = translator.translate("next");
      sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(title) + "\">");       
      sb.append(title);
      sb.append("</a>");
    }       
  }

  private void displayFeedback(StringOutput sb, GenericQTIElement feedback, AssessmentInstance ai, Locale locale) {
    StringBuilder sbTmp = new StringBuilder();
    Resolver resolver = ai.getResolver();
    RenderInstructions ri = new RenderInstructions();
    ri.put(RenderInstructions.KEY_STATICS_PATH, resolver.getStaticsBaseURI() + "/");
    ri.put(RenderInstructions.KEY_LOCALE, locale);
    feedback.render(sbTmp, ri);
    sb.append(sbTmp);
  }
   
  /**
   * @see org.olat.core.gui.render.ui.ComponentRenderer#render(org.olat.core.gui.render.Renderer, org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component, org.olat.core.gui.render.URLBuilder, org.olat.core.gui.translator.Translator, org.olat.core.gui.render.RenderResult, java.lang.String[])
   */
  public void render(Renderer renderer, StringOutput target, Component source, URLBuilder ubu, Translator translator, RenderResult renderResult, String[] args) {

    IQComponent qticomp = (IQComponent)source;

    if (args[0].equals("menu")) { // render the menu
      target.append(buildMenu(qticomp, translator, renderer, ubu));
    } else if (args[0].equals("qtiform")) { // render the content
      target.append(buildForm(qticomp, translator, renderer, ubu));
    }   
  }

  /**
   * @see org.olat.core.gui.render.ui.ComponentRenderer#renderHeaderIncludes(org.olat.core.gui.render.Renderer, org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component, org.olat.core.gui.render.URLBuilder, org.olat.core.gui.translator.Translator)
   */
  public void renderHeaderIncludes(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, RenderingState rstate) {
    //
  }

  /**
   * @see org.olat.core.gui.render.ui.ComponentRenderer#renderBodyOnLoadJSFunctionCall(org.olat.core.gui.render.Renderer, org.olat.core.gui.render.StringOutput, org.olat.core.gui.components.Component)
   */
  public void renderBodyOnLoadJSFunctionCall(Renderer renderer, StringOutput sb, Component source, RenderingState rstate) {
    //
  }
 
 
}
TOP

Related Classes of org.olat.modules.iq.IQComponentRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.