diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php
index d17b0da0ddb..5f5820a1221 100644
--- a/backup/moodle2/backup_stepslib.php
+++ b/backup/moodle2/backup_stepslib.php
@@ -2806,12 +2806,13 @@ class backup_completion_defaults_structure_step extends backup_structure_step {
$cc = new backup_nested_element('course_completion_defaults');
$defaults = new backup_nested_element('course_completion_default', array('id'), array(
- 'modulename', 'completion', 'completionview', 'completionusegrade', 'completionexpected', 'customrules'
+ 'modulename', 'completion', 'completionview', 'completionusegrade', 'completionpassgrade',
+ 'completionexpected', 'customrules'
));
// Use module name instead of module id so we can insert into another site later.
$sourcesql = "SELECT d.id, m.name as modulename, d.completion, d.completionview, d.completionusegrade,
- d.completionexpected, d.customrules
+ d.completionpassgrade, d.completionexpected, d.customrules
FROM {course_completion_defaults} d join {modules} m on d.module = m.id
WHERE d.course = ?";
$defaults->set_source_sql($sourcesql, array(backup::VAR_COURSEID));
diff --git a/completion/classes/bulkedit_form.php b/completion/classes/bulkedit_form.php
index a67cce75f69..0e6159aee3c 100644
--- a/completion/classes/bulkedit_form.php
+++ b/completion/classes/bulkedit_form.php
@@ -137,7 +137,8 @@ class core_completion_bulkedit_form extends core_completion_edit_base_form {
// Completion: Don't let them choose automatic completion without turning
// on some conditions.
if (array_key_exists('completion', $data) &&
- $data['completion'] == COMPLETION_TRACKING_AUTOMATIC && !empty($data['completionusegrade'])) {
+ $data['completion'] == COMPLETION_TRACKING_AUTOMATIC &&
+ (!empty($data['completionusegrade']) || !empty($data['completionpassgrade']))) {
require_once($CFG->libdir.'/gradelib.php');
$moduleswithoutgradeitem = [];
foreach ($this->cms as $cm) {
diff --git a/completion/classes/edit_base_form.php b/completion/classes/edit_base_form.php
index 505c7891689..903a51ab00a 100644
--- a/completion/classes/edit_base_form.php
+++ b/completion/classes/edit_base_form.php
@@ -199,10 +199,19 @@ abstract class core_completion_edit_base_form extends moodleform {
// Automatic completion once it's graded.
if ($this->support_grades()) {
- $mform->addElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
+ $group = [];
+ $group[] = $mform->createElement('advcheckbox', 'completionusegrade', get_string('completionusegrade', 'completion'),
get_string('completionusegrade_desc', 'completion'));
- $mform->disabledIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
- $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
+ $mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion', '', true);
+
+ $group[] = $mform->createElement('advcheckbox', 'completionpassgrade', get_string('completionpassgrade', 'completion'),
+ get_string('completionpassgrade_desc', 'completion'));
+ $mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion', '', true);
+ $mform->disabledIf('completionpassgrade', 'completionusegrade', 'notchecked');
+
+ $mform->addGroup($group, 'completionpassgroup', get_string('completionpassgrade', 'completion'), ' ', false);
+ $mform->disabledIf('completionpassgroup', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+
$autocompletionpossible = true;
}
@@ -247,7 +256,7 @@ abstract class core_completion_edit_base_form extends moodleform {
// on some conditions.
if (array_key_exists('completion', $data) &&
$data['completion'] == COMPLETION_TRACKING_AUTOMATIC) {
- if (empty($data['completionview']) && empty($data['completionusegrade']) &&
+ if (empty($data['completionview']) && empty($data['completionusegrade']) && empty($data['completionpassgrade']) &&
!$this->completion_rule_enabled($data)) {
$errors['completion'] = get_string('badautocompletion', 'completion');
}
diff --git a/completion/classes/manager.php b/completion/classes/manager.php
index 2f66775e7f0..146ae16f565 100644
--- a/completion/classes/manager.php
+++ b/completion/classes/manager.php
@@ -176,7 +176,13 @@ class manager {
}
if ($moduledata instanceof cm_info && !is_null($moduledata->completiongradeitemnumber) ||
($moduledata instanceof stdClass && !empty($moduledata->completionusegrade))) {
- $activeruledescriptions[] = get_string('completionusegrade_desc', 'completion');
+
+ $description = 'completionusegrade_desc';
+ if (!empty($moduledata->completionpassgrade)) {
+ $description = 'completionpassgrade_desc';
+ }
+
+ $activeruledescriptions[] = get_string($description, 'completion');
}
// Now, ask the module to provide descriptions for its custom conditional completion rules.
@@ -351,8 +357,11 @@ class manager {
protected function apply_completion_cm(\cm_info $cm, $data, $updateinstance) {
global $DB;
- $defaults = ['completion' => COMPLETION_DISABLED, 'completionview' => COMPLETION_VIEW_NOT_REQUIRED,
- 'completionexpected' => 0, 'completiongradeitemnumber' => null];
+ $defaults = [
+ 'completion' => COMPLETION_DISABLED, 'completionview' => COMPLETION_VIEW_NOT_REQUIRED,
+ 'completionexpected' => 0, 'completiongradeitemnumber' => null,
+ 'completionpassgrade' => 0
+ ];
$data += ['completion' => $cm->completion,
'completionexpected' => $cm->completionexpected,
diff --git a/completion/criteria/completion_criteria_activity.php b/completion/criteria/completion_criteria_activity.php
index 118db635b31..3b80836636a 100644
--- a/completion/criteria/completion_criteria_activity.php
+++ b/completion/criteria/completion_criteria_activity.php
@@ -240,6 +240,10 @@ class completion_criteria_activity extends completion_criteria {
if (!is_null($cm->completiongradeitemnumber)) {
$details['requirement'][] = get_string('achievinggrade', 'completion');
}
+
+ if ($cm->completionpassgrade) {
+ $details['requirement'][] = get_string('achievingpassinggrade', 'completion');
+ }
}
$details['requirement'] = implode(', ', $details['requirement']);
diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php
index c99ca7122e3..4ee1b1a2ee7 100644
--- a/course/moodleform_mod.php
+++ b/course/moodleform_mod.php
@@ -370,6 +370,9 @@ abstract class moodleform_mod extends moodleform {
if ($mform->elementExists('completionusegrade')) {
$mform->freeze('completionusegrade');
}
+ if ($mform->elementExists('completionpassgrade')) {
+ $mform->freeze('completionpassgrade');
+ }
if ($mform->elementExists('completiongradeitemnumber')) {
$mform->freeze('completiongradeitemnumber');
}
@@ -468,6 +471,28 @@ abstract class moodleform_mod extends moodleform {
);
}
}
+
+ if (isset($data['completionpassgrade']) && $data['completionpassgrade']) {
+ // We need to check whether there's a valid gradepass value.
+ // This can either be in completiongradeitemnumber when there are multiple options OR,
+ // The first grade item if completionusegrade is specified.
+ $validategradepass = false;
+ if (isset($data['completiongradeitemnumber'])) {
+ if ($data['completiongradeitemnumber'] == (string)$itemnumber) {
+ $validategradepass = true;
+ }
+ } else if (isset($data['completionusegrade']) && $data['completionusegrade']) {
+ $validategradepass = true;
+ }
+
+ // Confirm gradepass is a valid non-zero value.
+ if ($validategradepass && (!isset($data[$gradepassfieldname]) || grade_floatval($data[$gradepassfieldname]) == 0)) {
+ $errors['completionpassgrade'] = get_string(
+ 'activitygradetopassnotset',
+ 'completion'
+ );
+ }
+ }
}
// Completion: Don't let them choose automatic completion without turning
@@ -482,7 +507,7 @@ abstract class moodleform_mod extends moodleform {
$rulesenabled = !empty($data['completionview']);
// Use grade to complete (only one grade item).
- $rulesenabled = $rulesenabled || !empty($data['completionusegrade']);
+ $rulesenabled = $rulesenabled || !empty($data['completionusegrade']) || !empty($data['completionpassgrade']);
// Use grade to complete (specific grade item).
if (!$rulesenabled && isset($data['completiongradeitemnumber'])) {
@@ -724,16 +749,28 @@ abstract class moodleform_mod extends moodleform {
$mform->hideIf('completionusegrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
$mform->addHelpButton('completionusegrade', 'completionusegrade', 'completion');
+ // Complete if the user has reached the pass grade.
+ $mform->addElement(
+ 'checkbox',
+ 'completionpassgrade', null,
+ get_string('completionpassgrade_desc', 'completion')
+ );
+ $mform->hideIf('completionpassgrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $mform->disabledIf('completionpassgrade', 'completionusegrade', 'notchecked');
+ $mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion');
+
// The disabledIf logic differs between ratings and other grade items due to different field types.
if ($this->_features->rating) {
// If using the rating system, there is no grade unless ratings are enabled.
$mform->disabledIf('completionusegrade', 'assessed', 'eq', 0);
+ $mform->disabledIf('completionpassgrade', 'assessed', 'eq', 0);
} else {
// All other field types use the '$gradefieldname' field's modgrade_type.
$itemnumbers = array_keys($itemnames);
$itemnumber = array_shift($itemnumbers);
$gradefieldname = component_gradeitems::get_field_name_for_itemnumber($component, $itemnumber, 'grade');
$mform->disabledIf('completionusegrade', "{$gradefieldname}[modgrade_type]", 'eq', 'none');
+ $mform->disabledIf('completionpassgrade', "{$gradefieldname}[modgrade_type]", 'eq', 'none');
}
} else if (count($itemnames) > 1) {
// There are multiple grade items in this activity.
@@ -752,6 +789,16 @@ abstract class moodleform_mod extends moodleform {
$options
);
$mform->hideIf('completiongradeitemnumber', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+
+ // Complete if the user has reached the pass grade.
+ $mform->addElement(
+ 'checkbox',
+ 'completionpassgrade', null,
+ get_string('completionpassgrade_desc', 'completion')
+ );
+ $mform->hideIf('completionpassgrade', 'completion', 'ne', COMPLETION_TRACKING_AUTOMATIC);
+ $mform->disabledIf('completionpassgrade', 'completiongradeitemnumber', 'eq', '');
+ $mform->addHelpButton('completionpassgrade', 'completionpassgrade', 'completion');
}
}
diff --git a/lang/en/completion.php b/lang/en/completion.php
index 9c60666c087..1f4ec18664e 100644
--- a/lang/en/completion.php
+++ b/lang/en/completion.php
@@ -25,6 +25,7 @@
*/
$string['achievinggrade'] = 'Achieving grade';
+$string['achievingpassinggrade'] = 'Achieving passing grade';
$string['activities'] = 'Activities';
$string['activitieslabel'] = 'Activities / resources';
$string['activityaggregation'] = 'Condition requires';
@@ -35,6 +36,7 @@ $string['activitiescompletednote'] = 'Note: Activity completion must be set for
$string['activitycompletion'] = 'Activity completion';
$string['activitycompletionupdated'] = 'Changes saved';
$string['activitygradenotrequired'] = 'Grade not required';
+$string['activitygradetopassnotset'] = 'This activity does not have a valid grade to pass set. It may be set in the Grade section of the activity settings.';
$string['affectedactivities'] = 'The changes will affect the following {$a} activities or resources:';
$string['aggregationmethod'] = 'Aggregation method';
$string['all'] = 'All';
@@ -106,6 +108,9 @@ $string['completionondate'] = 'Date';
$string['completionondatevalue'] = 'Date when course will be marked as complete';
$string['completionduration'] = 'Enrolment';
$string['completionsettingslocked'] = 'Completion settings locked';
+$string['completionpassgrade'] = 'Require passing grade';
+$string['completionpassgrade_desc'] = 'Student must receive a passing grade to complete this activity';
+$string['completionpassgrade_help'] = 'If enabled, the activity is considered complete when a student receives a passing grade.';
$string['completionusegrade'] = 'Require grade';
$string['completionusegrade_desc'] = 'Student must receive a grade to complete this activity';
$string['completionusegrade_help'] = 'If enabled, the activity is considered complete when a student receives a grade. If a pass grade for the activity is set, then pass and fail icons are displayed in the activity completion report.';
@@ -141,6 +146,7 @@ $string['deletecompletiondata'] = 'Delete completion data';
$string['dependencies'] = 'Dependencies';
$string['dependenciescompleted'] = 'Completion of other courses';
$string['detail_desc:receivegrade'] = 'Receive a grade';
+$string['detail_desc:receivepassgrade'] = 'Receive a passing grade';
$string['detail_desc:view'] = 'View';
$string['hiddenrules'] = 'Some settings specific to {$a} have been hidden. To view unselect other activities';
$string['editcoursecompletionsettings'] = 'Edit course completion settings';
diff --git a/lib/db/install.xml b/lib/db/install.xml
index eeb1af648de..a35a832e9e7 100644
--- a/lib/db/install.xml
+++ b/lib/db/install.xml
@@ -1,5 +1,5 @@
-
@@ -304,6 +304,7 @@
+
@@ -3866,6 +3867,7 @@
+
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php
index f6919eb54d5..5f509472de4 100644
--- a/lib/db/upgrade.php
+++ b/lib/db/upgrade.php
@@ -2862,5 +2862,21 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2021092400.03);
}
+ if ($oldversion < 2021100300.01) {
+ $table = new xmldb_table('course_completion_defaults');
+
+ // Adding fields to table course_completion_defaults.
+ $field = new xmldb_field('completionpassgrade', XMLDB_TYPE_INTEGER, '1', null,
+ XMLDB_NOTNULL, null, '0', 'completionusegrade');
+
+ // Conditionally launch add field for course_completion_defaults.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2021100300.01);
+ }
+
return true;
}
diff --git a/mod/workshop/mod_form.php b/mod/workshop/mod_form.php
index db9a52eaaf6..2860ecf1002 100644
--- a/mod/workshop/mod_form.php
+++ b/mod/workshop/mod_form.php
@@ -32,6 +32,7 @@ require_once($CFG->dirroot . '/course/moodleform_mod.php');
require_once(__DIR__ . '/locallib.php');
require_once($CFG->libdir . '/filelib.php');
+use core_grades\component_gradeitems;
/**
* Module settings form for Workshop instances
*/
@@ -462,6 +463,23 @@ class mod_workshop_mod_form extends moodleform_mod {
}
}
+ // We need to do a custom completion validation because workshop grade items identifiers divert from standard.
+ // Refer to validation defined in moodleform_mod.php.
+ if (isset($data['completionpassgrade']) && $data['completionpassgrade'] &&
+ isset($data['completiongradeitemnumber'])) {
+ $itemnames = component_gradeitems::get_itemname_mapping_for_component('mod_workshop');
+ $gradepassfield = $itemnames[(int) $data['completiongradeitemnumber']] . 'gradepass';
+ if (!isset($data[$gradepassfield]) || grade_floatval($data[$gradepassfield]) == 0) {
+ $errors['completionpassgrade'] = get_string(
+ 'activitygradetopassnotset',
+ 'completion'
+ );
+ } else {
+ // We have validated grade pass. Unset any errors.
+ unset($errors['completionpassgrade']);
+ }
+ }
+
if (!$data['submissiontypetextavailable'] && !$data['submissiontypefileavailable']) {
// One submission type must be available.
$errors['submissiontypes'] = get_string('nosubmissiontype', 'workshop');
diff --git a/version.php b/version.php
index e59cc0b730b..6b98bb9c1af 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
-$version = 2021100300.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2021100300.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev (Build: 20211003)'; // Human-friendly version name