From 2c9fcd00d38d6fe3f2e4d73b40788f31cfe9f277 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Mon, 29 Jul 2019 15:05:44 +0200 Subject: [PATCH] OO-4161: additional option to relax access if results are visible --- .../org/olat/course/condition/Condition.java | 31 +++++++++++++-- .../ConditionConfigEasyController.java | 32 +++++++++++++-- .../condition/_content/easycondedit.html | 3 ++ .../_i18n/LocalStrings_de.properties | 1 + .../_i18n/LocalStrings_en.properties | 1 + .../interpreter/IsAssessmentModeFunction.java | 39 +++++++++++++------ .../course/run/scoring/ScoreAccounting.java | 30 +++++++++++--- 7 files changed, 114 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/olat/course/condition/Condition.java b/src/main/java/org/olat/course/condition/Condition.java index 8214f992d64..c3c5624c6a3 100644 --- a/src/main/java/org/olat/course/condition/Condition.java +++ b/src/main/java/org/olat/course/condition/Condition.java @@ -36,7 +36,7 @@ import org.olat.core.util.StringHelper; * @author Mike Stock Comment: */ public class Condition implements Serializable, Cloneable { - transient private String conditionId = null; + private transient String conditionId = null; private String condition = null; private boolean expertMode = false; @@ -60,6 +60,8 @@ public class Condition implements Serializable, Cloneable { // true: only in assessment mode private boolean assessmentMode; + private boolean assessmentModeViewResults; + private String easyModeAssessmentModeNodeId; // This is the MapList in which the extended easy mode conditions are stored private List<ExtendedCondition> attributeConditions = null; @@ -233,7 +235,7 @@ public class Condition implements Serializable, Cloneable { String[] longStrArr = ids.split(","); List<Long> keys = new ArrayList<>(longStrArr.length); for(String longStr:longStrArr) { - keys.add(new Long(longStr.trim())); + keys.add(Long.valueOf(longStr.trim())); } return keys; } @@ -262,6 +264,22 @@ public class Condition implements Serializable, Cloneable { this.assessmentMode = assessmentMode; } + public boolean isAssessmentModeViewResults() { + return assessmentModeViewResults; + } + + public void setAssessmentModeViewResults(boolean viewResults) { + this.assessmentModeViewResults = viewResults; + } + + public String getEasyModeAssessmentModeNodeId() { + return easyModeAssessmentModeNodeId; + } + + public void setEasyModeAssessmentModeNodeId(String nodeId) { + this.easyModeAssessmentModeNodeId = nodeId; + } + /** * @return true */ @@ -311,7 +329,7 @@ public class Condition implements Serializable, Cloneable { */ public String getConditionFromEasyModeConfiguration() { boolean needsAmpersand = false; - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(512); sb.append("( "); // BEGIN all enclosing bracket @@ -391,7 +409,12 @@ public class Condition implements Serializable, Cloneable { } if(isAssessmentMode()) { if (needsAmpersand) sb.append(" & "); - sb.append(" isAssessmentMode(0)"); + + if(isAssessmentModeViewResults()) { + sb.append(" isAssessmentMode(\"").append(getEasyModeAssessmentModeNodeId()).append("\",true)"); + } else { + sb.append(" isAssessmentMode(0)"); + } needsAmpersand = true; } if (isEasyModeCoachesAndAdmins()) { diff --git a/src/main/java/org/olat/course/condition/ConditionConfigEasyController.java b/src/main/java/org/olat/course/condition/ConditionConfigEasyController.java index d0a6cd60938..af42dbf1e5d 100644 --- a/src/main/java/org/olat/course/condition/ConditionConfigEasyController.java +++ b/src/main/java/org/olat/course/condition/ConditionConfigEasyController.java @@ -90,6 +90,7 @@ public class ConditionConfigEasyController extends FormBasicController implement private List<CourseNode> nodeIdentList; private MultipleSelectionElement coachExclusive; private MultipleSelectionElement assessmentMode; + private MultipleSelectionElement assessmentModeResultVisible; private JSDateChooser fromDate; private JSDateChooser toDate; private FormItemContainer dateSubContainer; @@ -189,7 +190,8 @@ public class ConditionConfigEasyController extends FormBasicController implement @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { // or blocked for learner - coachExclusive = uifactory.addCheckboxesHorizontal("coachExclusive", null, formLayout, new String[] { "ison" }, new String[] { translate("form.easy.coachExclusive") }); + coachExclusive = uifactory.addCheckboxesHorizontal("coachExclusive", null, formLayout, + new String[] { "ison" }, new String[] { translate("form.easy.coachExclusive") }); coachExclusive.setElementCssClass("o_sel_condition_coach_exclusive"); coachExclusive.addActionListener(FormEvent.ONCLICK); if(validatedCondition.isEasyModeCoachesAndAdmins()) { @@ -206,14 +208,23 @@ public class ConditionConfigEasyController extends FormBasicController implement } flc.contextPut("shibbolethEnabled", Boolean.valueOf(enableShibbolethEasyConfig)); - assessmentMode = uifactory.addCheckboxesHorizontal("assessmentMode", null, formLayout, new String[] { "ison" }, new String[] { translate("form.easy.assessmentMode") }); + assessmentMode = uifactory.addCheckboxesHorizontal("assessmentMode", null, formLayout, + new String[] { "ison" }, new String[] { translate("form.easy.assessmentMode") }); if(validatedCondition.isAssessmentMode()) { assessmentMode.select("ison", true); } assessmentMode.addActionListener(FormEvent.ONCLICK); assessmentMode.setVisible(assessmentModule.isAssessmentModeEnabled()); - applyRulesForCoach = uifactory.addCheckboxesHorizontal("applyRulesForCoach", null, formLayout, new String[] { "ison" }, new String[] { translate("form.easy.applyRulesForCoach") }); + assessmentModeResultVisible = uifactory.addCheckboxesHorizontal("assessmentModeResultVisible", null, formLayout, + new String[] { "ison" }, new String[] { translate("form.easy.assessmentMode.visible") }); + if(validatedCondition.isAssessmentModeViewResults()) { + assessmentModeResultVisible.select("ison", true); + } + assessmentModeResultVisible.setVisible(assessmentModule.isAssessmentModeEnabled() && assessmentMode.isAtLeastSelected(1)); + + applyRulesForCoach = uifactory.addCheckboxesHorizontal("applyRulesForCoach", null, formLayout, + new String[] { "ison" }, new String[] { translate("form.easy.applyRulesForCoach") }); applyRulesForCoach.setVisible(isDateGroupAssessmentOrAttributeSwitchOnOrAssessmentModeOn()); // note that in the condition this rule is saved with the opposite meaning: // true when coach and admins always have access, false when rule should apply also to them @@ -452,6 +463,8 @@ public class ConditionConfigEasyController extends FormBasicController implement validatedCondition.setAttributeConditions(null); validatedCondition.setAttributeConditionsConnectorIsAND(null); validatedCondition.setAssessmentMode(false); + validatedCondition.setAssessmentModeViewResults(false); + validatedCondition.setEasyModeAssessmentModeNodeId(null); validatedCondition.setEasyModeGroupAccessIdList(null); validatedCondition.setEasyModeGroupAreaAccessIdList(null); } else { @@ -540,6 +553,15 @@ public class ConditionConfigEasyController extends FormBasicController implement // assessment mode validatedCondition.setAssessmentMode(assessmentMode.isAtLeastSelected(1)); + + if(assessmentMode.isAtLeastSelected(1) && assessmentModeResultVisible.isAtLeastSelected(1)) { + validatedCondition.setAssessmentModeViewResults(true); + String currentNodeId = courseEditorEnv.getCurrentCourseNodeId(); + validatedCondition.setEasyModeAssessmentModeNodeId(currentNodeId); + } else { + validatedCondition.setAssessmentModeViewResults(false); + validatedCondition.setEasyModeAssessmentModeNodeId(null); + } } // calculate expression from easy mode form @@ -883,6 +905,9 @@ public class ConditionConfigEasyController extends FormBasicController implement //assessment switch only enabled if nodes to be selected assessmentSwitch.setEnabled(!blockedForLearner && (!nodeIdentList.isEmpty() || isSelectedNodeDeleted())); assessmentMode.setEnabled(!blockedForLearner); + + assessmentModeResultVisible.setVisible(assessmentMode.isAtLeastSelected(1)); + assessmentModeResultVisible.setEnabled(assessmentMode.isEnabled()); //default is a checked disabled apply rules for coach if (attributeSwitch != null) { @@ -946,6 +971,7 @@ public class ConditionConfigEasyController extends FormBasicController implement easyGroupList.setValue(""); easyGroupList.setUserObject(new ArrayList<Long>()); assessmentMode.uncheckAll(); + assessmentModeResultVisible.uncheckAll(); // disable the shibboleth attributes switch and reset the row subform if (attributeSwitch != null) { diff --git a/src/main/java/org/olat/course/condition/_content/easycondedit.html b/src/main/java/org/olat/course/condition/_content/easycondedit.html index 50737c88cb4..bdb9453db46 100644 --- a/src/main/java/org/olat/course/condition/_content/easycondedit.html +++ b/src/main/java/org/olat/course/condition/_content/easycondedit.html @@ -44,6 +44,9 @@ #end $r.render("assessmentMode") + #if($r.visible("assessmentModeResultVisible")) + <div class="col-sm-offset-1">$r.render("assessmentModeResultVisible")</div> + #end $r.render("applyRulesForCoach") <div class="o_button_group"> $r.render("subm") diff --git a/src/main/java/org/olat/course/condition/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/condition/_i18n/LocalStrings_de.properties index aff4058634d..c8d9b6f8902 100644 --- a/src/main/java/org/olat/course/condition/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/condition/_i18n/LocalStrings_de.properties @@ -71,6 +71,7 @@ form.easy.extCondConn.or=ODER form.easy.group=Nur f\u00FCr die Lerngruppen form.easy.groupSwitch=Gruppenabh\u00E4ngig form.easy.assessmentMode=Nur in Pr\u00FCfungsmodus +form.easy.assessmentMode.visible=Sichtbar wenn Resultate ver\u00F6ffentlicht werden form.easy.nodePassed=Kursbaustein form.easy.nodePassed.deletedNode=Gel\u00F6schter Kursbaustein, bitte \u00E4ndern form.easy.nodePassed.noNodes=Nicht m\u00F6glich - keine bewerteten Kursbausteine diff --git a/src/main/java/org/olat/course/condition/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/condition/_i18n/LocalStrings_en.properties index d05d5c178ba..89cdef79bbc 100644 --- a/src/main/java/org/olat/course/condition/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/condition/_i18n/LocalStrings_en.properties @@ -71,6 +71,7 @@ form.easy.extCondConn.or=OR form.easy.group=Only for learning groups form.easy.groupSwitch=Depending on group form.easy.assessmentMode=Only in assessment mode +form.easy.assessmentMode.visible=Visible if the results are visible form.easy.nodePassed=Course element form.easy.nodePassed.deletedNode=Deleted course element, please modify. form.easy.nodePassed.noNodes=Not possible, no assessed course element diff --git a/src/main/java/org/olat/course/condition/interpreter/IsAssessmentModeFunction.java b/src/main/java/org/olat/course/condition/interpreter/IsAssessmentModeFunction.java index 2f36be97969..2556fa0a4cf 100644 --- a/src/main/java/org/olat/course/condition/interpreter/IsAssessmentModeFunction.java +++ b/src/main/java/org/olat/course/condition/interpreter/IsAssessmentModeFunction.java @@ -47,9 +47,6 @@ public class IsAssessmentModeFunction extends AbstractFunction { super(userCourseEnv); } - /** - * @see com.neemsoft.jmep.FunctionCB#call(java.lang.Object[]) - */ @Override public Object call(Object[] inStack) { /* @@ -57,6 +54,19 @@ public class IsAssessmentModeFunction extends AbstractFunction { */ CourseEditorEnv cev = getUserCourseEnv().getCourseEditorEnv(); if (cev != null) { + if(inStack != null && inStack.length == 2) { + if (!(inStack[0] instanceof String)) { + return handleException(new ArgumentParseException(ArgumentParseException.WRONG_ARGUMENT_FORMAT, name, "", + "error.argtype.coursnodeidexpeted", "solution.example.node.infunction")); + } + + String nodeId = (String) inStack[0]; + if (!cev.existsNode(nodeId)) { + return handleException( new ArgumentParseException(ArgumentParseException.REFERENCE_NOT_FOUND, name, nodeId, + "error.notfound.coursenodeid", "solution.copypastenodeid")); + } + } + // return a valid value to continue with condition evaluation test return defaultValue(); } @@ -70,19 +80,26 @@ public class IsAssessmentModeFunction extends AbstractFunction { return ConditionInterpreter.INT_FALSE; } OLATResourceable lockedResource = chiefController.getLockResource(); - if(lockedResource == null) { - return ConditionInterpreter.INT_FALSE; + + boolean open = false; + if(inStack != null && inStack.length == 2) { + String nodeId = (String) inStack[0]; + open = isAssessmentModeActive(lockedResource) || + getUserCourseEnv().getScoreAccounting().evalUserVisibleOfCourseNode(nodeId); + } else { + open = isAssessmentModeActive(lockedResource); } - + return open ? ConditionInterpreter.INT_TRUE: ConditionInterpreter.INT_FALSE; + } + + private boolean isAssessmentModeActive(OLATResourceable lockedResource) { Long resourceableId = getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(); - if(lockedResource.getResourceableId().equals(resourceableId)) { + if(lockedResource != null && lockedResource.getResourceableId().equals(resourceableId)) { RepositoryEntry entry = getUserCourseEnv().getCourseEnvironment().getCourseGroupManager().getCourseEntry(); AssessmentModeManager assessmentModeMgr = CoreSpringFactory.getImpl(AssessmentModeManager.class); - boolean inAssessment = assessmentModeMgr.isInAssessmentMode(entry, new Date()); - return inAssessment ? ConditionInterpreter.INT_TRUE: ConditionInterpreter.INT_FALSE; - } else { - return ConditionInterpreter.INT_FALSE; + return assessmentModeMgr.isInAssessmentMode(entry, new Date()); } + return false; } @Override diff --git a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java index d7bd69b7a67..e30a6d54c7b 100644 --- a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java +++ b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java @@ -217,7 +217,7 @@ public class ScoreAccounting { AssessmentEntryStatus assessmentStatus = AssessmentEntryStatus.inProgress; ConditionInterpreter ci = userCourseEnvironment.getConditionInterpreter(); if (cNode.hasScoreConfigured() && scoreExpressionStr != null) { - score = new Float(ci.evaluateCalculation(scoreExpressionStr)); + score = Float.valueOf(ci.evaluateCalculation(scoreExpressionStr)); } if (cNode.hasPassedConfigured() && passedExpressionStr != null) { boolean hasPassed = ci.evaluateCondition(passedExpressionStr); @@ -403,15 +403,15 @@ public class ScoreAccounting { if(se.getUserVisible() == null || se.getUserVisible().booleanValue()) { score = se.getScore(); } else { - score = new Float(0.0f); + score = Float.valueOf(0.0f); } } if (score == null) { // a child has no score yet - score = new Float(0.0f); // default to 0.0, so that the condition can be evaluated (zero points makes also the most sense for "no results yet", if to be expressed in a number) + score = Float.valueOf(0.0f); // default to 0.0, so that the condition can be evaluated (zero points makes also the most sense for "no results yet", if to be expressed in a number) } } else { error = true; - score = new Float(0.0f); + score = Float.valueOf(0.0f); } return score; @@ -472,7 +472,7 @@ public class ScoreAccounting { AssessableCourseNode acn = (AssessableCourseNode) foundNode; ScoreEvaluation se = evalCourseNode(acn); if (se == null) { // the node could not provide any sensible information on scoring. e.g. a STNode with no calculating rules - log.error("could not evaluate node '" + acn.getShortTitle() + "' (" + acn.getClass().getName() + "," + childId + ")"); + log.error("could not evaluate node '{}' ({},{})", acn.getShortTitle(), acn.getClass().getName(), childId); return Boolean.FALSE; } // check if the results are visible @@ -485,6 +485,26 @@ public class ScoreAccounting { } return passed; } + + public boolean evalUserVisibleOfCourseNode(String childId) { + CourseNode foundNode = findChildByID(childId); + if (foundNode == null) { + error = true; + return Boolean.FALSE; + } + if (!(foundNode instanceof AssessableCourseNode)) { + error = true; + return Boolean.FALSE; + } + AssessableCourseNode acn = (AssessableCourseNode) foundNode; + ScoreEvaluation se = evalCourseNode(acn); + if (se == null) { // the node could not provide any sensible information on scoring. e.g. a STNode with no calculating rules + log.error("could not evaluate node '{}' ({},{})", acn.getShortTitle(), acn.getClass().getName(), childId); + return Boolean.FALSE; + } + // check if the results are visible + return se.getUserVisible() != null && se.getUserVisible().booleanValue(); + } private CourseNode findChildByID(String id) { return userCourseEnvironment.getCourseEnvironment().getRunStructure().getNode(id); -- GitLab