/**
* <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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.olat.basesecurity.BaseSecurity;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
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.FlexiTableElement;
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.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.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer;
import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer;
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.id.Roles;
import org.olat.core.id.UserConstants;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
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.ui.CheckboxAssessmentDataModel.Cols;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.modules.ModuleConfiguration;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
/**
*
* Initial date: 14.02.2014<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class CheckboxAssessmentController extends FormBasicController {
private static final String[] onKeys = new String[]{ "on" };
private static final String[] onValues = new String[]{ "" };
private FlexiTableElement table;
private SingleSelection checkboxEl;
private FormLink selectAllBoxButton;
private CheckboxAssessmentDataModel model;
private List<CheckboxAssessmentRow> boxRows;
private List<CheckListAssessmentRow> initialRows;
private final CheckboxList checkboxList;
private final boolean isAdministrativeUser;
private final List<UserPropertyHandler> userPropertyHandlers;
private final UserManager userManager;
private final BaseSecurity securityManager;
private final CheckboxManager checkboxManager;
private final boolean withScore;
private int currentCheckboxIndex = 0;
private final OLATResourceable courseOres;
private final CheckListCourseNode courseNode;
public CheckboxAssessmentController(UserRequest ureq, WindowControl wControl, CheckboxList checkboxList,
List<CheckListAssessmentRow> initialRows, OLATResourceable courseOres, CheckListCourseNode courseNode) {
super(ureq, wControl, "assessment_per_box");
this.courseNode = courseNode;
this.courseOres = courseOres;
this.initialRows = initialRows;
this.checkboxList = checkboxList;
ModuleConfiguration config = courseNode.getModuleConfiguration();
Boolean hasScore = (Boolean)config.get(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD);
withScore = (hasScore == null || hasScore.booleanValue());
userManager = CoreSpringFactory.getImpl(UserManager.class);
securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);
checkboxManager = CoreSpringFactory.getImpl(CheckboxManager.class);
BaseSecurityModule securityModule = CoreSpringFactory.getImpl(BaseSecurityModule.class);
Roles roles = ureq.getUserSession().getRoles();
isAdministrativeUser = securityModule.isUserAllowedAdminProps(roles);
userPropertyHandlers = userManager.getUserPropertyHandlersFor(CheckListAssessmentController.USER_PROPS_ID, isAdministrativeUser);
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
setFormDescription("assessment.checkbox.description");
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
if(isAdministrativeUser) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.username.i18nKey(), Cols.username.ordinal(),
true, Cols.username.name()));
}
int i=0;
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
int colIndex = CheckListAssessmentDataModel.USER_PROPS_OFFSET + i++;
if (userPropertyHandler == null) continue;
String propName = userPropertyHandler.getName();
boolean visible = userManager.isMandatoryUserProperty(CheckListAssessmentController.USER_PROPS_ID , userPropertyHandler);
if(visible) {
FlexiColumnModel col;
if(UserConstants.FIRSTNAME.equals(propName)
|| UserConstants.LASTNAME.equals(propName)) {
col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(),
colIndex, userPropertyHandler.getName(), true, propName,
new StaticFlexiCellRenderer(userPropertyHandler.getName(), new TextFlexiCellRenderer()));
} else {
col = new DefaultFlexiColumnModel(true, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex, true, propName);
}
columnsModel.addFlexiColumnModel(col);
}
}
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.check.i18nKey(), Cols.check.ordinal(),
true, Cols.check.name()));
if(withScore) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.points.i18nKey(), Cols.points.ordinal(),
true, Cols.points.name()));
}
int numOfCheckbox = checkboxList.getList().size();
String[] keys = new String[numOfCheckbox];
String[] values = new String[numOfCheckbox];
List<Checkbox> checkbox = checkboxList.getList();
for(int j=0; j<numOfCheckbox; j++) {
keys[j] = checkbox.get(j).getCheckboxId();
values[j] = checkbox.get(j).getTitle();
}
FormLayoutContainer selectCont = FormLayoutContainer.createDefaultFormLayout("checkbox_sel_cont", getTranslator());
formLayout.add(selectCont);
checkboxEl = uifactory.addDropdownSingleselect("checkbox", "select.checkbox", selectCont, keys, values, null);
checkboxEl.addActionListener(FormEvent.ONCHANGE);
checkboxEl.select(keys[0], true);
Checkbox box = checkboxList.getList().get(currentCheckboxIndex);
boolean hasPoints = box.getPoints() != null && box.getPoints().floatValue() > 0f;
boxRows = new ArrayList<CheckboxAssessmentRow>(initialRows.size());
for(CheckListAssessmentRow initialRow: initialRows) {
Boolean[] checked = new Boolean[numOfCheckbox];
if(initialRow.getChecked() != null) {
System.arraycopy(initialRow.getChecked(), 0, checked, 0, initialRow.getChecked().length);
}
Float[] scores = new Float[numOfCheckbox];
if(initialRow.getScores() != null) {
System.arraycopy(initialRow.getScores(), 0, scores, 0, initialRow.getScores().length);
}
CheckboxAssessmentRow row = new CheckboxAssessmentRow(initialRow, checked, scores);
String name = "box_" + boxRows.size() + "_";
String pointVal = "";
if(scores != null && scores.length > currentCheckboxIndex
&& scores[currentCheckboxIndex] != null) {
pointVal = AssessmentHelper.getRoundedScore(scores[currentCheckboxIndex]);
}
TextElement pointEl = uifactory.addTextElement(name + "point", null, 5, pointVal, formLayout);
pointEl.setDisplaySize(5);
MultipleSelectionElement checkEl = uifactory.addCheckboxesHorizontal(name + "check", null, formLayout, onKeys, onValues);
checkEl.setDomReplacementWrapperRequired(false);
checkEl.addActionListener(FormEvent.ONCHANGE);
checkEl.setUserObject(row);
if(checked != null && checked.length > currentCheckboxIndex
&& checked[currentCheckboxIndex] != null && checked[currentCheckboxIndex].booleanValue()) {
checkEl.select(onKeys[0], true);
}
pointEl.setVisible(hasPoints);
row.setCheckedEl(checkEl);
row.setPointEl(pointEl);
boxRows.add(row);
}
model = new CheckboxAssessmentDataModel(boxRows, columnsModel, getLocale());
table = uifactory.addTableElement(getWindowControl(), "checkbox-list", model, getTranslator(), formLayout);
table.setCustomizeColumns(true);
table.setEditMode(true);
table.setAndLoadPersistedPreferences(ureq, "checkbox-assessment");
FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
formLayout.add(buttonsCont);
uifactory.addFormSubmitButton("save", buttonsCont);
selectAllBoxButton = uifactory.addFormLink("selectall", buttonsCont, Link.BUTTON);
uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl());
}
@Override
protected void doDispose() {
//
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(checkboxEl == source) {
int nextCheckboxIndex = checkboxEl.getSelected();
saveCurrentSelectCheckbox();
Checkbox box = checkboxList.getList().get(nextCheckboxIndex);
boolean hasPoints = box.getPoints() != null && box.getPoints().floatValue() > 0f;
List<CheckboxAssessmentRow> rows = model.getObjects();
for(CheckboxAssessmentRow row:rows) {
Boolean[] checkedArr = row.getChecked();
if(checkedArr[nextCheckboxIndex] != null && checkedArr[nextCheckboxIndex].booleanValue()) {
row.getCheckedEl().select(onKeys[0], true);
} else {
row.getCheckedEl().select(onKeys[0], false);
}
Float[] scores = row.getScores();
if(scores[nextCheckboxIndex] != null && scores[nextCheckboxIndex] != null) {
row.getPointEl().setValue(AssessmentHelper.getRoundedScore(scores[nextCheckboxIndex]));
} else {
row.getPointEl().setValue("");
}
row.getPointEl().setVisible(hasPoints);
}
currentCheckboxIndex = nextCheckboxIndex;
} else if(source instanceof MultipleSelectionElement) {
MultipleSelectionElement checkEl = (MultipleSelectionElement)source;
if(checkEl.getUserObject() instanceof CheckboxAssessmentRow) {
CheckboxAssessmentRow row = (CheckboxAssessmentRow)checkEl.getUserObject();
if(row.getPointEl().isVisible()) {
boolean checked = checkEl.isAtLeastSelected(1);
if(checked) {
int nextCheckboxIndex = checkboxEl.getSelected();
Checkbox box = checkboxList.getList().get(nextCheckboxIndex);
String pointVal = AssessmentHelper.getRoundedScore(box.getPoints());
row.getPointEl().setValue(pointVal);
} else {
row.getPointEl().setValue("");
}
}
}
} else if(selectAllBoxButton == source) {
doSelectAll();
}
super.formInnerEvent(ureq, source, event);
}
private void saveCurrentSelectCheckbox() {
Float defaultScore = checkboxList.getList().get(currentCheckboxIndex).getPoints();
List<CheckboxAssessmentRow> rows = model.getObjects();
for(CheckboxAssessmentRow row:rows) {
boolean checked = row.getCheckedEl().isAtLeastSelected(1);
Boolean[] checkedArr = row.getChecked();
if(checked) {
checkedArr[currentCheckboxIndex] = Boolean.TRUE;
} else {
checkedArr[currentCheckboxIndex] = Boolean.FALSE;
}
Float points;
if(checked) {
points = getPointVal(row);
if(points == null) {
points = defaultScore;
}
} else {
points = new Float(0f);
}
row.getScores()[currentCheckboxIndex] = points;
}
}
private Float getPointVal(CheckboxAssessmentRow row) {
String pointVal = row.getPointEl().getValue();
try {
return new Float(pointVal);
} catch (NumberFormatException e) {
return null;
}
}
private void doSelectAll() {
List<CheckboxAssessmentRow> rows = model.getObjects();
for(CheckboxAssessmentRow row:rows) {
boolean checked = row.getCheckedEl().isAtLeastSelected(1);
Boolean[] checkedArr = row.getChecked();
if(!checked) {
checkedArr[currentCheckboxIndex] = Boolean.TRUE;
row.getCheckedEl().select(onKeys[0], true);
}
}
}
@Override
protected void formOK(UserRequest ureq) {
saveCurrentSelectCheckbox();
int numOfCheckbox = checkboxList.getNumOfCheckbox();
Map<Long, CheckListAssessmentRow> identityToInitialRow = new HashMap<>();
for(CheckListAssessmentRow initialRow:initialRows) {
identityToInitialRow.put(initialRow.getIdentityKey(), initialRow);
}
Set<Long> assessedIdentityToUpdate = new HashSet<>();
List<CheckboxAssessmentRow> rows = model.getObjects();
List<AssessmentBatch> batchElements = new ArrayList<>();
for(CheckboxAssessmentRow row:rows) {
CheckListAssessmentRow initialRow = identityToInitialRow.get(row.getIdentityKey());
Boolean[] checked = initialRow.getChecked();
Boolean[] editedChecked = row.getChecked();
Float[] scores = initialRow.getScores();
Float[] editedScores = row.getScores();
for(int i=0; i<numOfCheckbox; i++) {
Checkbox box = checkboxList.getList().get(i);
boolean currentValue = getSecureValue(checked, i);
boolean editedValue = getSecureValue(editedChecked, i);
Float currentPoint = getSecureValue(scores, i);;
Float editedPoint;
if(box.getPoints() != null && box.getPoints().floatValue() > 0f) {
editedPoint = getSecureValue(editedScores, i);
} else {
editedPoint = null;
}
if((editedValue != currentValue)
|| ((currentPoint == null && editedPoint != null)
|| (currentPoint != null && editedPoint == null)
|| (currentPoint != null && !currentPoint.equals(editedPoint)))) {
Checkbox checkbox = checkboxList.getList().get(i);
String checkboxId = checkbox.getCheckboxId();
batchElements.add(new AssessmentBatch(row.getIdentityKey(), checkboxId, editedPoint, editedValue));
assessedIdentityToUpdate.add(row.getIdentityKey());
}
}
}
checkboxManager.check(courseOres, courseNode.getIdent(), batchElements);
if(assessedIdentityToUpdate.size() > 0) {
DBFactory.getInstance().commit();
ICourse course = CourseFactory.loadCourse(courseOres);
List<Identity> assessedIdentities = securityManager.loadIdentityByKeys(assessedIdentityToUpdate);
for(Identity assessedIdentity:assessedIdentities) {
UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course);
courseNode.updateScoreEvaluation(getIdentity(), assessedUserCourseEnv, assessedIdentity);
}
}
fireEvent(ureq, Event.DONE_EVENT);
}
private boolean getSecureValue(Boolean[] checked, int i) {
boolean value;
if(checked != null && checked.length > 0 && i<checked.length && checked[i] != null) {
value = checked[i].booleanValue();
} else {
value = false;
}
return value;
}
private Float getSecureValue(Float[] scores, int i) {
Float value;
if(scores != null && scores.length > 0 && i<scores.length && scores[i] != null) {
value = scores[i];
} else {
value = null;
}
return value;
}
@Override
protected void formCancelled(UserRequest ureq) {
fireEvent(ureq, Event.CANCELLED_EVENT);
}
}