diff --git a/lib/grade/grade_category.php b/lib/grade/grade_category.php index 9917e7142f6..b5be55b6314 100644 --- a/lib/grade/grade_category.php +++ b/lib/grade/grade_category.php @@ -160,6 +160,12 @@ class grade_category extends grade_object { */ public $coefstring = null; + /** + * Static variable storing the result from {@link self::can_apply_limit_rules}. + * @var bool + */ + protected $canapplylimitrules; + /** * Builds this category's path string based on its parents (if any) and its own id number. * This is typically done just before inserting this object in the DB for the first time, @@ -645,7 +651,9 @@ class grade_category extends grade_object { // limit and sort $allvalues = $grade_values; - $this->apply_limit_rules($grade_values, $items); + if ($this->can_apply_limit_rules()) { + $this->apply_limit_rules($grade_values, $items); + } $moredropped = array_diff($allvalues, $grade_values); foreach ($moredropped as $drop => $unused) { @@ -1124,8 +1132,11 @@ class grade_category extends grade_object { $maxes[$item->id] = $item->grademax; // 0 = nograde, 1 = first scale item, 2 = second scale item } } - // apply droplow and keephigh - $this->apply_limit_rules($maxes, $items); + + if ($this->can_apply_limit_rules()) { + // Apply droplow and keephigh. + $this->apply_limit_rules($maxes, $items); + } $max = array_sum($maxes); // update db if anything changed @@ -1296,6 +1307,70 @@ class grade_category extends grade_object { } } + /** + * Returns whether or not we can apply the limit rules. + * + * There are cases where drop lowest or keep highest should not be used + * at all. This method will determine whether or not this logic can be + * applied considering the current setup of the category. + * + * @return bool + */ + public function can_apply_limit_rules() { + if ($this->canapplylimitrules !== null) { + return $this->canapplylimitrules; + } + + // Set it to be supported by default. + $this->canapplylimitrules = true; + + // Natural aggregation. + if ($this->aggregation == GRADE_AGGREGATE_SUM) { + $canapply = true; + + // Check until one child breaks the rules. + $gradeitems = $this->get_children(); + $validitems = 0; + $lastweight = null; + $lastmaxgrade = null; + foreach ($gradeitems as $gradeitem) { + $gi = $gradeitem['object']; + + if ($gradeitem['type'] == 'category') { + // Sub categories are not allowed because they can have dynamic weights/maxgrades. + $canapply = false; + break; + } + + if ($gi->aggregationcoef > 0) { + // Extra credit items are not allowed. + $canapply = false; + break; + } + + if ($lastweight !== null && $lastweight != $gi->aggregationcoef2) { + // One of the weight differs from another item. + $canapply = false; + break; + } + + if ($lastmaxgrade !== null && $lastmaxgrade != $gi->grademax) { + // One of the max grade differ from another item. This is not allowed for now + // because we could be end up with different max grade between users for this category. + $canapply = false; + break; + } + + $lastweight = $gi->aggregationcoef2; + $lastmaxgrade = $gi->grademax; + } + + $this->canapplylimitrules = $canapply; + } + + return $this->canapplylimitrules; + } + /** * Returns true if category uses extra credit of any kind *