diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index f24abcefee6..eccc06d4464 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -999,6 +999,13 @@ class backup_gradebook_structure_step extends backup_structure_step { $gradebook->add_child($grade_settings); $grade_settings->add_child($grade_setting); + // Add attribute with gradebook calculation freeze date if needed. + $gradebookcalculationfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->get_courseid()); + if ($gradebookcalculationfreeze) { + $gradebook->add_attributes(array('calculations_freeze')); + $gradebook->get_attribute('calculations_freeze')->set_value($gradebookcalculationfreeze); + } + // Define sources //Include manual, category and the course grade item diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index c9038cfc954..ec11ffdc484 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -142,6 +142,18 @@ class restore_gradebook_structure_step extends restore_structure_step { } protected function process_gradebook($data) { + // For non-merge restore types: + // Unset 'gradebook_calculations_freeze_' in the course and replace with the one from the backup. + $target = $this->get_task()->get_target(); + if ($target == backup::TARGET_CURRENT_DELETING || $target == backup::TARGET_EXISTING_DELETING) { + set_config('gradebook_calculations_freeze_' . $this->get_courseid(), null); + } + if (!empty($data['calculations_freeze']) && $this->get_task()->get_target() == backup::TARGET_NEW_COURSE) { + if ($target == backup::TARGET_NEW_COURSE || $target == backup::TARGET_CURRENT_DELETING || + $target == backup::TARGET_EXISTING_DELETING) { + set_config('gradebook_calculations_freeze_' . $this->get_courseid(), $data['calculations_freeze']); + } + } } protected function process_grade_item($data) { @@ -451,9 +463,24 @@ class restore_gradebook_structure_step extends restore_structure_step { } $rs->close(); + // Freeze gradebook calculations if needed. + $this->gradebook_calculation_freeze(); + // Restore marks items as needing update. Update everything now. grade_regrade_final_grades($this->get_courseid()); } + + /** + * Freeze gradebook calculation if needed. + * + * This is similar to various upgrade scripts that check if the freeze is needed. + */ + protected function gradebook_calculation_freeze() { + global $CFG; + $gradebookcalculationsfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->get_courseid()); + preg_match('/(\d{8})/', $this->get_task()->get_info()->moodle_release, $matches); + $backupbuild = (int)$matches[1]; + } } /** diff --git a/grade/lib.php b/grade/lib.php index b78f8013b10..9c2c351eff7 100644 --- a/grade/lib.php +++ b/grade/lib.php @@ -515,6 +515,15 @@ function hide_aggregatesubcats_upgrade_notice($courseid) { unset_config('show_aggregatesubcats_upgrade_' . $courseid); } +/** + * Hide warning about changed grades due to bug fixes + * + * @param int $courseid The current course id. + */ +function hide_gradebook_calculations_freeze_notice($courseid) { + unset_config('gradebook_calculations_freeze_' . $courseid); +} + /** * Print warning about changed grades during upgrade to 2.8. * @@ -547,6 +556,9 @@ function print_natural_aggregation_upgrade_notice($courseid, $context, $thispage $minmaxtouse = grade_get_setting($courseid, 'minmaxtouse', $CFG->grade_minmaxtouse); + $gradebookcalculationsfreeze = get_config('core', 'gradebook_calculations_freeze_' . $courseid); + $acceptgradebookchanges = optional_param('acceptgradebookchanges', false, PARAM_BOOL) && confirm_sesskey(); + // Hide the warning if the user told it to go away. if ($hidenaturalwarning) { hide_natural_aggregation_upgrade_notice($courseid); @@ -641,6 +653,39 @@ function print_natural_aggregation_upgrade_notice($courseid, $context, $thispage } } + if ($gradebookcalculationsfreeze) { + if ($acceptgradebookchanges) { + // Accept potential changes in grades caused by extra credit bug MDL-49257. + hide_gradebook_calculations_freeze_notice($courseid); + $courseitem = grade_item::fetch_course_item($courseid); + $courseitem->force_regrading(); + grade_regrade_final_grades($courseid); + + $html .= $OUTPUT->notification(get_string('gradebookcalculationsuptodate', 'grades'), 'notifysuccess'); + } else { + // Show the warning that there may be extra credit weights problems. + $a = new stdClass(); + $a->gradebookversion = $gradebookcalculationsfreeze; + if (preg_match('/(\d{8,})/', $CFG->release, $matches)) { + $a->currentversion = $matches[1]; + } else { + $a->currentversion = $CFG->release; + } + $a->url = get_docs_url('Gradebook_calculations_changes'); + $message = get_string('gradebookcalculationswarning', 'grades', $a); + + $fixmessage = get_string('gradebookcalculationsfixbutton', 'grades'); + $urlparams = array('id' => $courseid, + 'acceptgradebookchanges' => true, + 'sesskey' => sesskey()); + $fixurl = new moodle_url($thispage, $urlparams); + $fixbutton = $OUTPUT->single_button($fixurl, $fixmessage, 'get'); + + $html .= $OUTPUT->notification($message, 'notifywarning'); + $html .= $fixbutton; + } + } + if (!empty($html)) { $html = html_writer::tag('div', $html, array('class' => 'core_grades_notices')); } diff --git a/grade/tests/behat/behat_grade.php b/grade/tests/behat/behat_grade.php index 5b5ed8159c0..f7ff737e3c3 100644 --- a/grade/tests/behat/behat_grade.php +++ b/grade/tests/behat/behat_grade.php @@ -158,4 +158,18 @@ class behat_grade extends behat_base { $steps[] = new Given('I click on "' . $this->escape($linktext) . '" "link"'); return $steps; } + + /** + * Step allowing to test before-the-fix behaviour of the gradebook + * + * @Given /^gradebook calculations for the course "(?P(?:[^"]|\\")*)" are frozen at version "(?P(?:[^"]|\\")*)"$/ + * @param string $coursename + * @param string $version + * @return Given + */ + public function gradebook_calculations_for_the_course_are_frozen_at_version($coursename, $version) { + global $DB; + $courseid = $DB->get_field('course', 'id', array('shortname' => $coursename), MUST_EXIST); + set_config('gradebook_calculations_freeze_' . $courseid, $version); + } } diff --git a/lang/en/grades.php b/lang/en/grades.php index b0628922af0..6ca38336b52 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -239,6 +239,9 @@ $string['gradeadministration'] = 'Grade administration'; $string['gradealreadyupdated'] = '{$a} grades have not been imported because the grades in the import file are older than in the grader report. To proceed with the grade import anyway, use the force import option.'; $string['gradeanalysis'] = 'Grade analysis'; $string['gradebook'] = 'Gradebook'; +$string['gradebookcalculationsuptodate'] = 'The calculations in the gradebook are up to date. You may need to reload this page to see changes.'; +$string['gradebookcalculationsfixbutton'] = 'Accept the changes and recalculate grades'; +$string['gradebookcalculationswarning'] = 'Note: Some errors have been detected in gradebook calculations that affect this course. The errors have been fixed but the fix was not yet applied to this course since it may lead to the changes in existing grades. You can review the changes on this documentation page. Look for the changes between versions {$a->gradebookversion} and {$a->currentversion}'; $string['gradebookhiddenerror'] = 'The gradebook is currently set to hide everything from students.'; $string['gradebookhistories'] = 'Grade histories'; $string['gradeboundary'] = 'Letter grade boundary';