/** * 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. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.course.nodes.st; import java.util.ArrayList; import java.util.Iterator; import java.util.List; 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.IntegerElement; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; 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.course.editor.StatusDescription; import org.olat.course.nodes.CourseNode; import org.olat.course.run.scoring.FailedEvaluationType; import org.olat.course.run.scoring.ScoreCalculator; /** * Description:<br> * This form is used to generate score and passed expression for structure * course nodes in an easy way. See EditScoreCalculationExpertForm for the * expert way. * <p> * * @author gnaegi */ public class EditScoreCalculationEasyForm extends FormBasicController { private MultipleSelectionElement hasScore, hasPassed; private SingleSelection passedType, failedType; private MultipleSelectionElement scoreNodeIdents, passedNodeIdents; private IntegerElement passedCutValue; private ScoreCalculator sc; private static final String DELETED_NODE_IDENTIFYER = "deletedNode"; private List<CourseNode> assessableNodesList; private List<CourseNode> nodeIdentList; /** * @param name * @param trans * @param scoreCalculator * @param nodeIdentList */ public EditScoreCalculationEasyForm(UserRequest ureq, WindowControl wControl, ScoreCalculator scoreCalculator, List<CourseNode> nodeIdentList) { super(ureq, wControl); sc = scoreCalculator; this.assessableNodesList = nodeIdentList; this.nodeIdentList = nodeIdentList; initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { hasScore = uifactory.addCheckboxesHorizontal("scform.hasScore", formLayout, new String[]{"xx"}, new String[]{null}); hasScore.select("xx", sc != null && sc.getSumOfScoreNodes() != null && sc.getSumOfScoreNodes().size() > 0); hasScore.addActionListener(FormEvent.ONCLICK); hasScore.setElementCssClass("o_sel_has_score"); List<String> sumOfScoreNodes = (sc == null ? null : sc.getSumOfScoreNodes()); scoreNodeIdents = initNodeSelectionElement(formLayout, "scform.scoreNodeIndents", sc, sumOfScoreNodes, nodeIdentList); scoreNodeIdents.setVisible(hasScore.isSelected(0)); uifactory.addSpacerElement("spacer", formLayout, false); hasPassed = uifactory.addCheckboxesHorizontal("scform.passedtype", formLayout, new String[]{"xx"}, new String[]{null}); hasPassed.select("xx", sc != null && sc.getPassedType() != null && !sc.getPassedType().equals(ScoreCalculator.PASSED_TYPE_NONE)); hasPassed.addActionListener(FormEvent.ONCLICK); // Radios/Checkboxes need onclick because of IE bug OLAT-5753 hasPassed.setElementCssClass("o_sel_has_passed"); String[] passedTypeKeys = new String[] { ScoreCalculator.PASSED_TYPE_CUTVALUE, ScoreCalculator.PASSED_TYPE_INHERIT }; String[] passedTypeValues = new String[] { translate("scform.passedtype.cutvalue"), translate("scform.passedtype.inherit") }; passedType = uifactory.addRadiosVertical("passedType", null, formLayout, passedTypeKeys, passedTypeValues); passedType.setVisible(hasPassed.isSelected(0)); if (sc != null && sc.getPassedType() != null && !sc.getPassedType().equals(ScoreCalculator.PASSED_TYPE_NONE)) { passedType.select(sc.getPassedType(), true); } else { passedType.select(ScoreCalculator.PASSED_TYPE_CUTVALUE, true); } passedType.addActionListener(FormEvent.ONCLICK); // Radios/Checkboxes need onclick because of IE bug OLAT-5753 int cutinitval = 0; if (sc != null) cutinitval = sc.getPassedCutValue(); passedCutValue = uifactory.addIntegerElement("scform.passedCutValue", cutinitval, formLayout); passedCutValue.setDisplaySize(4); passedCutValue.setVisible(passedType.isVisible() && passedType.isSelected(0)); passedCutValue.setMandatory(true); passedNodeIdents = initNodeSelectionElement( formLayout, "scform.passedNodeIndents", sc, (sc == null ? null : sc.getPassedNodes()), nodeIdentList ); passedNodeIdents.setVisible(passedType.isVisible() && passedType.isSelected(1)); String[] failedTypeKeys = new String[]{ FailedEvaluationType.failedAsNotPassed.name(), FailedEvaluationType.failedAsNotPassedAfterEndDate.name(), FailedEvaluationType.manual.name() }; String[] failedTypeValues = new String[]{ translate(FailedEvaluationType.failedAsNotPassed.name()), translate(FailedEvaluationType.failedAsNotPassedAfterEndDate.name()), translate(FailedEvaluationType.manual.name()) }; failedType = uifactory.addDropdownSingleselect("scform.failedtype", formLayout, failedTypeKeys, failedTypeValues, null); failedType.addActionListener(FormEvent.ONCLICK); FailedEvaluationType failedTypeValue = sc.getFailedType() == null ? FailedEvaluationType.failedAsNotPassed : sc.getFailedType(); boolean failedSelected = false; for(String failedTypeKey:failedTypeKeys) { if(failedTypeKey.equals(failedTypeValue.name())) { failedType.select(failedTypeKey, true); failedSelected = true; } } if(!failedSelected) { failedType.select(failedTypeKeys[0], true); } FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator()); formLayout.add(buttonGroupLayout); uifactory.addFormSubmitButton("submit", buttonGroupLayout); uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl()); } /** * Initializes the node selection form elements first check if the form has a * selection on a node that has been deleted in since the last edition of this * form. if so, set remember this to later add a dummy placeholder for the * deleted node. We do not just ignore this since the form would look ok then * to the user, the generated rule visible in the expert mode however would * still be invalid. user must explicitly uncheck this deleted node reference. * * @param elemId name of the generated form element * @param scoreCalculator * @param selectedNodeList List of course nodes that are preselected * @param allNodesList List of all assessable course nodes * @return StaticMultipleSelectionElement The configured form element */ private MultipleSelectionElement initNodeSelectionElement(FormItemContainer formLayout, String elemId, ScoreCalculator scoreCalculator, List<String> selectedNodeList, List<CourseNode> allNodesList) { boolean addDeletedNodeIdent = false; if (scoreCalculator != null && selectedNodeList != null) { for (Iterator<String> iter = selectedNodeList.iterator(); iter.hasNext();) { String nodeIdent = iter.next(); boolean found = false; for (Iterator<CourseNode> nodeIter = allNodesList.iterator(); nodeIter.hasNext();) { CourseNode node = nodeIter.next(); if (node.getIdent().equals(nodeIdent)) { found = true; } } if (!found) addDeletedNodeIdent = true; } } String[] nodeKeys = new String[allNodesList.size() + (addDeletedNodeIdent ? 1 : 0)]; String[] nodeValues = new String[allNodesList.size() + (addDeletedNodeIdent ? 1 : 0)]; for (int i = 0; i < allNodesList.size(); i++) { CourseNode courseNode = allNodesList.get(i); nodeKeys[i] = courseNode.getIdent(); nodeValues[i] = courseNode.getShortName() + " (Id:" + courseNode.getIdent() + ")"; } // add a deleted dummy node at last position if (addDeletedNodeIdent) { nodeKeys[allNodesList.size()] = DELETED_NODE_IDENTIFYER; nodeValues[allNodesList.size()] = translate("scform.deletedNode"); } MultipleSelectionElement mse = uifactory.addCheckboxesVertical(elemId, formLayout, nodeKeys, nodeValues, 2); // preselect nodes from configuration if (scoreCalculator != null && selectedNodeList != null) { for (Iterator<String> iter = selectedNodeList.iterator(); iter.hasNext();) { String nodeIdent = iter.next(); boolean found = false; for (Iterator<CourseNode> nodeIter = allNodesList.iterator(); nodeIter.hasNext();) { CourseNode node = nodeIter.next(); if (node.getIdent().equals(nodeIdent)) { found = true; } } if (found) { mse.select(nodeIdent, true); } else { mse.select(DELETED_NODE_IDENTIFYER, true); } } } return mse; } @Override protected void doDispose() { // } /** * @see org.olat.core.gui.components.Form#validate(org.olat.core.gui.UserRequest) */ @Override public boolean validateFormLogic(UserRequest ureq) { boolean rv = true; if (hasScore.isSelected(0)) { if (scoreNodeIdents.getSelectedKeys().size() == 0) { scoreNodeIdents.setErrorKey("scform.scoreNodeIndents.error", null); rv = false; } else if (scoreNodeIdents.getSelectedKeys().contains(DELETED_NODE_IDENTIFYER)) { scoreNodeIdents.setErrorKey("scform.deletedNode.error", null); rv = false; } else { scoreNodeIdents.clearError(); } } if (hasPassed.isSelected(0)) { if (passedType.getSelectedKey().equals(ScoreCalculator.PASSED_TYPE_INHERIT)) { if (passedNodeIdents.getSelectedKeys().size() == 0) { passedNodeIdents.setErrorKey("scform.passedNodeIndents.error", null); rv = false; } else { passedNodeIdents.clearError(); } } else if (passedType.getSelectedKey().equals(ScoreCalculator.PASSED_TYPE_CUTVALUE)) { if (!hasScore.isSelected(0)) { passedType.setErrorKey("scform.passedType.error", null); rv = false; } else { passedType.clearError(); } } } return rv; } private void updateUI() { scoreNodeIdents.setVisible(hasScore.isSelected(0)); if (!scoreNodeIdents.isVisible()) { scoreNodeIdents.clearError(); } passedType.setVisible(hasPassed.isSelected(0)); failedType.setVisible(hasPassed.isSelected(0)); passedCutValue.setVisible(passedType.isVisible() && passedType.isSelected(0)); if (!passedCutValue.isVisible()) { passedCutValue.setIntValue(0); passedCutValue.clearError(); } passedNodeIdents.setVisible(passedType.isVisible() && passedType.isSelected(1)); if (!passedNodeIdents.isVisible()) { passedNodeIdents.clearError(); } } /** * @return ScoreCalcualtor or null if no score calculator is set */ public ScoreCalculator getScoreCalulator() { if (!hasScore.isSelected(0) && !hasPassed.isSelected(0)) { return null; } // 1) score configuration if (hasScore.isSelected(0)) { sc.setSumOfScoreNodes(new ArrayList<String>(scoreNodeIdents.getSelectedKeys())); }else { //reset sc.setSumOfScoreNodes(null); } // 2) passed configuration if (!hasPassed.isSelected(0)) { sc.setPassedType(ScoreCalculator.PASSED_TYPE_NONE); } else if (passedType.getSelectedKey().equals(ScoreCalculator.PASSED_TYPE_CUTVALUE)) { sc.setPassedType(ScoreCalculator.PASSED_TYPE_CUTVALUE); sc.setPassedCutValue(passedCutValue.getIntValue()); } else if (passedType.getSelectedKey().equals(ScoreCalculator.PASSED_TYPE_INHERIT)) { sc.setPassedType(ScoreCalculator.PASSED_TYPE_INHERIT); sc.setPassedNodes(new ArrayList<>(passedNodeIdents.getSelectedKeys())); } // update score and passed expression from easy mode configuration sc.setScoreExpression(sc.getScoreExpressionFromEasyModeConfiguration()); sc.setPassedExpression(sc.getPassedExpressionFromEasyModeConfiguration()); sc.setFailedType(FailedEvaluationType.valueOf(failedType.getSelectedKey())); if (sc.getScoreExpression() == null && sc.getPassedExpression() == null) { return null; } sc.setExpertMode(false); return sc; } public boolean hasScore (){ return hasScore.isSelected(0); } /** * * @return Returns a list with the invalid node descriptions, * ("invalid" is a node that is not associated with a test resource) */ public List<String> getInvalidNodeDescriptions() { List<String> testElemWithNoResource = new ArrayList<String>(); List<String> selectedNodesIds = new ArrayList<String>(scoreNodeIdents.getSelectedKeys()); for (Iterator<CourseNode> nodeIter = assessableNodesList.iterator(); nodeIter.hasNext();) { CourseNode node = nodeIter.next(); if (selectedNodesIds.contains(node.getIdent())) { StatusDescription isConfigValid = node.isConfigValid(); if (isConfigValid != null && isConfigValid.isError()) { String nodeDescription = node.getShortName() + " (Id:" + node.getIdent() + ")"; if (!testElemWithNoResource.contains(nodeDescription)) { testElemWithNoResource.add(nodeDescription); } } } } return testElemWithNoResource; } @Override protected void formOK(UserRequest ureq) { fireEvent (ureq, Event.DONE_EVENT); } @Override protected void formCancelled(UserRequest ureq) { fireEvent (ureq, Event.CANCELLED_EVENT); } @Override protected void formInnerEvent (UserRequest ureq, FormItem source, FormEvent event) { if (source == passedType) { passedType.clearError(); } updateUI(); } }