Merge branch 'MDL-72433-master' of git://github.com/peterRd/moodle

This commit is contained in:
Andrew Nicols 2021-10-29 11:27:39 +08:00
commit f764fa8e62
5 changed files with 6 additions and 658 deletions

View File

@ -34,395 +34,6 @@ require_once("$CFG->dirroot/grade/querylib.php");
* core grades functions
*/
class core_grades_external extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 2.7
* @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more.
* @see gradereport_user_external::get_grade_items for a similar function
*/
public static function get_grades_parameters() {
return new external_function_parameters(
array(
'courseid' => new external_value(PARAM_INT, 'id of course'),
'component' => new external_value(
PARAM_COMPONENT, 'A component, for example mod_forum or mod_quiz', VALUE_DEFAULT, ''),
'activityid' => new external_value(PARAM_INT, 'The activity ID', VALUE_DEFAULT, null),
'userids' => new external_multiple_structure(
new external_value(PARAM_INT, 'user ID'),
'An array of user IDs, leave empty to just retrieve grade item information', VALUE_DEFAULT, array()
)
)
);
}
/**
* Returns student course total grade and grades for activities.
* This function does not return category or manual items.
* This function is suitable for managers or teachers not students.
*
* @param int $courseid Course id
* @param string $component Component name
* @param int $activityid Activity id
* @param array $userids Array of user ids
* @return array Array of grades
* @since Moodle 2.7
* @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more.
* @see gradereport_user_external::get_grade_items for a similar function
*/
public static function get_grades($courseid, $component = null, $activityid = null, $userids = array()) {
global $CFG, $USER, $DB;
$params = self::validate_parameters(self::get_grades_parameters(),
array('courseid' => $courseid, 'component' => $component, 'activityid' => $activityid, 'userids' => $userids));
$gradesarray = array(
'items' => array(),
'outcomes' => array()
);
$coursecontext = context_course::instance($params['courseid']);
try {
self::validate_context($coursecontext);
} catch (Exception $e) {
$exceptionparam = new stdClass();
$exceptionparam->message = $e->getMessage();
$exceptionparam->courseid = $params['courseid'];
throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
}
require_capability('moodle/grade:viewhidden', $coursecontext);
$course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
$access = false;
if (has_capability('moodle/grade:viewall', $coursecontext)) {
// Can view all user's grades in this course.
$access = true;
} else if ($course->showgrades && count($params['userids']) == 1) {
// Course showgrades == students/parents can access grades.
if ($params['userids'][0] == $USER->id and has_capability('moodle/grade:view', $coursecontext)) {
// Student can view their own grades in this course.
$access = true;
} else if (has_capability('moodle/grade:viewall', context_user::instance($params['userids'][0]))) {
// User can view the grades of this user. Parent most probably.
$access = true;
}
}
if (!$access) {
throw new moodle_exception('nopermissiontoviewgrades', 'error');
}
$itemtype = null;
$itemmodule = null;
$iteminstance = null;
if (!empty($params['component'])) {
list($itemtype, $itemmodule) = normalize_component($params['component']);
}
$cm = null;
if (!empty($itemmodule) && !empty($params['activityid'])) {
if (!$cm = get_coursemodule_from_id($itemmodule, $params['activityid'])) {
throw new moodle_exception('invalidcoursemodule');
}
$iteminstance = $cm->instance;
}
// Load all the module info.
$modinfo = get_fast_modinfo($params['courseid']);
$activityinstances = $modinfo->get_instances();
$gradeparams = array('courseid' => $params['courseid']);
if (!empty($itemtype)) {
$gradeparams['itemtype'] = $itemtype;
}
if (!empty($itemmodule)) {
$gradeparams['itemmodule'] = $itemmodule;
}
if (!empty($iteminstance)) {
$gradeparams['iteminstance'] = $iteminstance;
}
if ($activitygrades = grade_item::fetch_all($gradeparams)) {
$canviewhidden = has_capability('moodle/grade:viewhidden', context_course::instance($params['courseid']));
foreach ($activitygrades as $activitygrade) {
if ($activitygrade->itemtype != 'course' and $activitygrade->itemtype != 'mod') {
// This function currently only supports course and mod grade items. Manual and category not supported.
continue;
}
$context = $coursecontext;
if ($activitygrade->itemtype == 'course') {
$item = grade_get_course_grades($course->id, $params['userids']);
$item->itemnumber = 0;
$grades = new stdClass;
$grades->items = array($item);
$grades->outcomes = array();
} else {
$cm = $activityinstances[$activitygrade->itemmodule][$activitygrade->iteminstance];
$instance = $cm->instance;
$context = context_module::instance($cm->id, IGNORE_MISSING);
$grades = grade_get_grades($params['courseid'], $activitygrade->itemtype,
$activitygrade->itemmodule, $instance, $params['userids']);
}
// Convert from objects to arrays so all web service clients are supported.
// While we're doing that we also remove grades the current user can't see due to hiding.
foreach ($grades->items as $gradeitem) {
// Switch the stdClass instance for a grade item instance so we can call is_hidden() and use the ID.
$gradeiteminstance = self::get_grade_item(
$course->id, $activitygrade->itemtype, $activitygrade->itemmodule, $activitygrade->iteminstance, 0);
if (!$canviewhidden && $gradeiteminstance->is_hidden()) {
continue;
}
// Format mixed bool/integer parameters.
$gradeitem->hidden = (empty($gradeitem->hidden)) ? 0 : $gradeitem->hidden;
$gradeitem->locked = (empty($gradeitem->locked)) ? 0 : $gradeitem->locked;
$gradeitemarray = (array)$gradeitem;
$gradeitemarray['grades'] = array();
if (!empty($gradeitem->grades)) {
foreach ($gradeitem->grades as $studentid => $studentgrade) {
if (!$canviewhidden) {
// Need to load the grade_grade object to check visibility.
$gradegradeinstance = grade_grade::fetch(
array(
'userid' => $studentid,
'itemid' => $gradeiteminstance->id
)
);
// The grade grade may be legitimately missing if the student has no grade.
if (!empty($gradegradeinstance) && $gradegradeinstance->is_hidden()) {
continue;
}
}
// Format mixed bool/integer parameters.
$studentgrade->hidden = (empty($studentgrade->hidden)) ? 0 : $studentgrade->hidden;
$studentgrade->locked = (empty($studentgrade->locked)) ? 0 : $studentgrade->locked;
$studentgrade->overridden = (empty($studentgrade->overridden)) ? 0 : $studentgrade->overridden;
if ($gradeiteminstance->itemtype != 'course' and !empty($studentgrade->feedback)) {
list($studentgrade->feedback, $studentgrade->feedbackformat) =
external_format_text($studentgrade->feedback, $studentgrade->feedbackformat,
$context->id, $params['component'], 'feedback', null);
}
$gradeitemarray['grades'][$studentid] = (array)$studentgrade;
// Add the student ID as some WS clients can't access the array key.
$gradeitemarray['grades'][$studentid]['userid'] = $studentid;
}
}
if ($gradeiteminstance->itemtype == 'course') {
$gradesarray['items']['course'] = $gradeitemarray;
$gradesarray['items']['course']['activityid'] = 'course';
} else {
$gradesarray['items'][$cm->id] = $gradeitemarray;
// Add the activity ID as some WS clients can't access the array key.
$gradesarray['items'][$cm->id]['activityid'] = $cm->id;
}
}
foreach ($grades->outcomes as $outcome) {
// Format mixed bool/integer parameters.
$outcome->hidden = (empty($outcome->hidden)) ? 0 : $outcome->hidden;
$outcome->locked = (empty($outcome->locked)) ? 0 : $outcome->locked;
$gradesarray['outcomes'][$cm->id] = (array)$outcome;
$gradesarray['outcomes'][$cm->id]['activityid'] = $cm->id;
$gradesarray['outcomes'][$cm->id]['grades'] = array();
if (!empty($outcome->grades)) {
foreach ($outcome->grades as $studentid => $studentgrade) {
if (!$canviewhidden) {
// Need to load the grade_grade object to check visibility.
$gradeiteminstance = self::get_grade_item($course->id, $activitygrade->itemtype,
$activitygrade->itemmodule, $activitygrade->iteminstance,
$activitygrade->itemnumber);
$gradegradeinstance = grade_grade::fetch(
array(
'userid' => $studentid,
'itemid' => $gradeiteminstance->id
)
);
// The grade grade may be legitimately missing if the student has no grade.
if (!empty($gradegradeinstance ) && $gradegradeinstance->is_hidden()) {
continue;
}
}
// Format mixed bool/integer parameters.
$studentgrade->hidden = (empty($studentgrade->hidden)) ? 0 : $studentgrade->hidden;
$studentgrade->locked = (empty($studentgrade->locked)) ? 0 : $studentgrade->locked;
if (!empty($studentgrade->feedback)) {
list($studentgrade->feedback, $studentgrade->feedbackformat) =
external_format_text($studentgrade->feedback, $studentgrade->feedbackformat,
$context->id, $params['component'], 'feedback', null);
}
$gradesarray['outcomes'][$cm->id]['grades'][$studentid] = (array)$studentgrade;
// Add the student ID into the grade structure as some WS clients can't access the key.
$gradesarray['outcomes'][$cm->id]['grades'][$studentid]['userid'] = $studentid;
}
}
}
}
}
return $gradesarray;
}
/**
* Get a grade item
* @param int $courseid Course id
* @param string $itemtype Item type
* @param string $itemmodule Item module
* @param int $iteminstance Item instance
* @param int $itemnumber Item number
* @return grade_item A grade_item instance
* @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more.
* @see gradereport_user_external::get_grade_items for a similar function
*/
private static function get_grade_item($courseid, $itemtype, $itemmodule = null, $iteminstance = null, $itemnumber = null) {
$gradeiteminstance = null;
if ($itemtype == 'course') {
$gradeiteminstance = grade_item::fetch(array('courseid' => $courseid, 'itemtype' => $itemtype));
} else {
$gradeiteminstance = grade_item::fetch(
array('courseid' => $courseid, 'itemtype' => $itemtype,
'itemmodule' => $itemmodule, 'iteminstance' => $iteminstance, 'itemnumber' => $itemnumber));
}
return $gradeiteminstance;
}
/**
* Returns description of method result value
*
* @return external_description
* @since Moodle 2.7
* @deprecated Moodle 3.2 MDL-51373 - Please do not call this function any more.
* @see gradereport_user_external::get_grade_items for a similar function
*/
public static function get_grades_returns() {
return new external_single_structure(
array(
'items' => new external_multiple_structure(
new external_single_structure(
array(
'activityid' => new external_value(
PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'),
'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'),
'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'),
'name' => new external_value(PARAM_RAW, 'The module name'),
'grademin' => new external_value(PARAM_FLOAT, 'Minimum grade'),
'grademax' => new external_value(PARAM_FLOAT, 'Maximum grade'),
'gradepass' => new external_value(PARAM_FLOAT, 'The passing grade threshold'),
'locked' => new external_value(PARAM_INT, '0 means not locked, > 1 is a date to lock until'),
'hidden' => new external_value(PARAM_INT, '0 means not hidden, > 1 is a date to hide until'),
'grades' => new external_multiple_structure(
new external_single_structure(
array(
'userid' => new external_value(
PARAM_INT, 'Student ID'),
'grade' => new external_value(
PARAM_FLOAT, 'Student grade'),
'locked' => new external_value(
PARAM_INT, '0 means not locked, > 1 is a date to lock until'),
'hidden' => new external_value(
PARAM_INT, '0 means not hidden, 1 hidden, > 1 is a date to hide until'),
'overridden' => new external_value(
PARAM_INT, '0 means not overridden, > 1 means overridden'),
'feedback' => new external_value(
PARAM_RAW, 'Feedback from the grader'),
'feedbackformat' => new external_value(
PARAM_INT, 'The format of the feedback'),
'usermodified' => new external_value(
PARAM_INT, 'The ID of the last user to modify this student grade'),
'datesubmitted' => new external_value(
PARAM_INT, 'A timestamp indicating when the student submitted the activity'),
'dategraded' => new external_value(
PARAM_INT, 'A timestamp indicating when the assignment was grades'),
'str_grade' => new external_value(
PARAM_RAW, 'A string representation of the grade'),
'str_long_grade' => new external_value(
PARAM_RAW, 'A nicely formatted string representation of the grade'),
'str_feedback' => new external_value(
PARAM_RAW, 'A formatted string representation of the feedback from the grader'),
)
)
),
)
)
),
'outcomes' => new external_multiple_structure(
new external_single_structure(
array(
'activityid' => new external_value(
PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'),
'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'),
'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'),
'name' => new external_value(PARAM_RAW, 'The module name'),
'locked' => new external_value(PARAM_INT, '0 means not locked, > 1 is a date to lock until'),
'hidden' => new external_value(PARAM_INT, '0 means not hidden, > 1 is a date to hide until'),
'grades' => new external_multiple_structure(
new external_single_structure(
array(
'userid' => new external_value(
PARAM_INT, 'Student ID'),
'grade' => new external_value(
PARAM_FLOAT, 'Student grade'),
'locked' => new external_value(
PARAM_INT, '0 means not locked, > 1 is a date to lock until'),
'hidden' => new external_value(
PARAM_INT, '0 means not hidden, 1 hidden, > 1 is a date to hide until'),
'feedback' => new external_value(
PARAM_RAW, 'Feedback from the grader'),
'feedbackformat' => new external_value(
PARAM_INT, 'The feedback format'),
'usermodified' => new external_value(
PARAM_INT, 'The ID of the last user to modify this student grade'),
'str_grade' => new external_value(
PARAM_RAW, 'A string representation of the grade'),
'str_feedback' => new external_value(
PARAM_RAW, 'A formatted string representation of the feedback from the grader'),
)
)
),
)
), 'An array of outcomes associated with the grade items', VALUE_OPTIONAL
)
)
);
}
/**
* Marking the method as deprecated.
*
* @return bool
*/
public static function get_grades_is_deprecated() {
return true;
}
/**
* Returns description of method parameters
*

View File

@ -906,16 +906,6 @@ $functions = array(
'type' => 'read',
'ajax' => true,
),
'core_grades_get_grades' => array(
'classname' => 'core_grades_external',
'methodname' => 'get_grades',
'description' => '** DEPRECATED ** Please do not call this function any more.
Returns student course total grade and grades for activities.
This function does not return category or manual items.
This function is suitable for managers or teachers not students.',
'type' => 'read',
'capabilities' => 'moodle/grade:view, moodle/grade:viewall, moodle/grade:viewhidden'
),
'core_grades_update_grades' => array(
'classname' => 'core_grades_external',
'methodname' => 'update_grades',

View File

@ -134,262 +134,6 @@ class core_grades_external_testcase extends externallib_advanced_testcase {
return array($course, $assignment, $student1, $student2, $teacher, $parent);
}
/**
* Test get_grades()
*/
public function test_get_grades() {
global $CFG;
$this->resetAfterTest(true);
$CFG->enableoutcomes = 1;
$assignmentname = 'The assignment';
$student1rawgrade = 10;
$student2rawgrade = 20;
list($course, $assignment, $student1, $student2, $teacher, $parent) =
$this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
$assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST);
// Teacher requesting a student grade for the assignment.
$this->setUser($teacher);
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student1->id)
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
// Teacher requesting all the grades of student1 in a course.
$grades = core_grades_external::get_grades(
$course->id,
null,
null,
array($student1->id)
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertTrue(count($grades['items']) == 2);
$this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
$this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, 'course', $student1->id));
$outcome = $this->get_outcome($grades, $assigmentcm->id);
$this->assertEquals($outcome['name'], 'Team work');
$this->assertEquals(0, $this->get_outcome_student_grade($grades, $assigmentcm->id, $student1->id));
// Teacher requesting all the grades of all the students in a course.
$grades = core_grades_external::get_grades(
$course->id,
null,
null,
array($student1->id, $student2->id)
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertTrue(count($grades['items']) == 2);
$this->assertTrue(count($grades['items'][0]['grades']) == 2);
$this->assertTrue(count($grades['items'][1]['grades']) == 2);
// Student requesting another student's grade for the assignment (should fail).
$this->setUser($student1);
try {
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student2->id)
);
$this->fail('moodle_exception expected');
} catch (moodle_exception $ex) {
$this->assertTrue(true);
}
// Parent requesting their child's grade for the assignment (should fail).
$this->setUser($parent);
try {
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student1->id)
);
$this->fail('moodle_exception expected');
} catch (moodle_exception $ex) {
$this->assertTrue(true);
}
// Parent requesting another student's grade for the assignment(should fail).
try {
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student2->id)
);
$this->fail('moodle_exception expected');
} catch (moodle_exception $ex) {
$this->assertTrue(true);
}
// Student requesting all other student grades for the assignment (should fail).
try {
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student1->id, $student2->id)
);
$this->fail('moodle_exception expected');
} catch (moodle_exception $ex) {
$this->assertTrue(true);
}
// Student requesting only grade item information (should fail).
try {
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array()
);
$this->fail('moodle_exception expected');
} catch (moodle_exception $ex) {
$this->assertTrue(true);
}
// Teacher requesting student grades for a course.
$this->setUser($teacher);
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student1->id, $student2->id)
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
$this->assertEquals($student2rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student2->id));
// Teacher requesting grade item information.
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$activity = $this->get_activity($grades, $assigmentcm->id);
$this->assertEquals($activity['name'], $assignmentname);
$this->assertEquals(count($activity['grades']), 0);
// Teacher requesting all grade items in a course.
$grades = core_grades_external::get_grades(
$course->id
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertTrue(count($grades['items']) == 2);
$activity = $this->get_activity($grades, $assigmentcm->id);
$this->assertEquals($activity['name'], $assignmentname);
$this->assertEquals(count($activity['grades']), 0);
$outcome = $this->get_outcome($grades, $assigmentcm->id);
$this->assertEquals($outcome['name'], 'Team work');
// Hide a grade item then have student request it.
$result = core_grades_external::update_grades(
'test',
$course->id,
'mod_assign',
$assigmentcm->id,
0,
array(),
array('hidden' => 1)
);
$result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
$this->assertTrue($result == GRADE_UPDATE_OK);
// Check it's definitely hidden.
$grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
$this->assertEquals($grades->items[0]->hidden, 1);
// Teacher should still be able to see the hidden grades.
$this->setUser($teacher);
$grades = core_grades_external::get_grades(
$course->id,
'mod_assign',
$assigmentcm->id,
array($student1->id)
);
$grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
$this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
}
/**
* Get an activity
*
* @param array $grades Array of grades
* @param int $cmid Activity course module id
* @return strdClass Activity object
*/
private function get_activity($grades, $cmid) {
foreach ($grades['items'] as $item) {
if ($item['activityid'] == $cmid) {
return $item;
}
}
return null;
}
/**
* Get a grade for an activity
*
* @param array $grades Array of grades
* @param int $cmid Activity course module id
* @param int $studentid Student it
* @return stdClass Activity Object
*/
private function get_activity_student_grade($grades, $cmid, $studentid) {
$item = $this->get_activity($grades, $cmid);
foreach ($item['grades'] as $grade) {
if ($grade['userid'] == $studentid) {
return $grade['grade'];
}
}
return null;
}
/**
* Get an ouctome
*
* @param array $grades Array of grades
* @param int $cmid Activity course module id
* @return stdClass Outcome object
*/
private function get_outcome($grades, $cmid) {
foreach ($grades['outcomes'] as $outcome) {
if ($outcome['activityid'] == $cmid) {
return $outcome;
}
}
return null;
}
/**
* Get a grade from an outcome
*
* @param array $grades Array of grades
* @param int $cmid Activity course module id
* @param int $studentid Student id
* @return stdClass Outcome object
*/
private function get_outcome_student_grade($grades, $cmid, $studentid) {
$outcome = $this->get_outcome($grades, $cmid);
foreach ($outcome['grades'] as $grade) {
if ($grade['userid'] == $studentid) {
return $grade['grade'];
}
}
return null;
}
/**
* Test update_grades()
*/

View File

@ -111,8 +111,11 @@ completely removed from Moodle core too.
Refer to upgrade.php to see transitioning from similar plugin criteria to core
Refer to completion/upgrade.txt for additional information.
* The method enable_plugin() has been added to the core_plugininfo\base class and it has been implemented by all the plugininfo
classes extending it. When possible, the enable_plugin() method will store these changes into the config_log table, to let admins
check when and who has enabled/disabled plugins.
classes extending it. When possible, the enable_plugin() method will store these changes into the config_log table, to let admins
check when and who has enabled/disabled plugins.
* Final deprecation: The following functions along with associated tests have been removed:
- core_grades_external::get_grades
- core_grades_external::get_grade_item
=== 3.11.4 ===
* A new option dontforcesvgdownload has been added to the $options parameter of the send_file() function.

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2021102900.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2021102900.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev+ (Build: 20211026)'; // Human-friendly version name