MDL-30085 core_grades functions moved to correct location
[moodle.git] / grade / externallib.php
CommitLineData
3f0ea6d8
PC
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
9028d9b5 18 * External grading API
3f0ea6d8 19 *
9028d9b5 20 * @package core_grading
3f0ea6d8
PC
21 * @since Moodle 2.5
22 * @copyright 2013 Paul Charsley
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die;
27
a446fc00 28// NOTE: add any new core_grades_ classes to /lib/classes/ directory.
9028d9b5
PC
29
30/**
dd8b6a19
MG
31 * core grading functions. Renamed to core_grading_external
32 *
33 * @since Moodle 2.5
34 * @deprecated since 2.6 See MDL-30085. Please do not use this class any more.
9028d9b5
PC
35 * @see core_grading_external
36 */
37class core_grade_external extends external_api {
38
dd8b6a19 39 public static function get_definitions_parameters() {
9028d9b5
PC
40 return core_grading_external::get_definitions_parameters();
41 }
42
dd8b6a19 43 public static function get_definitions($cmids, $areaname, $activeonly = false) {
9028d9b5
PC
44 return core_grading_external::get_definitions($cmids, $areaname, $activeonly = false);
45 }
46
47 public static function get_definitions_returns() {
48 return core_grading_external::get_definitions_returns();
49 }
50
3f0ea6d8 51}
198e802f
JL
52
53/**
54 * Core grades external functions
55 *
56 * @package core_grades
57 * @category external
58 * @copyright 2012 Andrew Davis
59 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
60 * @since Moodle 2.7
61 */
62class core_grades_external extends external_api {
63 /**
64 * Returns description of method parameters
65 *
66 * @return external_function_parameters
67 * @since Moodle 2.7
68 */
69 public static function get_grades_parameters() {
70 return new external_function_parameters(
71 array(
72 'courseid' => new external_value(PARAM_INT, 'id of course'),
73 'component' => new external_value(
74 PARAM_COMPONENT, 'A component, for example mod_forum or mod_quiz', VALUE_DEFAULT, ''),
75 'activityid' => new external_value(PARAM_INT, 'The activity ID', VALUE_DEFAULT, null),
76 'userids' => new external_multiple_structure(
77 new external_value(PARAM_INT, 'user ID'),
78 'An array of user IDs, leave empty to just retrieve grade item information', VALUE_DEFAULT, array()
79 )
80 )
81 );
82 }
83
84 /**
85 * Retrieve grade items and, optionally, student grades
86 *
87 * @param int $courseid Course id
88 * @param string $component Component name
89 * @param int $activityid Activity id
90 * @param array $userids Array of user ids
91 * @return array Array of grades
92 * @since Moodle 2.7
93 */
94 public static function get_grades($courseid, $component = null, $activityid = null, $userids = array()) {
95 global $CFG, $USER, $DB;
96 require_once("$CFG->libdir/gradelib.php");
97
98 $params = self::validate_parameters(self::get_grades_parameters(),
99 array('courseid' => $courseid, 'component' => $component, 'activityid' => $activityid, 'userids' => $userids));
100
101 $coursecontext = context_course::instance($params['courseid']);
102
103 try {
104 self::validate_context($coursecontext);
105 } catch (Exception $e) {
106 $exceptionparam = new stdClass();
107 $exceptionparam->message = $e->getMessage();
108 $exceptionparam->courseid = $params['courseid'];
109 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
110 }
111
112 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
113
114 $access = false;
115 if (has_capability('moodle/grade:viewall', $coursecontext)) {
116 // Can view all user's grades in this course.
117 $access = true;
118
119 } else if ($course->showgrades && count($params['userids']) == 1) {
120 // Course showgrades == students/parents can access grades.
121
122 if ($params['userids'][0] == $USER->id and has_capability('moodle/grade:view', $coursecontext)) {
123 // Student can view their own grades in this course.
124 $access = true;
125
126 } else if (has_capability('moodle/grade:viewall', context_user::instance($params['userids'][0]))) {
127 // User can view the grades of this user. Parent most probably.
128 $access = true;
129 }
130 }
131
132 if (!$access) {
133 throw new moodle_exception('nopermissiontoviewgrades', 'error');
134 }
135
136 $itemtype = null;
137 $itemmodule = null;
138 if (!empty($params['component'])) {
139 list($itemtype, $itemmodule) = normalize_component($params['component']);
140 }
141
142 $cm = null;
143 if (!empty($itemmodule) && !empty($activityid)) {
144 if (! $cm = get_coursemodule_from_id($itemmodule, $activityid)) {
145 throw new moodle_exception('invalidcoursemodule');
146 }
147 }
148
149 $cminstanceid = null;
150 if (!empty($cm)) {
151 $cminstanceid = $cm->instance;
152 }
153 $grades = grade_get_grades($params['courseid'], $itemtype, $itemmodule, $cminstanceid, $params['userids']);
154
155 $acitivityinstances = null;
156 if (empty($cm)) {
157 // If we're dealing with multiple activites load all the module info.
158 $modinfo = get_fast_modinfo($params['courseid']);
159 $acitivityinstances = $modinfo->get_instances();
160 }
161
162 foreach ($grades->items as $gradeitem) {
163 if (!empty($cm)) {
164 // If they only requested one activity we will already have the cm.
165 $modulecm = $cm;
166 } else if (!empty($gradeitem->itemmodule)) {
167 $modulecm = $acitivityinstances[$gradeitem->itemmodule][$gradeitem->iteminstance];
168 } else {
169 // Course grade item.
170 continue;
171 }
172
173 // Make student feedback ready for output.
174 foreach ($gradeitem->grades as $studentgrade) {
175 if (!empty($studentgrade->feedback)) {
176 list($studentgrade->feedback, $categoryinfo->feedbackformat) =
177 external_format_text($studentgrade->feedback, $studentgrade->feedbackformat,
178 $modulecm->id, $params['component'], 'feedback', null);
179 }
180 }
181 }
182
183 // Convert from objects to arrays so all web service clients are supported.
184 // While we're doing that we also remove grades the current user can't see due to hiding.
185 $gradesarray = array();
186 $canviewhidden = has_capability('moodle/grade:viewhidden', context_course::instance($params['courseid']));
187
188 $gradesarray['items'] = array();
189 foreach ($grades->items as $gradeitem) {
190 // Switch the stdClass instance for a grade item instance so we can call is_hidden() and use the ID.
191 $gradeiteminstance = self::get_grade_item(
192 $course->id, $gradeitem->itemtype, $gradeitem->itemmodule, $gradeitem->iteminstance, 0);
193 if (!$canviewhidden && $gradeiteminstance->is_hidden()) {
194 continue;
195 }
196 $gradeitemarray = (array)$gradeitem;
197 $gradeitemarray['grades'] = array();
198
199 if (!empty($gradeitem->grades)) {
200 foreach ($gradeitem->grades as $studentid => $studentgrade) {
201 $gradegradeinstance = grade_grade::fetch(
202 array(
203 'userid' => $studentid,
204 'itemid' => $gradeiteminstance->id
205 )
206 );
207 if (!$canviewhidden && $gradegradeinstance->is_hidden()) {
208 continue;
209 }
210 $gradeitemarray['grades'][$studentid] = (array)$studentgrade;
211 // Add the student ID as some WS clients can't access the array key.
212 $gradeitemarray['grades'][$studentid]['userid'] = $studentid;
213 }
214 }
215
216 // If they requested grades for multiple activities load the cm object now.
217 $modulecm = $cm;
218 if (empty($modulecm) && !empty($gradeiteminstance->itemmodule)) {
219 $modulecm = $acitivityinstances[$gradeiteminstance->itemmodule][$gradeiteminstance->iteminstance];
220 }
221 if ($gradeiteminstance->itemtype == 'course') {
222 $gradesarray['items']['course'] = $gradeitemarray;
223 $gradesarray['items']['course']['activityid'] = 'course';
224 } else {
225 $gradesarray['items'][$modulecm->id] = $gradeitemarray;
226 // Add the activity ID as some WS clients can't access the array key.
227 $gradesarray['items'][$modulecm->id]['activityid'] = $modulecm->id;
228 }
229 }
230
231 $gradesarray['outcomes'] = array();
232 foreach ($grades->outcomes as $outcome) {
233 $modulecm = $cm;
234 if (empty($modulecm)) {
235 $modulecm = $acitivityinstances[$outcome->itemmodule][$outcome->iteminstance];
236 }
237 $gradesarray['outcomes'][$modulecm->id] = (array)$outcome;
238 $gradesarray['outcomes'][$modulecm->id]['activityid'] = $modulecm->id;
239
240 $gradesarray['outcomes'][$modulecm->id]['grades'] = array();
241 if (!empty($outcome->grades)) {
242 foreach ($outcome->grades as $studentid => $studentgrade) {
243 if (!$canviewhidden) {
244 // Need to load the grade_grade object to check visibility.
245 $gradeiteminstance = self::get_grade_item(
246 $course->id, $outcome->itemtype, $outcome->itemmodule, $outcome->iteminstance, $outcome->itemnumber);
247 $gradegradeinstance = grade_grade::fetch(
248 array(
249 'userid' => $studentid,
250 'itemid' => $gradeiteminstance->id
251 )
252 );
253 // The grade grade may be legitimately missing if the student has no grade.
254 if (!empty($gradegradeinstance ) && $gradegradeinstance->is_hidden()) {
255 continue;
256 }
257 }
258 $gradesarray['outcomes'][$modulecm->id]['grades'][$studentid] = (array)$studentgrade;
259
260 // Add the student ID into the grade structure as some WS clients can't access the key.
261 $gradesarray['outcomes'][$modulecm->id]['grades'][$studentid]['userid'] = $studentid;
262 }
263 }
264 }
265
266 return $gradesarray;
267 }
268
269 /**
270 * Get a grade item
271 * @param int $courseid Course id
272 * @param string $itemtype Item type
273 * @param string $itemmodule Item module
274 * @param int $iteminstance Item instance
275 * @param int $itemnumber Item number
276 * @return grade_item A grade_item instance
277 */
278 private static function get_grade_item($courseid, $itemtype, $itemmodule = null, $iteminstance = null, $itemnumber = null) {
279 $gradeiteminstance = null;
280 if ($itemtype == 'course') {
281 $gradeiteminstance = grade_item::fetch(array('courseid' => $courseid, 'itemtype' => $itemtype));
282 } else {
283 $gradeiteminstance = grade_item::fetch(
284 array('courseid' => $courseid, 'itemtype' => $itemtype,
285 'itemmodule' => $itemmodule, 'iteminstance' => $iteminstance, 'itemnumber' => $itemnumber));
286 }
287 return $gradeiteminstance;
288 }
289
290 /**
291 * Returns description of method result value
292 *
293 * @return external_description
294 * @since Moodle 2.7
295 */
296 public static function get_grades_returns() {
297 return new external_single_structure(
298 array(
299 'items' => new external_multiple_structure(
300 new external_single_structure(
301 array(
302 'activityid' => new external_value(
303 PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'),
304 'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'),
305 'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'),
306 'name' => new external_value(PARAM_RAW, 'The module name'),
307 'grademin' => new external_value(PARAM_FLOAT, 'Minimum grade'),
308 'grademax' => new external_value(PARAM_FLOAT, 'Maximum grade'),
309 'gradepass' => new external_value(PARAM_FLOAT, 'The passing grade threshold'),
310 'locked' => new external_value(PARAM_BOOL, 'Is the grade item locked?'),
311 'hidden' => new external_value(PARAM_BOOL, 'Is the grade item hidden?'),
312 'grades' => new external_multiple_structure(
313 new external_single_structure(
314 array(
315 'userid' => new external_value(
316 PARAM_INT, 'Student ID'),
317 'grade' => new external_value(
318 PARAM_FLOAT, 'Student grade'),
319 'locked' => new external_value(
320 PARAM_BOOL, 'Is the student\'s grade locked?'),
321 'hidden' => new external_value(
322 PARAM_BOOL, 'Is the student\'s grade hidden?'),
323 'overridden' => new external_value(
324 PARAM_BOOL, 'Is the student\'s grade overridden?'),
325 'feedback' => new external_value(
326 PARAM_RAW, 'Feedback from the grader'),
327 'feedbackformat' => new external_value(
328 PARAM_INT, 'The format of the feedback'),
329 'usermodified' => new external_value(
330 PARAM_INT, 'The ID of the last user to modify this student grade'),
331 'datesubmitted' => new external_value(
332 PARAM_INT, 'A timestamp indicating when the student submitted the activity'),
333 'dategraded' => new external_value(
334 PARAM_INT, 'A timestamp indicating when the assignment was grades'),
335 'str_grade' => new external_value(
336 PARAM_RAW, 'A string representation of the grade'),
337 'str_long_grade' => new external_value(
338 PARAM_RAW, 'A nicely formatted string representation of the grade'),
339 'str_feedback' => new external_value(
340 PARAM_TEXT, 'A string representation of the feedback from the grader'),
341 )
342 )
343 ),
344 )
345 )
346 ),
347 'outcomes' => new external_multiple_structure(
348 new external_single_structure(
349 array(
350 'activityid' => new external_value(
351 PARAM_ALPHANUM, 'The ID of the activity or "course" for the course grade item'),
352 'itemnumber' => new external_value(PARAM_INT, 'Will be 0 unless the module has multiple grades'),
353 'scaleid' => new external_value(PARAM_INT, 'The ID of the custom scale or 0'),
354 'name' => new external_value(PARAM_RAW, 'The module name'),
355 'locked' => new external_value(PARAM_BOOL, 'Is the grade item locked?'),
356 'hidden' => new external_value(PARAM_BOOL, 'Is the grade item hidden?'),
357 'grades' => new external_multiple_structure(
358 new external_single_structure(
359 array(
360 'userid' => new external_value(
361 PARAM_INT, 'Student ID'),
362 'grade' => new external_value(
363 PARAM_FLOAT, 'Student grade'),
364 'locked' => new external_value(
365 PARAM_BOOL, 'Is the student\'s grade locked?'),
366 'hidden' => new external_value(
367 PARAM_BOOL, 'Is the student\'s grade hidden?'),
368 'feedback' => new external_value(
369 PARAM_RAW, 'Feedback from the grader'),
370 'feedbackformat' => new external_value(
371 PARAM_INT, 'The feedback format'),
372 'usermodified' => new external_value(
373 PARAM_INT, 'The ID of the last user to modify this student grade'),
374 'str_grade' => new external_value(
375 PARAM_RAW, 'A string representation of the grade'),
376 'str_feedback' => new external_value(
377 PARAM_TEXT, 'A string representation of the feedback from the grader'),
378 )
379 )
380 ),
381 )
382 ), 'An array of outcomes associated with the grade items', VALUE_OPTIONAL
383 )
384 )
385 );
386
387 }
388
389 /**
390 * Returns description of method parameters
391 *
392 * @return external_function_parameters
393 * @since Moodle 2.7
394 */
395 public static function update_grades_parameters() {
396 return new external_function_parameters(
397 array(
398 'source' => new external_value(PARAM_TEXT, 'The source of the grade update'),
399 'courseid' => new external_value(PARAM_INT, 'id of course'),
400 'component' => new external_value(PARAM_COMPONENT, 'A component, for example mod_forum or mod_quiz'),
401 'activityid' => new external_value(PARAM_INT, 'The activity ID'),
402 'itemnumber' => new external_value(
403 PARAM_INT, 'grade item ID number for modules that have multiple grades. Typically this is 0.'),
404 'grades' => new external_multiple_structure(
405 new external_single_structure(
406 array(
407 'studentid' => new external_value(PARAM_INT, 'Student ID'),
408 'grade' => new external_value(PARAM_FLOAT, 'Student grade'),
409 'str_feedback' => new external_value(
410 PARAM_TEXT, 'A string representation of the feedback from the grader', VALUE_OPTIONAL),
411 )
412 ), 'Any student grades to alter', VALUE_OPTIONAL),
413 'itemdetails' => new external_single_structure(
414 array(
415 'itemname' => new external_value(
416 PARAM_ALPHANUMEXT, 'The grade item name', VALUE_OPTIONAL),
417 'idnumber' => new external_value(
418 PARAM_INT, 'Arbitrary ID provided by the module responsible for the grade item', VALUE_OPTIONAL),
419 'gradetype' => new external_value(
420 PARAM_INT, 'The type of grade (0 = none, 1 = value, 2 = scale, 3 = text)', VALUE_OPTIONAL),
421 'grademax' => new external_value(
422 PARAM_FLOAT, 'Maximum grade allowed', VALUE_OPTIONAL),
423 'grademin' => new external_value(
424 PARAM_FLOAT, 'Minimum grade allowed', VALUE_OPTIONAL),
425 'scaleid' => new external_value(
426 PARAM_INT, 'The ID of the custom scale being is used', VALUE_OPTIONAL),
427 'multfactor' => new external_value(
428 PARAM_FLOAT, 'Multiply all grades by this number', VALUE_OPTIONAL),
429 'plusfactor' => new external_value(
430 PARAM_FLOAT, 'Add this to all grades', VALUE_OPTIONAL),
431 'deleted' => new external_value(
432 PARAM_BOOL, 'True if the grade item should be deleted', VALUE_OPTIONAL),
433 'hidden' => new external_value(
434 PARAM_BOOL, 'True if the grade item is hidden', VALUE_OPTIONAL),
435 ), 'Any grade item settings to alter', VALUE_OPTIONAL
436 )
437 )
438 );
439 }
440
441 /**
442 * Update a grade item and, optionally, student grades
443 *
444 * @param string $source The source of the grade update
445 * @param int $courseid The course id
446 * @param string $component Component name
447 * @param int $activityid The activity id
448 * @param int $itemnumber The item number
449 * @param array $grades Array of grades
450 * @param array $itemdetails Array of item details
451 * @return int A status flag
452 * @since Moodle 2.7
453 */
454 public static function update_grades($source, $courseid, $component, $activityid,
455 $itemnumber, $grades = array(), $itemdetails = array()) {
456 global $CFG;
457
458 require_once("$CFG->libdir/gradelib.php");
459
460 $params = self::validate_parameters(
461 self::update_grades_parameters(),
462 array(
463 'source' => $source,
464 'courseid' => $courseid,
465 'component' => $component,
466 'activityid' => $activityid,
467 'itemnumber' => $itemnumber,
468 'grades' => $grades,
469 'itemdetails' => $itemdetails
470 )
471 );
472
473 list($itemtype, $itemmodule) = normalize_component($params['component']);
474
475 if (! $cm = get_coursemodule_from_id($itemmodule, $activityid)) {
476 throw new moodle_exception('invalidcoursemodule');
477 }
478 $iteminstance = $cm->instance;
479
480 $coursecontext = context_course::instance($params['courseid']);
481
482 try {
483 self::validate_context($coursecontext);
484 } catch (Exception $e) {
485 $exceptionparam = new stdClass();
486 $exceptionparam->message = $e->getMessage();
487 $exceptionparam->courseid = $params['courseid'];
488 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
489 }
490
491 $hidinggrades = false;
492 $editinggradeitem = false;
493 $editinggrades = false;
494
495 $gradestructure = array();
496 foreach ($grades as $grade) {
497 $editinggrades = true;
498 $gradestructure[ $grade['studentid'] ] = array('userid' => $grade['studentid'], 'rawgrade' => $grade['grade']);
499 }
500 if (!empty($params['itemdetails'])) {
501 if (isset($params['itemdetails']['hidden'])) {
502 $hidinggrades = true;
503 } else {
504 $editinggradeitem = true;
505 }
506 }
507
508 if ($editinggradeitem && !has_capability('moodle/grade:manage', $coursecontext)) {
509 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null,
510 'moodle/grade:manage required to edit grade information');
511 }
512 if ($hidinggrades && !has_capability('moodle/grade:hide', $coursecontext) &&
513 !has_capability('moodle/grade:hide', $coursecontext)) {
514 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null,
515 'moodle/grade:hide required to hide grade items');
516 }
517 if ($editinggrades && !has_capability('moodle/grade:edit', $coursecontext)) {
518 throw new moodle_exception('nopermissiontoviewgrades', 'error', '', null,
519 'moodle/grade:edit required to edit grades');
520 }
521
522 return grade_update($params['source'], $params['courseid'], $itemtype,
523 $itemmodule, $iteminstance, $itemnumber, $gradestructure, $params['itemdetails']);
524 }
525
526 /**
527 * Returns description of method result value
528 *
529 * @return external_description
530 * @since Moodle 2.7
531 */
532 public static function update_grades_returns() {
533 return new external_single_structure(
534 array (
535 'result' => new external_value(
536 PARAM_INT,
537 'A value like ' . GRADE_UPDATE_OK . ' => OK, ' . GRADE_UPDATE_FAILED . ' => FAILED
538 as defined in lib/grade/constants.php')
539 )
540 );
541 }
542}