/**
* 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) frentix GmbH<br>
* http://www.frentix.com<br>
* <p>
*/
package org.olat.course.nodes.st;
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.FormUIFactory;
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.SpacerElement;
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.rules.RulesFactory;
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.tree.CourseEditorTreeNode;
import org.olat.modules.ModuleConfiguration;
/**
* <h3>Description:</h3> The STCourseNodeDisplayConfigFormController displays
* the layout configuration form for an ST node. It lets the user decide if he
* wants to display a custom file layout, a system generated TOC layout or a
* system generated peekview layout. In both system generated layouts he can
* further define the number of columns (1 or 2).
* <p>
* When the peek view configuration is used, the children that should be
* displayed in the peekview must be selected manually. For performance reasons
* only 5 direct children can be selected by default. This behavior can be
* overridden by using the spring setter method setMaxPeekviewChildNodes() on
* the org.olat.course.nodes.st.STCourseNodeConfiguration bean
* <p>
* <h4>Events fired by this Controller</h4>
* <ul>
* <li>Event.DONE_EVENT when the form is submitted</li>
* </ul>
* <p>
* Initial Date: 15.09.2009 <br>
*
* @author gnaegi, gnaegi@frentix.com, www.frentix.com
*/
public class STCourseNodeDisplayConfigFormController extends FormBasicController {
private static final String[] keys_displayType = new String[] { "system", "peekview", "file" };
// read current configuration
private String displayConfig = null;
private int columnsConfig = 1;
private SingleSelection displayTypeRadios;
private MultipleSelectionElement displayTwoColumns;
private MultipleSelectionElement selectedPeekviewChildren;
private String[] selectedPeekviewChildKeys;
private String[] selectedPeekviewChildValues;
private String[] selectedPeekviewChildCssClasses;
private String selectedPeekviewChildNodesConfig = null;
/**
* Constructor for the config form
*
* @param ureq
* @param wControl
* @param config the module configuration
* @param node the course editor node
*/
STCourseNodeDisplayConfigFormController(UserRequest ureq, WindowControl wControl, ModuleConfiguration config, CourseEditorTreeNode node) {
super(ureq, wControl);
// Read current configuration
this.displayConfig = config.getStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE,
STCourseNodeEditController.CONFIG_VALUE_DISPLAY_TOC);
this.columnsConfig = config.getIntegerSafe(STCourseNodeEditController.CONFIG_KEY_COLUMNS, 1);
// Build data model for the selected child peekview checkboxes
int childCount = node.getChildCount();
selectedPeekviewChildKeys = new String[childCount];
selectedPeekviewChildValues = new String[childCount];
selectedPeekviewChildCssClasses = new String[childCount];
for (int i = 0; i < childCount; i++) {
CourseEditorTreeNode child = node.getCourseEditorTreeNodeChildAt(i);
selectedPeekviewChildKeys[i] = child.getIdent();
selectedPeekviewChildValues[i] = child.getTitle() + " (" + child.getIdent() + ")";
selectedPeekviewChildCssClasses[i] = child.getIconCssClass() + " b_with_small_icon_left";
}
selectedPeekviewChildNodesConfig = config.getStringValue(STCourseNodeEditController.CONFIG_KEY_PEEKVIEW_CHILD_NODES, "");
// initialize the form now
initForm(ureq);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#doDispose()
*/
@Override
protected void doDispose() {
// nothing to dispose
}
/**
* @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#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, @SuppressWarnings("unused") Controller listener,
@SuppressWarnings("unused") UserRequest ureq) {
setFormTitle("config.fieldset.view");
setFormContextHelp("org.olat.course.nodes.st", "ced-st-overview.html", "help.st.design");
//
FormUIFactory formFact = FormUIFactory.getInstance();
// Display type
String[] values_displayType = new String[] { translate("form.system"), translate("form.peekview"), translate("form.self") };
displayTypeRadios = formFact.addRadiosVertical("selforsystemoverview", formLayout, keys_displayType, values_displayType);
displayTypeRadios.addActionListener(this, FormEvent.ONCLICK);
if (displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE)) {
displayTypeRadios.select("file", true);
} else if (displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_PEEKVIEW)) {
displayTypeRadios.select("peekview", true);
} else {
displayTypeRadios.select("system", true);
}
// Peekview details configuration - allow only MAX_PEEKVIEW_CHILD_NODES
// peekviews to be selected
if (selectedPeekviewChildKeys.length > 0) {
SpacerElement spacerChild = formFact.addSpacerElement("spacerChild", formLayout, true);
selectedPeekviewChildren = formFact.addCheckboxesVertical("selectedPeekviewChildren", formLayout, selectedPeekviewChildKeys,
selectedPeekviewChildValues, selectedPeekviewChildCssClasses, 1);
selectedPeekviewChildren.setLabel("selectedPeekviewChildren",
new String[] { STCourseNodeConfiguration.MAX_PEEKVIEW_CHILD_NODES + "" });
// visibility rules for peekview children selection
RulesFactory.createHideRule(displayTypeRadios, "file", selectedPeekviewChildren, formLayout);
RulesFactory.createHideRule(displayTypeRadios, "system", selectedPeekviewChildren, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "peekview", selectedPeekviewChildren, formLayout);
RulesFactory.createHideRule(displayTypeRadios, "file", spacerChild, formLayout);
RulesFactory.createHideRule(displayTypeRadios, "system", spacerChild, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "peekview", spacerChild, formLayout);
// Pre-select configured keys. Discard configured selections that are not
// found (e.g. deleted or moved nodes)
String[] preSelectedChildNodes = (selectedPeekviewChildNodesConfig == null ? new String[0] : selectedPeekviewChildNodesConfig
.split(","));
for (String preSelectedNode : preSelectedChildNodes) {
for (String selectableNode : selectedPeekviewChildKeys) {
if (preSelectedNode.equals(selectableNode)) {
selectedPeekviewChildren.select(selectableNode, true);
break;
}
}
}
// Allow only MAX_PEEKVIEW_CHILD_NODES child nodes to be enabled
if (selectedPeekviewChildren.getSelectedKeys().size() >= STCourseNodeConfiguration.MAX_PEEKVIEW_CHILD_NODES) {
for (int i = 0; i < selectedPeekviewChildKeys.length; i++) {
if (!selectedPeekviewChildren.isSelected(i)) {
selectedPeekviewChildren.setEnabled(selectedPeekviewChildKeys[i], false);
}
}
}
// Pre-select the first MAX_PEEKVIEW_CHILD_NODES child nodes if none is
// selected to reflect meaningfull default configuration
if (selectedPeekviewChildren.getSelectedKeys().size() == 0) {
for (int i = 0; i < selectedPeekviewChildKeys.length; i++) {
if (i < STCourseNodeConfiguration.MAX_PEEKVIEW_CHILD_NODES) {
selectedPeekviewChildren.select(selectedPeekviewChildKeys[i], true);
} else {
selectedPeekviewChildren.setEnabled(selectedPeekviewChildKeys[i], false);
}
}
}
// Add as listener for any changes
selectedPeekviewChildren.addActionListener(this, FormEvent.ONCLICK);
}
//
// Number of rows (only available in system or peekview type)
SpacerElement spacerCols = formFact.addSpacerElement("spacerCols", formLayout, true);
displayTwoColumns = formFact
.addCheckboxesVertical("displayTwoColumns", formLayout, new String[] { "on" }, new String[] { "" }, null, 1);
displayTwoColumns.setLabel("displayTwoColumns", null);
if (columnsConfig == 2) {
displayTwoColumns.selectAll();
}
if (displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE)) {
displayTwoColumns.setVisible(false);
}
//
// Visibility rules for display columns switch
RulesFactory.createHideRule(displayTypeRadios, "file", displayTwoColumns, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "peekview", displayTwoColumns, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "system", displayTwoColumns, formLayout);
RulesFactory.createHideRule(displayTypeRadios, "file", spacerCols, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "peekview", spacerCols, formLayout);
RulesFactory.createShowRule(displayTypeRadios, "system", spacerCols, formLayout);
//
formFact.addFormSubmitButton("submit", formLayout);
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#formInnerEvent(org.olat.core.gui.UserRequest,
* org.olat.core.gui.components.form.flexible.FormItem,
* org.olat.core.gui.components.form.flexible.impl.FormEvent)
*/
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
super.formInnerEvent(ureq, source, event);
if (source == selectedPeekviewChildren) {
if (selectedPeekviewChildren.getSelectedKeys().size() == 0) {
// There must be at least one selected child
selectedPeekviewChildren.setErrorKey("form.peekview.error.mandatory.child", null);
return;
}
// Clean potential previous error and continue with rules to
// enable/disable the checkboxes to ensure that users can't select more
// than the allowed number of child nodes
selectedPeekviewChildren.clearError();
if (selectedPeekviewChildren.getSelectedKeys().size() >= STCourseNodeConfiguration.MAX_PEEKVIEW_CHILD_NODES) {
// Max reached, disabled all not already enabled checkboxes.
for (int i = 0; i < selectedPeekviewChildKeys.length; i++) {
if (!selectedPeekviewChildren.isSelected(i)) {
selectedPeekviewChildren.setEnabled(selectedPeekviewChildKeys[i], false);
}
}
showInfo("form.peekview.max.reached", STCourseNodeConfiguration.MAX_PEEKVIEW_CHILD_NODES + "");
} else {
// Enable all checkboxes
selectedPeekviewChildren.setEnabled(true);
}
}
}
/**
* @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#validateFormLogic(org.olat.core.gui.UserRequest)
*/
@Override
protected boolean validateFormLogic(UserRequest ureq) {
if ( ! super.validateFormLogic(ureq)) {
return false;
}
// Check if at least one child is selected when peekview is enabled, but only when children are available.
String displayType = displayTypeRadios.getSelectedKey();
if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_PEEKVIEW.equals(displayType) && selectedPeekviewChildKeys.length > 0) {
if (selectedPeekviewChildren.getSelectedKeys().size() == 0) {
// There must be at least one selected child
selectedPeekviewChildren.setErrorKey("form.peekview.error.mandatory.child", null);
return false;
}
}
return true;
}
/**
* Update the given module config object from the data in the form
*
* @param moduleConfig
*/
public void updateModuleConfiguration(ModuleConfiguration moduleConfig) {
String displayType = displayTypeRadios.getSelectedKey();
if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE.equals(displayType)) {
// manual file view selected, remove columns config
moduleConfig.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE);
moduleConfig.setBooleanEntry(STCourseNodeEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS, false);
// remove config values from other setup
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_COLUMNS);
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_PEEKVIEW_CHILD_NODES);
} else {
// auto generated view selected, set TOC or peekview
if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_PEEKVIEW.equals(displayType)) {
moduleConfig.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE,
STCourseNodeEditController.CONFIG_VALUE_DISPLAY_PEEKVIEW);
// update selected peekview children
if (selectedPeekviewChildren == null || selectedPeekviewChildren.getSelectedKeys().size() == 0) {
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_PEEKVIEW_CHILD_NODES);
} else {
selectedPeekviewChildNodesConfig = "";
for (String childKey : selectedPeekviewChildren.getSelectedKeys()) {
if (selectedPeekviewChildNodesConfig.length() != 0) {
// separate node id's with commas
selectedPeekviewChildNodesConfig += ",";
}
selectedPeekviewChildNodesConfig += childKey;
}
moduleConfig.set(STCourseNodeEditController.CONFIG_KEY_PEEKVIEW_CHILD_NODES, selectedPeekviewChildNodesConfig);
}
} else {
// the old auto generated TOC view without peekview
moduleConfig
.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_TOC);
// remove config values from peekview setup
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_PEEKVIEW_CHILD_NODES);
}
// in both cases, also set the columns configuration
int cols = (displayTwoColumns.isSelected(0) ? 2 : 1);
moduleConfig.setIntValue(STCourseNodeEditController.CONFIG_KEY_COLUMNS, Integer.valueOf(cols));
// and clean up any file references
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_FILE);
moduleConfig.remove(STCourseNodeEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS);
}
}
}