/** * 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; import java.io.File; import java.util.ArrayList; import java.util.List; import org.olat.core.commons.controllers.linkchooser.CustomLinkTreeModel; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; import org.olat.core.commons.modules.singlepage.SinglePageController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.creator.ControllerCreator; import org.olat.core.gui.control.generic.clone.CloneController; import org.olat.core.gui.control.generic.clone.CloneLayoutControllerCreatorCallback; import org.olat.core.gui.control.generic.clone.CloneableController; import org.olat.core.gui.control.generic.iframe.DeliveryOptions; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.resource.OresHelper; import org.olat.course.CourseFactory; import org.olat.course.CourseModule; import org.olat.course.ICourse; import org.olat.course.condition.Condition; import org.olat.course.condition.KeyAndNameConverter; 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.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.export.CourseEnvironmentMapper; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.groupsandrights.CourseRights; import org.olat.course.nodes.sp.SPEditController; import org.olat.course.nodes.sp.SPPeekviewController; import org.olat.course.nodes.st.STCourseNodeEditController; import org.olat.course.nodes.st.STCourseNodeRunController; import org.olat.course.nodes.st.STPeekViewController; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.AssessmentEvaluation; import org.olat.course.run.scoring.FailedEvaluationType; import org.olat.course.run.scoring.ScoreCalculator; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseInternalLinkTreeModel; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; import org.olat.repository.RepositoryEntry; import org.olat.util.logging.activity.LoggingResourceable; /** * Description:<br> * The structure node (ST) is used to build structures in the course hierarchy. * In addition it is also used to calculate score and passed values, to syndicate * these values e.g. from children nodes. Example: a lesson with two tests is * passed when both tests are passed. This would be designed as an ST node with * two IMSTEST nodes as children and a scoring rule on the ST node that syndicates * the testresults. In the assessment tool the ST node results can be seen but not * changed since these are calculated values and not saved values from properties. * * <P> * Initial Date: Feb 9, 2004<br> * @author Mike Stock * @author BPS (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>) */ public class STCourseNode extends AbstractAccessableCourseNode implements CalculatedAssessableCourseNode { private static final long serialVersionUID = -7460670977531082040L; private static final String TYPE = "st"; private static final String ICON_CSS_CLASS = "o_st_icon"; private ScoreCalculator scoreCalculator; transient private Condition scoreExpression; transient private Condition passedExpression; transient private Condition failedExpression; /** * Constructor for a course building block of the type structure */ public STCourseNode() { super(TYPE); updateModuleConfigDefaults(true); } /** * @see org.olat.course.nodes.CourseNode#createEditController(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.WindowControl, org.olat.course.ICourse) */ @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { updateModuleConfigDefaults(false); // only the precondition "access" can be configured till now STCourseNodeEditController childTabCntrllr = new STCourseNodeEditController(ureq, wControl, this, course.getCourseFolderContainer(), course.getEditorTreeModel(), euce); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); NodeEditController nodeEditController = new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, childTabCntrllr); // special case: listen to st edit controller, must be informed when the short title is being modified nodeEditController.addControllerListener(childTabCntrllr); return nodeEditController; } /** * @see org.olat.course.nodes.CourseNode#createNodeRunConstructionResult(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.WindowControl, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.course.run.userview.NodeEvaluation) */ @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, final UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { updateModuleConfigDefaults(false); Controller cont; String displayType = getModuleConfiguration().getStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE); String relPath = STCourseNodeEditController.getFileName(getModuleConfiguration()); if (relPath != null && displayType.equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE)) { // we want a user chosen overview, so display the chosen file from the // material folder, otherwise display the normal overview // reuse the Run controller from the "Single Page" building block, since // we need to do exactly the same task Boolean allowRelativeLinks = getModuleConfiguration().getBooleanEntry(STCourseNodeEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS); if(allowRelativeLinks == null) { allowRelativeLinks = Boolean.FALSE; } DeliveryOptions deliveryOptions = (DeliveryOptions)getModuleConfiguration().get(SPEditController.CONFIG_KEY_DELIVERYOPTIONS); OLATResourceable ores = OresHelper.createOLATResourceableInstance(CourseModule.class, userCourseEnv.getCourseEnvironment().getCourseResourceableId()); SinglePageController spCtr = new SinglePageController(ureq, wControl, userCourseEnv.getCourseEnvironment().getCourseFolderContainer(), relPath, allowRelativeLinks.booleanValue(), null, ores, deliveryOptions, userCourseEnv.getCourseEnvironment().isPreview()); // check if user is allowed to edit the page in the run view CourseGroupManager cgm = userCourseEnv.getCourseEnvironment().getCourseGroupManager(); boolean hasEditRights = (cgm.isIdentityCourseAdministrator(ureq.getIdentity()) || cgm.hasRight(ureq.getIdentity(),CourseRights.RIGHT_COURSEEDITOR)) || (getModuleConfiguration().getBooleanSafe(SPEditController.CONFIG_KEY_ALLOW_COACH_EDIT, false) && cgm.isIdentityCourseCoach(ureq.getIdentity())); if (hasEditRights) { spCtr.allowPageEditing(); // set the link tree model to internal for the HTML editor CustomLinkTreeModel linkTreeModel = new CourseInternalLinkTreeModel(userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode()); spCtr.setInternalLinkTreeModel(linkTreeModel); } spCtr.addLoggingResourceable(LoggingResourceable.wrap(this)); // create clone wrapper layout, allow popping into second window CloneLayoutControllerCreatorCallback clccc = new CloneLayoutControllerCreatorCallback() { @Override public ControllerCreator createLayoutControllerCreator(final UserRequest uureq, final ControllerCreator contentControllerCreator) { return BaseFullWebappPopupLayoutFactory.createAuthMinimalPopupLayout(uureq, new ControllerCreator() { @Override public Controller createController(UserRequest lureq, WindowControl lwControl) { // wrap in column layout, popup window needs a layout controller Controller ctr = contentControllerCreator.createController(lureq, lwControl); LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(lureq, lwControl, ctr); layoutCtr.setCustomCSS(CourseFactory.getCustomCourseCss(lureq.getUserSession(), userCourseEnv.getCourseEnvironment())); Controller wrappedCtrl = TitledWrapperHelper.getWrapper(lureq, lwControl, ctr, STCourseNode.this, ICON_CSS_CLASS); layoutCtr.addDisposableChildController(wrappedCtrl); return layoutCtr; } }); } }; Controller wrappedCtrl = TitledWrapperHelper.getWrapper(ureq, wControl, spCtr, this, ICON_CSS_CLASS); if(wrappedCtrl instanceof CloneableController) { cont = new CloneController(ureq, wControl, (CloneableController)wrappedCtrl, clccc); } else { throw new AssertException("Need to be a cloneable"); } } else { // evaluate the score accounting for this node. this uses the score accountings local // cache hash map to reduce unnecessary calculations ScoreEvaluation se = userCourseEnv.getScoreAccounting().evalCourseNode(this); cont = TitledWrapperHelper.getWrapper(ureq, wControl, new STCourseNodeRunController(ureq, wControl, userCourseEnv, this, se, ne), this, ICON_CSS_CLASS); } // access the current calculated score, if there is one, so that it can be // displayed in the ST-Runcontroller return new NodeRunConstructionResult(cont); } /** * Checks if the given CourseNode is of type "Structure Node" and if it is set * to delegate to it's first visible child * * @param nodeToCheck * @return returns true if the given coursenNode is a STCourseNode and is configured to delegate */ public static boolean isDelegatingSTCourseNode(CourseNode nodeToCheck) { if (!(nodeToCheck instanceof STCourseNode)) return false; STCourseNode node = (STCourseNode) nodeToCheck; String displayMode = node.getModuleConfiguration().getStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_TOC); return (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_DELEGATE.equals(displayMode)); } /** * @see org.olat.course.nodes.GenericCourseNode#createPreviewController(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.WindowControl, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.course.run.userview.NodeEvaluation) */ @Override public Controller createPreviewController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne) { return createNodeRunConstructionResult(ureq, wControl, userCourseEnv, ne, null).getRunController(); } /** * @see org.olat.course.nodes.GenericCourseNode#createPeekViewRunController(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.WindowControl, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.course.run.userview.NodeEvaluation) */ @Override public Controller createPeekViewRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne) { if (ne.isAtLeastOneAccessible()) { ModuleConfiguration config = getModuleConfiguration(); if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE.equals(config.getStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE))) { // use single page preview if a file is configured OLATResourceable ores = OresHelper.createOLATResourceableInstance(CourseModule.class, userCourseEnv.getCourseEnvironment().getCourseResourceableId()); return new SPPeekviewController(ureq, wControl, userCourseEnv, config, ores); } else { // a peekview controller that displays the listing of the next ST level return new STPeekViewController(ureq, wControl, ne); } } else { // use standard peekview without content return super.createPeekViewRunController(ureq, wControl, userCourseEnv, ne); } } /** * the structure node does not have a score itself, but calculates the * score/passed info by evaluating the configured expression in the the * (condition)interpreter. * * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public AssessmentEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) { Float score = null; Boolean passed = null; if (scoreCalculator == null) { // this is a not-computable course node at the moment (no scoring/passing rules defined) return null; } String scoreExpressionStr = scoreCalculator.getScoreExpression(); String passedExpressionStr = scoreCalculator.getPassedExpression(); ConditionInterpreter ci = userCourseEnv.getConditionInterpreter(); if (scoreExpressionStr != null) { score = new Float(ci.evaluateCalculation(scoreExpressionStr)); } if (passedExpressionStr != null) { passed = new Boolean(ci.evaluateCondition(passedExpressionStr)); } return new AssessmentEvaluation(score, passed); } @Override public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry) { return AssessmentEvaluation.toAssessmentEvalutation(entry, this); } /** * @see org.olat.course.nodes.CourseNode#isConfigValid() */ @Override public StatusDescription isConfigValid() { /* * first check the one click cache */ if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } ModuleConfiguration config = getModuleConfiguration(); StatusDescription sd = StatusDescription.NOERROR; if (STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE.equals(config.getStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE))){ String fileName = (String) config.get(STCourseNodeEditController.CONFIG_KEY_FILE); if (fileName == null || !StringHelper.containsNonWhitespace(fileName)){ String shortKey = "error.missingfile.short"; String longKey = "error.missingfile.long"; String[] params = new String[] { this.getShortTitle() }; String translPackage = Util.getPackageName(SPEditController.class); sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, translPackage); sd.setDescriptionForUnit(getIdent()); // set which pane is affected by error sd.setActivateableViewIdentifier(STCourseNodeEditController.PANE_TAB_ST_CONFIG); } } return sd; } /** * @see org.olat.course.nodes.CourseNode#isConfigValid(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public StatusDescription[] isConfigValid(CourseEditorEnv cev) { oneClickStatusCache = null; // only here we know which translator to take for translating condition // error messages String translatorStr = Util.getPackageName(STCourseNodeEditController.class); List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); oneClickStatusCache = StatusDescriptionHelper.sort(sds); return oneClickStatusCache; } /** * @see org.olat.course.nodes.CourseNode#getReferencedRepositoryEntry() */ @Override public RepositoryEntry getReferencedRepositoryEntry() { return null; } /** * @see org.olat.course.nodes.CourseNode#needsReferenceToARepositoryEntry() */ @Override public boolean needsReferenceToARepositoryEntry() { return false; } /** * @return Returns the scoreCalculator. */ @Override public ScoreCalculator getScoreCalculator() { if (scoreCalculator == null) { scoreCalculator = new ScoreCalculator(); scoreCalculator.setFailedType(FailedEvaluationType.failedAsNotPassedAfterEndDate); } passedExpression = new Condition(); passedExpression.setConditionId("passed"); if (scoreCalculator.getPassedExpression() != null) { passedExpression.setConditionExpression(scoreCalculator.getPassedExpression()); passedExpression.setExpertMode(true); } scoreExpression = new Condition(); scoreExpression.setConditionId("score"); if (scoreCalculator.getScoreExpression() != null) { scoreExpression.setConditionExpression(scoreCalculator.getScoreExpression()); scoreExpression.setExpertMode(true); } failedExpression = new Condition(); failedExpression.setConditionId("failed"); if (scoreCalculator.getFailedExpression() != null) { failedExpression.setConditionExpression(scoreCalculator.getFailedExpression()); failedExpression.setExpertMode(true); } return scoreCalculator; } /** * @param scoreCalculator The scoreCalculator to set. */ public void setScoreCalculator(ScoreCalculator scoreCalculatorP) { scoreCalculator = scoreCalculatorP; if (scoreCalculatorP == null) { scoreCalculator = getScoreCalculator(); } String score = scoreCalculator.getScoreExpression(); scoreExpression.setExpertMode(true); scoreExpression.setConditionExpression(score); scoreExpression.setConditionId("score"); String passed = scoreCalculator.getPassedExpression(); passedExpression.setExpertMode(true); passedExpression.setConditionExpression(passed); passedExpression.setConditionId("passed"); String failed = scoreCalculator.getFailedExpression(); failedExpression.setExpertMode(true); failedExpression.setConditionExpression(failed); failedExpression.setConditionId("failed"); } @Override public boolean isAssessedBusinessGroups() { return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#getCutValueConfiguration() */ @Override public Float getCutValueConfiguration() { throw new OLATRuntimeException(STCourseNode.class, "Cut value never defined for ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getMaxScoreConfiguration() */ @Override public Float getMaxScoreConfiguration() { throw new OLATRuntimeException(STCourseNode.class, "Max score never defined for ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getMinScoreConfiguration() */ @Override public Float getMinScoreConfiguration() { throw new OLATRuntimeException(STCourseNode.class, "Min score never defined for ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserCoachComment(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserCoachComment(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "No coach comments available in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserLog(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserLog(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "No user logs available in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserUserComment(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public String getUserUserComment(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "No comments available in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#hasCommentConfigured() */ @Override public boolean hasCommentConfigured() { // never has comments return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasPassedConfigured() */ @Override public boolean hasPassedConfigured() { if (scoreCalculator != null && StringHelper.containsNonWhitespace(scoreCalculator.getPassedExpression())) { return true; } return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasScoreConfigured() */ @Override public boolean hasScoreConfigured() { if (scoreCalculator != null && StringHelper.containsNonWhitespace(scoreCalculator.getScoreExpression())) { return true; } return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#hasStatusConfigured() */ @Override public boolean hasStatusConfigured() { return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#isEditableConfigured() */ @Override public boolean isEditableConfigured() { // ST nodes never editable, data generated on the fly return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#updateUserCoachComment(java.lang.String, * org.olat.course.run.userview.UserCourseEnvironment) */ @Override public void updateUserCoachComment(String coachComment, UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "Coach comment variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#updateUserScoreEvaluation(org.olat.course.run.scoring.ScoreEvaluation, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.core.id.Identity) */ @Override public void updateUserScoreEvaluation(ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity, boolean incrementAttempts) { throw new OLATRuntimeException(STCourseNode.class, "Score variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#updateUserUserComment(java.lang.String, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.core.id.Identity) */ @Override public void updateUserUserComment(String userComment, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity) { throw new OLATRuntimeException(STCourseNode.class, "Comment variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public Integer getUserAttempts(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "No attempts available in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#hasAttemptsConfigured() */ @Override public boolean hasAttemptsConfigured() { return false; } /** * @see org.olat.course.nodes.AssessableCourseNode#updateUserAttempts(java.lang.Integer, * org.olat.course.run.userview.UserCourseEnvironment, * org.olat.core.id.Identity) */ @Override public void updateUserAttempts(Integer userAttempts, UserCourseEnvironment userCourseEnvironment, Identity coachingIdentity) { throw new OLATRuntimeException(STCourseNode.class, "Attempts variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#incrementUserAttempts(org.olat.course.run.userview.UserCourseEnvironment) */ @Override public void incrementUserAttempts(UserCourseEnvironment userCourseEnvironment) { throw new OLATRuntimeException(STCourseNode.class, "Attempts variable can't be updated in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#getDetailsEditController(org.olat.core.gui.UserRequest, * org.olat.core.gui.control.WindowControl, * org.olat.course.run.userview.UserCourseEnvironment) */ @Override public Controller getDetailsEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, UserCourseEnvironment coachCourseEnv, UserCourseEnvironment assessedUserCourseEnv) { throw new OLATRuntimeException(STCourseNode.class, "Details controler not available in ST nodes", null); } @Override public String getDetailsListView(UserCourseEnvironment userCourseEnvironment) { return null; } /** * @see org.olat.course.nodes.AssessableCourseNode#getDetailsListViewHeaderKey() */ @Override public String getDetailsListViewHeaderKey() { throw new OLATRuntimeException(STCourseNode.class, "Details not available in ST nodes", null); } /** * @see org.olat.course.nodes.AssessableCourseNode#hasDetails() */ @Override public boolean hasDetails() { return false; } /** * 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 */ @Override public void updateModuleConfigDefaults(boolean isNewNode) { ModuleConfiguration config = getModuleConfiguration(); if (isNewNode) { // use defaults for new course building blocks config.setBooleanEntry(STCourseNodeEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS, Boolean.FALSE.booleanValue()); // set the default display to peekview in two columns config.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_PEEKVIEW); config.setIntValue(STCourseNodeEditController.CONFIG_KEY_COLUMNS, 2); DeliveryOptions defaultOptions = DeliveryOptions.defaultWithGlossary(); config.set(SPEditController.CONFIG_KEY_DELIVERYOPTIONS, defaultOptions); config.setConfigurationVersion(3); scoreCalculator = new ScoreCalculator(); scoreCalculator.setFailedType(FailedEvaluationType.failedAsNotPassedAfterEndDate); } else { // update to version 2 if (config.getConfigurationVersion() < 2) { // use values accoring to previous functionality config.setBooleanEntry(STCourseNodeEditController.CONFIG_KEY_ALLOW_RELATIVE_LINKS, Boolean.FALSE.booleanValue()); // previous version of score st node didn't have easy mode on score // calculator, se to expert mode if (getScoreCalculator() != null) { getScoreCalculator().setExpertMode(true); } config.setConfigurationVersion(2); } // update to version 3 if (config.getConfigurationVersion() < 3) { String fileName = (String) config.get(STCourseNodeEditController.CONFIG_KEY_FILE); if (fileName != null) { // set to custom file display config config.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE); } else { // set the default display to plain vanilla TOC view in one column config.setStringValue(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE, STCourseNodeEditController.CONFIG_VALUE_DISPLAY_TOC); config.setIntValue(STCourseNodeEditController.CONFIG_KEY_COLUMNS, 1); } config.setConfigurationVersion(3); } if (config.getConfigurationVersion() < 4) { if(config.get(SPEditController.CONFIG_KEY_DELIVERYOPTIONS) == null) { DeliveryOptions defaultOptions = DeliveryOptions.defaultWithGlossary(); config.set(SPEditController.CONFIG_KEY_DELIVERYOPTIONS, defaultOptions); } config.setConfigurationVersion(4); } } } @Override public void postCopy(CourseEnvironmentMapper envMapper, Processing processType, ICourse course, ICourse sourceCrourse) { super.postCopy(envMapper, processType, course, sourceCrourse); postImportCopy(envMapper); } @Override public void postImport(File importDirectory, ICourse course, CourseEnvironmentMapper envMapper, Processing processType) { super.postImport(importDirectory, course, envMapper, processType); postImportCopy(envMapper); } private void postImportCopy(CourseEnvironmentMapper envMapper) { ScoreCalculator calculator = getScoreCalculator(); boolean changed = false; if(StringHelper.containsNonWhitespace(calculator.getScoreExpression())) { String score = calculator.getScoreExpression(); String processedExpression = KeyAndNameConverter.convertExpressionNameToKey(score, envMapper); processedExpression = KeyAndNameConverter.convertExpressionKeyToKey(score, envMapper); if(!processedExpression.equals(score)) { calculator.setScoreExpression(processedExpression); changed = true; } } if(StringHelper.containsNonWhitespace(calculator.getPassedExpression())) { String passed = calculator.getPassedExpression(); String processedExpression = KeyAndNameConverter.convertExpressionNameToKey(passed, envMapper); processedExpression = KeyAndNameConverter.convertExpressionKeyToKey(passed, envMapper); if(!processedExpression.equals(passed)) { calculator.setScoreExpression(processedExpression); changed = true; } } if(changed) { setScoreCalculator(calculator); } } @Override public void postExport(CourseEnvironmentMapper envMapper, boolean backwardsCompatible) { super.postExport(envMapper, backwardsCompatible); //if backwards compatible, convert expression to use names if(backwardsCompatible) { ScoreCalculator calculator = getScoreCalculator(); boolean changed = false; if(StringHelper.containsNonWhitespace(calculator.getScoreExpression())) { String score = calculator.getScoreExpression(); String processedExpression = KeyAndNameConverter.convertExpressionKeyToName(score, envMapper); if(!processedExpression.equals(score)) { calculator.setScoreExpression(processedExpression); changed = true; } } if(StringHelper.containsNonWhitespace(calculator.getPassedExpression())) { String passed = calculator.getPassedExpression(); String processedExpression = KeyAndNameConverter.convertExpressionKeyToName(passed, envMapper); if(!processedExpression.equals(passed)) { calculator.setScoreExpression(processedExpression); changed = true; } } if(changed) { setScoreCalculator(calculator); } } } /** * @see org.olat.course.nodes.AbstractAccessableCourseNode#getConditionExpressions() */ @Override public List<ConditionExpression> getConditionExpressions() { List<ConditionExpression> retVal; List<ConditionExpression> parentsConditions = super.getConditionExpressions(); if (parentsConditions.size() > 0) { retVal = new ArrayList<ConditionExpression>(parentsConditions); } else { retVal = new ArrayList<ConditionExpression>(); } // init passedExpression and scoreExpression getScoreCalculator(); passedExpression.setExpertMode(true); String coS = passedExpression.getConditionExpression(); if (StringHelper.containsNonWhitespace(coS)) { // an active condition is defined ConditionExpression ce = new ConditionExpression(passedExpression.getConditionId()); ce.setExpressionString(passedExpression.getConditionExpression()); retVal.add(ce); } scoreExpression.setExpertMode(true); coS = scoreExpression.getConditionExpression(); if (StringHelper.containsNonWhitespace(coS)) { // an active condition is defined ConditionExpression ce = new ConditionExpression(scoreExpression.getConditionId()); ce.setExpressionString(scoreExpression.getConditionExpression()); retVal.add(ce); } failedExpression.setExpertMode(true); coS = failedExpression.getConditionExpression(); if (StringHelper.containsNonWhitespace(coS)) { // an active condition is defined ConditionExpression ce = new ConditionExpression(failedExpression.getConditionId()); ce.setExpressionString(failedExpression.getConditionExpression()); retVal.add(ce); } return retVal; } /** * @see org.olat.course.nodes.GenericCourseNode#getDefaultTitleOption() */ @Override public String getDisplayOption() { // if nothing other defined, view content only, when a structure node // contains an html-file. OLog logger = Tracing.createLoggerFor(this.getClass()); ModuleConfiguration config = getModuleConfiguration(); String thisConf = super.getDisplayOption(false); if (thisConf == null && config.get(STCourseNodeEditController.CONFIG_KEY_DISPLAY_TYPE).equals(STCourseNodeEditController.CONFIG_VALUE_DISPLAY_FILE)) { if (logger.isDebug()) { logger.debug("no displayOption set, use default (content)", thisConf); } return CourseNode.DISPLAY_OPTS_CONTENT; } if (logger.isDebug()) { logger.debug("there is a config set, use it: " + thisConf); } return super.getDisplayOption(); } }