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

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

Ported code to xenial

File size: 24.1 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 * External grading API
19 *
20 * @package    core_grading
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
28require_once("$CFG->libdir/externallib.php");
29require_once("$CFG->dirroot/grade/grading/lib.php");
30
31/**
32 * core grading functions
33 */
34class core_grading_external extends external_api {
35
36    /**
37     * Describes the parameters for get_definitions
38     * @return external_function_parameters
39     * @since Moodle 2.5
40     */
41    public static function get_definitions_parameters() {
42        return new external_function_parameters(
43            array(
44                'cmids' => new external_multiple_structure(
45                        new external_value(PARAM_INT, 'course module id'), '1 or more course module ids'),
46                'areaname' => new external_value(PARAM_AREA, 'area name'),
47                'activeonly' => new external_value(PARAM_BOOL, 'Only the active method', VALUE_DEFAULT, 0)
48            )
49        );
50    }
51
52    /**
53     * Returns the definitions for the requested course module ids
54     * @param array of ints $cmids
55     * @param string $areaname
56     * @param boolean $activeonly default is false, if true, only the active method is returned
57     * @return array of areas with definitions for each requested course module id
58     * @since Moodle 2.5
59     */
60    public static function get_definitions($cmids, $areaname, $activeonly = false) {
61        global $DB, $CFG;
62        require_once("$CFG->dirroot/grade/grading/form/lib.php");
63        $params = self::validate_parameters(self::get_definitions_parameters(),
64            array('cmids' => $cmids,
65                'areaname' => $areaname,
66                'activeonly' => $activeonly));
67        $warnings = array();
68        $areas = array();
69        foreach ($params['cmids'] as $cmid) {
70            $context = context_module::instance($cmid);
71            try {
72                self::validate_context($context);
73            } catch (Exception $e) {
74                $warnings[] = array(
75                    'item' => 'module',
76                    'itemid' => $cmid,
77                    'message' => 'No access rights in module context',
78                    'warningcode' => '1'
79                );
80                continue;
81            }
82            // Check if the user has managegradingforms capability.
83            $isgradingmethodmanager = false;
84            if (has_capability('moodle/grade:managegradingforms', $context)) {
85                $isgradingmethodmanager = true;
86            }
87            $module = get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST);
88            $componentname = "mod_".$module->modname;
89
90            // Get the grading manager.
91            $gradingmanager = get_grading_manager($context, $componentname, $params['areaname']);
92            // Get the controller for each grading method.
93            $methods = array();
94            if ($params['activeonly'] == true) {
95                $methods[] = $gradingmanager->get_active_method();
96            } else {
97                $methods = array_keys($gradingmanager->get_available_methods(false));
98            }
99
100            $area = array();
101            $area['cmid'] = $cmid;
102            $area['contextid'] = $context->id;
103            $area['component'] = $componentname;
104            $area['areaname'] = $params['areaname'];
105            $area['activemethod'] = $gradingmanager->get_active_method();
106            $area['definitions'] = array();
107
108            foreach ($methods as $method) {
109                $controller = $gradingmanager->get_controller($method);
110                $def = $controller->get_definition(true);
111                if ($def == false) {
112                    continue;
113                }
114                if ($isgradingmethodmanager == false) {
115                    $isviewable = true;
116                    if ($def->status != gradingform_controller::DEFINITION_STATUS_READY) {
117                        $warnings[] = array(
118                            'item' => 'module',
119                            'itemid' => $cmid,
120                            'message' => 'Capability moodle/grade:managegradingforms required to view draft definitions',
121                            'warningcode' => '1'
122                        );
123                        $isviewable = false;
124                    }
125                    if (!empty($def->options)) {
126                        $options = json_decode($def->options);
127                        if (isset($options->alwaysshowdefinition) &&
128                            $options->alwaysshowdefinition == 0) {
129                            $warnings[] = array(
130                                'item' => 'module',
131                                'itemid' => $cmid,
132                                'message' => 'Capability moodle/grade:managegradingforms required to preview definition',
133                                'warningcode' => '1'
134                            );
135                            $isviewable = false;
136                        }
137                    }
138                    if ($isviewable == false) {
139                        continue;
140                    }
141                }
142                $definition = array();
143                $definition['id'] = $def->id;
144                $definition['method'] = $method;
145                $definition['name'] = $def->name;
146                $definition['description'] = $def->description;
147                $definition['descriptionformat'] = $def->descriptionformat;
148                $definition['status'] = $def->status;
149                $definition['copiedfromid'] = $def->copiedfromid;
150                $definition['timecreated'] = $def->timecreated;
151                $definition['usercreated'] = $def->usercreated;
152                $definition['timemodified'] = $def->timemodified;
153                $definition['usermodified'] = $def->usermodified;
154                $definition['timecopied'] = $def->timecopied;
155                // Format the description text field.
156                $formattedtext = external_format_text($definition['description'],
157                    $definition['descriptionformat'],
158                    $context->id,
159                    $componentname,
160                    'description',
161                    $def->id);
162                $definition['description'] = $formattedtext[0];
163                $definition['descriptionformat'] = $formattedtext[1];
164
165                $details = $controller->get_external_definition_details();
166                $items = array();
167                foreach ($details as $key => $value) {
168                    $items[$key] = self::format_text($def->{$key}, $context->id, $componentname, $def->id);
169                }
170                $definition[$method] = $items;
171                $area['definitions'][] = $definition;
172            }
173            $areas[] = $area;
174        }
175        $result = array(
176            'areas' => $areas,
177            'warnings' => $warnings
178        );
179        return $result;
180    }
181
182    /**
183     * Recursively processes all elements in an array and runs external_format_text()on
184     * all elements which have a text field and associated format field with a key name
185     * that ends with the text 'format'. The modified array is returned.
186     * @param array $items the array to be processed
187     * @param int $contextid
188     * @param string $componentname
189     * @param int $itemid
190     * @see external_format_text in lib/externallib.php
191     * @return array the input array with all fields formatted
192     */
193    private static function format_text($items, $contextid, $componentname, $itemid) {
194        $formatkeys = array();
195        foreach ($items as $key => $value) {
196            if (!is_array($value) && substr_compare($key, 'format', -6, 6) === 0) {
197                $formatkeys[] = $key;
198            }
199        }
200        foreach ($formatkeys as $formatkey) {
201            $descriptionkey = substr($formatkey, 0, -6);
202            $formattedtext = external_format_text($items[$descriptionkey],
203                $items[$formatkey],
204                $contextid,
205                $componentname,
206                'description',
207                $itemid);
208            $items[$descriptionkey] = $formattedtext[0];
209            $items[$formatkey] = $formattedtext[1];
210        }
211        foreach ($items as $key => $value) {
212            if (is_array($value)) {
213                $items[$key] = self::format_text($value, $contextid, $componentname, $itemid);
214            }
215        }
216        return $items;
217    }
218
219    /**
220     * Creates a grading area
221     * @return external_single_structure
222     * @since  Moodle 2.5
223     */
224    private static function grading_area() {
225        return new external_single_structure(
226            array (
227                'cmid'    => new external_value(PARAM_INT, 'course module id'),
228                'contextid'  => new external_value(PARAM_INT, 'context id'),
229                'component' => new external_value(PARAM_TEXT, 'component name'),
230                'areaname' => new external_value(PARAM_TEXT, 'area name'),
231                'activemethod' => new external_value(PARAM_TEXT, 'active method', VALUE_OPTIONAL),
232                'definitions'  => new external_multiple_structure(self::definition(), 'definitions')
233            )
234        );
235    }
236
237    /**
238     * creates a grading form definition
239     * @return external_single_structure
240     * @since  Moodle 2.5
241     */
242    private static function definition() {
243        global $CFG;
244        $definition = array();
245        $definition['id']                = new external_value(PARAM_INT, 'definition id', VALUE_OPTIONAL);
246        $definition['method']            = new external_value(PARAM_TEXT, 'method');
247        $definition['name']              = new external_value(PARAM_TEXT, 'name');
248        $definition['description']       = new external_value(PARAM_RAW, 'description', VALUE_OPTIONAL);
249        $definition['descriptionformat'] = new external_format_value('description', VALUE_OPTIONAL);
250        $definition['status']            = new external_value(PARAM_INT, 'status');
251        $definition['copiedfromid']      = new external_value(PARAM_INT, 'copied from id', VALUE_OPTIONAL);
252        $definition['timecreated']       = new external_value(PARAM_INT, 'creation time');
253        $definition['usercreated']       = new external_value(PARAM_INT, 'user who created definition');
254        $definition['timemodified']      = new external_value(PARAM_INT, 'last modified time');
255        $definition['usermodified']      = new external_value(PARAM_INT, 'user who modified definition');
256        $definition['timecopied']        = new external_value(PARAM_INT, 'time copied', VALUE_OPTIONAL);
257        foreach (self::get_grading_methods() as $method) {
258            require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
259            $details  = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details');
260            if ($details != null) {
261                $items = array();
262                foreach ($details as $key => $value) {
263                    $details[$key]->required = VALUE_OPTIONAL;
264                    $items[$key] = $value;
265                }
266                $definition[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
267            }
268        }
269        return new external_single_structure($definition);
270    }
271
272    /**
273     * Describes the get_definitions return value
274     * @return external_single_structure
275     * @since Moodle 2.5
276     */
277    public static function get_definitions_returns() {
278        return new external_single_structure(
279            array(
280                'areas' => new external_multiple_structure(self::grading_area(), 'list of grading areas'),
281                'warnings' => new external_warnings()
282            )
283        );
284    }
285
286    /**
287     * @return array of available grading methods
288     * @since Moodle 2.5
289     */
290    private static function get_grading_methods() {
291        $methods = array_keys(grading_manager::available_methods(false));
292        return $methods;
293    }
294
295    /**
296     * Describes the parameters for get_gradingform_instances
297     *
298     * @return external_function_parameters
299     * @since Moodle 2.6
300     */
301    public static function get_gradingform_instances_parameters() {
302        return new external_function_parameters(
303            array(
304                'definitionid' => new external_value(PARAM_INT, 'definition id'),
305                'since' => new external_value(PARAM_INT, 'submitted since', VALUE_DEFAULT, 0)
306            )
307        );
308    }
309
310    /**
311     * Returns the instances and fillings for the requested definition id
312     *
313     * @param int $definitionid
314     * @param int $since only return instances with timemodified >= since
315     * @return array of grading instances with fillings for the definition id
316     * @since Moodle 2.6
317     */
318    public static function get_gradingform_instances($definitionid, $since = 0) {
319        global $DB, $CFG;
320        require_once("$CFG->dirroot/grade/grading/form/lib.php");
321        $params = self::validate_parameters(self::get_gradingform_instances_parameters(),
322            array('definitionid' => $definitionid,
323                'since' => $since));
324        $instances = array();
325        $warnings = array();
326
327        $definition = $DB->get_record('grading_definitions',
328            array('id' => $params['definitionid']),
329            'areaid,method', MUST_EXIST);
330        $area = $DB->get_record('grading_areas',
331            array('id' => $definition->areaid),
332            'contextid,component', MUST_EXIST);
333
334        $context = context::instance_by_id($area->contextid);
335        require_capability('moodle/grade:managegradingforms', $context);
336
337        $gradingmanager = get_grading_manager($definition->areaid);
338        $controller = $gradingmanager->get_controller($definition->method);
339        $activeinstances = $controller->get_all_active_instances ($params['since']);
340        $details = $controller->get_external_instance_filling_details();
341        if ($details == null) {
342            $warnings[] = array(
343                'item' => 'definition',
344                'itemid' => $params['definitionid'],
345                'message' => 'Fillings unavailable because get_external_instance_filling_details is not defined',
346                'warningcode' => '1'
347            );
348        }
349        $getfilling = null;
350        if (method_exists('gradingform_'.$definition->method.'_instance', 'get_'.$definition->method.'_filling')) {
351            $getfilling = 'get_'.$definition->method.'_filling';
352        } else {
353            $warnings[] = array(
354                'item' => 'definition',
355                'itemid' => $params['definitionid'],
356                'message' => 'Fillings unavailable because get_'.$definition->method.'_filling is not defined',
357                'warningcode' => '1'
358            );
359        }
360        foreach ($activeinstances as $activeinstance) {
361            $instance = array();
362            $instance['id'] = $activeinstance->get_id();
363            $instance['raterid'] = $activeinstance->get_data('raterid');
364            $instance['itemid'] = $activeinstance->get_data('itemid');
365            $instance['rawgrade'] = $activeinstance->get_data('rawgrade');
366            $instance['status'] = $activeinstance->get_data('status');
367            $instance['feedback'] = $activeinstance->get_data('feedback');
368            $instance['feedbackformat'] = $activeinstance->get_data('feedbackformat');
369            // Format the feedback text field.
370            $formattedtext = external_format_text($activeinstance->get_data('feedback'),
371                $activeinstance->get_data('feedbackformat'),
372                $context->id,
373                $area->component,
374                'feedback',
375                $params['definitionid']);
376            $instance['feedback'] = $formattedtext[0];
377            $instance['feedbackformat'] = $formattedtext[1];
378            $instance['timemodified'] = $activeinstance->get_data('timemodified');
379
380            if ($details != null && $getfilling != null) {
381                $fillingdata = $activeinstance->$getfilling();
382                $filling = array();
383                foreach ($details as $key => $value) {
384                    $filling[$key] = self::format_text($fillingdata[$key],
385                        $context->id,
386                        $area->component,
387                        $params['definitionid']);
388                }
389                $instance[$definition->method] = $filling;
390            }
391            $instances[] = $instance;
392        }
393        $result = array(
394            'instances' => $instances,
395            'warnings' => $warnings
396        );
397        return $result;
398    }
399
400    /**
401     * Creates a grading instance
402     *
403     * @return external_single_structure
404     * @since  Moodle 2.6
405     */
406    private static function grading_instance() {
407        global $CFG;
408        $instance = array();
409        $instance['id']                = new external_value(PARAM_INT, 'instance id');
410        $instance['raterid']           = new external_value(PARAM_INT, 'rater id');
411        $instance['itemid']            = new external_value(PARAM_INT, 'item id');
412        $instance['rawgrade']          = new external_value(PARAM_TEXT, 'raw grade', VALUE_OPTIONAL);
413        $instance['status']            = new external_value(PARAM_INT, 'status');
414        $instance['feedback']          = new external_value(PARAM_RAW, 'feedback', VALUE_OPTIONAL);
415        $instance['feedbackformat']    = new external_format_value('feedback', VALUE_OPTIONAL);
416        $instance['timemodified']      = new external_value(PARAM_INT, 'modified time');
417        foreach (self::get_grading_methods() as $method) {
418            require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
419            $details  = call_user_func('gradingform_'.$method.'_controller::get_external_instance_filling_details');
420            if ($details != null) {
421                $items = array();
422                foreach ($details as $key => $value) {
423                    $details[$key]->required = VALUE_OPTIONAL;
424                    $items[$key] = $value;
425                }
426                $instance[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
427            }
428        }
429        return new external_single_structure($instance);
430    }
431
432    /**
433     * Describes the get_gradingform_instances return value
434     *
435     * @return external_single_structure
436     * @since Moodle 2.6
437     */
438    public static function get_gradingform_instances_returns() {
439        return new external_single_structure(
440            array(
441                'instances' => new external_multiple_structure(self::grading_instance(), 'list of grading instances'),
442                'warnings' => new external_warnings()
443            )
444        );
445    }
446
447    /**
448     * Describes the parameters for save_definitions
449     *
450     * @return external_function_parameters
451     * @since Moodle 2.8
452     */
453    public static function save_definitions_parameters() {
454        return new external_function_parameters(
455            array(
456                'areas' => new external_multiple_structure(self::grading_area(), 'areas with definitions to save')
457            )
458        );
459    }
460
461    /**
462     * Saves the areas and definitions
463     * @param array $areas array of areas containing definitions to be saved
464     * @return null
465     * @throws invalid_parameter_exception
466     * @since Moodle 2.8
467     */
468    public static function save_definitions($areas) {
469        $params = self::validate_parameters(self::save_definitions_parameters(),
470                                            array('areas' => $areas));
471
472        foreach ($params['areas'] as $area) {
473
474            $context = context::instance_by_id($area['contextid']);
475            require_capability('moodle/grade:managegradingforms', $context);
476            $gradingmanager = get_grading_manager($context, $area['component'], $area['areaname']);
477            $gradingmanager->set_active_method($area['activemethod']);
478            $availablemethods = $gradingmanager->get_available_methods();
479
480            foreach ($area['definitions'] as $definition) {
481                if (array_key_exists($definition['method'], $availablemethods)) {
482                    $controller = $gradingmanager->get_controller($definition['method']);
483                    $controller->update_definition(self::create_definition_object($definition));
484                } else {
485                    throw new invalid_parameter_exception('Unknown Grading method: '. $definition['method']);
486                }
487            }
488        }
489    }
490
491    /**
492     * Describes the return value for save_definitions
493     *
494     * @return external_single_structure
495     * @since Moodle 2.8
496     */
497    public static function save_definitions_returns() {
498        return null;
499    }
500
501    /**
502     * Creates a definition stdClass object using the values from the definition
503     * array that is passed in as a parameter
504     *
505     * @param array $definition
506     * @return stdClass definition object
507     * @since Moodle 2.8
508     */
509    private static function create_definition_object($definition) {
510        global $CFG;
511
512        $method = $definition['method'];
513        $definitionobject = new stdClass();
514        foreach ($definition as $key => $value) {
515            if (!is_array($value)) {
516                $definitionobject->$key = $value;
517            }
518        }
519        $text = '';
520        $format = FORMAT_MOODLE;
521        if (isset($definition['description'])) {
522            $text = $definition['description'];
523            if (isset($definition['descriptionformat'])) {
524                $format = $definition['descriptionformat'];
525            }
526        }
527        $definitionobject->description_editor = array('text' => $text, 'format' => $format);
528
529        require_once("$CFG->libdir/filelib.php");
530        require_once($CFG->dirroot.'/grade/grading/form/'.$method.'/lib.php');
531        $details  = call_user_func('gradingform_'.$method.'_controller::get_external_definition_details');
532        $methodarray = array();
533        foreach (array_keys($details) as $definitionkey) {
534            $items = array();
535            $idnumber = 1;
536            foreach ($definition[$method][$definitionkey] as $item) {
537                $processeditem = self::set_new_ids($item, $idnumber);
538                $items[$processeditem['id']] = $processeditem;
539                $idnumber++;
540            }
541            $definitionobjectkey = substr($definitionkey, strlen($method.'_'));
542            $methodarray[$definitionobjectkey] = $items;
543            $definitionobject->$method = $methodarray;
544        }
545
546        return $definitionobject;
547    }
548
549    /**
550     * Recursively iterates through arrays. Any array without an id key-value combination
551     * is assumed to be an array of values to be inserted and an id key-value is added with
552     * the value matching the regex '/^NEWID\d+$/' that is expected by each grading form implementation.
553     *
554     * @param array $arraytoset the array to be processed
555     * @param int $startnumber the starting number for the new id numbers
556     * @return array with missing id keys added for all arrays
557     * @since Moodle 2.8
558     */
559    private static function set_new_ids($arraytoset, $startnumber) {
560        $result = array();
561        $foundid = false;
562        $number = $startnumber;
563        foreach ($arraytoset as $key1 => $value1) {
564            if (is_array($value1)) {
565                foreach ($value1 as $key2 => $value2) {
566                    $processedvalue = self::set_new_ids($value2, $number);
567                    $result[$key1][$processedvalue['id']] = $processedvalue;
568                    $number++;
569                }
570            } else {
571                $result[$key1] = $value1;
572            }
573            if ($key1 === 'id') {
574                $foundid = true;
575            }
576        }
577        if (!$foundid) {
578            $result['id'] = 'NEWID'.$number;
579        }
580        return $result;
581    }
582
583}
Note: See TracBrowser for help on using the repository browser.