/**
* 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) frentix GmbH<br>
* http://www.frentix.com<br>
* <p>
*/
package org.olat.ims.qti.editor;
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.FormUIFactory;
import org.olat.core.gui.components.form.flexible.elements.IntegerElement;
import org.olat.core.gui.components.form.flexible.elements.RichTextElement;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
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.elements.richText.RichTextConfiguration;
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.filter.Filter;
import org.olat.core.util.filter.FilterFactory;
import org.olat.ims.qti.editor.beecom.objects.ChoiceQuestion;
import org.olat.ims.qti.editor.beecom.objects.Control;
import org.olat.ims.qti.editor.beecom.objects.Duration;
import org.olat.ims.qti.editor.beecom.objects.Item;
import org.olat.ims.qti.editor.beecom.objects.Question;
/**
* This is the form controller responsible for item metadata like title and
* description but also for setting feedback information and hints and display
* properties.
*
* Fires: Event.DONE_EVENT, NodeBeforeChangeEvent
* <P>
* Initial Date: Jul 17, 2009 <br>
*
* @author gwassmann
*/
public class ItemMetadataFormController extends FormBasicController {
private Item item;
private boolean isSurvey, isRestrictedEditMode;
private TextElement title;
private RichTextElement desc, hint, solution;
private SingleSelection layout, limitAttempts, limitTime, shuffle, showHints, showSolution;
private IntegerElement attempts, timeMin, timeSec;
private QTIEditorPackage qti;
public ItemMetadataFormController(UserRequest ureq, WindowControl control, Item item, QTIEditorPackage qti, boolean restrictedEdit) {
super(ureq, control);
this.item = item;
this.qti = qti;
this.isSurvey = qti.getQTIDocument().isSurvey();
this.isRestrictedEditMode = restrictedEdit;
initForm(ureq);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#doDispose()
*/
protected void doDispose() {
// still to come
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formInnerEvent(org.olat.core.gui.UserRequest,
* org.olat.core.gui.components.form.flexible.FormItem,
* org.olat.core.gui.components.form.flexible.impl.FormEvent)
*/
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if (source == limitAttempts) {
toggle(attempts);
} else if (source == limitTime) {
toggle(timeMin);
toggle(timeSec);
} else if (source == showHints) {
toggle(hint);
} else if (source == showSolution) {
toggle(solution);
}
}
protected boolean validateFormLogic(UserRequest ureq) {
if (title.getValue().trim().isEmpty()) { // Remove empty title to fix OLAT-2296
title.setValue("");
title.setErrorKey("form.imd.error.empty.title", null);
return false;
}
return true;
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formOK(org.olat.core.gui.UserRequest)
*/
protected void formOK(UserRequest ureq) {
// Fire Change Event
String newTitle = title.getValue();
String oldTitle = item.getTitle();
boolean hasTitleChange = newTitle != null && !newTitle.equals(oldTitle);
String newObjectives = desc.getRawValue(); // trust authors, don't do XSS filtering
// Remove any conditional comments due to strange behavior in test (OLAT-4518)
Filter conditionalCommentFilter = FilterFactory.getConditionalHtmlCommentsFilter();
newObjectives = conditionalCommentFilter.filter(newObjectives);
String oldObjectives = item.getObjectives();
boolean hasObjectivesChange = newObjectives != null && !newObjectives.equals(oldObjectives);
NodeBeforeChangeEvent nce = new NodeBeforeChangeEvent();
if (hasTitleChange) {
nce.setNewTitle(newTitle);
}
if (hasObjectivesChange) {
nce.setNewObjectives(newObjectives);
}
if (hasTitleChange || hasObjectivesChange) {
// create a memento first
nce.setItemIdent(item.getIdent());
nce.setQuestionIdent(item.getQuestion().getQuestion().getId());
fireEvent(ureq, nce);
}
// Update item
item.setTitle(newTitle);
item.setObjectives(newObjectives); // trust authors, don't to XSS filtering
Question q = item.getQuestion();
if (layout != null) {
((ChoiceQuestion) q).setFlowLabelClass(layout.getSelectedKey() == "h" ? ChoiceQuestion.BLOCK : ChoiceQuestion.LIST);
}
if (!isSurvey && !isRestrictedEditMode) {
q.setShuffle(shuffle.getSelected() == 0);
Control itemControl = (Control) item.getItemcontrols().get(0);
itemControl.setFeedback(itemControl.getFeedback() == Control.CTRL_UNDEF ? Control.CTRL_NO : itemControl.getFeedback());
itemControl.setHint(showHints.getSelected() == 0 ? Control.CTRL_YES : Control.CTRL_NO);
itemControl.setSolution(showSolution.getSelected() == 0 ? Control.CTRL_YES : Control.CTRL_NO);
q.setHintText(conditionalCommentFilter.filter(hint.getRawValue())); // trust authors, don't to XSS filtering
q.setSolutionText(conditionalCommentFilter.filter(solution.getRawValue())); // trust authors, don't to XSS filtering
if (limitTime.getSelectedKey().equals("y")) {
item.setDuration(new Duration(1000 * timeSec.getIntValue() + 1000 * 60 * timeMin.getIntValue()));
} else {
item.setDuration(null);
}
if (limitAttempts.getSelectedKey().equals("y")) {
item.setMaxattempts(attempts.getIntValue());
} else {
item.setMaxattempts(0);
}
}
fireEvent(ureq, Event.DONE_EVENT);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#initForm(org.olat.core.gui.components.form.flexible.FormItemContainer,
* org.olat.core.gui.control.Controller, org.olat.core.gui.UserRequest)
*/
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
this.setFormTitle("fieldset.legend.metadata");
this.setFormContextHelp("org.olat.ims.qti.editor", "qed-meta-surv-fibess.html", "help.hover.qti-meta-fibess");
// Title
title = uifactory.addTextElement("title", "form.imd.title", -1, item.getTitle(), formLayout);
title.setMandatory(true);
title.setNotEmptyCheck("form.imd.error.empty.title");
// Question Type
String typeName = getType();
StaticTextElement type = uifactory.addStaticTextElement("type", "form.imd.type", typeName, formLayout);
// Description
desc = uifactory.addRichTextElementForStringData("desc", "form.imd.descr", item.getObjectives(), 8, -1, false, true, null, null,
formLayout, ureq.getUserSession(), getWindowControl());
RichTextConfiguration richTextConfig = desc.getEditorConfiguration();
// set upload dir to the media dir
richTextConfig.setFileBrowserUploadRelPath("media");
// Layout/Alignment
Question q = item.getQuestion();
// alignment of KPRIM is only horizontal
if (q instanceof ChoiceQuestion && item.getQuestion().getType() != Question.TYPE_KPRIM) {
String[] layoutKeys = new String[] { "h", "v" };
String[] layoutValues = new String[] { translate("form.imd.layout.horizontal"), translate("form.imd.layout.vertical") };
//layout = uifactory.addDropdownSingleselect("form.imd.layout", formLayout, layoutKeys, layoutValues, null);
layout = uifactory.addRadiosHorizontal("layout", "form.imd.layout", formLayout, layoutKeys, layoutValues);
layout.select(((ChoiceQuestion) q).getFlowLabelClass() == ChoiceQuestion.BLOCK ? "h" : "v", true);
}
if (!isSurvey && !isRestrictedEditMode) {
String[] yesnoKeys = new String[] { "y", "n" };
String[] yesnoValues = new String[] { translate("yes"), translate("no") };
// Attempts
limitAttempts = uifactory.addRadiosHorizontal("form.imd.limittries", formLayout, yesnoKeys, yesnoValues);
limitAttempts.addActionListener(this, FormEvent.ONCHANGE);
attempts = uifactory.addIntegerElement("form.imd.tries", 0, formLayout);
attempts.setDisplaySize(3);
if (item.getMaxattempts() > 0) {
limitAttempts.select("y", true);
attempts.setIntValue(item.getMaxattempts());
} else {
limitAttempts.select("n", true);
attempts.setVisible(false);
}
// Time Limit
limitTime = uifactory.addRadiosHorizontal("form.imd.limittime", formLayout, yesnoKeys, yesnoValues);
limitTime.addActionListener(this, FormEvent.ONCHANGE);
timeMin = uifactory.addIntegerElement("form.imd.time.min", 0, formLayout);
timeMin.setDisplaySize(3);
timeSec = uifactory.addIntegerElement("form.imd.time.sek", 0, formLayout);
timeSec.setDisplaySize(3);
if (item.getDuration() != null && item.getDuration().isSet()) {
limitTime.select("y", true);
timeMin.setIntValue(item.getDuration().getMin());
timeSec.setIntValue(item.getDuration().getSec());
} else {
limitTime.select("n", true);
timeMin.setVisible(false);
timeSec.setVisible(false);
}
// Shuffle Answers
shuffle = uifactory.addRadiosHorizontal("shuffle", "form.imd.shuffle", formLayout, yesnoKeys, yesnoValues);
if (item.getQuestion().isShuffle()) {
shuffle.select("y", true);
} else {
shuffle.select("n", true);
}
// Hints
Control itemControl = (Control) item.getItemcontrols().get(0);
showHints = uifactory.addRadiosHorizontal("showHints", "form.imd.solutionhints.show", formLayout, yesnoKeys, yesnoValues);
showHints.addActionListener(this, FormEvent.ONCHANGE);
hint = uifactory.addRichTextElementForStringData("hint", "form.imd.solutionhints", item.getQuestion().getHintText(), 8, -1, false,
true, qti.getBaseDir(), null, formLayout, ureq.getUserSession(), getWindowControl());
// set upload dir to the media dir
hint.getEditorConfiguration().setFileBrowserUploadRelPath("media");
if (itemControl.isHint()) {
showHints.select("y", true);
} else {
showHints.select("n", true);
hint.setVisible(false);
}
// Solution
showSolution = uifactory.addRadiosHorizontal("showSolution", "form.imd.correctsolution.show", formLayout, yesnoKeys, yesnoValues);
showSolution.addActionListener(this, FormEvent.ONCHANGE);
solution = uifactory.addRichTextElementForStringData("solution", "form.imd.correctsolution", item.getQuestion().getSolutionText(), 8,
-1, false, true, qti.getBaseDir(), null, formLayout, ureq.getUserSession(), getWindowControl());
// set upload dir to the media dir
hint.getEditorConfiguration().setFileBrowserUploadRelPath("media");
if (itemControl.isSolution()) {
showSolution.select("y", true);
} else {
showSolution.select("n", true);
solution.setVisible(false);
}
}
// Submit Button
uifactory.addFormSubmitButton("submit", formLayout);
}
/**
* @return The translated type string of the item
*/
private String getType() {
int questionType = item.getQuestion().getType();
String questionTypeLabel = "n/a";
switch (questionType) {
case Question.TYPE_SC:
questionTypeLabel = translate("item.type.sc");
break;
case Question.TYPE_MC:
questionTypeLabel = translate("item.type.mc");
break;
case Question.TYPE_FIB:
questionTypeLabel = translate("item.type.fib");
break;
case Question.TYPE_KPRIM:
questionTypeLabel = translate("item.type.kprim");
break;
case Question.TYPE_ESSAY:
questionTypeLabel = translate("item.type.essay");
break;
}
return questionTypeLabel;
}
/**
* @param item The item to be shown or hidden
*/
private void toggle(FormItem formItem) {
formItem.setVisible(!formItem.isVisible());
this.flc.setDirty(true);
}
}