/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.ims.qti21.ui; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.codec.binary.Base64; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.MultipartFileInfos; import org.olat.core.gui.control.WindowControl; import org.olat.core.util.StringHelper; import org.olat.ims.qti21.ui.ResponseInput.Base64Input; import org.olat.ims.qti21.ui.ResponseInput.FileInput; import org.olat.ims.qti21.ui.ResponseInput.StringInput; import uk.ac.ed.ph.jqtiplus.exception.QtiParseException; import uk.ac.ed.ph.jqtiplus.node.item.interaction.EndAttemptInteraction; import uk.ac.ed.ph.jqtiplus.types.Identifier; /** * * Initial date: 24.09.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public abstract class AbstractQtiWorksController extends FormBasicController { public static final String PNG_BASE64_PREFIX = "data:image/png;base64,"; public AbstractQtiWorksController(UserRequest ureq, WindowControl wControl, String pageName) { super(ureq, wControl, pageName); } @Override protected void doDispose() { // } protected String extractComment() { if (mainForm.getRequestParameter("qtiworks_comment_presented") == null) { /* No comment box given to candidate */ return null; } String comment = mainForm.getRequestParameter("qtiworks_comment"); return StringHelper.containsNonWhitespace(comment) ? comment : null; } protected abstract Identifier getResponseIdentifierFromUniqueId(String uniqueId); protected void processResponse(UserRequest ureq, FormItem source) { Map<Identifier, ResponseInput> stringResponseMap = extractStringResponseData(); Map<Identifier, ResponseInput> fileResponseMap; if(mainForm.isMultipartEnabled()) { fileResponseMap = extractFileResponseData(); } else { fileResponseMap = Collections.emptyMap(); } if(source instanceof FormLink) { FormLink button = (FormLink)source; String cmd = button.getCmd(); if(cmd != null && cmd.startsWith("qtiworks_response_")) { String responseIdentifierString = cmd.substring("qtiworks_response_".length()); String presentedFlag = "qtiworks_presented_".concat(responseIdentifierString); if(mainForm.getRequestParameterSet().contains(presentedFlag)) { Identifier responseIdentifier; try { responseIdentifier = getResponseIdentifierFromUniqueId(responseIdentifierString); //Identifier.parseString(responseIdentifierString); } catch (final QtiParseException e) { throw new RuntimeException("Bad response identifier encoded in parameter " + cmd, e); } String[] responseValues; if(button.getUserObject() instanceof EndAttemptInteraction) { responseValues = new String[]{ ((EndAttemptInteraction)button.getUserObject()).getTitle() }; } else { responseValues = new String[]{ "submit" }; } StringInput stringResponseData = new StringInput(responseValues); stringResponseMap.put(responseIdentifier, stringResponseData); } } } String candidateComment = extractComment(); fireResponse(ureq, source, stringResponseMap, fileResponseMap, candidateComment); } protected void processTemporaryResponse(UserRequest ureq) { Map<Identifier, ResponseInput> stringResponseMap = extractStringResponseData(); String cmd = ureq.getParameter("tmpResponse"); String responseIdentifierString = cmd.substring("qtiworks_response_".length()); String presentedFlag = "qtiworks_presented_".concat(responseIdentifierString); if(mainForm.getRequestParameterSet().contains(presentedFlag)) { Identifier responseIdentifier; try { responseIdentifier = getResponseIdentifierFromUniqueId(responseIdentifierString); //Identifier.parseString(responseIdentifierString); } catch (final QtiParseException e) { throw new RuntimeException("Bad response identifier encoded in parameter " + cmd, e); } String[] responseValues = new String[]{ "submit" }; StringInput stringResponseData = new StringInput(responseValues); stringResponseMap.put(responseIdentifier, stringResponseData); } fireTemporaryResponse(ureq, stringResponseMap); } protected abstract void fireTemporaryResponse(UserRequest ureq, Map<Identifier, ResponseInput> stringResponseMap); protected abstract void fireResponse(UserRequest ureq, FormItem source, Map<Identifier, ResponseInput> stringResponseMap, Map<Identifier, ResponseInput> fileResponseMap, String comment); protected Map<Identifier, ResponseInput> extractStringResponseData() { final Map<Identifier, ResponseInput> responseMap = new HashMap<>(); final Set<String> parameterNames = mainForm.getRequestParameterSet(); for (final String name : parameterNames) { if (name.startsWith("qtiworks_presented_")) { final String responseIdentifierString = name.substring("qtiworks_presented_".length()); final Identifier responseIdentifier; try { responseIdentifier = getResponseIdentifierFromUniqueId(responseIdentifierString); // responseIdentifier = Identifier.parseString(responseIdentifierString); } catch (final QtiParseException e) { //throw new BadResponseWebPayloadException("Bad response identifier encoded in parameter " + name, e); throw new RuntimeException("Bad response identifier encoded in parameter " + name, e); } String[] responseBase64Values = mainForm.getRequestParameterValues("qtiworks_response_64_" + responseIdentifierString); if(responseBase64Values != null && responseBase64Values.length == 1) { //only used from drawing interaction as image/png String responseData = responseBase64Values[0]; if(responseData.startsWith(PNG_BASE64_PREFIX)) { byte[] file = Base64.decodeBase64(responseData.substring(PNG_BASE64_PREFIX.length(), responseData.length())); final Base64Input stringResponseData = new Base64Input("image/png", file); responseMap.put(responseIdentifier, stringResponseData); } } else { final String[] responseValues = mainForm.getRequestParameterValues("qtiworks_response_" + responseIdentifierString); final StringInput stringResponseData = new StringInput(responseValues); responseMap.put(responseIdentifier, stringResponseData); } } } return responseMap; } protected Map<Identifier, ResponseInput> extractFileResponseData() { Map<Identifier, ResponseInput> fileResponseMap = new HashMap<>(); Set<String> parameterNames = new HashSet<>(mainForm.getRequestMultipartFilesSet()); parameterNames.addAll(mainForm.getRequestParameterSet()); for (String name : parameterNames) { if (name.startsWith("qtiworks_uploadpresented_")) { String responseIdentifierString = name.substring("qtiworks_uploadpresented_".length()); Identifier responseIdentifier; try { responseIdentifier = getResponseIdentifierFromUniqueId(responseIdentifierString); //responseIdentifier = Identifier.parseString(responseIdentifierString); } catch (final QtiParseException e) { throw new RuntimeException("Bad response identifier encoded in parameter " + name, e); } String multipartName = "qtiworks_uploadresponse_" + responseIdentifierString; MultipartFileInfos multipartFile = mainForm.getRequestMultipartFileInfos(multipartName); if (multipartFile == null) { throw new RuntimeException("Expected to find multipart file with name " + multipartName); } fileResponseMap.put(responseIdentifier, new FileInput(multipartFile)); } } return fileResponseMap; } }