Skip to content
Snippets Groups Projects
GenericCourseNode.java 23.9 KiB
Newer Older
Alan Moran's avatar
Alan Moran committed
/**
* 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.
srosse's avatar
srosse committed
* <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.
*/
Alan Moran's avatar
Alan Moran committed

package org.olat.course.nodes;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.stack.StackedController;
Alan Moran's avatar
Alan Moran committed
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.messages.MessageUIFactory;
import org.olat.core.gui.control.generic.tabbable.TabbableController;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.CodeHelper;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.nodes.GenericNode;
import org.olat.core.util.xml.XStreamHelper;
import org.olat.course.ICourse;
import org.olat.course.condition.Condition;
import org.olat.course.condition.additionalconditions.AdditionalCondition;
Alan Moran's avatar
Alan Moran committed
import org.olat.course.condition.interpreter.ConditionErrorMessage;
import org.olat.course.condition.interpreter.ConditionExpression;
import org.olat.course.condition.interpreter.ConditionInterpreter;
import org.olat.course.editor.CourseEditorEnv;
import org.olat.course.editor.NodeConfigFormController;
import org.olat.course.editor.StatusDescription;
import org.olat.course.export.CourseEnvironmentMapper;
Alan Moran's avatar
Alan Moran committed
import org.olat.course.run.navigation.NodeRunConstructionResult;
import org.olat.course.run.userview.NodeEvaluation;
import org.olat.course.run.userview.TreeEvaluation;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.group.model.BGAreaReference;
import org.olat.group.model.BusinessGroupReference;
Alan Moran's avatar
Alan Moran committed
import org.olat.modules.ModuleConfiguration;

/**
 * Description:<br>
 * @author Felix Jost
 * @author BPS (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>)
 */
public abstract class GenericCourseNode extends GenericNode implements CourseNode {
	private static final long serialVersionUID = -1093400247219150363L;
Alan Moran's avatar
Alan Moran committed
	private String type, shortTitle, longTitle, learningObjectives, displayOption;
	private ModuleConfiguration moduleConfiguration;
	private String noAccessExplanation;
	private Condition preConditionVisibility;
	private Condition preConditionAccess;
	protected transient StatusDescription[] oneClickStatusCache = null;
	protected List<AdditionalCondition> additionalConditions = new ArrayList<AdditionalCondition>();
Alan Moran's avatar
Alan Moran committed

	/**
	 * Generic course node constructor
	 * 
	 * @param type The course node type
	 *      
	 *      ATTENTION:
	 *      all course nodes must call updateModuleConfigDefaults(true) here
	 */
	public GenericCourseNode(String type) {
		super();
		this.type = type;
		moduleConfiguration = new ModuleConfiguration();
	}

	/**
	 * @see org.olat.course.nodes.CourseNode#createEditController(org.olat.core.gui.UserRequest,
	 *      org.olat.core.gui.control.WindowControl, org.olat.course.ICourse)
	 *      
	 *      ATTENTION:
	 *      all course nodes must call updateModuleConfigDefaults(false) here
	 */
	public abstract TabbableController createEditController(UserRequest ureq, WindowControl wControl, StackedController stackPanel, ICourse course,
Alan Moran's avatar
Alan Moran committed
			UserCourseEnvironment euce);

	/**
	 * @see org.olat.course.nodes.CourseNode#createNodeRunConstructionResult(UserRequest,
	 *      WindowControl, UserCourseEnvironment, NodeEvaluation, String)
	 * 
	 *      ATTENTION: all course nodes must call
	 *      updateModuleConfigDefaults(false) here
Alan Moran's avatar
Alan Moran committed
	 */
	public abstract NodeRunConstructionResult createNodeRunConstructionResult(
			UserRequest ureq, WindowControl wControl,
			UserCourseEnvironment userCourseEnv, NodeEvaluation ne,
			String nodecmd);

Alan Moran's avatar
Alan Moran committed
	protected String getDefaultTitleOption() {
		return CourseNode.DISPLAY_OPTS_TITLE_DESCRIPTION_CONTENT;
	}

	/**
	 * Default implementation of the peekview controller that returns NULL: no
	 * node specific peekview information should be shown<br>
	 * Override this method with a specific implementation if you have
	 * something interesting to show in the peekview
	 * 
	 * @see org.olat.course.nodes.CourseNode#createPeekViewRunController(UserRequest, WindowControl, UserCourseEnvironment, NodeEvaluation)
Alan Moran's avatar
Alan Moran committed
	 */
	public Controller createPeekViewRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv,
			NodeEvaluation ne) {
		return null;
	}
	
	/**
	 * default implementation of the previewController
	 * 
	 * @see org.olat.course.nodes.CourseNode#createPreviewController(org.olat.core.gui.UserRequest,
	 * @see org.olat.core.gui.control.WindowControl,
	 * @see org.olat.course.run.userview.UserCourseEnvironment,
	 * @see org.olat.course.run.userview.NodeEvaluation)
	 */
	//no userCourseEnv or NodeEvaluation needed here
Alan Moran's avatar
Alan Moran committed
	public Controller createPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne) {
		Translator translator = Util.createPackageTranslator(GenericCourseNode.class, ureq.getLocale());
		String text = translator.translate("preview.notavailable");
		return MessageUIFactory.createInfoMessage(ureq, wControl, null, text);
	}

	/**
	 * @return String
	 */
	public String getLearningObjectives() {
		return learningObjectives;
	}

	/**
	 * @return String
	 */
	public String getLongTitle() {
		return longTitle;
	}

	/**
	 * @return String
	 */
	public String getShortTitle() {
		return shortTitle;
	}
	
	/**
	 * allows to specify if default value should be returned in case where there is no value.
	 * @param returnDefault if false: null may be returned if no value found!
	 * @return String 
	 */
	public String getDisplayOption(boolean returnDefault) {
		if(!StringHelper.containsNonWhitespace(displayOption) && returnDefault) {
			return getDefaultTitleOption();
		}
		return displayOption;
	}
	
	/**
	 * @return String with the old behavior (default value if none existing)
	 */
	public String getDisplayOption() {
		return getDisplayOption(true);		
	}

	/**
	 * @return String
	 */
	public String getType() {
		return type;
	}

	/**
	 * Sets the learningObjectives.
	 * 
	 * @param learningObjectives The learningObjectives to set
	 */
	public void setLearningObjectives(String learningObjectives) {
		this.learningObjectives = learningObjectives;
	}

	/**
	 * Sets the longTitle.
	 * 
	 * @param longTitle The longTitle to set
	 */
	public void setLongTitle(String longTitle) {
		this.longTitle = longTitle;
	}

	/**
	 * Sets the shortTitle.
	 * 
	 * @param shortTitle The shortTitle to set
	 */
	public void setShortTitle(String shortTitle) {
		this.shortTitle = shortTitle;
	}
	
	/**
	 * Sets the display option
	 * @param displayOption
	 */
	public void setDisplayOption(String displayOption) {
		this.displayOption = displayOption;
	}

	/**
	 * Sets the type.
	 * 
	 * @param type The type to set
	 */
	public void setType(String type) {
		this.type = type;
	}

	/**
	 * @return ModuleConfiguration
	 */
	public ModuleConfiguration getModuleConfiguration() {
		return moduleConfiguration;
	}

	/**
	 * Sets the moduleConfiguration.
	 * 
	 * @param moduleConfiguration The moduleConfiguration to set
	 */
	public void setModuleConfiguration(ModuleConfiguration moduleConfiguration) {
		this.moduleConfiguration = moduleConfiguration;
	}

	/**
	 * @see org.olat.course.nodes.CourseNode#eval(org.olat.course.condition.interpreter.ConditionInterpreter,
	 *      org.olat.course.run.userview.TreeEvaluation)
	 */
	public NodeEvaluation eval(ConditionInterpreter ci, TreeEvaluation treeEval) {
		// each CourseNodeImplementation has the full control over all children eval.
Alan Moran's avatar
Alan Moran committed
		// default behaviour is to eval all visible children
		NodeEvaluation nodeEval = new NodeEvaluation(this);
		calcAccessAndVisibility(ci, nodeEval);
Alan Moran's avatar
Alan Moran committed
		nodeEval.build();
		treeEval.cacheCourseToTreeNode(this, nodeEval.getTreeNode());
		// only add children (coursenodes/nodeeval) when I am visible and
		// atleastOneAccessible myself
		if (nodeEval.isVisible() && nodeEval.isAtLeastOneAccessible()) {
			int childcnt = getChildCount();
			for (int i = 0; i < childcnt; i++) {
				CourseNode cn = (CourseNode)getChildAt(i);
Alan Moran's avatar
Alan Moran committed
				NodeEvaluation chdEval = cn.eval(ci, treeEval);
				if (chdEval.isVisible()) { // child is visible
					nodeEval.addNodeEvaluationChild(chdEval);
				}
			}
		}
		return nodeEval;
	}

	/**
	 * @param ci the ConditionInterpreter as the calculating machine
	 * @param nodeEval the object to write the results into
	 */
	protected abstract void calcAccessAndVisibility(ConditionInterpreter ci, NodeEvaluation nodeEval);

	/**
	 * @return String
	 */
	public String getNoAccessExplanation() {
		return noAccessExplanation;
	}

	/**
	 * Sets the noAccessExplanation.
	 * 
	 * @param noAccessExplanation The noAccessExplanation to set
	 */
	public void setNoAccessExplanation(String noAccessExplanation) {
		this.noAccessExplanation = noAccessExplanation;
	}

	/**
	 * @return Condition
	 */
	public Condition getPreConditionVisibility() {
		if (preConditionVisibility == null) {
			preConditionVisibility = new Condition();
		}
		preConditionVisibility.setConditionId("visibility");
		return preConditionVisibility;
	}

	/**
	 * Sets the preConditionVisibility.
	 * 
	 * @param preConditionVisibility The preConditionVisibility to set
	 */
	public void setPreConditionVisibility(Condition preConditionVisibility) {
		if (preConditionVisibility == null) {
			preConditionVisibility = getPreConditionVisibility();
		}
		this.preConditionVisibility = preConditionVisibility;
		this.preConditionVisibility.setConditionId("visibility");
	}

	/**
	 * @return Condition
	 */
	public Condition getPreConditionAccess() {
		if (preConditionAccess == null) {
			preConditionAccess = new Condition();
		}
		preConditionAccess.setConditionId("accessability");
		return preConditionAccess;
	}

	/**
	 * Generic interface implementation. May be overriden by specific node's
	 * implementation.
	 * 
	 * @see org.olat.course.nodes.CourseNode#informOnDelete(org.olat.core.gui.UserRequest,
	 *      org.olat.course.ICourse)
	 */
	public String informOnDelete(Locale locale, ICourse course) {
		return null;
	}

	/**
	 * Generic interface implementation. May be overriden by specific node's
	 * implementation.
	 * 
	 * @see org.olat.course.nodes.CourseNode#cleanupOnDelete(org.olat.course.ICourse)
	 */
	public void cleanupOnDelete(ICourse course) {
	/**
	 * do nothing in default implementation
	 */
	}

	/**
	 * Generic interface implementation. May be overriden by specific node's
	 * implementation.
	 * 
	 * @see org.olat.course.nodes.CourseNode#archiveNodeData(java.util.Locale,
	 *      org.olat.course.ICourse, java.io.File)
	 */
	//implemented by specialized node
Alan Moran's avatar
Alan Moran committed
	public boolean archiveNodeData(Locale locale, ICourse course, File exportDirectory, String charset) {
	// nothing to do in default implementation
		return true;
	}

	/**
	 * @see org.olat.course.nodes.CourseNode#exportNode(java.io.File,
	 *      org.olat.course.ICourse)
	 */
Alan Moran's avatar
Alan Moran committed
	public void exportNode(File exportDirectory, ICourse course) {
	// nothing to do in default implementation
	}

	/**
	 * Implemented by specialized node
Alan Moran's avatar
Alan Moran committed
	 * @see org.olat.course.nodes.CourseNode#importNode(java.io.File,
	 *      org.olat.course.ICourse, org.olat.core.gui.UserRequest,
	 *      org.olat.core.gui.control.WindowControl)
	 */
	public Controller importNode(File importDirectory, ICourse course, boolean unattendedImport, UserRequest ureq, WindowControl wControl) {
		// nothing to do in default implementation
		return null;
	}

	@Override
	public void postImport(CourseEnvironmentMapper envMapper) {
		postImportCondition(preConditionAccess, envMapper);
		postImportCondition(preConditionVisibility, envMapper);
	}
	
	protected void postImportCondition(Condition condition, CourseEnvironmentMapper envMapper) {
		if(condition == null) return;
		
		if(condition.isExpertMode()) {
			String expression = condition.getConditionExpression();
			if(StringHelper.containsNonWhitespace(expression)) {
				String processExpression = convertExpressionNameToKey(expression, envMapper);
				processExpression = convertExpressionKeyToKey(processExpression, envMapper);
				if(!expression.equals(processExpression)) {
					condition.setConditionExpression(processExpression);
				}
			}
		} else if(StringHelper.containsNonWhitespace(condition.getConditionFromEasyModeConfiguration())) {
			List<Long> groupKeys = condition.getEasyModeGroupAccessIdList();
				//this is an old course -> get group keys from original names
				groupKeys = envMapper.toGroupKeyFromOriginalNames(condition.getEasyModeGroupAccess());
			} else {
				//map the original exported group key to the newly created one
				groupKeys = envMapper.toGroupKeyFromOriginalKeys(groupKeys);
			}
			condition.setEasyModeGroupAccessIdList(groupKeys);//update keys
			condition.setEasyModeGroupAccess(envMapper.toGroupNames(groupKeys));//update names with the current values
			
			List<Long> areaKeys = condition.getEasyModeGroupAreaAccessIdList();
				areaKeys = envMapper.toAreaKeyFromOriginalNames(condition.getEasyModeGroupAreaAccess());
			} else {
				areaKeys = envMapper.toAreaKeyFromOriginalKeys(areaKeys);
			}
			condition.setEasyModeGroupAreaAccessIdList(areaKeys);
			condition.setEasyModeGroupAreaAccess(envMapper.toAreaNames(areaKeys));
			
			String condString = condition.getConditionFromEasyModeConfiguration();
			condition.setConditionExpression(condString);
		}
	}

	@Override
	public void postExport(CourseEnvironmentMapper envMapper, boolean backwardsCompatible) {
		postExportCondition(preConditionAccess, envMapper, backwardsCompatible);
		postExportCondition(preConditionVisibility, envMapper, backwardsCompatible);
	}
	
	protected void postExportCondition(Condition condition, CourseEnvironmentMapper envMapper, boolean backwardsCompatible) {
		if(condition == null) return;
		
		boolean easy = StringHelper.containsNonWhitespace(condition.getConditionFromEasyModeConfiguration());
		if(easy) {
			//already processed?
			if(condition.getEasyModeGroupAccessIdList() != null 
					|| condition.getEasyModeGroupAreaAccessIdList() != null) {
			
				String groupNames = envMapper.toGroupNames(condition.getEasyModeGroupAccessIdList());
				condition.setEasyModeGroupAccess(groupNames);
				String areaNames = envMapper.toAreaNames(condition.getEasyModeGroupAreaAccessIdList());
				condition.setEasyModeGroupAreaAccess(areaNames);
				String condString = condition.getConditionFromEasyModeConfiguration();
				if(backwardsCompatible) {
					condString = convertExpressionKeyToName(condString, envMapper);
				}
				condition.setConditionExpression(condString);
			}
		} else if(condition.isExpertMode() && backwardsCompatible) {
			String expression = condition.getConditionExpression();
			if(StringHelper.containsNonWhitespace(expression)) {
				String processExpression = convertExpressionKeyToName(expression, envMapper);
				if(!expression.equals(processExpression)) {
					condition.setConditionExpression(processExpression);
				}
			}
		}
		
		if(backwardsCompatible) {
			condition.setEasyModeGroupAreaAccessIds(null);
			condition.setEasyModeGroupAccessIds(null);
			//condition.setConditionUpgraded(null);
		}
	}
	
	protected String convertExpressionKeyToName(String expression, CourseEnvironmentMapper envMapper) {
		for(BusinessGroupReference group:envMapper.getGroups()) {
			String strToMatch = "\"" + group.getKey() + "\"";
			String replacement = "\"" + group.getName() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		for(BGAreaReference area:envMapper.getAreas()) {
			String strToMatch = "\"" + area.getKey() + "\"";
			String replacement = "\"" + area.getName() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		return expression;
	}
	
	protected String convertExpressionNameToKey(String expression, CourseEnvironmentMapper envMapper) {
		for(BusinessGroupReference group:envMapper.getGroups()) {
			String strToMatch = "\"" + group.getOriginalName() + "\"";
			String replacement = "\"" + group.getKey() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		for(BGAreaReference area:envMapper.getAreas()) {
			String strToMatch = "\"" + area.getOriginalName() + "\"";
			String replacement = "\"" + area.getKey() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		return expression;
	}
	
	protected String convertExpressionKeyToKey(String expression, CourseEnvironmentMapper envMapper) {
		for(BusinessGroupReference group:envMapper.getGroups()) {
			String strToMatch = "\"" + group.getOriginalKey() + "\"";
			String replacement = "\"" + group.getKey() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		for(BGAreaReference area:envMapper.getAreas()) {
			String strToMatch = "\"" + area.getOriginalKey() + "\"";
			String replacement = "\"" + area.getKey() + "\"";
			expression = StringHelper.replaceAllCaseInsensitive(expression, strToMatch, replacement);
		}
		return expression;
	}

Alan Moran's avatar
Alan Moran committed
	/**
	 * @see org.olat.core.gui.ShortName#getShortName()
	 */
	public String getShortName() {
		return getShortTitle();
	}
	
	/**
	 * @see org.olat.course.nodes.CourseNode#createInstanceForCopy()
	 */
	public CourseNode createInstanceForCopy() {
		return createInstanceForCopy(true);
	}
	
	public CourseNode createInstanceForCopy(boolean isNewTitle) {
		CourseNode copyInstance = (CourseNode) XStreamHelper.xstreamClone(this);
		copyInstance.setIdent(String.valueOf(CodeHelper.getForeverUniqueID()));
		copyInstance.setPreConditionVisibility(null);
		if (isNewTitle) {
			// FIXME:pb:ms translation for COPY OF
			String newTitle = "Copy of " + getShortTitle();
			if (newTitle.length() > NodeConfigFormController.SHORT_TITLE_MAX_LENGTH) newTitle = newTitle.substring(0,
					NodeConfigFormController.SHORT_TITLE_MAX_LENGTH - 1);
			copyInstance.setShortTitle(newTitle);
		}
		return copyInstance;
	}

	@Override
	public void copyConfigurationTo(CourseNode courseNode) {
		if(courseNode instanceof GenericCourseNode) {
			GenericCourseNode newNode = (GenericCourseNode)courseNode;
			newNode.setDisplayOption(getDisplayOption());
			newNode.setLearningObjectives(getLearningObjectives());
			newNode.setLongTitle(getLongTitle());
			newNode.setNoAccessExplanation(getNoAccessExplanation());
			newNode.setShortTitle(getShortTitle());
			
			if(preConditionVisibility != null) {
				newNode.setPreConditionVisibility(preConditionVisibility.clone());
			}
		}
	}

Alan Moran's avatar
Alan Moran committed
	/**
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "Id: " + getIdent() + ", '" + getShortTitle() + "' " + super.toString();
	}

	/**
	 * @see org.olat.course.nodes.CourseNode#getConditionExpressions()
	 */
	public List<ConditionExpression> getConditionExpressions() {
		ArrayList<ConditionExpression> retVal = new ArrayList<ConditionExpression>();
		String coS = getPreConditionVisibility().getConditionExpression();
		if (coS != null && !coS.equals("")) {
			// an active condition is defined
			ConditionExpression ce = new ConditionExpression(getPreConditionVisibility().getConditionId());
			ce.setExpressionString(getPreConditionVisibility().getConditionExpression());
			retVal.add(ce);
		}
		//
		return retVal;
	}

	/**
	 * must be implemented in the concrete subclasses as a translator is needed
	 * for the errormessages which comes with evaluating condition expressions
	 * 
	 * @see org.olat.course.nodes.CourseNode#isConfigValid(org.olat.course.run.userview.UserCourseEnvironment)
	 */
	public abstract StatusDescription[] isConfigValid(CourseEditorEnv cev);

	/**
	 * @param userCourseEnv
	 * @param translatorStr
	 * @return
	 */
Alan Moran's avatar
Alan Moran committed
	protected List<StatusDescription> isConfigValidWithTranslator(CourseEditorEnv cev, String translatorStr, List<ConditionExpression> condExprs) {
		List<StatusDescription> condExprsStatusDescs = new ArrayList<StatusDescription>();
		// check valid configuration without course environment
		StatusDescription first = isConfigValid();
		// check valid configuration within the course environment
		if (cev == null) {
			// course environment not configured!??
			condExprsStatusDescs.add(first);
			return condExprsStatusDescs;
		}
		/*
		 * there is course editor environment, we can check further. Iterate over
		 * all conditions of this course node, validate the condition expression and
		 * transform the condition error message into a status description
		 */
		for (int i = 0; i < condExprs.size(); i++) {
			ConditionExpression ce = condExprs.get(i);
			ConditionErrorMessage[] cems = cev.validateConditionExpression(ce);
			if (cems != null && cems.length > 0) {
				for (int j = 0; j < cems.length; j++) {
					StatusDescription sd = new StatusDescription(StatusDescription.WARNING, cems[j].errorKey, cems[j].solutionMsgKey,
							cems[j].errorKeyParams, translatorStr);
					sd.setDescriptionForUnit(getIdent());
					condExprsStatusDescs.add(sd);
				}
			}
		}
		condExprsStatusDescs.add(first);
		return condExprsStatusDescs;
	}

	/**
	 * @see org.olat.course.nodes.CourseNode#explainThisDuringPublish(org.olat.core.gui.control.StatusDescription)
	 */
	public StatusDescription explainThisDuringPublish(StatusDescription description) {
		if (description == null) return null;
		StatusDescription retVal = null;
		if (description.getShortDescriptionKey().equals("error.notfound.coursenodeid")) {
			retVal = description.transformTo("error.notfound.coursenodeid.publish", "error.notfound.coursenodeid.publish", null);
		} else if (description.getShortDescriptionKey().equals("error.notfound.name")) {
			retVal = description.transformTo("error.notfound.name.publish", "error.notfound.name.publish", null);
		} else if (description.getShortDescriptionKey().equals("error.notassessable.coursenodid")) {
			retVal = description.transformTo("error.notassessable.coursenodid.publish", "error.notassessable.coursenodid.publish", null);
		} else {
			// throw new OLATRuntimeException("node does not know how to translate <b
			// style='color:red'>" + description.getShortDescriptionKey()
			// + "</b> in publish env", new IllegalArgumentException());
			return description;
		}
		return retVal;
	}

	/**
	 * Update the module configuration to have all mandatory configuration flags
	 * set to usefull default values
	 * 
	 * @param isNewNode true: an initial configuration is set; false: upgrading
	 *          from previous node configuration version, set default to maintain
	 *          previous behaviour
	 *          
	 * This is the workflow:
	 * On every click on a entry of the navigation tree, this method will be called
	 * to ensure a valid configration of the depending module. This is only done in
	 * RAM. If the user clicks on that node in course editor and publishes the course
	 * after that, then the updated config will be persisted to disk. Otherwise
	 * everything what is done here has to be done once at every course start.
	 */
	//implemented by specialized node
Alan Moran's avatar
Alan Moran committed
	public void updateModuleConfigDefaults(boolean isNewNode) {
		/**
		 *  Do NO updating here, since this method can be overwritten by all classes
		 *  implementing this. This is only implemented here to avoid changing all
		 *  couseNode classes which do not implement this method.
		 */
	}

}