/** * <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.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 10 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", "delegate" }; // 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] = "o_icon " + child.getIconCssClass(); } 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) { // No explicit submit button. Form is submitted every time when a radio or // checkbox is clicked (OLAT-5610) } /** * @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, Controller listener, UserRequest ureq) { setFormTitle("config.fieldset.view"); setFormContextHelp("Knowledge Transfer#_struktur"); FormUIFactory formFact = FormUIFactory.getInstance(); // Display type String[] values_displayType = new String[] { translate("form.system"), translate("form.peekview"), translate("form.self"), translate("form.delegate") }; displayTypeRadios = formFact.addRadiosVertical("selforsystemoverview", formLayout, keys_displayType, values_displayType); displayTypeRadios.addActionListener(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 if (displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_DELEGATE)) { displayTypeRadios.select("delegate", 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, "delegate", selectedPeekviewChildren, formLayout); RulesFactory.createHideRule(displayTypeRadios, "file", spacerChild, formLayout); RulesFactory.createHideRule(displayTypeRadios, "system", spacerChild, formLayout); RulesFactory.createShowRule(displayTypeRadios, "peekview", spacerChild, formLayout); RulesFactory.createHideRule(displayTypeRadios, "delegate", spacerChild, formLayout); // Pre-select the first MAX_PEEKVIEW_CHILD_NODES child nodes if none is // selected to reflect meaningfull default configuration preselectConfiguredOrMaxChildNodes(); // Add as listener for any changes selectedPeekviewChildren.addActionListener(FormEvent.ONCLICK); } // // Number of rows (only available in system or peekview type) SpacerElement spacerCols = formFact.addSpacerElement("spacerCols", formLayout, true); displayTwoColumns = formFact .addCheckboxesHorizontal("displayTwoColumns", formLayout, new String[] { "on" }, new String[] { "" }); displayTwoColumns.setLabel("displayTwoColumns", null); displayTwoColumns.addActionListener(FormEvent.ONCLICK); if (columnsConfig == 2) { displayTwoColumns.selectAll(); } if (displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE) || displayConfig.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_DELEGATE)) { 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, "delegate", displayTwoColumns, formLayout); RulesFactory.createHideRule(displayTypeRadios, "file", spacerCols, formLayout); RulesFactory.createShowRule(displayTypeRadios, "peekview", spacerCols, formLayout); RulesFactory.createShowRule(displayTypeRadios, "system", spacerCols, formLayout); RulesFactory.createHideRule(displayTypeRadios, "delegate", spacerCols, 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; // abort } // 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); } } else { // Fix problem of not-preselected items (OLAT-5610). The initial status // from the initForm method gets lost by the re-evaluation of the // selection element. Seems to be a flexi form bug, could not find other // solution as this workaround. preselectConfiguredOrMaxChildNodes(); } // Submit form on each click on any radio or checkbox (OLAT-5610) fireEvent(ureq, Event.DONE_EVENT); } /** * Helper to select the configured child nodes or the maximum of child nodes * when no child is selected at all */ private void preselectConfiguredOrMaxChildNodes() { // Pre-select configured keys. Discard configured selections that are not // found (e.g. deleted or moved nodes) // // SelectedPeekviewChildren can be NULL in case this structure element has // no child elements at all, e.g. during development of the course. if (selectedPeekviewChildren != null) { 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); } } } // remove errors from previous invalid form selection selectedPeekviewChildren.clearError(); } } /** * 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); // Let other config values from old config setup remain in config, maybe // used when user switches back to other config (OLAT-5610) } 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 if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_DELEGATE.equals(displayType)) { moduleConfig.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_DELEGATE); } else { // the old auto generated TOC view without peekview moduleConfig .setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_TOC); // Let other config values from old config setup remain in config, maybe // used when user switches back to other config (OLAT-5610) } // in both cases, also set the columns configuration int cols = (displayTwoColumns.isSelected(0) ? 2 : 1); moduleConfig.setIntValue(STCourseNodeEditController.CONFIG_KEY_COLUMNS, Integer.valueOf(cols)); // Let other config values from old config setup remain in config, maybe // used when user switches back to other config (OLAT-5610) } } }