/**
* 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) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.course.nodes.st;
import java.util.Iterator;
import java.util.List;
import org.olat.core.commons.controllers.filechooser.LinkFileCombiCalloutController;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.tabbedpane.TabbedPane;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.ControllerEventListener;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.iframe.DeliveryOptions;
import org.olat.core.gui.control.generic.iframe.DeliveryOptionsConfigurationController;
import org.olat.core.gui.control.generic.tabbable.ActivateableTabbableDefaultController;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.Util;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSManager;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.condition.Condition;
import org.olat.course.condition.ConditionEditController;
import org.olat.course.editor.CourseEditorHelper;
import org.olat.course.editor.NodeEditController;
import org.olat.course.highscore.ui.HighScoreEditController;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.STCourseNode;
import org.olat.course.nodes.sp.SecuritySettingsForm;
import org.olat.course.run.scoring.ScoreCalculator;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.course.tree.CourseEditorTreeModel;
import org.olat.course.tree.CourseInternalLinkTreeModel;
import org.olat.modules.ModuleConfiguration;
/**
* Description:<BR/> Edit controller for a course node of type structure <P/>
*
* Initial Date: Oct 12, 2004
* @author gnaegi
*/
public class STCourseNodeEditController extends ActivateableTabbableDefaultController implements ControllerEventListener {
public static final String ACCESSABILITY_CONDITION_FORM = "accessabilityConditionForm";
private static final String PANE_TAB_ST_SCORECALCULATION = "pane.tab.st_scorecalculation";
private static final String PANE_TAB_DELIVERYOPTIONS = "pane.tab.deliveryOptions";
public static final String PANE_TAB_ST_CONFIG = "pane.tab.st_config";
private static final String PANE_TAB_ACCESSIBILITY = "pane.tab.accessibility";
private static final String PANE_TAB_HIGHSCORE = "pane.tab.highscore";
/** configuration key for the filename */
public static final String CONFIG_KEY_FILE = "file";
/**
* configuration key: should relative links like ../otherfolder/my.css be
* allowed? *
*/
public static final String CONFIG_KEY_ALLOW_RELATIVE_LINKS = "allowRelativeLinks";
public static final String CONFIG_KEY_DELIVERYOPTIONS = "deliveryOptions";
/** configuration key: should the students be allowed to edit the page? */
public static final String CONFIG_KEY_ALLOW_COACH_EDIT = "allowCoachEdit";
// key to store information on what to display in the run
public static final String CONFIG_KEY_DISPLAY_TYPE = "display";
// display a custom file
public static final String CONFIG_VALUE_DISPLAY_FILE = "file";
// display a simple table on content
public static final String CONFIG_VALUE_DISPLAY_TOC = "toc";
// display a detailed peek view
public static final String CONFIG_VALUE_DISPLAY_PEEKVIEW = "peekview";
//do not display peek view, delegate to first child CourseNode
public static final String CONFIG_VALUE_DISPLAY_DELEGATE = "delegate";
// key to display the enabled child node peek views
public static final String CONFIG_KEY_PEEKVIEW_CHILD_NODES = "peekviewChildNodes";
// key to store the number of columns
public static final String CONFIG_KEY_COLUMNS = "columns";
private static final String[] paneKeys = { PANE_TAB_ST_SCORECALCULATION, PANE_TAB_ST_CONFIG, PANE_TAB_ACCESSIBILITY };
private STCourseNode stNode;
private EditScoreCalculationExpertForm scoreExpertForm;
private EditScoreCalculationEasyForm scoreEasyForm;
private List<CourseNode> assessableChildren;
private STCourseNodeDisplayConfigFormController nodeDisplayConfigFormController;
private HighScoreEditController highScoreNodeConfigController;
private VelocityContainer score, configvc;
private Link activateEasyModeButton;
private Link activateExpertModeButton;
private VFSContainer courseFolderContainer;
private String chosenFile;
private boolean allowRelativeLinks;
private DeliveryOptions deliveryOptions;
private LinkFileCombiCalloutController combiLinkCtr;
private SecuritySettingsForm securitySettingForm;
private DeliveryOptionsConfigurationController deliveryOptionsCtrl;
private ConditionEditController accessibilityCondContr;
private boolean editorEnabled = false;
private UserCourseEnvironment euce;
private TabbedPane myTabbedPane;
private CourseEditorTreeModel editorModel;
/**
* @param ureq
* @param wControl
* @param stNode
* @param courseFolderPath
* @param groupMgr
* @param editorModel
*/
public STCourseNodeEditController(UserRequest ureq, WindowControl wControl, STCourseNode stNode, VFSContainer courseFolderContainer,
CourseEditorTreeModel editorModel, UserCourseEnvironment euce) {
super(ureq, wControl);
this.stNode = stNode;
this.courseFolderContainer = courseFolderContainer;
this.euce = euce;
this.editorModel = editorModel;
Translator fallback = Util.createPackageTranslator(Condition.class, getLocale());
Translator newTranslator = Util.createPackageTranslator(STCourseNodeEditController.class, getLocale(), fallback);
setTranslator(newTranslator);
score = createVelocityContainer("scoreedit");
activateEasyModeButton = LinkFactory.createButtonSmall("cmd.activate.easyMode", score, this);
activateExpertModeButton = LinkFactory.createButtonSmall("cmd.activate.expertMode", score, this);
configvc = createVelocityContainer("config");
// type of display configuration: manual, auto, peekview etc
nodeDisplayConfigFormController = new STCourseNodeDisplayConfigFormController(ureq, wControl, stNode.getModuleConfiguration(), editorModel.getCourseEditorNodeById(stNode.getIdent()));
listenTo(nodeDisplayConfigFormController);
configvc.put("nodeDisplayConfigFormController", nodeDisplayConfigFormController.getInitialComponent());
// Load configured value for file if available and enable editor when in
// file display move, even when no file is selected (this will display the
// file selector button)
chosenFile = (String) stNode.getModuleConfiguration().get(CONFIG_KEY_FILE);
editorEnabled = (CONFIG_VALUE_DISPLAY_FILE.equals(stNode.getModuleConfiguration().getStringValue(CONFIG_KEY_DISPLAY_TYPE)));
allowRelativeLinks = stNode.getModuleConfiguration().getBooleanSafe(CONFIG_KEY_ALLOW_RELATIVE_LINKS);
deliveryOptions = (DeliveryOptions)stNode.getModuleConfiguration().get(CONFIG_KEY_DELIVERYOPTIONS);
if (editorEnabled) {
addCustomFileConfigToView(ureq);
}
deliveryOptionsCtrl = new DeliveryOptionsConfigurationController(ureq, getWindowControl(), deliveryOptions, "Knowledge Transfer#_splayout");
listenTo(deliveryOptionsCtrl);
// Find assessable children nodes
assessableChildren = AssessmentHelper.getAssessableNodes(editorModel, stNode);
// Accessibility precondition
Condition accessCondition = stNode.getPreConditionAccess();
accessibilityCondContr = new ConditionEditController(ureq, getWindowControl(), accessCondition,
assessableChildren, euce, true);
listenTo(accessibilityCondContr);
// HighScore Controller
highScoreNodeConfigController = new HighScoreEditController(ureq, wControl, stNode.getModuleConfiguration());
listenTo(highScoreNodeConfigController);
ScoreCalculator scoreCalc = stNode.getScoreCalculator();
if (scoreCalc != null) {
if (scoreCalc.isExpertMode() && scoreCalc.getPassedExpression() == null && scoreCalc.getScoreExpression() == null) {
scoreCalc = null;
} else if (!scoreCalc.isExpertMode() && scoreCalc.getPassedExpressionFromEasyModeConfiguration() == null
&& scoreCalc.getScoreExpressionFromEasyModeConfiguration() == null) {
scoreCalc = null;
}
}
if (assessableChildren.size() == 0 && scoreCalc == null) {
// show only the no assessable children message, if no previous score
// config exists.
score.contextPut("noAssessableChildren", Boolean.TRUE);
} else {
score.contextPut("noAssessableChildren", Boolean.FALSE);
}
// Init score calculator form
if (scoreCalc != null && scoreCalc.isExpertMode()) {
initScoreExpertForm(ureq);
} else {
initScoreEasyForm(ureq);
}
}
/**
* Initialize an easy mode score calculator form and push it to the score
* velocity container
*/
private void initScoreEasyForm(UserRequest ureq) {
removeAsListenerAndDispose(scoreEasyForm);
scoreEasyForm = new EditScoreCalculationEasyForm(ureq, getWindowControl(), stNode.getScoreCalculator(), assessableChildren);
listenTo(scoreEasyForm);
score.put("scoreForm", scoreEasyForm.getInitialComponent());
score.contextPut("isExpertMode", Boolean.FALSE);
}
/**
* Initialize an expert mode score calculator form and push it to the score
* velocity container
*/
private void initScoreExpertForm(UserRequest ureq) {
removeAsListenerAndDispose(scoreExpertForm);
scoreExpertForm = new EditScoreCalculationExpertForm(ureq, getWindowControl(), stNode.getScoreCalculator(), euce, assessableChildren);
listenTo(scoreExpertForm);
scoreExpertForm.setScoreCalculator(stNode.getScoreCalculator());
score.put("scoreForm", scoreExpertForm.getInitialComponent());
score.contextPut("isExpertMode", Boolean.TRUE);
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Component source, Event event) {
if (source == activateEasyModeButton) {
initScoreEasyForm(ureq);
} else if (source == activateExpertModeButton){
initScoreExpertForm(ureq);
}
}
/**
*
* @param nodeDescriptions
* @return the warning message if any, null otherwise
*/
private String getWarningMessage(List<String> nodeDescriptions) {
if(nodeDescriptions.size()>0) {
String invalidNodeTitles = "";
Iterator<String> titleIterator = nodeDescriptions.iterator();
while(titleIterator.hasNext()) {
if(!invalidNodeTitles.equals("")) {
invalidNodeTitles += "; ";
}
invalidNodeTitles += titleIterator.next();
}
return translate("scform.error.configuration") + ": " + invalidNodeTitles;
}
return null;
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Controller source, Event event) {
if (source instanceof NodeEditController) {
if(combiLinkCtr != null && combiLinkCtr.isDoProposal()){
combiLinkCtr.setRelFilePath(CourseEditorHelper.createUniqueRelFilePathFromShortTitle(stNode, courseFolderContainer));
}
} else if (source == accessibilityCondContr) {
if (event == Event.CHANGED_EVENT) {
Condition cond = accessibilityCondContr.getCondition();
stNode.setPreConditionAccess(cond);
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
} else if (source == deliveryOptionsCtrl) {
deliveryOptions = deliveryOptionsCtrl.getDeliveryOptions();
stNode.getModuleConfiguration().set(CONFIG_KEY_DELIVERYOPTIONS, deliveryOptions);
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
} else if(source == combiLinkCtr){
if(event == Event.DONE_EVENT){
chosenFile = VFSManager.getRelativeItemPath(combiLinkCtr.getFile(), courseFolderContainer, null);
stNode.getModuleConfiguration().set(CONFIG_KEY_FILE, chosenFile);
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
if(!myTabbedPane.containsTab(deliveryOptionsCtrl.getInitialComponent())) {
myTabbedPane.addTab(translate(PANE_TAB_DELIVERYOPTIONS), deliveryOptionsCtrl.getInitialComponent());
}
configvc.contextPut("editorEnabled", combiLinkCtr.isEditorEnabled());
}
} else if(source == securitySettingForm){
if(event == Event.DONE_EVENT){
boolean allowRelativeLinks = securitySettingForm.getAllowRelativeLinksConfig();
stNode.getModuleConfiguration().set(CONFIG_KEY_ALLOW_RELATIVE_LINKS, allowRelativeLinks);
stNode.getModuleConfiguration().set(CONFIG_KEY_ALLOW_COACH_EDIT, securitySettingForm.getAllowCoachEditConfig());
combiLinkCtr.setAllowEditorRelativeLinks(allowRelativeLinks);
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
} else if (source == nodeDisplayConfigFormController) {
if (event == Event.DONE_EVENT) {
// update the module configuration
ModuleConfiguration moduleConfig = stNode.getModuleConfiguration();
nodeDisplayConfigFormController.updateModuleConfiguration(moduleConfig);
allowRelativeLinks = moduleConfig.getBooleanSafe(CONFIG_KEY_ALLOW_RELATIVE_LINKS);
// update some class vars
if (CONFIG_VALUE_DISPLAY_FILE.equals(moduleConfig.getStringValue(CONFIG_KEY_DISPLAY_TYPE))) {
editorEnabled = true;
addCustomFileConfigToView(ureq);
} else { // user generated overview
editorEnabled = false;
removeCustomFileConfigFromView();
}
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
} else if (source == scoreEasyForm) {
if (event == Event.DONE_EVENT) {
//show warning if the score might be wrong because of the invalid nodes used for calculation
List<String> testElemWithNoResource = scoreEasyForm.getInvalidNodeDescriptions();
String msg = getWarningMessage(testElemWithNoResource);
if(msg!=null) {
showWarning(msg);
}
ScoreCalculator sc = scoreEasyForm.getScoreCalulator();
/*
* OLAT-1144 bug fix if Calculation Score -> NO and Calculate passing
* score -> NO we get a ScoreCalculator == NULL !
*/
if (sc != null) {
sc.setPassedExpression(sc.getPassedExpressionFromEasyModeConfiguration());
sc.setScoreExpression(sc.getScoreExpressionFromEasyModeConfiguration());
}
// ..setScoreCalculator(sc) can handle NULL values!
stNode.setScoreCalculator(sc);
initScoreEasyForm(ureq); // reload form, remove deleted nodes
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
updateHighscoreTab();
} else if (event == Event.CANCELLED_EVENT) { // reload form
initScoreEasyForm(ureq);
}
} else if (source == scoreExpertForm) {
if (event == Event.DONE_EVENT) {
//show warning if the score might be wrong because of the invalid nodes used for calculation
List<String> testElemWithNoResource = scoreExpertForm.getInvalidNodeDescriptions();
String msg = getWarningMessage(testElemWithNoResource);
if(msg!=null) {
getWindowControl().setWarning(msg);
}
ScoreCalculator sc = scoreExpertForm.getScoreCalulator();
/*
* OLAT-1144 bug fix if a ScoreCalculator == NULL !
*/
if (sc != null) {
sc.clearEasyMode();
}
// ..setScoreCalculator(sc) can handle NULL values!
stNode.setScoreCalculator(sc);
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
updateHighscoreTab();
} else if (event == Event.CANCELLED_EVENT) { // reload form
initScoreExpertForm(ureq);
}
} else if (source == highScoreNodeConfigController){
if (event == Event.DONE_EVENT) {
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
}
}
private void addCustomFileConfigToView(UserRequest ureq) {
// Read configuration
boolean relFilPathIsProposal = false;
String relFilePath = chosenFile;
if(relFilePath == null){
// Use calculated file and folder name as default when not yet configured
relFilePath = CourseEditorHelper.createUniqueRelFilePathFromShortTitle(stNode, courseFolderContainer);
relFilPathIsProposal = true;
}
// File create/select controller
combiLinkCtr = new LinkFileCombiCalloutController(ureq, getWindowControl(), courseFolderContainer, relFilePath, relFilPathIsProposal, allowRelativeLinks, new CourseInternalLinkTreeModel(editorModel) );
listenTo(combiLinkCtr);
configvc.put("combiCtr", combiLinkCtr.getInitialComponent());
configvc.contextPut("editorEnabled", combiLinkCtr.isEditorEnabled());
// Security configuration form
boolean allowCoachEdit = stNode.getModuleConfiguration().getBooleanSafe(CONFIG_KEY_ALLOW_COACH_EDIT, false);
securitySettingForm = new SecuritySettingsForm(ureq, getWindowControl(), allowRelativeLinks, allowCoachEdit);
listenTo(securitySettingForm);
configvc.put("allowRelativeLinksForm", securitySettingForm.getInitialComponent());
// Add options tab
if(myTabbedPane != null) {
if(!myTabbedPane.containsTab(deliveryOptionsCtrl.getInitialComponent())) {
myTabbedPane.addTab(translate(PANE_TAB_DELIVERYOPTIONS), deliveryOptionsCtrl.getInitialComponent());
}
}
}
private void removeCustomFileConfigFromView() {
// Remove options tab
if(myTabbedPane != null) {
if(myTabbedPane.containsTab(deliveryOptionsCtrl.getInitialComponent())) {
myTabbedPane.removeTab(deliveryOptionsCtrl.getInitialComponent());
}
}
// Remove combi link
if (combiLinkCtr != null) {
configvc.remove(combiLinkCtr.getInitialComponent());
removeAsListenerAndDispose(combiLinkCtr);
combiLinkCtr = null;
}
// Remove security settings form
if (securitySettingForm != null) {
configvc.remove(securitySettingForm.getInitialComponent());
removeAsListenerAndDispose(securitySettingForm);
securitySettingForm = null;
}
}
private void updateHighscoreTab() {
Boolean sf = stNode.hasScoreConfigured();
myTabbedPane.setEnabled(5, sf);
}
/**
* @see org.olat.core.gui.control.generic.tabbable.TabbableDefaultController#addTabs(org.olat.core.gui.components.TabbedPane)
*/
public void addTabs(TabbedPane tabbedPane) {
myTabbedPane = tabbedPane;
tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondContr.getWrappedDefaultAccessConditionVC(translate("condition.accessibility.title")));
tabbedPane.addTab(translate(PANE_TAB_ST_CONFIG), configvc);
tabbedPane.addTab(translate(PANE_TAB_ST_SCORECALCULATION), score);
tabbedPane.addTab(translate(PANE_TAB_HIGHSCORE) , highScoreNodeConfigController.getInitialComponent());
updateHighscoreTab();
if(editorEnabled) {
tabbedPane.addTab(translate(PANE_TAB_DELIVERYOPTIONS), deliveryOptionsCtrl.getInitialComponent());
}
}
/**
* @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
*/
protected void doDispose() {
//child controllers registered with listenTo() get disposed in BasicController
}
/**
* @param mc The module confguration
* @return The configured file name
*/
public static String getFileName(ModuleConfiguration mc) {
return (String) mc.get(CONFIG_KEY_FILE);
}
public String[] getPaneKeys() {
return paneKeys;
}
public TabbedPane getTabbedPane() {
return myTabbedPane;
}
}