/**
* <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.ms;
import java.util.Map;
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.RichTextElement;
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.course.nodes.MSCourseNode;
import org.olat.modules.ModuleConfiguration;
/**
* Provides a FlexiForm for the assesment settings dialog, including custom
* rules for when to show which dialog element.
*
* @author twuersch
*
*/
public class MSEditFormController extends FormBasicController {
/** Configuration this controller will modify. */
private ModuleConfiguration modConfig;
/** whether score will be awarded or not. */
private SelectionElement scoreGranted;
/** Dropdown for choosing whether pass/fail will be displayed or not. */
private SelectionElement displayPassed;
/**
* whether pass and fail will be decided automatically
* or manually.
*/
private SingleSelection displayType;
/** whether results will be commented individually. */
private SelectionElement commentFlag;
/** Text input element for the minimum score. */
private TextElement minVal;
/** Text input element for the maximum score. */
private TextElement maxVal;
/** Text input element for the passing score. */
private TextElement cutVal;
/** Rich text input element for a notice to all users. */
private RichTextElement infotextUser;
/** Rich text input element for a notice to all tutors. */
private RichTextElement infotextCoach;
/** The keys for true / false dropdowns. */
private String[] trueFalseKeys;
/** The keys for manual/automatic scoring dropdown. */
private String[] passedTypeValues;
private static final String scoreRex = "^[0-9]+(\\.[0-9]+)?$";
/**
* Creates this controller.
*
* @param ureq
* @param wControl
* @param modConfig
*/
public MSEditFormController(UserRequest ureq, WindowControl wControl, ModuleConfiguration modConfig) {
super(ureq, wControl, FormBasicController.LAYOUT_DEFAULT);
this.modConfig = modConfig;
this.trueFalseKeys = new String[] { Boolean.TRUE.toString(), Boolean.FALSE.toString() };
this.passedTypeValues = new String[] { translate("form.passedtype.cutval"), translate("form.passedtype.manual") };
initForm(ureq);
}
/**
*
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#doDispose
* ()
*/
@Override
protected void doDispose() {
// Don't dispose anything
}
/**
*
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formOK
* (org.olat.core.gui.UserRequest)
*/
@Override
protected void formOK(UserRequest ureq) {
fireEvent(ureq, Event.DONE_EVENT);
}
/**
*
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formNOK
* (org.olat.core.gui.UserRequest)
*/
@Override
protected void formNOK(UserRequest ureq) {
fireEvent(ureq, Event.FAILED_EVENT);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formCancelled(org.olat.core.gui.UserRequest)
*/
@Override
protected void formCancelled(UserRequest ureq) {
fireEvent(ureq, Event.CANCELLED_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)
*/
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
formLayout.setElementCssClass("o_sel_course_ms_form");
// Create the "score granted" field...
scoreGranted = uifactory.addCheckboxesHorizontal("form.score", formLayout, new String[]{"xx"}, new String[]{null});
scoreGranted.addActionListener(FormEvent.ONCLICK);
Boolean sf = (Boolean) modConfig.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD);
scoreGranted.select("xx", sf == null ? false : sf.booleanValue());
scoreGranted.setElementCssClass("o_sel_course_ms_score");
// ...minimum value...
Float min = (Float) modConfig.get(MSCourseNode.CONFIG_KEY_SCORE_MIN);
if (min == null) {
min = new Float(0.0);
}
minVal = uifactory.addTextElement("form.min", "form.min", 8, min.toString(), formLayout);
minVal.setDisplaySize(5);
minVal.setRegexMatchCheck(scoreRex, "form.error.wrongFloat");
minVal.setElementCssClass("o_sel_course_ms_min_val");
Float max = (Float) modConfig.get(MSCourseNode.CONFIG_KEY_SCORE_MAX);
if (max == null) {
max = new Float(0.0);
}
// ...and maximum value input.
maxVal = uifactory.addTextElement("form.max", "form.max", 8, max.toString(), formLayout);
maxVal.setDisplaySize(5);
maxVal.setRegexMatchCheck(scoreRex, "form.error.wrongFloat");
maxVal.setElementCssClass("o_sel_course_ms_max_val");
uifactory.addSpacerElement("spacer1", formLayout, false);
// Create the "display passed / failed"
displayPassed = uifactory.addCheckboxesHorizontal("form.passed", formLayout, new String[]{"xx"}, new String[]{null});
displayPassed.addActionListener(FormEvent.ONCLICK);
Boolean pf = (Boolean) modConfig.get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD);
if (pf == null) pf = Boolean.TRUE;
displayPassed.select("xx", pf);
// ...the automatic / manual dropdown (note that TRUE means automatic and
// FALSE means manually)...
Float cut = (Float) modConfig.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE);
displayType = uifactory.addRadiosVertical("form.passed.type", formLayout, trueFalseKeys, passedTypeValues);
displayType.addActionListener(FormEvent.ONCLICK);
displayType.setElementCssClass("o_sel_course_ms_display_type");
displayType.select(trueFalseKeys[1], true);
if (cut != null) {
displayType.select(trueFalseKeys[0], true);
}
// ...and the passing grade input field.
if (cut == null) cut = new Float(0.0);
cutVal = uifactory.addTextElement("form.cut", "form.cut", 8, cut.toString(), formLayout);
cutVal.setDisplaySize(5);
cutVal.setRegexMatchCheck(scoreRex, "form.error.wrongFloat");
cutVal.setElementCssClass("o_sel_course_ms_cut_val");
uifactory.addSpacerElement("spacer2", formLayout, false);
// Create the "individual comment" dropdown.
commentFlag = uifactory.addCheckboxesHorizontal("form.comment", formLayout, new String[]{"xx"}, new String[]{null});
Boolean cf = (Boolean) modConfig.get(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD);
if (cf == null) cf = Boolean.TRUE;
commentFlag.select("xx", cf.booleanValue());
uifactory.addSpacerElement("spacer3", formLayout, false);
// Create the rich text fields.
String infoUser = (String) modConfig.get(MSCourseNode.CONFIG_KEY_INFOTEXT_USER);
if (infoUser == null) infoUser = new String("");
infotextUser = uifactory.addRichTextElementForStringDataMinimalistic("infotextUser", "form.infotext.user", infoUser, 10, -1,
formLayout, getWindowControl());
String infoCoach = (String) modConfig.get(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH);
if (infoCoach == null) infoCoach = new String("");
infotextCoach = uifactory.addRichTextElementForStringDataMinimalistic("infotextCoach", "form.infotext.coach", infoCoach, 10, -1,
formLayout, getWindowControl());
// Create submit and cancel buttons
final FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttonLayout", getTranslator());
formLayout.add(buttonLayout);
uifactory.addFormSubmitButton("submit", buttonLayout);
uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl());
update(ureq);
}
@Override
protected void formInnerEvent (UserRequest ureq, FormItem source, FormEvent event) {
update(ureq);
}
private void update(UserRequest ureq) {
minVal.setVisible(scoreGranted.isSelected(0));
maxVal.setVisible(scoreGranted.isSelected(0));
minVal.setMandatory(minVal.isVisible());
maxVal.setMandatory(maxVal.isVisible());
displayType.setVisible(displayPassed.isSelected(0));
cutVal.setVisible(displayType.isVisible() && displayType.isSelected(0));
cutVal.setMandatory(cutVal.isVisible());
validateFormLogic(ureq);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#validateFormLogic(org.olat.core.gui.UserRequest)
*/
@Override
protected boolean validateFormLogic(UserRequest ureq) {
// coach info text
if (infotextCoach.getValue().length() > 4000) {
infotextCoach.setErrorKey("input.toolong", new String[] {"4000"});
return false;
} else {
infotextCoach.clearError();
}
// user info text
if (infotextUser.getValue().length() > 4000) {
infotextUser.setErrorKey("input.toolong", new String[] {"4000"});
return false;
} else {
infotextUser.clearError();
}
// score flag
if (scoreGranted.isSelected(0)) {
if (!minVal.getValue().matches(scoreRex)) {
minVal.setErrorKey("form.error.wrongFloat", null);
return false;
} else {
minVal.clearError();
}
if (!maxVal.getValue().matches(scoreRex)) {
maxVal.setErrorKey("form.error.wrongFloat", null);
return false;
} else if (Float.parseFloat(minVal.getValue()) > Float.parseFloat(maxVal.getValue())) {
maxVal.setErrorKey("form.error.minGreaterThanMax", null);
return false;
} else {
maxVal.clearError();
}
}
// display flag
displayType.clearError();
if (displayPassed.isSelected(0) && displayType.isSelected(0)) {
if (Boolean.valueOf(displayType.getSelectedKey()).booleanValue() && !scoreGranted.isSelected(0)) {
displayType.setErrorKey("form.error.cutButNoScore", null);
return false;
}
if (!cutVal.getValue().matches(scoreRex)) {
cutVal.setErrorKey("form.error.wrongFloat", null);
return false;
} else if (Float.parseFloat(cutVal.getValue()) < Float.parseFloat(minVal.getValue())
|| Float.parseFloat(cutVal.getValue()) > Float.parseFloat(maxVal.getValue())) {
cutVal.setErrorKey("form.error.cutOutOfRange", null);
return false;
} else {
cutVal.clearError();
}
}
return true;
}
/**
* Sets this form to be write-protected.
*
* @param displayOnly
*/
public void setDisplayOnly(boolean displayOnly) {
Map<String, FormItem> formItems = flc.getFormComponents();
for (String formItemName : formItems.keySet()) {
//formItems.get(formItemName).setVisible(true);
formItems.get(formItemName).setEnabled(!displayOnly);
}
}
/**
* @param moduleConfiguration
*/
public void updateModuleConfiguration(ModuleConfiguration moduleConfiguration) {
// mandatory score flag
Boolean sf = new Boolean(scoreGranted.isSelected(0));
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD, sf);
if (sf.booleanValue()) {
// do min/max value
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_SCORE_MIN, new Float(minVal.getValue()));
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_SCORE_MAX, new Float(maxVal.getValue()));
} else {
// remove old config
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_SCORE_MIN);
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_SCORE_MAX);
}
// mandatory passed flag
Boolean pf = new Boolean(displayPassed.isSelected(0));
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD, pf);
if (pf.booleanValue()) {
// do cut value
Boolean cf = new Boolean(displayType.getSelectedKey());
if (cf.booleanValue()) {
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE, new Float(cutVal.getValue()));
} else {
// remove old config
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE);
}
} else {
// remove old config
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE);
}
// mandatory comment flag
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD, new Boolean(commentFlag.isSelected(0)));
// set info text only if something is in there
String iu = infotextUser.getValue();
if (StringHelper.containsNonWhitespace(iu)) {
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_INFOTEXT_USER, iu);
} else {
// remove old config
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_INFOTEXT_USER);
}
String ic = infotextCoach.getValue();
if (StringHelper.containsNonWhitespace(ic)) {
moduleConfiguration.set(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH, ic);
} else {
// remove old config
moduleConfiguration.remove(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH);
}
}
/**
* @param config the module configuration
* @return true if valid, false otherwhise
*/
public static boolean isConfigValid(ModuleConfiguration config) {
boolean isValid = true;
Object confElement;
// score flag is mandatory
confElement = config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD);
if (confElement != null && confElement instanceof Boolean) {
Boolean hasScore = (Boolean) confElement;
if (hasScore.booleanValue()) {
// score min and max are mandatory if score flag is set to true
confElement = config.get(MSCourseNode.CONFIG_KEY_SCORE_MIN);
isValid = (confElement != null && confElement instanceof Float);
confElement = config.get(MSCourseNode.CONFIG_KEY_SCORE_MAX);
isValid = (confElement != null && confElement instanceof Float);
}
} else return false;
// passed flag is mandatory
confElement = config.get(MSCourseNode.CONFIG_KEY_HAS_PASSED_FIELD);
if (confElement != null && confElement instanceof Boolean) {
Boolean hasPassed = (Boolean) confElement;
if (hasPassed.booleanValue()) {
// cut value is optional if passed flag set to true, but type must match
confElement = config.get(MSCourseNode.CONFIG_KEY_PASSED_CUT_VALUE);
if (!((confElement == null) || (confElement instanceof Float))) return false;
}
} else return false;
// comment flag is mandatory
confElement = config.get(MSCourseNode.CONFIG_KEY_HAS_COMMENT_FIELD);
isValid = (confElement != null && confElement instanceof Boolean);
// infotext is optional
confElement = config.get(MSCourseNode.CONFIG_KEY_INFOTEXT_USER);
if (!((confElement == null) || (confElement instanceof String))) return false;
confElement = config.get(MSCourseNode.CONFIG_KEY_INFOTEXT_COACH);
if (!((confElement == null) || (confElement instanceof String))) return false;
return isValid;
}
}