Merge branch 'MDL-78082-master' of https://github.com/rezaies/moodle

This commit is contained in:
Ilya Tregubov
2023-09-07 14:31:03 +08:00
10 changed files with 142 additions and 39 deletions
+2 -2
View File
@@ -92,7 +92,7 @@ switch ($action) {
if ($type == 'grade' and empty($object->id)) {
$object->insert();
}
$object->set_locked(1, true, true);
$object->set_locked(1, false, true);
}
break;
@@ -104,7 +104,7 @@ switch ($action) {
if ($type == 'grade' and empty($object->id)) {
$object->insert();
}
$object->set_locked(0, true, true);
$object->set_locked(0, false, true);
}
break;
+4 -2
View File
@@ -158,7 +158,7 @@ class grade_edit_tree {
$level++;
$this->categories[$object->id] = $strippedname;
$category = grade_category::fetch(array('id' => $object->id));
$item = $category->get_grade_item();
$category->load_grade_item();
// Add aggregation coef input if not a course item and if parent category has correct aggregation type
// Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
@@ -271,7 +271,7 @@ class grade_edit_tree {
$categoryrow->id = 'grade-item-' . $eid;
$categoryrow->attributes['class'] = $courseclass . ' category ';
$categoryrow->attributes['data-category'] = $eid;
$categoryrow->attributes['data-itemid'] = $category->get_grade_item()->id;
$categoryrow->attributes['data-itemid'] = $category->grade_item->id;
$categoryrow->attributes['data-hidden'] = 'false';
foreach ($rowclasses as $class) {
$categoryrow->attributes['class'] .= ' ' . $class;
@@ -1058,6 +1058,8 @@ class grade_edit_tree_column_status extends grade_edit_tree_column {
*/
public function get_category_cell($category, $levelclass, $params) {
global $OUTPUT, $gtree;
$category->load_grade_item();
$categorycell = parent::get_category_cell($category, $levelclass, $params);
$element = [];
$element['object'] = $category;
+41 -9
View File
@@ -2379,7 +2379,26 @@ class grade_structure {
['id' => $this->courseid, 'sesskey' => sesskey(), 'eid' => $element['eid']]);
$url = $gpr->add_url_params($url);
if (($element['type'] == 'grade') && ($element['object']->grade_item->is_locked())) {
if ($element['type'] == 'category') {
// Grade categories themselves cannot be locked. We lock/unlock their grade items.
$children = $element['object']->get_children(true);
$alllocked = true;
foreach ($children as $child) {
if (!$child['object']->is_locked()) {
$alllocked = false;
break;
}
}
if ($alllocked && has_capability('moodle/grade:unlock', $this->context)) {
$title = get_string('unlock', 'grades');
$url->param('action', 'unlock');
} else if (!$alllocked && has_capability('moodle/grade:lock', $this->context)) {
$title = get_string('lock', 'grades');
$url->param('action', 'lock');
} else {
return null;
}
} else if (($element['type'] == 'grade') && ($element['object']->grade_item->is_locked())) {
// Don't allow an unlocking action for a grade whose grade item is locked: just print a state icon.
$strparamobj = new stdClass();
$strparamobj->itemname = $element['object']->grade_item->get_name(true, true);
@@ -2501,7 +2520,6 @@ class grade_structure {
$context = [
'hidden' => $element['object']->is_hidden(),
'locked' => $element['object']->is_locked(),
];
if ($element['object'] instanceof grade_grade) {
@@ -2511,18 +2529,32 @@ class grade_structure {
$context['feedback'] = !empty($grade->feedback) && $grade->load_grade_item()->gradetype != GRADE_TYPE_TEXT;
}
// Early return if there aren't any statuses that we need to show.
if (!in_array(true, $context)) {
return null;
}
$context['classes'] = 'grade_icons data-collapse_gradeicons';
if (isset($element['type']) && ($element['type'] == 'category')) {
if ($element['object'] instanceof grade_category) {
$context['classes'] = 'category_grade_icons';
$children = $element['object']->get_children(true);
$alllocked = true;
foreach ($children as $child) {
if (!$child['object']->is_locked()) {
$alllocked = false;
break;
}
}
if ($alllocked) {
$context['locked'] = true;
}
} else {
$context['locked'] = $element['object']->is_locked();
}
return $OUTPUT->render_from_template('core_grades/status_icons', $context);
// Don't even attempt rendering if there is no status to show.
if (in_array(true, $context)) {
return $OUTPUT->render_from_template('core_grades/status_icons', $context);
} else {
return null;
}
}
/**
@@ -23,7 +23,7 @@
"name": "Meaning of life"
}
}}
<button class="btn btn-link btn-icon icon-size-3" data-hider="expand" data-col="{{field}}">
<button type="button" class="btn btn-link btn-icon icon-size-3" data-hider="expand" data-col="{{field}}">
<i class="icon fa fa-plus m-0" title="Reopen column" aria-hidden="true"></i>
<span class="sr-only">{{#str}}reopencolumn, gradereport_grader, {{name}}{{/str}}</span>
</button>
@@ -0,0 +1,63 @@
Feature: Locking Grade Items and Categories in Gradebook
In order to ensure that grade items and categories can be securely locked in the gradebook,
As a teacher,
I need to perform locking actions and verify the locking status.
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "users" exist:
| username | firstname | lastname |
| teacher1 | Teacher | 1 |
| student1 | Student | 1 |
| student2 | Student | 2 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "grade categories" exist:
| fullname | course |
| Category 1 | C1 |
And the following "grade items" exist:
| itemname | course | gradecategory |
| Manual grade 1 | C1 | Category 1 |
| Manual grade 2 | C1 | Category 1 |
And the following "grade items" exist:
| itemname | course |
| Manual grade 3 | C1 |
@javascript
Scenario: Locking and unlocking a grade item preserves individual student locks
Given I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I turn editing mode on
And I change window size to "large"
When I click on "Cell actions" "button" in the "//td[count(//th[.//descendant::a[normalize-space(text())='Manual grade 1']]/preceding-sibling::*)][../th[1]/a[normalize-space(text())='Student 1']]" "xpath_element"
And I choose "Lock" in the open action menu
And I click on grade item menu "Manual grade 1" of type "gradeitem" on "grader" page
And I choose "Lock" in the open action menu
And I click on grade item menu "Manual grade 1" of type "gradeitem" on "grader" page
And I choose "Unlock" in the open action menu
Then "Locked" "icon" should exist in the "Student 1" "table_row"
And "Locked" "icon" should not exist in the "Student 2" "table_row"
@javascript
Scenario: Locking and unlocking a grade item through editing form preserves individual student locks
Given I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I turn editing mode on
And I change window size to "large"
When I click on "Cell actions" "button" in the "//td[count(//th[.//descendant::a[normalize-space(text())='Manual grade 1']]/preceding-sibling::*)][../th[1]/a[normalize-space(text())='Student 1']]" "xpath_element"
And I choose "Edit grade" in the open action menu
And I set the field "Locked" to "1"
And I press "Save changes"
And I click on grade item menu "Manual grade 1" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
And I set the field "Locked" to "1"
And I click on "Save" "button" in the "Edit grade item" "dialogue"
And I click on grade item menu "Manual grade 1" of type "gradeitem" on "grader" page
And I choose "Edit grade item" in the open action menu
And I set the field "Locked" to "0"
And I click on "Save" "button" in the "Edit grade item" "dialogue"
Then "Locked" "icon" should exist in the "Student 1" "table_row"
And "Locked" "icon" should not exist in the "Student 2" "table_row"
+1 -1
View File
@@ -68,7 +68,7 @@ define('GRADE_AGGREGATE_WEIGHTED_MEAN2', 11);
define('GRADE_AGGREGATE_EXTRACREDIT_MEAN', 12);
/**
* GRADE_AGGREGATE_WEIGHTED_MEAN2 - Use Natural in the category for grade aggregation.
* GRADE_AGGREGATE_SUM - Use Natural in the category for grade aggregation.
*/
define('GRADE_AGGREGATE_SUM', 13);
+11 -15
View File
@@ -2536,25 +2536,21 @@ class grade_category extends grade_object {
$result = $this->grade_item->set_locked($lockedstate, $cascade, true);
if ($cascade) {
//process all children - items and categories
if ($children = grade_item::fetch_all(array('categoryid'=>$this->id))) {
// Process all children - items and categories.
if ($children = grade_item::fetch_all(['categoryid' => $this->id])) {
foreach ($children as $child) {
$child->set_locked($lockedstate, $cascade, false);
foreach ($children as $child) {
$child->set_locked($lockedstate, true, false);
if (empty($lockedstate) and $refresh) {
//refresh when unlocking
$child->refresh_grades();
}
if (empty($lockedstate) && $refresh) {
// Refresh when unlocking.
$child->refresh_grades();
}
}
}
if ($children = grade_category::fetch_all(array('parent'=>$this->id))) {
foreach ($children as $child) {
$child->set_locked($lockedstate, true, true);
}
if ($children = static::fetch_all(['parent' => $this->id])) {
foreach ($children as $child) {
$child->set_locked($lockedstate, $cascade, true);
}
}
+9 -5
View File
@@ -649,12 +649,16 @@ class grade_item extends grade_object {
*/
public function set_locked($lockedstate, $cascade=false, $refresh=true) {
if ($lockedstate) {
/// setting lock
if ($this->needsupdate) {
return false; // can not lock grade without first having final grade
// Setting lock.
if (empty($this->id)) {
return false;
} else if ($this->needsupdate) {
// Can not lock grade without first having final grade,
// so we schedule it to be locked as soon as regrading is finished.
$this->locktime = time() - 1;
} else {
$this->locked = time();
}
$this->locked = time();
$this->update();
if ($cascade) {
+7 -3
View File
@@ -784,13 +784,17 @@ class grade_category_test extends \grade_base_testcase {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'set_locked'));
// Will return false as cannot lock a grade that needs updating.
$this->assertFalse($category->set_locked(1));
// Even though a grade that needs updating cannot be locked, set_locked will return true because it will successfully
// schedule the locking for as soon as final grades are recalculated.
$this->assertTrue($category->set_locked(1));
// The category should not be locked yet as we are waiting for a recalculation.
$this->assertFalse($category->is_locked());
grade_regrade_final_grades($this->courseid);
// Get the category from the db again.
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue($category->set_locked(1));
// The category is locked now.
$this->assertTrue($category->is_locked());
}
protected function sub_test_grade_category_is_hidden() {
+3 -1
View File
@@ -64,7 +64,7 @@ information provided here is intended especially for developers.
the page displays a heading for the activity (usually a h2 heading containing the activity name).
* New method moodleform::filter_shown_headers() is created to show some expanded headers only and hide the rest.
* count_words() and count_letters() have a new optional parameter called $format to format the text before doing the counting.
* New core_renderer::sr_text method to generate screen reader only inline texts without using html_writter.
* New core_renderer::sr_text method to generate screen reader only inline texts without using html_writer.
* New events \core\event\qbank_plugin_enabled and \core\event\qbank_plugin_disabled are triggered when a qbank plugin is enabled or
disabled respectively, with the plugin's frankenstyle name. Any plugins that need to perform an action in response to a qbank
plugin being enabled or disabled should observe these events.
@@ -163,6 +163,8 @@ being forced open in all behat tests.
but a subset can be specified instead.
* core/form-autocomplete now supports disabled options in the source select list. These will be displayed in the autocomplete
options with the aria-disabled attribute, and will not be selectable.
* The method grade_item::set_locked() now returns true if the grade item needs to be updated. The method schedules the locking of
the grade item once the recalculations are completed. (This was fixed in 4.3, 4.2.2)
=== 4.2 ===