diff --git a/admin/tool/task/tests/behat/manage_tasks.feature b/admin/tool/task/tests/behat/manage_tasks.feature index dd27d85f713..8d0c0f34b4f 100644 --- a/admin/tool/task/tests/behat/manage_tasks.feature +++ b/admin/tool/task/tests/behat/manage_tasks.feature @@ -62,8 +62,8 @@ Feature: Manage scheduled tasks And I press "Save changes" Then I should see "Changes saved" And the following should not exist in the "admintable" table: - | Name | Component | Minute | Hour | Day | Day of week | Month | - | Log table cleanup | Standard log | */5 | 1 | 2 | 4 | 3 | + | Name | Minute | Hour | Day | Day of week | Month | + | Log table cleanup | */5 | 1 | 2 | 4 | 3 | And I should see "Log table cleanup" in the "tr.table-primary" "css_element" Scenario: Disabled plugin's tasks are labelled as disabled too diff --git a/lib/form/modgrade.php b/lib/form/modgrade.php index e0107fa3187..e08dd223e84 100644 --- a/lib/form/modgrade.php +++ b/lib/form/modgrade.php @@ -60,8 +60,8 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group { /** @var int $currentscaleid The current scale id */ public $currentscaleid = null; - /** @var string $currentgradetype The current gradetype - can either be 'none', 'scale', or 'point' */ - public $currentgradetype = 'none'; + /** @var ?string $currentgradetype The current gradetype - can either be 'none', 'scale', or 'point' */ + public $currentgradetype = null; /** @var boolean $useratings Set to true if the activity is using ratings, false otherwise */ public $useratings = false; @@ -514,18 +514,30 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group { // This means that the subelements data (inc const and default values) can be overridden by form code. // So - when we call this code really we can't be sure that will be the end value for the element. if (!empty($this->_elements)) { - if (!empty($value)) { - if ($value < 0) { - $this->gradetypeformelement->setValue('scale'); - $this->scaleformelement->setValue(($value * -1)); - } else if ($value > 0) { - $this->gradetypeformelement->setValue('point'); - $maxvalue = !empty($this->currentgrade) ? (string)unformat_float($this->currentgrade) : $value; - $this->maxgradeformelement->setValue($maxvalue); + if (!$this->currentgradetype) { + if (!empty($value)) { + $this->currentgradetype = $value < 0 ? 'scale' : 'point'; + } else { + $this->currentgradetype = 'none'; } - } else { - $this->gradetypeformelement->setValue('none'); - $this->maxgradeformelement->setValue(100); + } + + $this->gradetypeformelement->setValue($this->currentgradetype); + + switch ($this->currentgradetype) { + case 'scale': + if (!empty($value)) { + $this->currentscaleid = $value * -1; + } + $this->scaleformelement->setValue($this->currentscaleid); + break; + case 'point': + case 'none': + if (!empty($value)) { + $maxvalue = !empty($this->currentgrade) ? (string)unformat_float($this->currentgrade) : $value; + } + $this->maxgradeformelement->setValue($maxvalue ?? 100); + break; } } break; diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index b38bd60956d..fc001187fc5 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -1453,7 +1453,8 @@ EOF; // Get row xpath. // Some drivers make XPath relative to the current context, so use descendant. $rowxpath = $tablexpath . "/tbody/tr[descendant::*[@class='rowtitle'][normalize-space(.)=" . $rowliteral . "] | " . " - descendant::th[normalize-space(.)=" . $rowliteral . "] | descendant::td[normalize-space(.)=" . $rowliteral . "]]"; + descendant::th[contains(normalize-space(.)," . $rowliteral . ")] | " . " + descendant::td[contains(normalize-space(.)," . $rowliteral . ")]]"; $columnvaluexpath = $rowxpath . $columnpositionxpath . "[contains(normalize-space(.)," . $valueliteral . ")]"; diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php index baf1610a6c4..57563937fd9 100644 --- a/mod/assign/lang/en/assign.php +++ b/mod/assign/lang/en/assign.php @@ -155,6 +155,10 @@ $string['cutoffdatecolon'] = 'Cut-off date: {$a}'; $string['cutoffdate_help'] = 'If set, submissions will not be accepted after this date without an extension. If not set, submissions will always be accepted.'; $string['cutoffdatevalidation'] = 'Cut-off date cannot be earlier than the due date.'; $string['cutoffdatefromdatevalidation'] = 'Cut-off date cannot be earlier than the allow submissions from date.'; +$string['defaultgradescale'] = 'Default grade scale'; +$string['defaultgradescale_help'] = 'The default grade scale to be used when creating new assignments. Only site level scales can be used as the default.'; +$string['defaultgradetype'] = 'Default grade type'; +$string['defaultgradetype_help'] = 'The default grade type to be used when creating new assignments'; $string['defaultlayout'] = 'Restore default layout'; $string['defaultsettings'] = 'Default assignment settings'; $string['defaultsettings_help'] = 'These settings define the defaults for all new assignments.'; diff --git a/mod/assign/lib.php b/mod/assign/lib.php index 36c5c3ba67f..3976dbfeb9f 100644 --- a/mod/assign/lib.php +++ b/mod/assign/lib.php @@ -945,20 +945,19 @@ function assign_print_recent_mod_activity($activity, $courseid, $detail, $modnam } /** - * Checks if scale is being used by any instance of assignment + * Checks if scale is being used by any instance of assignment or is the default scale used for assignments. * * This is used to find out if scale used anywhere * @param int $scaleid - * @return boolean True if the scale is used by any assignment + * @return boolean True if the scale is used by any assignment or is the default scale used for assignments. */ function assign_scale_used_anywhere($scaleid) { global $DB; - if ($scaleid and $DB->record_exists('assign', array('grade'=>-$scaleid))) { - return true; - } else { - return false; - } + return $scaleid && ( + $DB->record_exists('assign', ['grade' => -(int)$scaleid]) || + (int)get_config('mod_assign', 'defaultgradescale') === (int)$scaleid + ); } /** diff --git a/mod/assign/mod_form.php b/mod/assign/mod_form.php index 0d15ee52f4e..332389cc3a9 100644 --- a/mod/assign/mod_form.php +++ b/mod/assign/mod_form.php @@ -354,4 +354,52 @@ class mod_assign_mod_form extends moodleform_mod { return !empty($data['completionsubmit' . $suffix]); } + /** + * Get the list of admin settings for this module and apply any defaults/advanced/locked/required settings. + * + * @param array $datetimeoffsets - If passed, this is an array of fieldnames => times that the + * default date/time value should be relative to. If not passed, all + * date/time fields are set relative to the users current midnight. + * @return void + */ + public function apply_admin_defaults($datetimeoffsets = []): void { + parent::apply_admin_defaults($datetimeoffsets); + + $isupdate = !empty($this->_cm); + if ($isupdate) { + return; + } + + $settings = get_config('mod_assign'); + $mform = $this->_form; + + if ($mform->elementExists('grade')) { + $element = $mform->getElement('grade'); + + if (property_exists($settings, 'defaultgradetype')) { + switch ((int)$settings->defaultgradetype) { + case GRADE_TYPE_NONE : + $element->currentgradetype = 'none'; + break; + case GRADE_TYPE_SCALE : + $element->currentgradetype = 'scale'; + break; + case GRADE_TYPE_VALUE : + $element->currentgradetype = 'point'; + break; + } + } + + if (property_exists($settings, 'defaultgradescale')) { + /** @var grade_scale|false $gradescale */ + $gradescale = grade_scale::fetch(['id' => (int)$settings->defaultgradescale, 'courseid' => 0]); + + if ($gradescale) { + $element->currentscaleid = $gradescale->id; + } + } + + $element->onQuickFormEvent('updateValue', null, $mform); + } + } } diff --git a/mod/assign/settings.php b/mod/assign/settings.php index b0386120b8e..ea372176361 100644 --- a/mod/assign/settings.php +++ b/mod/assign/settings.php @@ -24,7 +24,9 @@ defined('MOODLE_INTERNAL') || die; +global $CFG; require_once($CFG->dirroot . '/mod/assign/adminlib.php'); +require_once($CFG->dirroot . '/grade/lib.php'); $ADMIN->add('modsettings', new admin_category('modassignfolder', new lang_string('pluginname', 'mod_assign'), $module->is_enabled() === false)); @@ -163,6 +165,39 @@ if ($ADMIN->fulltree) { ); $settings->add($setting); + $name = new lang_string('defaultgradetype', 'mod_assign'); + $description = new lang_string('defaultgradetype_help', 'mod_assign'); + $setting = new admin_setting_configselect('mod_assign/defaultgradetype', + $name, + $description, + GRADE_TYPE_VALUE, [ + GRADE_TYPE_NONE => new lang_string('modgradetypenone', 'grades'), + GRADE_TYPE_SCALE => new lang_string('modgradetypescale', 'grades'), + GRADE_TYPE_VALUE => new lang_string('modgradetypepoint', 'grades'), + ]); + $settings->add($setting); + + /** @var grade_scale[] $scales */ + $scales = grade_scale::fetch_all_global(); + $choices = ['' => new lang_string('choosedots')]; + foreach ($scales as $scale) { + $choices[$scale->id] = $scale->get_name(); + } + $name = new lang_string('defaultgradescale', 'mod_assign'); + $description = new lang_string('defaultgradescale_help', 'mod_assign'); + if (count($choices) > 1) { + $setting = new admin_setting_configselect('mod_assign/defaultgradescale', + $name, + $description, + '', $choices); + } else { + $setting = new admin_setting_configempty('mod_assign/defaultgradescale', + $name, + $description + ); + } + $settings->add($setting); + $name = new lang_string('gradingduedate', 'mod_assign'); $description = new lang_string('gradingduedate_help', 'mod_assign'); $setting = new admin_setting_configduration('assign/gradingduedate', diff --git a/mod/assign/tests/behat/default_grade_values.feature b/mod/assign/tests/behat/default_grade_values.feature new file mode 100644 index 00000000000..bf41c0c8210 --- /dev/null +++ b/mod/assign/tests/behat/default_grade_values.feature @@ -0,0 +1,76 @@ +@mod @mod_assign +Feature: Assignments can have default grades and scales defined + In order to improve assignment creation + As a teacher + The grade and scale should be set by default + + Background: + Given the following "courses" exist: + | fullname | shortname | category | groupmode | + | Course 1 | C1 | 0 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + + @javascript + Scenario: When creating an assignment the default grade type and grade scale are set as default in the form + Given the following config values are set as admin: + | config | value | plugin | + | defaultgradetype | 2 | mod_assign | + | defaultgradescale | 2 | mod_assign | + When I am on the "Course 1" course page logged in as teacher1 + And I wait until the page is ready + And I switch editing mode on + And I click on "Add an activity or resource" "button" + And I follow "Assignment" + And I set the following fields to these values: + | name | Test assignment | + Then the following fields match these values: + | grade[modgrade_type] | Scale | + | grade[modgrade_scale] | Default competence scale | + And I press "Save and return to course" + And I am on the "Test assignment" "assign activity editing" page + And the following fields match these values: + | grade[modgrade_type] | Scale | + | grade[modgrade_scale] | Default competence scale | + + Scenario: Editing an assignment will save the selected grade type and grade scale + Given the following "activity" exists: + | activity | assign | + | course | C1 | + | name | Test assignment | + | description | Assignment description | + | gradetype | 1 | + | gradescale | 1 | + When I am on the "Test assignment" "assign activity editing" page logged in as teacher1 + And the following fields match these values: + | grade[modgrade_type] | Point | + | grade[modgrade_scale] | Default competence scale | + And I set the following fields to these values: + | grade[modgrade_type] | Scale | + | grade[modgrade_scale] | Separate and Connected ways of knowing | + And I press "Save and return to course" + And I am on the "Test assignment" "assign activity editing" page + Then the following fields match these values: + | grade[modgrade_type] | Scale | + | grade[modgrade_scale] | Separate and Connected ways of knowing | + + Scenario: When a scale is set as the default grade scale it should be shown as in use + Given the following config values are set as admin: + | config | value | plugin | + | defaultgradetype | 2 | mod_assign | + | defaultgradescale | 2 | mod_assign | + When I log in as "admin" + And I navigate to "Grades > Scales" in site administration + Then "Default competence scale" row "Used" column of "generaltable" table should contain "Yes" + And "Separate and Connected ways of knowing" row "Used" column of "generaltable" table should contain "No" + And the following config values are set as admin: + | config | value | plugin | + | defaultgradetype | 2 | mod_assign | + | defaultgradescale | 1 | mod_assign | + And I navigate to "Grades > Scales" in site administration + And "Default competence scale" row "Used" column of "generaltable" table should contain "No" + And "Separate and Connected ways of knowing" row "Used" column of "generaltable" table should contain "Yes" diff --git a/mod/assign/tests/generator/lib.php b/mod/assign/tests/generator/lib.php index 027aa4e5e44..ca0e93b511d 100644 --- a/mod/assign/tests/generator/lib.php +++ b/mod/assign/tests/generator/lib.php @@ -16,6 +16,9 @@ defined('MOODLE_INTERNAL') || die(); +global $CFG; +require_once($CFG->libdir . '/gradelib.php'); + /** * assign module data generator class * @@ -66,6 +69,18 @@ class mod_assign_generator extends testing_module_generator { $record->teamsubmissiongroupingid = $this->get_grouping_id($record->teamsubmissiongroupingid); } + if (property_exists($record, 'gradetype')) { + if ((int)$record->gradetype === GRADE_TYPE_SCALE && property_exists($record, 'gradescale')) { + // Get the scale id and apply it. + $defaultsettings['grade[modgrade_type]'] = GRADE_TYPE_SCALE; + $defaultsettings['grade[modgrade_scale]'] = $record->gradescale; + $defaultsettings['grade'] = -$record->gradescale; + } else if ((int)$record->gradetype === GRADE_TYPE_NONE) { + $defaultsettings['grade[modgrade_type]'] = GRADE_TYPE_NONE; + $defaultsettings['grade'] = 0; + } + } + foreach ($defaultsettings as $name => $value) { if (!isset($record->{$name})) { $record->{$name} = $value; diff --git a/mod/assign/tests/generator_test.php b/mod/assign/tests/generator_test.php new file mode 100644 index 00000000000..09f5c946cc5 --- /dev/null +++ b/mod/assign/tests/generator_test.php @@ -0,0 +1,60 @@ +. + +namespace mod_assign; + +/** + * Test for mod assign generator. + * @package mod_assign + * @copyright Frederik Pytlick + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \mod_assign_generator + */ +final class generator_test extends \advanced_testcase { + /** + * Test creating an assignment instance using the mod_assign generator. + */ + public function test_create_instance(): void { + $this->resetAfterTest(); + + $datagenerator = self::getDataGenerator(); + $course = $datagenerator->create_course(); + + $scale = $datagenerator->create_scale(); + + $generator = $datagenerator->get_plugin_generator('mod_assign'); + $instance1 = $generator->create_instance([ + 'course' => $course, + 'name' => 'Assign 1', + 'gradetype' => GRADE_TYPE_SCALE, + 'gradescale' => $scale->id, + ]); + $instance2 = $generator->create_instance([ + 'course' => $course, + 'name' => 'Assign 2', + 'gradetype' => GRADE_TYPE_NONE, + ]); + $instance3 = $generator->create_instance([ + 'course' => $course, + 'name' => 'Assign 3', + 'gradetype' => GRADE_TYPE_VALUE, + ]); + + $this->assertEquals(-$scale->id, $instance1->grade); + $this->assertEquals(0, $instance2->grade); + $this->assertEquals(100, $instance3->grade); + } +} diff --git a/mod/assign/upgrade.txt b/mod/assign/upgrade.txt index 0d203b9e95f..c4de6a6389d 100644 --- a/mod/assign/upgrade.txt +++ b/mod/assign/upgrade.txt @@ -4,6 +4,15 @@ This file has been replaced by UPGRADING.md. See MDL-81125 for further informati === This files describes API changes in the assign code. +=== 4.5 === +* Added 2 new settings: + - "mod_assign/defaultgradetype" + - The value of this setting dictates which of the GRADE_TYPE_X constants is the default option, + when creating new instances of the assignment. + - The default value is GRADE_TYPE_VALUE (Point) + - "mod_assign/defaultgradescale" + - The value of this setting dictates which of the existing scales is the default option, + when creating new instances of the assignment. === 4.3 === * The following deprecated methods have been removed and should not be used any more: diff --git a/mod/assign/version.php b/mod/assign/version.php index df7d76b383d..2a15deefeee 100644 --- a/mod/assign/version.php +++ b/mod/assign/version.php @@ -25,5 +25,5 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics). -$plugin->version = 2024042201; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2024042202; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2024041600; // Requires this Moodle version.