/**
* <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.course.nodes.iq;
import java.io.File;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
import org.olat.core.gui.components.form.flexible.elements.SelectionElement;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.TextElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
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.util.StringHelper;
import org.olat.fileresource.FileResourceManager;
import org.olat.ims.qti21.QTI21DeliveryOptions;
import org.olat.ims.qti21.QTI21Module;
import org.olat.ims.qti21.QTI21Service;
import org.olat.modules.ModuleConfiguration;
import org.olat.repository.RepositoryEntry;
import org.springframework.beans.factory.annotation.Autowired;
import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
/**
*
* Initial date: 26.06.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class QTI21EditLayoutForm extends FormBasicController {
private static final String[] onKeys = new String[]{ "on" };
private static final String[] onValues = new String[]{ "" };
private static final String[] configKeys = new String[]{ "repo", "node" };
private SingleSelection configEl;
private SelectionElement fullWindowEl;
private MultipleSelectionElement showTitlesEl, showMenuEl;
private MultipleSelectionElement personalNotesEl;
private MultipleSelectionElement enableCancelEl, enableSuspendEl;
private MultipleSelectionElement limitAttemptsEl, blockAfterSuccessEl;
private MultipleSelectionElement displayQuestionProgressEl, displayScoreProgressEl;
private MultipleSelectionElement allowAnonymEl;
private MultipleSelectionElement digitalSignatureEl, digitalSignatureMailEl;
private FormLayoutContainer maxTimeCont;
private MultipleSelectionElement maxTimeEl;
private TextElement maxTimeHourEl, maxTimeMinuteEl;
private TextElement maxAttemptsEl;
private final RepositoryEntry testEntry;
private final ModuleConfiguration modConfig;
private final QTI21DeliveryOptions deliveryOptions;
@Autowired
private QTI21Module qtiModule;
@Autowired
private QTI21Service qtiService;
public QTI21EditLayoutForm(UserRequest ureq, WindowControl wControl, ModuleConfiguration modConfig,
RepositoryEntry testEntry, QTI21DeliveryOptions deliveryOptions) {
super(ureq, wControl);
this.modConfig = modConfig;
this.testEntry = testEntry;
this.deliveryOptions = deliveryOptions;
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
formLayout.setElementCssClass("o_qti_21_layout_configuration");
String[] configValues = new String[]{ translate("qti.form.setting.repo"), translate("qti.form.setting.node") };
configEl = uifactory.addRadiosHorizontal("config", null, formLayout, configKeys, configValues);
configEl.setElementCssClass("o_qti_21_configuration_settings");
configEl.addActionListener(FormEvent.ONCHANGE);
boolean configRef = modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_CONFIG_REF, false);
String key = configRef ? configKeys[0] : configKeys[1];
configEl.select(key, true);
//time limits
long timeLimits = configRef ? getMaxTimeLimit() :
modConfig.getIntegerSafe(IQEditController.CONFIG_KEY_TIME_LIMIT, -1);
String timeMaxHour = "";
String timeMaxMinute = "";
if(timeLimits > 0) {
timeMaxHour = Long.toString(timeLimits / 3600);
timeMaxMinute = Long.toString((timeLimits % 3600) / 60);
}
maxTimeEl = uifactory.addCheckboxesVertical("time.limit.enable", "time.limit.max", formLayout, onKeys, onValues, 1);
maxTimeEl.addActionListener(FormEvent.ONCHANGE);
maxTimeEl.setEnabled(!configRef);
if(timeLimits > 0) {
maxTimeEl.select(onKeys[0], true);
}
String page = velocity_root + "/max_time_limit.html";
maxTimeCont = FormLayoutContainer.createCustomFormLayout("time.limit.cont", getTranslator(), page);
maxTimeCont.setVisible(maxTimeEl.isAtLeastSelected(1));
formLayout.add(maxTimeCont);
maxTimeHourEl = uifactory.addTextElement("time.limit.hour", "time.limit.max", 4, timeMaxHour, maxTimeCont);
maxTimeHourEl.setDomReplacementWrapperRequired(false);
maxTimeHourEl.setDisplaySize(4);
maxTimeHourEl.setEnabled(!configRef);
maxTimeMinuteEl = uifactory.addTextElement("time.limit.minute", "time.limit.max", 4, timeMaxMinute, maxTimeCont);
maxTimeMinuteEl.setDomReplacementWrapperRequired(false);
maxTimeMinuteEl.setDisplaySize(4);
maxTimeMinuteEl.setEnabled(!configRef);
//max attempts
limitAttemptsEl = uifactory.addCheckboxesHorizontal("limitAttempts", "qti.form.limit.attempts", formLayout, onKeys, onValues);
limitAttemptsEl.addActionListener(FormEvent.ONCLICK);
limitAttemptsEl.setEnabled(!configRef);
String maxAttemptsValue = "";
int maxAttempts = configRef ? deliveryOptions.getMaxAttempts() :
modConfig.getIntegerSafe(IQEditController.CONFIG_KEY_ATTEMPTS, deliveryOptions.getMaxAttempts());
if(maxAttempts > 0) {
limitAttemptsEl.select(onKeys[0], true);
maxAttemptsValue = Integer.toString(maxAttempts);
}
maxAttemptsEl = uifactory.addTextElement("maxAttempts", "qti.form.attempts", 8, maxAttemptsValue, formLayout);
maxAttemptsEl.setDisplaySize(2);
maxAttemptsEl.setMandatory(true);
maxAttemptsEl.setEnabled(!configRef);
maxAttemptsEl.setVisible(maxAttempts > 0);
boolean blockAfterSuccess = configRef ? deliveryOptions.isBlockAfterSuccess() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS, deliveryOptions.isBlockAfterSuccess());
blockAfterSuccessEl = uifactory.addCheckboxesHorizontal("blockAfterSuccess", "qti.form.block.afterSuccess", formLayout, onKeys, onValues);
blockAfterSuccessEl.setEnabled(!configRef);
if(blockAfterSuccess) {
blockAfterSuccessEl.select(onKeys[0], true);
}
boolean allowAnonym = configRef ? deliveryOptions.isAllowAnonym() :
modConfig.getBooleanSafe(IQEditController.CONFIG_ALLOW_ANONYM, deliveryOptions.isAllowAnonym());
allowAnonymEl = uifactory.addCheckboxesHorizontal("allowAnonym", "qti.form.allow.anonym", formLayout, onKeys, onValues);
allowAnonymEl.setEnabled(!configRef);
if(allowAnonym) {
allowAnonymEl.select(onKeys[0], true);
}
boolean fullWindow = configRef ? deliveryOptions.isHideLms() :
modConfig.getBooleanSafe(IQEditController.CONFIG_FULLWINDOW, deliveryOptions.isHideLms());
fullWindowEl = uifactory.addCheckboxesHorizontal("fullwindow", "qti.form.fullwindow", formLayout, new String[]{"x"}, new String[]{""});
fullWindowEl.setElementCssClass("o_qti_21_configuration_full_window");
fullWindowEl.setEnabled(!configRef);
if(fullWindow) {
fullWindowEl.select("x", fullWindow);
}
boolean digitalSignature = configRef ? deliveryOptions.isDigitalSignature() :
modConfig.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE, deliveryOptions.isDigitalSignature());
digitalSignatureEl = uifactory.addCheckboxesHorizontal("digital.signature", "digital.signature", formLayout, new String[]{"x"}, new String[]{""});
digitalSignatureEl.setEnabled(!configRef);
if(digitalSignature) {
digitalSignatureEl.select("x", digitalSignature);
}
digitalSignatureEl.setVisible(qtiModule.isDigitalSignatureEnabled());
digitalSignatureEl.addActionListener(FormEvent.ONCHANGE);
boolean digitalSignatureSendMail = configRef ? deliveryOptions.isDigitalSignatureMail() :
modConfig.getBooleanSafe(IQEditController.CONFIG_DIGITAL_SIGNATURE_SEND_MAIL, deliveryOptions.isDigitalSignatureMail());
digitalSignatureMailEl = uifactory.addCheckboxesHorizontal("digital.signature.mail", "digital.signature.mail", formLayout, new String[]{"x"}, new String[]{""});
digitalSignatureMailEl.setEnabled(!configRef);
if(digitalSignatureSendMail) {
digitalSignatureMailEl.select("x", digitalSignatureSendMail);
}
digitalSignatureMailEl.setVisible(qtiModule.isDigitalSignatureEnabled() && digitalSignatureEl.isAtLeastSelected(1));
boolean showTitles = configRef ? deliveryOptions.isShowTitles() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_QUESTIONTITLE, deliveryOptions.isShowTitles());
showTitlesEl = uifactory.addCheckboxesHorizontal("showTitles", "qti.form.questiontitle", formLayout, onKeys, onValues);
showTitlesEl.setEnabled(!configRef);
if(showTitles) {
showTitlesEl.select(onKeys[0], true);
}
boolean showMenu = configRef ? deliveryOptions.isShowMenu() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLEMENU, deliveryOptions.isShowMenu());
showMenuEl = uifactory.addCheckboxesHorizontal("showmenu", "qti.form.menuenable", formLayout, onKeys, onValues);
showMenuEl.setEnabled(!configRef);
if(showMenu) {
showMenuEl.select(onKeys[0], true);
}
boolean personalNotes = configRef ? deliveryOptions.isPersonalNotes() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_MEMO, deliveryOptions.isPersonalNotes());
personalNotesEl = uifactory.addCheckboxesHorizontal("personalNotes", "qti.form.auto.memofield", formLayout, onKeys, onValues);
personalNotesEl.setEnabled(!configRef);
if(personalNotes) {
personalNotesEl.select(onKeys[0], true);
}
boolean questionProgress = configRef ? deliveryOptions.isDisplayQuestionProgress() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_QUESTIONPROGRESS, deliveryOptions.isDisplayQuestionProgress());
displayQuestionProgressEl = uifactory.addCheckboxesHorizontal("questionProgress", "qti.form.questionprogress", formLayout, onKeys, onValues);
displayQuestionProgressEl.setEnabled(!configRef);
if(questionProgress) {
displayQuestionProgressEl.select(onKeys[0], true);
}
boolean questionScore = configRef ? deliveryOptions.isDisplayScoreProgress() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_SCOREPROGRESS, deliveryOptions.isDisplayScoreProgress());
displayScoreProgressEl = uifactory.addCheckboxesHorizontal("scoreProgress", "qti.form.scoreprogress", formLayout, onKeys, onValues);
displayScoreProgressEl.setEnabled(!configRef);
if(questionScore) {
displayScoreProgressEl.select(onKeys[0], true);
}
boolean enableSuspend = configRef ? deliveryOptions.isEnableSuspend() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLESUSPEND, deliveryOptions.isEnableSuspend());
enableSuspendEl = uifactory.addCheckboxesHorizontal("suspend", "qti.form.enablesuspend", formLayout, onKeys, onValues);
enableSuspendEl.setElementCssClass("o_sel_qti_enable_suspend");
enableSuspendEl.setEnabled(!configRef);
if(enableSuspend) {
enableSuspendEl.select(onKeys[0], true);
}
boolean enableCancel = configRef ? deliveryOptions.isEnableCancel() :
modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLECANCEL, deliveryOptions.isEnableCancel());
enableCancelEl = uifactory.addCheckboxesHorizontal("cancel", "qti.form.enablecancel", formLayout, onKeys, onValues);
enableCancelEl.setElementCssClass("o_sel_qti_enable_cancel");
enableCancelEl.setEnabled(!configRef);
if(enableCancel) {
enableCancelEl.select(onKeys[0], true);
}
if(!configRef) {
uifactory.addFormSubmitButton("submit", formLayout);
}
update();
}
/**
* @return The time limit of the assessment test in seconds or -1 if not configured
*/
private long getMaxTimeLimit() {
FileResourceManager frm = FileResourceManager.getInstance();
File unzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource());
ResolvedAssessmentTest resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false, false);
AssessmentTest assessmentTest = resolvedAssessmentTest.getRootNodeLookup().extractIfSuccessful();
long timeLimit = -1;
if(assessmentTest != null && assessmentTest.getTimeLimits() != null && assessmentTest.getTimeLimits().getMaximum() != null) {
timeLimit = assessmentTest.getTimeLimits().getMaximum().longValue();
}
return timeLimit;
}
@Override
protected void doDispose() {
//
}
@Override
protected boolean validateFormLogic(UserRequest ureq) {
boolean allOk = true;
configEl.clearError();
if(!configEl.isOneSelected()) {
configEl.setErrorKey("form.legende.mandatory", null);
allOk &= false;
}
if(limitAttemptsEl.isAtLeastSelected(1)) {
maxAttemptsEl.clearError();
if(StringHelper.containsNonWhitespace(maxAttemptsEl.getValue())) {
try {
int val = Integer.parseInt(maxAttemptsEl.getValue());
if(val <= 0) {
maxAttemptsEl.setErrorKey("form.error.nointeger", null);
}
} catch(NumberFormatException e) {
maxAttemptsEl.setErrorKey("form.error.nointeger", null);
allOk &= false;
}
} else {
maxAttemptsEl.setErrorKey("form.legende.mandatory", null);
allOk &= false;
}
}
maxTimeCont.clearError();
if(maxTimeEl.isAtLeastSelected(1)) {
allOk &= validateTime(maxTimeHourEl);
allOk &= validateTime(maxTimeMinuteEl);
}
return allOk & super.validateFormLogic(ureq);
}
private boolean validateTime(TextElement timeEl) {
boolean allOk = true;
if(StringHelper.containsNonWhitespace(timeEl.getValue())) {
try {
double val = Long.parseLong(timeEl.getValue());
if(val < 0l) {
maxTimeCont.setErrorKey("form.error.nointeger", null);
allOk &= false;
}
} catch (NumberFormatException e) {
maxTimeCont.setErrorKey("form.error.nointeger", null);
allOk &= false;
}
}
return allOk;
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(limitAttemptsEl == source) {
update();
} else if(digitalSignatureEl == source) {
digitalSignatureMailEl.setVisible(digitalSignatureEl.isAtLeastSelected(1));
} else if(configEl == source) {
if(configEl.isOneSelected()) {
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_CONFIG_REF, configEl.isSelected(0));
if(configEl.isSelected(1)) {// manual configuration
long maxTime = getMaxTimeLimit();
if(maxTime > 0) {
modConfig.setIntValue(IQEditController.CONFIG_KEY_TIME_LIMIT, (int)maxTime);
} else {
modConfig.remove(IQEditController.CONFIG_KEY_TIME_LIMIT);
}
}
fireEvent(ureq, Event.DONE_EVENT);
}
} else if(maxTimeEl == source) {
maxTimeCont.setVisible(maxTimeEl.isAtLeastSelected(1));
}
super.formInnerEvent(ureq, source, event);
}
private void update() {
maxAttemptsEl.setVisible(limitAttemptsEl.isAtLeastSelected(1));
}
@Override
protected void formOK(UserRequest ureq) {
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_CONFIG_REF, configEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_FULLWINDOW, fullWindowEl.isSelected(0));
if(limitAttemptsEl.isSelected(0)) {
int maxAttempts = Integer.parseInt(maxAttemptsEl.getValue());
modConfig.setIntValue(IQEditController.CONFIG_KEY_ATTEMPTS, maxAttempts);
} else {
modConfig.setIntValue(IQEditController.CONFIG_KEY_ATTEMPTS, 0);
}
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS, blockAfterSuccessEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_ENABLEMENU, showMenuEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_QUESTIONTITLE, showTitlesEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_MEMO, personalNotesEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_ENABLECANCEL, enableCancelEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_ENABLESUSPEND, enableSuspendEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_QUESTIONPROGRESS, displayQuestionProgressEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_SCOREPROGRESS, displayScoreProgressEl.isSelected(0));
modConfig.setBooleanEntry(IQEditController.CONFIG_ALLOW_ANONYM, allowAnonymEl.isSelected(0));
if(qtiModule.isDigitalSignatureEnabled() && digitalSignatureEl.isSelected(0)) {
modConfig.setBooleanEntry(IQEditController.CONFIG_DIGITAL_SIGNATURE, true);
modConfig.setBooleanEntry(IQEditController.CONFIG_DIGITAL_SIGNATURE_SEND_MAIL, digitalSignatureMailEl.isSelected(0));
} else {
modConfig.setBooleanEntry(IQEditController.CONFIG_DIGITAL_SIGNATURE, false);
modConfig.setBooleanEntry(IQEditController.CONFIG_DIGITAL_SIGNATURE_SEND_MAIL, false);
}
if(maxTimeEl.isAtLeastSelected(1)) {
int maxTime = 0;
if(StringHelper.containsNonWhitespace(maxTimeHourEl.getValue())) {
maxTime += Integer.parseInt(maxTimeHourEl.getValue()) * 3600;
}
if(StringHelper.containsNonWhitespace(maxTimeMinuteEl.getValue())) {
maxTime += Integer.parseInt(maxTimeMinuteEl.getValue()) * 60;
}
if(maxTime > 0) {
modConfig.setIntValue(IQEditController.CONFIG_KEY_TIME_LIMIT, maxTime);
} else {
modConfig.remove(IQEditController.CONFIG_KEY_TIME_LIMIT);
}
} else {
modConfig.remove(IQEditController.CONFIG_KEY_TIME_LIMIT);
}
fireEvent(ureq, Event.DONE_EVENT);
}
}