source: moodle/trunk/fuentes/lib/classes/grades_external.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

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