/**
* <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.modules.forms.ui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
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.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.link.Link;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.StringHelper;
import org.olat.modules.forms.model.xml.Rubric;
import org.olat.modules.forms.model.xml.Rubric.SliderType;
import org.olat.modules.forms.model.xml.Slider;
import org.olat.modules.forms.model.xml.StepLabel;
import org.olat.modules.portfolio.ui.editor.PageElementEditorController;
import org.olat.modules.portfolio.ui.editor.event.ChangePartEvent;
import org.olat.modules.portfolio.ui.editor.event.ClosePartEvent;
/**
*
* Initial date: 7 déc. 2016<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class RubricEditorController extends FormBasicController implements PageElementEditorController {
private static AtomicInteger count = new AtomicInteger();
private final Rubric rubric;
private boolean editMode = false;
private final boolean restrictedEdit;
private RubricController rubricCtrl;
private final String[] sliderTypeKeys = new String[] { SliderType.discrete.name(), SliderType.discrete_slider.name(), SliderType.continuous.name() };
private final String[] sliderStepKeys = new String[] { "2", "3", "4", "5", "6", "7", "8", "9", "10" };
private List<StepLabelColumn> stepLabels;
private List<SliderRow> sliders = new ArrayList<>();
private Map<Integer,StepLabelColumn> stepToColumns = new HashMap<>();
private FormLink saveButton;
private SingleSelection typeEl;
private SingleSelection stepsEl;
private FormLink addSliderButton;
private FormLayoutContainer settingsLayout;
public RubricEditorController(UserRequest ureq, WindowControl wControl, Rubric rubric, boolean restrictedEdit) {
super(ureq, wControl, "rubric_editor");
this.rubric = rubric;
this.restrictedEdit = restrictedEdit;
initForm(ureq);
setEditMode(editMode);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
rubricCtrl = new RubricController(ureq, getWindowControl(), rubric, mainForm);
listenTo(rubricCtrl);
formLayout.add("rubric", rubricCtrl.getInitialFormItem());
settingsLayout = FormLayoutContainer.createDefaultFormLayout("settings", getTranslator());
settingsLayout.setRootForm(mainForm);
formLayout.add("settings", settingsLayout);
String[] sliderTypeValues = new String[] { translate("slider.discrete"), translate("slider.discrete.slider"), translate("slider.continuous") };
typeEl = uifactory.addDropdownSingleselect("slider.type." + count.incrementAndGet(), "slider.type", settingsLayout, sliderTypeKeys, sliderTypeValues, null);
typeEl.addActionListener(FormEvent.ONCHANGE);
typeEl.setEnabled(!restrictedEdit);
boolean typeSelected = false;
if(rubric != null && rubric.getSliderType() != null) {
for(String sliderTypeKey:sliderTypeKeys) {
if(sliderTypeKey.equals(rubric.getSliderType().name())) {
typeEl.select(sliderTypeKey, true);
typeSelected = true;
}
}
}
if(!typeSelected) {
typeEl.select(sliderTypeKeys[0], true);
}
stepsEl = uifactory.addDropdownSingleselect("slider.steps." + count.incrementAndGet(), "slider.steps", settingsLayout, sliderStepKeys, sliderStepKeys, null);
stepsEl.addActionListener(FormEvent.ONCHANGE);
stepsEl.setEnabled(!restrictedEdit);
boolean stepSelected = false;
if(rubric != null && rubric.getSteps() > 0) {
String steps = Integer.toString(rubric.getSteps());
for(String sliderStepKey:sliderStepKeys) {
if(sliderStepKey.equals(steps)) {
stepsEl.select(sliderStepKey, true);
stepSelected = true;
}
}
}
if(!stepSelected) {
stepsEl.select(sliderStepKeys[4], true);
}
updateTypeSettings();
updateSteps();
for(Slider slider:rubric.getSliders()) {
SliderRow row = forgeSliderRow(slider);
sliders.add(row);
}
if(formLayout instanceof FormLayoutContainer) {
FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
layoutCont.contextPut("sliders", sliders);
}
long postfix = CodeHelper.getRAMUniqueID();
saveButton = uifactory.addFormLink("save_" + postfix, "save", null, formLayout, Link.BUTTON);
if(!restrictedEdit) {
addSliderButton = uifactory.addFormLink("add.slider." + postfix, "add.slider", null, formLayout, Link.BUTTON);
addSliderButton.setIconLeftCSS("o_icon o_icon-lg o_icon_add");
}
if(formLayout instanceof FormLayoutContainer) {
FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
layoutCont.contextPut("postfix", Long.toString(postfix));
}
}
private void updateSteps() {
List<StepLabelColumn> stepLabelColumns = new ArrayList<>();
if(stepsEl.isVisible() && stepsEl.isOneSelected()
&& (typeEl.isSelected(0) || typeEl.isSelected(1))) {
int steps = Integer.parseInt(stepsEl.getSelectedKey());
for(int i=0; i<steps; i++) {
Integer step = new Integer(i);
StepLabelColumn col = stepToColumns.get(step);
if(col == null) {
String label = "";
if(rubric.getStepLabels() != null && i<rubric.getStepLabels().size()) {
label = rubric.getStepLabels().get(i).getLabel();
}
TextElement textEl = uifactory.addTextElement("steplabel_" + count.incrementAndGet(), "steplabel_" + count.incrementAndGet(), null, 256, label, flc);
textEl.setDomReplacementWrapperRequired(false);
textEl.setDisplaySize(4);
col = new StepLabelColumn(i, textEl);
}
stepLabelColumns.add(col);
}
int stepInPercent = Math.round(90.0f / steps);//90 is empirically choose to not make a second line
flc.contextPut("stepInPercent", stepInPercent);
}
stepLabels = stepLabelColumns;
flc.contextPut("stepLabels", stepLabelColumns);
}
private void updateTypeSettings() {
if(!typeEl.isOneSelected()) return;
SliderType selectedType = SliderType.valueOf(typeEl.getSelectedKey());
if(selectedType == SliderType.discrete || selectedType == SliderType.discrete_slider) {
stepsEl.setVisible(true);
} else if(selectedType == SliderType.continuous) {
stepsEl.setVisible(false);
}
}
private SliderRow forgeSliderRow(Slider slider) {
String startLabel = slider.getStartLabel();
TextElement startLabelEl = uifactory.addTextElement("start.label." + count.incrementAndGet(), "start.label", 256, startLabel, flc);
startLabelEl.setDomReplacementWrapperRequired(false);
String endLabel = slider.getEndLabel();
TextElement endLabelEl = uifactory.addTextElement("end.label." + count.incrementAndGet(), "end.label", 256, endLabel, flc);
endLabelEl.setDomReplacementWrapperRequired(false);
SliderRow row = new SliderRow(slider, startLabelEl, endLabelEl);
if(!restrictedEdit) {
FormLink deleteButton = uifactory.addFormLink("del." + count.incrementAndGet(), "delete_slider", "", null, flc, Link.BUTTON | Link.NONTRANSLATED);
deleteButton.setDomReplacementWrapperRequired(false);
deleteButton.setIconLeftCSS("o_icon o_icon-lg o_icon_delete_item");
deleteButton.setUserObject(row);
row.setDeleteButton(deleteButton);
flc.contextPut("deleteButtons", Boolean.TRUE);
}
return row;
}
@Override
protected void doDispose() {
//
}
@Override
public boolean isEditMode() {
return editMode;
}
@Override
public void setEditMode(boolean editMode) {
this.editMode = editMode;
flc.getFormItemComponent().contextPut("editMode", new Boolean(editMode));
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(addSliderButton == source) {
doAddSlider();
} else if(typeEl == source) {
updateTypeSettings();
updateSteps();
} else if(stepsEl == source) {
updateSteps();
} else if(saveButton == source) {
if(validateFormLogic(ureq)) {
formOK(ureq);
}
} else if(source instanceof FormLink) {
FormLink button = (FormLink)source;
if("delete_slider".equals(button.getCmd())) {
doRemoveSlider((SliderRow)button.getUserObject());
}
}
super.formInnerEvent(ureq, source, event);
}
private void doRemoveSlider(SliderRow row) {
updateSteps();
sliders.remove(row);
rubric.getSliders().remove(row.getSlider());
flc.setDirty(true);
}
private void doAddSlider() {
Slider slider = new Slider();
slider.setId(UUID.randomUUID().toString());
rubric.getSliders().add(slider);
SliderRow row = forgeSliderRow(slider);
sliders.add(row);
flc.setDirty(true);
}
@Override
protected boolean validateFormLogic(UserRequest ureq) {
boolean allOk = true;
typeEl.clearError();
if(!typeEl.isOneSelected()) {
typeEl.setErrorKey("form.legende.mandatory", null);
allOk &= false;
}
return allOk & super.validateFormLogic(ureq);
}
@Override
protected void formOK(UserRequest ureq) {
commitFields();
commitStepLabels();
String selectedType = typeEl.getSelectedKey();
SliderType type = SliderType.valueOf(selectedType);
rubric.setSliderType(type);
if(type == SliderType.continuous) {
rubric.setStart(1);
rubric.setEnd(100);
rubric.setSteps(100);
} else {
int steps = Integer.parseInt(stepsEl.getSelectedKey());
rubric.setStart(1);
rubric.setEnd(steps);
rubric.setSteps(steps);
}
for(Iterator<Slider> sliderIt=rubric.getSliders().iterator(); sliderIt.hasNext(); ) {
Slider slider = sliderIt.next();
if(!StringHelper.containsNonWhitespace(slider.getStartLabel()) && !StringHelper.containsNonWhitespace(slider.getEndLabel())) {
sliderIt.remove();
}
}
rubricCtrl.updateForm();
fireEvent(ureq, new ChangePartEvent(rubric));
fireEvent(ureq, new ClosePartEvent(rubric));
}
private void commitStepLabels() {
if(!typeEl.isOneSelected()) return;
SliderType selectedType = SliderType.valueOf(typeEl.getSelectedKey());
if(selectedType == SliderType.discrete || selectedType == SliderType.discrete_slider) {
if(rubric.getStepLabels() == null) {
rubric.setStepLabels(new ArrayList<>());
}
int steps = Integer.parseInt(stepsEl.getSelectedKey());
for(int i=0; i<stepLabels.size() && i<steps; i++) {
StepLabelColumn stepLabel = stepLabels.get(i);
if(i < rubric.getStepLabels().size()) {
rubric.getStepLabels().get(i).setLabel(stepLabel.getStepLabelEl().getValue());
} else {
StepLabel label = new StepLabel();
label.setId(UUID.randomUUID().toString());
label.setLabel(stepLabel.getStepLabelEl().getValue());
rubric.getStepLabels().add(label);
}
}
if(rubric.getStepLabels().size() > steps) {
List<StepLabel> labels = new ArrayList<>(rubric.getStepLabels().subList(0, steps));
rubric.setStepLabels(labels);
}
} else {
rubric.getStepLabels().clear();
}
}
private void commitFields() {
for(SliderRow row:sliders) {
String start = row.getStartLabelEl().getValue();
String end = row.getEndLabelEl().getValue();
if(StringHelper.containsNonWhitespace(start)) {
row.getSlider().setStartLabel(start);
} else {
row.getSlider().setStartLabel(null);
}
if(StringHelper.containsNonWhitespace(end)) {
row.getSlider().setEndLabel(end);
} else {
row.getSlider().setEndLabel(null);
}
}
}
public class StepLabelColumn {
private final int step;
private final TextElement stepLabelEl;
public StepLabelColumn(int step, TextElement stepLabelEl) {
this.step = step;
this.stepLabelEl = stepLabelEl;
}
public int getStep() {
return step;
}
public TextElement getStepLabelEl() {
return stepLabelEl;
}
}
public class SliderRow {
private final TextElement startLabelEl;
private final TextElement endLabelEl;
private FormLink deleteButton;
private final Slider slider;
public SliderRow(Slider slider, TextElement startLabelEl, TextElement endLabelEl) {
this.slider = slider;
this.startLabelEl = startLabelEl;
this.endLabelEl = endLabelEl;
}
public Slider getSlider() {
return slider;
}
public TextElement getStartLabelEl() {
return startLabelEl;
}
public TextElement getEndLabelEl() {
return endLabelEl;
}
public FormLink getDeleteButton() {
return deleteButton;
}
public void setDeleteButton(FormLink deleteButton) {
this.deleteButton = deleteButton;
}
}
}