/** * <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.cl.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; 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.FormLink; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; 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.components.form.flexible.impl.elements.FormSubmit; import org.olat.core.gui.components.link.Link; 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.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.util.StringHelper; import org.olat.course.assessment.AssessmentHelper; import org.olat.course.nodes.CheckListCourseNode; import org.olat.course.nodes.MSCourseNode; import org.olat.course.nodes.cl.CheckboxManager; import org.olat.course.nodes.cl.model.AssessmentBatch; import org.olat.course.nodes.cl.model.Checkbox; import org.olat.course.nodes.cl.model.CheckboxList; import org.olat.course.nodes.cl.model.DBCheck; import org.olat.course.nodes.cl.model.DBCheckbox; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.modules.ModuleConfiguration; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 10.02.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class AssessedIdentityCheckListController extends FormBasicController { private static final String[] onKeys = new String[]{ "on" }; private final boolean cancel; private final boolean withScore; private final boolean saveAndClose; private final ModuleConfiguration config; private final CheckListCourseNode courseNode; private final UserCourseEnvironment coachCourseEnv; private final UserCourseEnvironment assessedUserCourseEnv; private final OLATResourceable courseOres; private final Identity assessedIdentity; private final CheckboxList checkboxList; private List<CheckboxWrapper> wrappers; private FormLink saveAndCloseLink; @Autowired private CheckboxManager checkboxManager; public AssessedIdentityCheckListController(UserRequest ureq, WindowControl wControl, Identity assessedIdentity, OLATResourceable courseOres, UserCourseEnvironment coachCourseEnv, UserCourseEnvironment assessedUserCourseEnv, CheckListCourseNode courseNode, boolean saveAndClose, boolean cancel) { super(ureq, wControl); this.cancel = cancel; this.courseNode = courseNode; this.courseOres = courseOres; this.saveAndClose = saveAndClose; this.coachCourseEnv = coachCourseEnv; this.assessedUserCourseEnv = assessedUserCourseEnv; config = courseNode.getModuleConfiguration(); Boolean hasScore = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD); withScore = (hasScore == null || hasScore.booleanValue()); this.assessedIdentity = assessedIdentity; CheckboxList configCheckboxList = (CheckboxList)config.get(CheckListCourseNode.CONFIG_KEY_CHECKBOX); if(configCheckboxList == null) { checkboxList = new CheckboxList(); checkboxList.setList(Collections.<Checkbox>emptyList()); } else { checkboxList = configCheckboxList; } initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; List<DBCheck> checks = checkboxManager.loadCheck(assessedIdentity, courseOres, courseNode.getIdent()); Map<String, DBCheck> uuidToCheckMap = new HashMap<>(); for(DBCheck check:checks) { uuidToCheckMap.put(check.getCheckbox().getCheckboxId(), check); } List<Checkbox> list = checkboxList.getList(); wrappers = new ArrayList<>(list.size()); for(Checkbox checkbox:list) { DBCheck check = uuidToCheckMap.get(checkbox.getCheckboxId()); boolean readOnly = false; CheckboxWrapper wrapper = forgeCheckboxWrapper(checkbox, check, readOnly, formLayout); wrappers.add(wrapper); } layoutCont.contextPut("checkboxList", wrappers); } FormLayoutContainer buttonCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); formLayout.add(buttonCont); FormSubmit saveButton = uifactory.addFormSubmitButton("save", "save", buttonCont); saveButton.setEnabled(checkboxList.getNumOfCheckbox() > 0); saveButton.setVisible(!coachCourseEnv.isCourseReadOnly()); saveAndCloseLink = uifactory.addFormLink("save.close", buttonCont, Link.BUTTON); saveAndCloseLink.setEnabled(checkboxList.getNumOfCheckbox() > 0); saveAndCloseLink.setVisible(saveAndClose && !coachCourseEnv.isCourseReadOnly()); if(cancel) { uifactory.addFormCancelButton("cancel", buttonCont, ureq, getWindowControl()); } } private CheckboxWrapper forgeCheckboxWrapper(Checkbox checkbox, DBCheck check, boolean readOnly, FormItemContainer formLayout) { String[] values = new String[]{ translate(checkbox.getLabel().i18nKey()) }; String boxId = "box_" + checkbox.getCheckboxId(); MultipleSelectionElement boxEl = uifactory .addCheckboxesHorizontal(boxId, null, formLayout, onKeys, values); boxEl.setEnabled(!readOnly && !coachCourseEnv.isCourseReadOnly()); boxEl.setLabel(checkbox.getTitle(), null, false); boxEl.showLabel(true); boxEl.addActionListener(FormEvent.ONCHANGE); TextElement pointEl = null; if(withScore && checkbox.getPoints() != null) { String pointId = "point_" + checkbox.getCheckboxId(); String points; if(check != null && check.getChecked() != null && check.getChecked().booleanValue()) { points = AssessmentHelper.getRoundedScore(check.getScore()); } else { points = null; } pointEl = uifactory.addTextElement(pointId, null, 16, points, formLayout); pointEl.setDisplaySize(5); pointEl.setEnabled(!coachCourseEnv.isCourseReadOnly()); Float maxScore = checkbox.getPoints(); if(maxScore != null) { String maxValue = AssessmentHelper.getRoundedScore(maxScore); pointEl.setExampleKey("checklist.point.example", new String[]{ "0", maxValue}); } // hide when not yet checked if(check != null) { pointEl.setVisible(check.getChecked()); } else { pointEl.setVisible(false); } } CheckboxWrapper wrapper = new CheckboxWrapper(checkbox, check, boxEl, pointEl); boxEl.setUserObject(wrapper); if(check != null && check.getChecked() != null && check.getChecked().booleanValue()) { boxEl.select(onKeys[0], true); wrapper.setDbCheckbox(check.getCheckbox()); } return wrapper; } @Override protected void doDispose() { // } @Override protected void formOK(UserRequest ureq) { doSave(); fireEvent(ureq, Event.CHANGED_EVENT); } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source instanceof MultipleSelectionElement) { MultipleSelectionElement boxEl = (MultipleSelectionElement)source; CheckboxWrapper wrapper = (CheckboxWrapper)boxEl.getUserObject(); doUpdateCheck(wrapper, boxEl.isAtLeastSelected(1)); } else if(saveAndCloseLink == source) { if(validateFormLogic(ureq)) { doSave(); fireEvent(ureq, Event.DONE_EVENT); } } super.formInnerEvent(ureq, source, event); } @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; for(CheckboxWrapper wrapper:wrappers) { TextElement pointEl = wrapper.getPointEl(); if(pointEl != null) { pointEl.clearError(); String val = pointEl.getValue(); if(StringHelper.containsNonWhitespace(val)) { try { Float max = wrapper.getCheckbox().getPoints(); float maxScore = max == null ? 0f : max.floatValue(); float score = Float.parseFloat(val); if(score < 0f || score > maxScore) { pointEl.setErrorKey("form.error.scoreOutOfRange", null); allOk &= false; } } catch (NumberFormatException e) { pointEl.setErrorKey("form.error.wrongFloat", null); allOk &= false; } } } } return allOk & super.validateFormLogic(ureq); } private void doSave() { List<AssessmentBatch> batchElements = new ArrayList<>(); for(CheckboxWrapper wrapper:wrappers) { Float editedPoint = null; if(wrapper.getPointEl() != null) { String val = wrapper.getPointEl().getValue(); if(StringHelper.containsNonWhitespace(val)) { try { editedPoint = new Float(val); } catch (NumberFormatException e) { editedPoint = null; } } } boolean editedValue = wrapper.getCheckboxEl().isAtLeastSelected(1); Float currentPoint = null; boolean currentValue = false; if(wrapper.getCheck() != null) { currentPoint = wrapper.getCheck().getScore(); Boolean checkObj = wrapper.getCheck().getChecked(); if(checkObj != null && checkObj.booleanValue()) { currentValue = checkObj.booleanValue(); } } if((editedValue != currentValue) || ((currentPoint == null && editedPoint != null) || (currentPoint != null && editedPoint == null) || (currentPoint != null && !currentPoint.equals(editedPoint)))) { String boxId = wrapper.getCheckbox().getCheckboxId(); batchElements.add(new AssessmentBatch(assessedIdentity.getKey(), boxId, editedPoint, editedValue)); } } checkboxManager.check(courseOres, courseNode.getIdent(), batchElements); courseNode.updateScoreEvaluation(getIdentity(), assessedUserCourseEnv, assessedIdentity); } private void doUpdateCheck(CheckboxWrapper wrapper, boolean check) { if(wrapper.getPointEl() == null) return;//nothing to do if(check) { if(!StringHelper.containsNonWhitespace(wrapper.getPointEl().getValue())) { Checkbox checkbox = wrapper.getCheckbox(); Float points = checkbox.getPoints(); if(points != null) { String val = AssessmentHelper.getRoundedScore(points); wrapper.getPointEl().setValue(val); } } } else if(wrapper.getPointEl() != null) { wrapper.getPointEl().setValue(""); } // hide when not yet checked wrapper.getPointEl().setVisible(check); } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } public static class CheckboxWrapper { private final TextElement pointEl; private final MultipleSelectionElement checkboxEl; private final Checkbox checkbox; private DBCheck check; private DBCheckbox dbCheckbox; public CheckboxWrapper(Checkbox checkbox, DBCheck check, MultipleSelectionElement checkboxEl, TextElement pointEl) { this.checkboxEl = checkboxEl; this.check = check; this.pointEl = pointEl; this.checkbox = checkbox; } public Checkbox getCheckbox() { return checkbox; } public DBCheck getCheck() { return check; } public void setCheck(DBCheck check) { this.check = check; } /** * This value is lazy loaded and can be null! * @return */ public DBCheckbox getDbCheckbox() { return dbCheckbox; } public void setDbCheckbox(DBCheckbox dbCheckbox) { this.dbCheckbox = dbCheckbox; } public String getTitle() { return checkbox.getTitle(); } public boolean isPointsAvailable() { return checkbox.getPoints() != null; } public TextElement getPointEl() { return pointEl; } public MultipleSelectionElement getCheckboxEl() { return checkboxEl; } public String getCheckboxElName() { return checkboxEl.getName();//getComponent().getComponentName(); } } }