From 749bcad8dfa62fc79110a7b71638031fe5fc46c4 Mon Sep 17 00:00:00 2001 From: uhensler <urs.hensler@frentix.com> Date: Fri, 25 Oct 2019 11:51:19 +0200 Subject: [PATCH] OO-4206: Scorm course elements can be fully assessed if passed --- .../manager/CourseAssessmentManagerImpl.java | 5 ++ .../ConditionNodeAccessProvider.java | 5 ++ .../learningpath/LearningPathConfigs.java | 2 + .../LearningPathNodeAccessProvider.java | 10 +++ .../model/ModuleLearningPathConfigs.java | 10 +++ .../model/UnsupportedLearningPathConfigs.java | 5 ++ .../ui/LearningPathNodeConfigController.java | 19 +++++ ...bbableLeaningPathNodeConfigController.java | 2 +- .../ui/_i18n/LocalStrings_de.properties | 2 + .../ui/_i18n/LocalStrings_en.properties | 2 + .../course/nodeaccess/NodeAccessProvider.java | 2 + .../course/nodeaccess/NodeAccessService.java | 11 ++- .../manager/NodeAccessServiceImpl.java | 12 ++- .../olat/course/nodes/ScormCourseNode.java | 76 ++++++++++++++----- .../nodes/scorm/ScormEditController.java | 2 +- .../scorm/ScormLearningPathNodeHandler.java | 1 + .../nodes/scorm/ScormRunController.java | 2 +- .../scorm/_i18n/LocalStrings_de.properties | 1 + .../scorm/_i18n/LocalStrings_en.properties | 1 + .../st/assessment/STLearningPathConfigs.java | 5 ++ .../LearningPathNodeAccessProviderTest.java | 42 ++++++++++ 21 files changed, 192 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java index 68e5b2c83ec..31c2fb4e6fd 100644 --- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java +++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java @@ -528,6 +528,11 @@ public class CourseAssessmentManagerImpl implements AssessmentManager { } assessmentEntry = assessmentService.updateAssessmentEntry(assessmentEntry); DBFactory.getInstance().commit();//commit before sending events + + if (Boolean.TRUE.equals(passed)) { + nodeAccessService.onPassed(courseNode, userCourseEnv , Role.auto); + } + //reevalute the tree ScoreAccounting scoreAccounting = userCourseEnv.getScoreAccounting(); scoreAccounting.evaluateAll(true); diff --git a/src/main/java/org/olat/course/condition/ConditionNodeAccessProvider.java b/src/main/java/org/olat/course/condition/ConditionNodeAccessProvider.java index 382432d5ae6..012aff75a2f 100644 --- a/src/main/java/org/olat/course/condition/ConditionNodeAccessProvider.java +++ b/src/main/java/org/olat/course/condition/ConditionNodeAccessProvider.java @@ -104,4 +104,9 @@ public class ConditionNodeAccessProvider implements NodeAccessProvider { // nothing to do } + @Override + public void onPassed(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Role by) { + // nothing to do + } + } diff --git a/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java index 504975bbf73..6332e0fcf1f 100644 --- a/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/LearningPathConfigs.java @@ -40,6 +40,8 @@ public interface LearningPathConfigs { public FullyAssessedResult isFullyAssessedOnConfirmation(); + public FullyAssessedResult isFullyAssessedOnPassed(); + public FullyAssessedResult isFullyAssessedOnCompletion(Double completion); public FullyAssessedResult isFullyAssessedOnStatus(AssessmentEntryStatus status); diff --git a/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java b/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java index 27adf9dfc61..489ba97ef4e 100644 --- a/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java +++ b/src/main/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProvider.java @@ -124,6 +124,16 @@ public class LearningPathNodeAccessProvider implements NodeAccessProvider { } } + @Override + public void onPassed(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Role by) { + FullyAssessedResult result = getConfigs(courseNode).isFullyAssessedOnPassed(); + boolean participant = userCourseEnv.isParticipant(); + if (participant && result.isFullyAssessed()) { + AssessmentEntryStatus status = getStatus(courseNode, userCourseEnv, result.isDone()); + courseAssessmentService.updateFullyAssessed(courseNode, userCourseEnv, Boolean.TRUE, status, by); + } + } + @Override public void onCompletionUpdate(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Double completion, AssessmentEntryStatus status, Role by) { diff --git a/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java index 14d7da4c6ba..0b1d769673a 100644 --- a/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/model/ModuleLearningPathConfigs.java @@ -26,6 +26,7 @@ import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.C import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.CONFIG_KEY_TRIGGER; import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.CONFIG_VALUE_TRIGGER_CONFIRMED; import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.CONFIG_VALUE_TRIGGER_NODE_VISITED; +import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.CONFIG_VALUE_TRIGGER_PASSED; import static org.olat.course.learningpath.ui.LearningPathNodeConfigController.CONFIG_VALUE_TRIGGER_STATUS_DONE; import org.olat.core.util.StringHelper; @@ -94,6 +95,15 @@ public class ModuleLearningPathConfigs implements LearningPathConfigs { return LearningPathConfigs.notFullyAssessed(); } + @Override + public FullyAssessedResult isFullyAssessedOnPassed() { + String doneTriggerName = getDoneTriggerName(); + if (CONFIG_VALUE_TRIGGER_PASSED.equals(doneTriggerName)) { + return LearningPathConfigs.fullyAssessed(true, true); + } + return LearningPathConfigs.notFullyAssessed(); + } + private String getDoneTriggerName() { return moduleConfiguration.getStringValue(CONFIG_KEY_TRIGGER, CONFIG_DEFAULT_TRIGGER); } diff --git a/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java b/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java index d5d4f6a80bb..0e99ad43658 100644 --- a/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java +++ b/src/main/java/org/olat/course/learningpath/model/UnsupportedLearningPathConfigs.java @@ -56,6 +56,11 @@ public class UnsupportedLearningPathConfigs implements LearningPathConfigs { return LearningPathConfigs.notFullyAssessed(); } + @Override + public FullyAssessedResult isFullyAssessedOnPassed() { + return LearningPathConfigs.notFullyAssessed(); + } + @Override public FullyAssessedResult isFullyAssessedOnCompletion(Double completion) { return LearningPathConfigs.notFullyAssessed(); diff --git a/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java b/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java index c23edb88740..94bf44c62f7 100644 --- a/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java +++ b/src/main/java/org/olat/course/learningpath/ui/LearningPathNodeConfigController.java @@ -58,6 +58,7 @@ public class LearningPathNodeConfigController extends FormBasicController { public static final String CONFIG_VALUE_TRIGGER_NODE_VISITED = "nodeVisited"; public static final String CONFIG_VALUE_TRIGGER_CONFIRMED = "confirmed"; public static final String CONFIG_VALUE_TRIGGER_STATUS_DONE = "statusDone"; + public static final String CONFIG_VALUE_TRIGGER_PASSED = "passed"; public static final String CONFIG_DEFAULT_TRIGGER = CONFIG_VALUE_TRIGGER_NONE; private TextElement durationEl; @@ -115,6 +116,9 @@ public class LearningPathNodeConfigController extends FormBasicController { if (ctrlConfig.isTriggerConfirmed()) { triggerKV.add(entry(CONFIG_VALUE_TRIGGER_CONFIRMED, translate("config.trigger.confirmed"))); } + if (ctrlConfig.isTriggerPassed()) { + triggerKV.add(entry(CONFIG_VALUE_TRIGGER_PASSED, translate("config.trigger.passed"))); + } TranslateableBoolean triggerStatusDone = ctrlConfig.getTriggerStatusDone(); if (triggerStatusDone.isTrue()) { triggerKV.add(entry(CONFIG_VALUE_TRIGGER_STATUS_DONE, @@ -211,6 +215,8 @@ public class LearningPathNodeConfigController extends FormBasicController { public boolean isTriggerConfirmed(); + public boolean isTriggerPassed(); + public TranslateableBoolean getTriggerStatusDone(); } @@ -223,6 +229,7 @@ public class LearningPathNodeConfigController extends FormBasicController { private boolean triggerNodeVisited; private boolean triggerConfirmed; + private boolean triggerPassed; private TranslateableBoolean triggerStatusDone; private ControllerConfigBuilder() { @@ -238,6 +245,11 @@ public class LearningPathNodeConfigController extends FormBasicController { return this; } + public ControllerConfigBuilder enablePassed() { + triggerPassed = true; + return this; + } + public ControllerConfigBuilder enableStatusDone() { triggerStatusDone = TranslateableBoolean.untranslatedTrue(); return this; @@ -256,11 +268,13 @@ public class LearningPathNodeConfigController extends FormBasicController { public final boolean triggerNodeVisited; public final boolean triggerConfirmed; + public final boolean triggerPassed; public final TranslateableBoolean triggerStatusDone; public ControllerConfigImpl(ControllerConfigBuilder builder) { this.triggerNodeVisited = builder.triggerNodeVisited; this.triggerConfirmed = builder.triggerConfirmed; + this.triggerPassed = builder.triggerPassed; this.triggerStatusDone = falseIfNull(builder.triggerStatusDone); } @@ -280,6 +294,11 @@ public class LearningPathNodeConfigController extends FormBasicController { return triggerConfirmed; } + @Override + public boolean isTriggerPassed() { + return triggerPassed; + } + @Override public TranslateableBoolean getTriggerStatusDone() { return triggerStatusDone; diff --git a/src/main/java/org/olat/course/learningpath/ui/TabbableLeaningPathNodeConfigController.java b/src/main/java/org/olat/course/learningpath/ui/TabbableLeaningPathNodeConfigController.java index f8b3d2a1eb8..e0da3e1cb8f 100644 --- a/src/main/java/org/olat/course/learningpath/ui/TabbableLeaningPathNodeConfigController.java +++ b/src/main/java/org/olat/course/learningpath/ui/TabbableLeaningPathNodeConfigController.java @@ -36,7 +36,7 @@ import org.olat.course.editor.NodeEditController; */ public class TabbableLeaningPathNodeConfigController extends ActivateableTabbableDefaultController { - private static final String PANE_TAB_LEARNIN_PATH = "pane.tab.learning.path"; + public static final String PANE_TAB_LEARNIN_PATH = "pane.tab.learning.path"; private final static String[] paneKeys = { PANE_TAB_LEARNIN_PATH }; private final Controller configCtrl; diff --git a/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_de.properties index b90d2531300..da1297e8448 100644 --- a/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_de.properties @@ -7,8 +7,10 @@ config.obligation.optional=Freiwillig config.trigger=Abschluss config.trigger.confirmed=Best\u00E4tigt config.trigger.none=Ohne Abschluss +config.trigger.passed=Bestanden config.trigger.status.done=Durchf\u00FChrung erledigt config.trigger.visited=Kursbaustein ge\u00F6ffnet +error.fully.assessed.passed=Der Abschluss des Kurselementes kann nicht auf "erledigt" gesetzt sein, wenn im Kursbaustein nicht erledigt werden kann. no.configurations=In diesen Kursbaustein stehen keine Konfigurationen zum Lernpfad zu Verf\u00FCgung. pane.tab.learning.path=Lernpfad reset.all.status=ALLE Stati zur\u00FCcksetzen diff --git a/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_en.properties index 3dc8c921706..3db4b3b5342 100644 --- a/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/learningpath/ui/_i18n/LocalStrings_en.properties @@ -7,8 +7,10 @@ config.obligation.optional=Optional config.trigger=Completion config.trigger.confirmed=Confirmed config.trigger.none=No completion +config.trigger.passed=Passed config.trigger.status.done=Run done config.trigger.visited=Course element visited +error.fully.assessed.passed=The completion of the course element can't be set to "passed", if the course element can't be passed. no.configuration=No configurations for the learning path are available in this course element. pane.tab.learning.path=Learning path reset.all.status=Reset ALL status diff --git a/src/main/java/org/olat/course/nodeaccess/NodeAccessProvider.java b/src/main/java/org/olat/course/nodeaccess/NodeAccessProvider.java index 8d827c56458..e919930b6b3 100644 --- a/src/main/java/org/olat/course/nodeaccess/NodeAccessProvider.java +++ b/src/main/java/org/olat/course/nodeaccess/NodeAccessProvider.java @@ -50,6 +50,8 @@ public interface NodeAccessProvider extends NodeAccessProviderIdentifier { public void onAssessmentConfirmed(CourseNode courseNode, UserCourseEnvironment userCourseEnv); + public void onPassed(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Role by); + public void onCompletionUpdate(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Double completion, AssessmentEntryStatus status, Role by); diff --git a/src/main/java/org/olat/course/nodeaccess/NodeAccessService.java b/src/main/java/org/olat/course/nodeaccess/NodeAccessService.java index 97d0d563e24..60fc86180c3 100644 --- a/src/main/java/org/olat/course/nodeaccess/NodeAccessService.java +++ b/src/main/java/org/olat/course/nodeaccess/NodeAccessService.java @@ -69,7 +69,7 @@ public interface NodeAccessService { public CourseTreeModelBuilder getCourseTreeModelBuilder(UserCourseEnvironment userCourseEnv); /** - * Returns if a user can confirm the execution of a assessemnt + * Returns if a user can confirm the execution of an assessment. * * @param courseNode * @param userCourseEnv @@ -85,6 +85,15 @@ public interface NodeAccessService { */ public void onAssessmentConfirmed(CourseNode courseNode, UserCourseEnvironment userCourseEnv); + /** + * Hook after the user has passed an assessment. + * + * @param courseNode + * @param userCourseEnv + * @param by + */ + public void onPassed(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Role by); + /** * Hook after the completion and the run status is updated. * diff --git a/src/main/java/org/olat/course/nodeaccess/manager/NodeAccessServiceImpl.java b/src/main/java/org/olat/course/nodeaccess/manager/NodeAccessServiceImpl.java index 5ee6111040d..20840c09295 100644 --- a/src/main/java/org/olat/course/nodeaccess/manager/NodeAccessServiceImpl.java +++ b/src/main/java/org/olat/course/nodeaccess/manager/NodeAccessServiceImpl.java @@ -103,6 +103,12 @@ public class NodeAccessServiceImpl implements NodeAccessService, NodeVisitedList getNodeAccessProvider(type).onAssessmentConfirmed(courseNode, userCourseEnv); } + @Override + public void onPassed(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Role by) { + NodeAccessType type = NodeAccessType.of(userCourseEnv); + getNodeAccessProvider(type).onPassed(courseNode, userCourseEnv, by); + } + @Override public void onCompletionUpdate(CourseNode courseNode, UserCourseEnvironment userCourseEnv, Double completion, AssessmentEntryStatus status, Role by) { @@ -111,9 +117,9 @@ public class NodeAccessServiceImpl implements NodeAccessService, NodeVisitedList } @Override - public boolean onNodeVisited(CourseNode courseNode, UserCourseEnvironment userCourseEnvironment) { - NodeAccessType type = NodeAccessType.of(userCourseEnvironment); - return getNodeAccessProvider(type).onNodeVisited(courseNode, userCourseEnvironment); + public boolean onNodeVisited(CourseNode courseNode, UserCourseEnvironment userCourseEnv) { + NodeAccessType type = NodeAccessType.of(userCourseEnv); + return getNodeAccessProvider(type).onNodeVisited(courseNode, userCourseEnv); } } diff --git a/src/main/java/org/olat/course/nodes/ScormCourseNode.java b/src/main/java/org/olat/course/nodes/ScormCourseNode.java index 9cbc5029ca0..7f47c159ff1 100644 --- a/src/main/java/org/olat/course/nodes/ScormCourseNode.java +++ b/src/main/java/org/olat/course/nodes/ScormCourseNode.java @@ -27,6 +27,7 @@ package org.olat.course.nodes; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; @@ -55,8 +56,11 @@ import org.olat.course.editor.ConditionAccessEditConfig; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; +import org.olat.course.learningpath.ui.TabbableLeaningPathNodeConfigController; import org.olat.course.nodes.cp.CPEditController; +import org.olat.course.nodes.scorm.ScormAssessmentConfig; import org.olat.course.nodes.scorm.ScormEditController; +import org.olat.course.nodes.scorm.ScormLearningPathNodeHandler; import org.olat.course.nodes.scorm.ScormRunController; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.navigation.NodeRunConstructionResult; @@ -81,6 +85,9 @@ public class ScormCourseNode extends AbstractAccessableCourseNode { private static final Logger log = Tracing.createLoggerFor(ScormCourseNode.class); private static final long serialVersionUID = 2970594874787761801L; + @SuppressWarnings("deprecation") + private static final String TRANSLATOR_PACKAGE = Util.getPackageName(ScormEditController.class); + public static final String TYPE = "scorm"; private static final int CURRENT_CONFIG_VERSION = 5; @@ -125,33 +132,66 @@ public class ScormCourseNode extends AbstractAccessableCourseNode { @Override public StatusDescription isConfigValid() { - if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } - - StatusDescription sd = StatusDescription.NOERROR; - boolean isValid = ScormEditController.isModuleConfigValid(getModuleConfiguration()); - if (!isValid) { - String shortKey = "error.noreference.short"; - String longKey = "error.noreference.long"; - String[] params = new String[] { this.getShortTitle() }; - String translPackage = Util.getPackageName(ScormEditController.class); - sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, translPackage); - sd.setDescriptionForUnit(getIdent()); - // set which pane is affected by error - sd.setActivateableViewIdentifier(ScormEditController.PANE_TAB_CPCONFIG); + if (oneClickStatusCache != null && oneClickStatusCache.length > 0) { + return oneClickStatusCache[0]; } - return sd; + + List<StatusDescription> statusDescs = validateInternalConfiguration(); + if(statusDescs.isEmpty()) { + statusDescs.add(StatusDescription.NOERROR); + } + oneClickStatusCache = StatusDescriptionHelper.sort(statusDescs); + return oneClickStatusCache[0]; } @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(ScormEditController.class); - List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); + + List<StatusDescription> sds = isConfigValidWithTranslator(cev, TRANSLATOR_PACKAGE, getConditionExpressions()); + if(oneClickStatusCache != null && oneClickStatusCache.length > 0) { + //isConfigValidWithTranslator add first + sds.remove(oneClickStatusCache[0]); + } + sds.addAll(validateInternalConfiguration()); oneClickStatusCache = StatusDescriptionHelper.sort(sds); return oneClickStatusCache; } + + private List<StatusDescription> validateInternalConfiguration() { + List<StatusDescription> sdList = new ArrayList<>(2); + + boolean hasScormReference = ScormEditController.hasScormReference(getModuleConfiguration()); + if (!hasScormReference) { + addStatusErrorDescription("error.noreference.short", "error.noreference.long", ScormEditController.PANE_TAB_CPCONFIG, sdList); + } + + if (isFullyAssessedPassedConfigError()) { + addStatusErrorDescription("error.fully.assessed.passed", "error.fully.assessed.passed", + TabbableLeaningPathNodeConfigController.PANE_TAB_LEARNIN_PATH, sdList); + } + + return sdList; + } + + private boolean isFullyAssessedPassedConfigError() { + boolean hasPassed = new ScormAssessmentConfig(getModuleConfiguration()).hasPassed(); + boolean isPassedTrigger = CoreSpringFactory.getImpl(ScormLearningPathNodeHandler.class) + .getConfigs(this) + .isFullyAssessedOnPassed() + .isFullyAssessed(); + return isPassedTrigger && !hasPassed; + } + + private void addStatusErrorDescription(String shortDescKey, String longDescKey, String pane, + List<StatusDescription> status) { + String[] params = new String[] { getShortTitle() }; + StatusDescription sd = new StatusDescription(StatusDescription.ERROR, shortDescKey, longDescKey, params, + TRANSLATOR_PACKAGE); + sd.setDescriptionForUnit(getIdent()); + sd.setActivateableViewIdentifier(pane); + status.add(sd); + } @Override public RepositoryEntry getReferencedRepositoryEntry() { diff --git a/src/main/java/org/olat/course/nodes/scorm/ScormEditController.java b/src/main/java/org/olat/course/nodes/scorm/ScormEditController.java index 661a5e522c0..36375989b9f 100644 --- a/src/main/java/org/olat/course/nodes/scorm/ScormEditController.java +++ b/src/main/java/org/olat/course/nodes/scorm/ScormEditController.java @@ -357,7 +357,7 @@ public class ScormEditController extends ActivateableTabbableDefaultController i moduleConfiguration.set(CONFIG_KEY_REPOSITORY_SOFTKEY, re.getSoftkey()); } - public static boolean isModuleConfigValid(ModuleConfiguration moduleConfiguration) { + public static boolean hasScormReference(ModuleConfiguration moduleConfiguration) { return (moduleConfiguration.get(CONFIG_KEY_REPOSITORY_SOFTKEY) != null); } diff --git a/src/main/java/org/olat/course/nodes/scorm/ScormLearningPathNodeHandler.java b/src/main/java/org/olat/course/nodes/scorm/ScormLearningPathNodeHandler.java index 9d6cca8035c..dba10904fa8 100644 --- a/src/main/java/org/olat/course/nodes/scorm/ScormLearningPathNodeHandler.java +++ b/src/main/java/org/olat/course/nodes/scorm/ScormLearningPathNodeHandler.java @@ -62,6 +62,7 @@ public class ScormLearningPathNodeHandler implements LearningPathNodeHandler { LearningPathControllerConfig ctrlConfig = LearningPathNodeConfigController.builder() .enableNodeVisited() .enableConfirmed() + .enablePassed() .build(); return new LearningPathNodeConfigController(ureq, wControl, courseEntry, courseNode.getModuleConfiguration(), ctrlConfig); } diff --git a/src/main/java/org/olat/course/nodes/scorm/ScormRunController.java b/src/main/java/org/olat/course/nodes/scorm/ScormRunController.java index 4d3d8083e3e..798d4c0f39b 100644 --- a/src/main/java/org/olat/course/nodes/scorm/ScormRunController.java +++ b/src/main/java/org/olat/course/nodes/scorm/ScormRunController.java @@ -124,7 +124,7 @@ public class ScormRunController extends BasicController implements ScormAPICallb ScormCourseNode scormNode, boolean isPreview) { super(ureq, wControl, Util.createPackageTranslator(CourseNode.class, ureq.getLocale())); // assertion to make sure the moduleconfig is valid - if (!ScormEditController.isModuleConfigValid(config)) + if (!ScormEditController.hasScormReference(config)) throw new AssertException("scorm run controller had an invalid module config:" + config.toString()); this.isPreview = isPreview || userCourseEnv.isCourseReadOnly() || !userCourseEnv.isParticipant(); this.userCourseEnv = userCourseEnv; diff --git a/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_de.properties index e8a175025db..6848b586b14 100644 --- a/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_de.properties @@ -37,6 +37,7 @@ cutvalue.label=Notwendige Punktzahl f\u00FCr 'bestanden' cutvalue.validation=Geben Sie eine Ganzzahl ein error.cprepoentrymissing=Der SCORM-Lerninhalt, den Sie anzeigen m\u00F6chten, wurde in der Zwischenzeit in der Ablage der Lernressourcen gel\u00F6scht error.cprepoentrymissing.user=Der SCORM-Lerninhalt den Sie anzeigen m\u00F6chten existiert nicht mehr. Bitte kontaktieren Sie ihren Kursbetreuer. +error.fully.assessed.passed=$org.olat.course.learningpath.ui:\error.fully.assessed.passed error.launch=SCORM-Lerninhalt konnte nicht gestartet werden. error.noreference.long=F\u00FCr "{0}" muss in der Konfiguration ein SCORM-Lerninhalt im Tab "Lerninhalt" ausgew\u00E4hlt werden. error.noreference.short=Es ist noch kein SCORM-Lerninhalt f\u00FCr "{0}" ausgew\u00E4hlt. diff --git a/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_en.properties index 8f0281ac344..3c637938bf7 100644 --- a/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/scorm/_i18n/LocalStrings_en.properties @@ -43,6 +43,7 @@ cutvalue.label=Score needed to pass cutvalue.validation=Please enter an integer error.cprepoentrymissing=This SCORM learning content was deleted in the meantime within the storage folder of learning resources. error.cprepoentrymissing.user=The SCORM learning content you were trying to access doesn't exist anymore. Please contact your course coach. +error.fully.assessed.passed=$org.olat.course.learningpath.ui:\error.fully.assessed.passed error.launch=Unable to start SCORM learning content. error.noreference.long=For "{0}" you have to select a SCORM learning content in the tab "Learning content" within the configuration section. error.noreference.short=No SCORM learning content selected for "{0}" yet. diff --git a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java index 37e3c563653..9e3323b9050 100644 --- a/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java +++ b/src/main/java/org/olat/course/nodes/st/assessment/STLearningPathConfigs.java @@ -56,6 +56,11 @@ public class STLearningPathConfigs implements LearningPathConfigs { return LearningPathConfigs.notFullyAssessed(); } + @Override + public FullyAssessedResult isFullyAssessedOnPassed() { + return LearningPathConfigs.notFullyAssessed(); + } + @Override public FullyAssessedResult isFullyAssessedOnStatus(AssessmentEntryStatus status) { return LearningPathConfigs.notFullyAssessed(); diff --git a/src/test/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProviderTest.java b/src/test/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProviderTest.java index d84204daa78..913e25ff750 100644 --- a/src/test/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProviderTest.java +++ b/src/test/java/org/olat/course/learningpath/manager/LearningPathNodeAccessProviderTest.java @@ -197,6 +197,48 @@ public class LearningPathNodeAccessProviderTest { AssessmentEntryStatus.done, Role.user); } + @Test + public void shouldSetAssessmentAsDoneIfPassed() { + LearningPathConfigs configs = mock(LearningPathConfigs.class); + when(configs.isFullyAssessedOnPassed()).thenReturn(fullyAssessed(true, true)); + LearningPathNodeHandler handler = mock(LearningPathNodeHandler.class); + when(handler.getConfigs(courseNodeMock)).thenReturn(configs); + when(registry.getLearningPathNodeHandler(courseNodeMock)).thenReturn(handler); + + sut.onPassed(courseNodeMock, participantCourseEnv, Role.user); + + verify(courseAssessmentService).updateFullyAssessed(courseNodeMock, participantCourseEnv, Boolean.TRUE, + AssessmentEntryStatus.done, Role.user); + } + + @Test + public void shouldSetAssessmentAsDoneIfPassedNotEnabled() { + LearningPathConfigs configs = mock(LearningPathConfigs.class); + when(configs.isFullyAssessedOnPassed()).thenReturn(notFullyAssessed()); + LearningPathNodeHandler handler = mock(LearningPathNodeHandler.class); + when(handler.getConfigs(courseNodeMock)).thenReturn(configs); + when(registry.getLearningPathNodeHandler(courseNodeMock)).thenReturn(handler); + + sut.onPassed(courseNodeMock, participantCourseEnv, Role.user); + + verify(courseAssessmentService, never()).updateFullyAssessed(courseNodeMock, participantCourseEnv, Boolean.TRUE, + AssessmentEntryStatus.done, Role.user); + } + + @Test + public void shouldNotChangeAssessmentIfNotAParticipantPassed() { + LearningPathConfigs configs = mock(LearningPathConfigs.class); + when(configs.isFullyAssessedOnPassed()).thenReturn(fullyAssessed(true, true)); + LearningPathNodeHandler handler = mock(LearningPathNodeHandler.class); + when(handler.getConfigs(courseNodeMock)).thenReturn(configs); + when(registry.getLearningPathNodeHandler(courseNodeMock)).thenReturn(handler); + + sut.onPassed(courseNodeMock, coachCourseEnv, Role.user); + + verify(courseAssessmentService, never()).updateFullyAssessed(courseNodeMock, participantCourseEnv, Boolean.TRUE, + AssessmentEntryStatus.done, Role.user); + } + @Test public void shouldSetAssessmentAsDoneIfRunStatusIsReached() { Role role = Role.auto; -- GitLab