From 9574527224e95d9414fe9833ea7e9e03eca4e592 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 11 Aug 2017 18:36:44 +0200
Subject: [PATCH] OO-2923: validate the total score of the sections against the
 configuration of the course element

---
 .../portfolio/BinderConfiguration.java        | 34 +++++++++++++----
 .../ui/BinderAssessmentController.java        | 38 ++++++++++++++++++-
 .../ui/_content/section_assessment.html       |  3 ++
 .../ui/_i18n/LocalStrings_de.properties       |  1 +
 .../ui/_i18n/LocalStrings_en.properties       |  1 +
 5 files changed, 67 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/olat/modules/portfolio/BinderConfiguration.java b/src/main/java/org/olat/modules/portfolio/BinderConfiguration.java
index a270bccc0a0..b9439ba4650 100644
--- a/src/main/java/org/olat/modules/portfolio/BinderConfiguration.java
+++ b/src/main/java/org/olat/modules/portfolio/BinderConfiguration.java
@@ -39,15 +39,19 @@ public class BinderConfiguration {
 	private final boolean timeline;
 	private final boolean shareable;
 	private final boolean options;
+	private final Float maxScore;
+	private final Float minScore;
 	
-	public BinderConfiguration(boolean assessable, boolean withScore, boolean withPassed,
-			boolean timeline, boolean shareable, boolean options) {
+	public BinderConfiguration(boolean assessable, boolean withScore, Float maxScore, Float minScore,
+			boolean withPassed, boolean timeline, boolean shareable, boolean options) {
 		this.assessable = assessable;
 		this.withScore = withScore;
 		this.withPassed = withPassed;
 		this.timeline = timeline;
 		this.shareable = shareable;
 		this.options = options;
+		this.maxScore = maxScore;
+		this.minScore = minScore;
 	}
 	
 	public boolean isAssessable() {
@@ -57,6 +61,14 @@ public class BinderConfiguration {
 	public boolean isWithScore() {
 		return withScore;
 	}
+	
+	public Float getMaxScore() {
+		return maxScore;
+	}
+	
+	public Float getMinScore() {
+		return minScore;
+	}
 
 	public boolean isWithPassed() {
 		return withPassed;
@@ -84,29 +96,31 @@ public class BinderConfiguration {
 	}
 	
 	public static BinderConfiguration createBusinessGroupConfig() {
-		return new BinderConfiguration(false, false, false, true, false, false);
+		return new BinderConfiguration(false, false, null, null, false, true, false, false);
 	}
 	
 	public static BinderConfiguration createTemplateConfig(boolean optionsEditable) {
-		return new BinderConfiguration(false, false, false, false, false, optionsEditable);
+		return new BinderConfiguration(false, false, null, null, false, false, false, optionsEditable);
 	}
 	
 	public static BinderConfiguration createInvitationConfig() {
-		return new BinderConfiguration(false, false, false, true, false, false);
+		return new BinderConfiguration(false, false, null, null, false, true, false, false);
 	}
 	
 	public static BinderConfiguration createMyPagesConfig() {
-		return new BinderConfiguration(false, false, false, true, true, false);
+		return new BinderConfiguration(false, false, null, null, false, true, true, false);
 	}
 	
 	public static BinderConfiguration createDeletedPagesConfig() {
-		return new BinderConfiguration(false, false, false, false, false, false);
+		return new BinderConfiguration(false, false, null, null, false, false, false, false);
 	}
 
 	public static BinderConfiguration createConfig(Binder binder) {
 		boolean withScore = false;
 		boolean withPassed = false;
 		boolean assessable = false;
+		Float maxScore = null;
+		Float minScore = null;
 		
 		RepositoryEntry entry = binder.getEntry();
 		if(binder.getSubIdent() != null) {
@@ -115,6 +129,10 @@ public class BinderConfiguration {
 			if(courseNode instanceof PortfolioCourseNode) {
 				PortfolioCourseNode pfNode = (PortfolioCourseNode)courseNode;
 				withScore = pfNode.hasScoreConfigured();
+				if(withScore) {
+					maxScore = pfNode.getMaxScoreConfiguration();
+					minScore = pfNode.getMinScoreConfiguration();
+				}
 				withPassed = pfNode.hasPassedConfigured();
 				assessable = withPassed || withScore;
 			} else {
@@ -129,6 +147,6 @@ public class BinderConfiguration {
 		} else {
 			withPassed = withScore = assessable = false;
 		}
-		return new BinderConfiguration(assessable, withScore, withPassed, true, true, false);
+		return new BinderConfiguration(assessable, withScore, maxScore, minScore, withPassed, true, true, false);
 	}
 }
diff --git a/src/main/java/org/olat/modules/portfolio/ui/BinderAssessmentController.java b/src/main/java/org/olat/modules/portfolio/ui/BinderAssessmentController.java
index 5e6a97b8b82..c1fe65d5bba 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/BinderAssessmentController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/BinderAssessmentController.java
@@ -95,6 +95,7 @@ public class BinderAssessmentController extends FormBasicController {
 
 	private boolean withScore;
 	private boolean withPassed;
+	private Float minScore, maxScore;
 	
 	@Autowired
 	private PortfolioService portfolioService;
@@ -104,8 +105,10 @@ public class BinderAssessmentController extends FormBasicController {
 		super(ureq, wControl, "section_assessment");
 		this.binder = binder;
 		this.secCallback = secCallback;
-		this.withPassed = config.isWithPassed();
-		this.withScore = config.isWithScore();
+		withPassed = config.isWithPassed();
+		withScore = config.isWithScore();
+		minScore = config.getMinScore();
+		maxScore = config.getMaxScore();
 		initForm(ureq);
 		loadModel();
 	}
@@ -296,6 +299,37 @@ public class BinderAssessmentController extends FormBasicController {
 		super.formInnerEvent(ureq, source, event);
 	}
 
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+
+		flc.contextRemove("scoreError");
+		if(withScore && (maxScore != null || minScore != null)) {
+			double scoreTotal = 0.0d;
+			List<AssessmentSectionWrapper> rows = model.getObjects();
+			for(AssessmentSectionWrapper row:rows) {
+				BigDecimal score = row.getScore();
+				if(row.getScoreEl() != null) {
+					String value = row.getScoreEl().getValue();
+					if(StringHelper.containsNonWhitespace(value)) {
+						score = new BigDecimal(value);
+					}
+				}
+				if(score != null) {
+					scoreTotal += score.doubleValue();
+				}
+			}
+			
+			if(maxScore != null && (maxScore.doubleValue() < scoreTotal)) {
+				flc.contextPut("scoreError", translate("error.score", new String[] { "0", AssessmentHelper.getRoundedScore(maxScore)}));
+			} else if(minScore != null && (minScore.doubleValue() > scoreTotal)) {
+				flc.contextPut("scoreError", translate("error.score", new String[] { "0", AssessmentHelper.getRoundedScore(maxScore)}));
+			}
+		}
+		
+		return allOk & super.validateFormLogic(ureq);
+	}
+
 	@Override
 	protected void formOK(UserRequest ureq) {
 		commitChanges();
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_content/section_assessment.html b/src/main/java/org/olat/modules/portfolio/ui/_content/section_assessment.html
index 74f51e91dc5..5d663c6d502 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_content/section_assessment.html
+++ b/src/main/java/org/olat/modules/portfolio/ui/_content/section_assessment.html
@@ -2,6 +2,9 @@
 	$r.contextHelpWithWrapper("Portfolio assignment: Grading")		
 </div>
 $r.render("section-list")
+#if($r.isNotEmpty($scoreError))
+<div class="o_error">$scoreError</div>
+#end
 <div class="o_button_group">
 $r.render("buttons")
 </div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
index ae230f6bc8e..20ee34d64d4 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties
@@ -153,6 +153,7 @@ error.invalid.type=Dieser Dateityp ist nicht unters\u00FCtzt.
 error.invitation.mail.used=Diese E-Mailadresse wird bereits von einem OpenOLAT-Benutzer verwendet.
 error.mail.invalid=Bitte geben Sie eine g\u00FCltige E-Mailadresse an.
 error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType
+error.score=Punkte müssen zwischen {0} und {1} sein.
 fileupload=Titelbild
 filter.show.all=Alle anzeigen
 firstName=Vorname
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
index 87a2eef8b56..25df7985f89 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
@@ -153,6 +153,7 @@ error.invalid.type=This file type is not supported.
 error.invitation.mail.used=This e-mail address is already used by an OpenOLAT user.
 error.mail.invalid=Please provide a valid e-mail address.
 error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType
+error.score=Score is not between {0} and {1}.
 fileupload=Teaser Image
 filter.show.all=Show all
 firstName=First name
-- 
GitLab