MDL-60637 group: remove useless id number validation on web services
[moodle.git] / group / externallib.php
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/>.
18 /**
19  * External groups API
20  *
21  * @package    core_group
22  * @category   external
23  * @copyright  2009 Petr Skodak
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once("$CFG->libdir/externallib.php");
31 /**
32  * Group external functions
33  *
34  * @package    core_group
35  * @category   external
36  * @copyright  2011 Jerome Mouneyrac
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  * @since Moodle 2.2
39  */
40 class core_group_external extends external_api {
42     /**
43      * Returns description of method parameters
44      *
45      * @return external_function_parameters
46      * @since Moodle 2.2
47      */
48     public static function create_groups_parameters() {
49         return new external_function_parameters(
50             array(
51                 'groups' => new external_multiple_structure(
52                     new external_single_structure(
53                         array(
54                             'courseid' => new external_value(PARAM_INT, 'id of course'),
55                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
56                             'description' => new external_value(PARAM_RAW, 'group description text'),
57                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
58                             'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
59                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
60                         )
61                     ), 'List of group object. A group has a courseid, a name, a description and an enrolment key.'
62                 )
63             )
64         );
65     }
67     /**
68      * Create groups
69      *
70      * @param array $groups array of group description arrays (with keys groupname and courseid)
71      * @return array of newly created groups
72      * @since Moodle 2.2
73      */
74     public static function create_groups($groups) {
75         global $CFG, $DB;
76         require_once("$CFG->dirroot/group/lib.php");
78         $params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));
80         $transaction = $DB->start_delegated_transaction();
82         $groups = array();
84         foreach ($params['groups'] as $group) {
85             $group = (object)$group;
87             if (trim($group->name) == '') {
88                 throw new invalid_parameter_exception('Invalid group name');
89             }
90             if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) {
91                 throw new invalid_parameter_exception('Group with the same name already exists in the course');
92             }
94             // now security checks
95             $context = context_course::instance($group->courseid, IGNORE_MISSING);
96             try {
97                 self::validate_context($context);
98             } catch (Exception $e) {
99                 $exceptionparam = new stdClass();
100                 $exceptionparam->message = $e->getMessage();
101                 $exceptionparam->courseid = $group->courseid;
102                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
103             }
104             require_capability('moodle/course:managegroups', $context);
106             // Validate format.
107             $group->descriptionformat = external_validate_format($group->descriptionformat);
109             // finally create the group
110             $group->id = groups_create_group($group, false);
111             if (!isset($group->enrolmentkey)) {
112                 $group->enrolmentkey = '';
113             }
114             if (!isset($group->idnumber)) {
115                 $group->idnumber = '';
116             }
118             $groups[] = (array)$group;
119         }
121         $transaction->allow_commit();
123         return $groups;
124     }
126     /**
127      * Returns description of method result value
128      *
129      * @return external_description
130      * @since Moodle 2.2
131      */
132     public static function create_groups_returns() {
133         return new external_multiple_structure(
134             new external_single_structure(
135                 array(
136                     'id' => new external_value(PARAM_INT, 'group record id'),
137                     'courseid' => new external_value(PARAM_INT, 'id of course'),
138                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
139                     'description' => new external_value(PARAM_RAW, 'group description text'),
140                     'descriptionformat' => new external_format_value('description'),
141                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
142                     'idnumber' => new external_value(PARAM_RAW, 'id number')
143                 )
144             ), 'List of group object. A group has an id, a courseid, a name, a description and an enrolment key.'
145         );
146     }
148     /**
149      * Returns description of method parameters
150      *
151      * @return external_function_parameters
152      * @since Moodle 2.2
153      */
154     public static function get_groups_parameters() {
155         return new external_function_parameters(
156             array(
157                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')
158                         ,'List of group id. A group id is an integer.'),
159             )
160         );
161     }
163     /**
164      * Get groups definition specified by ids
165      *
166      * @param array $groupids arrays of group ids
167      * @return array of group objects (id, courseid, name, enrolmentkey)
168      * @since Moodle 2.2
169      */
170     public static function get_groups($groupids) {
171         $params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids));
173         $groups = array();
174         foreach ($params['groupids'] as $groupid) {
175             // validate params
176             $group = groups_get_group($groupid, 'id, courseid, name, idnumber, description, descriptionformat, enrolmentkey', MUST_EXIST);
178             // now security checks
179             $context = context_course::instance($group->courseid, IGNORE_MISSING);
180             try {
181                 self::validate_context($context);
182             } catch (Exception $e) {
183                 $exceptionparam = new stdClass();
184                 $exceptionparam->message = $e->getMessage();
185                 $exceptionparam->courseid = $group->courseid;
186                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
187             }
188             require_capability('moodle/course:managegroups', $context);
190             list($group->description, $group->descriptionformat) =
191                 external_format_text($group->description, $group->descriptionformat,
192                         $context->id, 'group', 'description', $group->id);
194             $groups[] = (array)$group;
195         }
197         return $groups;
198     }
200     /**
201      * Returns description of method result value
202      *
203      * @return external_description
204      * @since Moodle 2.2
205      */
206     public static function get_groups_returns() {
207         return new external_multiple_structure(
208             new external_single_structure(
209                 array(
210                     'id' => new external_value(PARAM_INT, 'group record id'),
211                     'courseid' => new external_value(PARAM_INT, 'id of course'),
212                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
213                     'description' => new external_value(PARAM_RAW, 'group description text'),
214                     'descriptionformat' => new external_format_value('description'),
215                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
216                     'idnumber' => new external_value(PARAM_RAW, 'id number')
217                 )
218             )
219         );
220     }
222     /**
223      * Returns description of method parameters
224      *
225      * @return external_function_parameters
226      * @since Moodle 2.2
227      */
228     public static function get_course_groups_parameters() {
229         return new external_function_parameters(
230             array(
231                 'courseid' => new external_value(PARAM_INT, 'id of course'),
232             )
233         );
234     }
236     /**
237      * Get all groups in the specified course
238      *
239      * @param int $courseid id of course
240      * @return array of group objects (id, courseid, name, enrolmentkey)
241      * @since Moodle 2.2
242      */
243     public static function get_course_groups($courseid) {
244         $params = self::validate_parameters(self::get_course_groups_parameters(), array('courseid'=>$courseid));
246         // now security checks
247         $context = context_course::instance($params['courseid'], IGNORE_MISSING);
248         try {
249             self::validate_context($context);
250         } catch (Exception $e) {
251                 $exceptionparam = new stdClass();
252                 $exceptionparam->message = $e->getMessage();
253                 $exceptionparam->courseid = $params['courseid'];
254                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
255         }
256         require_capability('moodle/course:managegroups', $context);
258         $gs = groups_get_all_groups($params['courseid'], 0, 0,
259             'g.id, g.courseid, g.name, g.idnumber, g.description, g.descriptionformat, g.enrolmentkey');
261         $groups = array();
262         foreach ($gs as $group) {
263             list($group->description, $group->descriptionformat) =
264                 external_format_text($group->description, $group->descriptionformat,
265                         $context->id, 'group', 'description', $group->id);
266             $groups[] = (array)$group;
267         }
269         return $groups;
270     }
272     /**
273      * Returns description of method result value
274      *
275      * @return external_description
276      * @since Moodle 2.2
277      */
278     public static function get_course_groups_returns() {
279         return new external_multiple_structure(
280             new external_single_structure(
281                 array(
282                     'id' => new external_value(PARAM_INT, 'group record id'),
283                     'courseid' => new external_value(PARAM_INT, 'id of course'),
284                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
285                     'description' => new external_value(PARAM_RAW, 'group description text'),
286                     'descriptionformat' => new external_format_value('description'),
287                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
288                     'idnumber' => new external_value(PARAM_RAW, 'id number')
289                 )
290             )
291         );
292     }
294     /**
295      * Returns description of method parameters
296      *
297      * @return external_function_parameters
298      * @since Moodle 2.2
299      */
300     public static function delete_groups_parameters() {
301         return new external_function_parameters(
302             array(
303                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
304             )
305         );
306     }
308     /**
309      * Delete groups
310      *
311      * @param array $groupids array of group ids
312      * @since Moodle 2.2
313      */
314     public static function delete_groups($groupids) {
315         global $CFG, $DB;
316         require_once("$CFG->dirroot/group/lib.php");
318         $params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
320         $transaction = $DB->start_delegated_transaction();
322         foreach ($params['groupids'] as $groupid) {
323             // validate params
324             $groupid = validate_param($groupid, PARAM_INT);
325             if (!$group = groups_get_group($groupid, '*', IGNORE_MISSING)) {
326                 // silently ignore attempts to delete nonexisting groups
327                 continue;
328             }
330             // now security checks
331             $context = context_course::instance($group->courseid, IGNORE_MISSING);
332             try {
333                 self::validate_context($context);
334             } catch (Exception $e) {
335                 $exceptionparam = new stdClass();
336                 $exceptionparam->message = $e->getMessage();
337                 $exceptionparam->courseid = $group->courseid;
338                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
339             }
340             require_capability('moodle/course:managegroups', $context);
342             groups_delete_group($group);
343         }
345         $transaction->allow_commit();
346     }
348     /**
349      * Returns description of method result value
350      *
351      * @return null
352      * @since Moodle 2.2
353      */
354     public static function delete_groups_returns() {
355         return null;
356     }
359     /**
360      * Returns description of method parameters
361      *
362      * @return external_function_parameters
363      * @since Moodle 2.2
364      */
365     public static function get_group_members_parameters() {
366         return new external_function_parameters(
367             array(
368                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
369             )
370         );
371     }
373     /**
374      * Return all members for a group
375      *
376      * @param array $groupids array of group ids
377      * @return array with  group id keys containing arrays of user ids
378      * @since Moodle 2.2
379      */
380     public static function get_group_members($groupids) {
381         $members = array();
383         $params = self::validate_parameters(self::get_group_members_parameters(), array('groupids'=>$groupids));
385         foreach ($params['groupids'] as $groupid) {
386             // validate params
387             $group = groups_get_group($groupid, 'id, courseid, name, enrolmentkey', MUST_EXIST);
388             // now security checks
389             $context = context_course::instance($group->courseid, IGNORE_MISSING);
390             try {
391                 self::validate_context($context);
392             } catch (Exception $e) {
393                 $exceptionparam = new stdClass();
394                 $exceptionparam->message = $e->getMessage();
395                 $exceptionparam->courseid = $group->courseid;
396                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
397             }
398             require_capability('moodle/course:managegroups', $context);
400             $groupmembers = groups_get_members($group->id, 'u.id', 'lastname ASC, firstname ASC');
402             $members[] = array('groupid'=>$groupid, 'userids'=>array_keys($groupmembers));
403         }
405         return $members;
406     }
408     /**
409      * Returns description of method result value
410      *
411      * @return external_description
412      * @since Moodle 2.2
413      */
414     public static function get_group_members_returns() {
415         return new external_multiple_structure(
416             new external_single_structure(
417                 array(
418                     'groupid' => new external_value(PARAM_INT, 'group record id'),
419                     'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')),
420                 )
421             )
422         );
423     }
426     /**
427      * Returns description of method parameters
428      *
429      * @return external_function_parameters
430      * @since Moodle 2.2
431      */
432     public static function add_group_members_parameters() {
433         return new external_function_parameters(
434             array(
435                 'members'=> new external_multiple_structure(
436                     new external_single_structure(
437                         array(
438                             'groupid' => new external_value(PARAM_INT, 'group record id'),
439                             'userid' => new external_value(PARAM_INT, 'user id'),
440                         )
441                     )
442                 )
443             )
444         );
445     }
447     /**
448      * Add group members
449      *
450      * @param array $members of arrays with keys userid, groupid
451      * @since Moodle 2.2
452      */
453     public static function add_group_members($members) {
454         global $CFG, $DB;
455         require_once("$CFG->dirroot/group/lib.php");
457         $params = self::validate_parameters(self::add_group_members_parameters(), array('members'=>$members));
459         $transaction = $DB->start_delegated_transaction();
460         foreach ($params['members'] as $member) {
461             // validate params
462             $groupid = $member['groupid'];
463             $userid = $member['userid'];
465             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
466             $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
468             // now security checks
469             $context = context_course::instance($group->courseid, IGNORE_MISSING);
470             try {
471                 self::validate_context($context);
472             } catch (Exception $e) {
473                 $exceptionparam = new stdClass();
474                 $exceptionparam->message = $e->getMessage();
475                 $exceptionparam->courseid = $group->courseid;
476                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
477             }
478             require_capability('moodle/course:managegroups', $context);
480             // now make sure user is enrolled in course - this is mandatory requirement,
481             // unfortunately this is slow
482             if (!is_enrolled($context, $userid)) {
483                 throw new invalid_parameter_exception('Only enrolled users may be members of groups');
484             }
486             groups_add_member($group, $user);
487         }
489         $transaction->allow_commit();
490     }
492     /**
493      * Returns description of method result value
494      *
495      * @return null
496      * @since Moodle 2.2
497      */
498     public static function add_group_members_returns() {
499         return null;
500     }
503     /**
504      * Returns description of method parameters
505      *
506      * @return external_function_parameters
507      * @since Moodle 2.2
508      */
509     public static function delete_group_members_parameters() {
510         return new external_function_parameters(
511             array(
512                 'members'=> new external_multiple_structure(
513                     new external_single_structure(
514                         array(
515                             'groupid' => new external_value(PARAM_INT, 'group record id'),
516                             'userid' => new external_value(PARAM_INT, 'user id'),
517                         )
518                     )
519                 )
520             )
521         );
522     }
524     /**
525      * Delete group members
526      *
527      * @param array $members of arrays with keys userid, groupid
528      * @since Moodle 2.2
529      */
530     public static function delete_group_members($members) {
531         global $CFG, $DB;
532         require_once("$CFG->dirroot/group/lib.php");
534         $params = self::validate_parameters(self::delete_group_members_parameters(), array('members'=>$members));
536         $transaction = $DB->start_delegated_transaction();
538         foreach ($params['members'] as $member) {
539             // validate params
540             $groupid = $member['groupid'];
541             $userid = $member['userid'];
543             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
544             $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
546             // now security checks
547             $context = context_course::instance($group->courseid, IGNORE_MISSING);
548             try {
549                 self::validate_context($context);
550             } catch (Exception $e) {
551                 $exceptionparam = new stdClass();
552                 $exceptionparam->message = $e->getMessage();
553                 $exceptionparam->courseid = $group->courseid;
554                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
555             }
556             require_capability('moodle/course:managegroups', $context);
558             if (!groups_remove_member_allowed($group, $user)) {
559                 throw new moodle_exception('errorremovenotpermitted', 'group', '', fullname($user));
560             }
561             groups_remove_member($group, $user);
562         }
564         $transaction->allow_commit();
565     }
567     /**
568      * Returns description of method result value
569      *
570      * @return null
571      * @since Moodle 2.2
572      */
573     public static function delete_group_members_returns() {
574         return null;
575     }
577     /**
578      * Returns description of method parameters
579      *
580      * @return external_function_parameters
581      * @since Moodle 2.3
582      */
583     public static function create_groupings_parameters() {
584         return new external_function_parameters(
585             array(
586                 'groupings' => new external_multiple_structure(
587                     new external_single_structure(
588                         array(
589                             'courseid' => new external_value(PARAM_INT, 'id of course'),
590                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
591                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
592                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
593                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
594                         )
595                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
596                 )
597             )
598         );
599     }
601     /**
602      * Create groupings
603      *
604      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
605      * @return array of newly created groupings
606      * @since Moodle 2.3
607      */
608     public static function create_groupings($groupings) {
609         global $CFG, $DB;
610         require_once("$CFG->dirroot/group/lib.php");
612         $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings));
614         $transaction = $DB->start_delegated_transaction();
616         $groupings = array();
618         foreach ($params['groupings'] as $grouping) {
619             $grouping = (object)$grouping;
621             if (trim($grouping->name) == '') {
622                 throw new invalid_parameter_exception('Invalid grouping name');
623             }
624             if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
625                 throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
626             }
628             // Now security checks            .
629             $context = context_course::instance($grouping->courseid);
630             try {
631                 self::validate_context($context);
632             } catch (Exception $e) {
633                 $exceptionparam = new stdClass();
634                 $exceptionparam->message = $e->getMessage();
635                 $exceptionparam->courseid = $grouping->courseid;
636                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
637             }
638             require_capability('moodle/course:managegroups', $context);
640             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
642             // Finally create the grouping.
643             $grouping->id = groups_create_grouping($grouping);
644             $groupings[] = (array)$grouping;
645         }
647         $transaction->allow_commit();
649         return $groupings;
650     }
652     /**
653      * Returns description of method result value
654      *
655      * @return external_description
656      * @since Moodle 2.3
657      */
658     public static function create_groupings_returns() {
659         return new external_multiple_structure(
660             new external_single_structure(
661                 array(
662                     'id' => new external_value(PARAM_INT, 'grouping record id'),
663                     'courseid' => new external_value(PARAM_INT, 'id of course'),
664                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
665                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
666                     'descriptionformat' => new external_format_value('description'),
667                     'idnumber' => new external_value(PARAM_RAW, 'id number')
668                 )
669             ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.'
670         );
671     }
673     /**
674      * Returns description of method parameters
675      *
676      * @return external_function_parameters
677      * @since Moodle 2.3
678      */
679     public static function update_groupings_parameters() {
680         return new external_function_parameters(
681             array(
682                 'groupings' => new external_multiple_structure(
683                     new external_single_structure(
684                         array(
685                             'id' => new external_value(PARAM_INT, 'id of grouping'),
686                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
687                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
688                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
689                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
690                         )
691                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
692                 )
693             )
694         );
695     }
697     /**
698      * Update groupings
699      *
700      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
701      * @return array of newly updated groupings
702      * @since Moodle 2.3
703      */
704     public static function update_groupings($groupings) {
705         global $CFG, $DB;
706         require_once("$CFG->dirroot/group/lib.php");
708         $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings));
710         $transaction = $DB->start_delegated_transaction();
712         foreach ($params['groupings'] as $grouping) {
713             $grouping = (object)$grouping;
715             if (trim($grouping->name) == '') {
716                 throw new invalid_parameter_exception('Invalid grouping name');
717             }
719             if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) {
720                 throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course");
721             }
723             // Check if the new modified grouping name already exists in the course.
724             if ($grouping->name != $currentgrouping->name and
725                     $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
726                 throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
727             }
729             $grouping->courseid = $currentgrouping->courseid;
731             // Now security checks.
732             $context = context_course::instance($grouping->courseid);
733             try {
734                 self::validate_context($context);
735             } catch (Exception $e) {
736                 $exceptionparam = new stdClass();
737                 $exceptionparam->message = $e->getMessage();
738                 $exceptionparam->courseid = $grouping->courseid;
739                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
740             }
741             require_capability('moodle/course:managegroups', $context);
743             // We must force allways FORMAT_HTML.
744             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
746             // Finally update the grouping.
747             groups_update_grouping($grouping);
748         }
750         $transaction->allow_commit();
752         return null;
753     }
755     /**
756      * Returns description of method result value
757      *
758      * @return external_description
759      * @since Moodle 2.3
760      */
761     public static function update_groupings_returns() {
762         return null;
763     }
765     /**
766      * Returns description of method parameters
767      *
768      * @return external_function_parameters
769      * @since Moodle 2.3
770      */
771     public static function get_groupings_parameters() {
772         return new external_function_parameters(
773             array(
774                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')
775                         , 'List of grouping id. A grouping id is an integer.'),
776                 'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0)
777             )
778         );
779     }
781     /**
782      * Get groupings definition specified by ids
783      *
784      * @param array $groupingids arrays of grouping ids
785      * @param boolean $returngroups return the associated groups if true. The default is false.
786      * @return array of grouping objects (id, courseid, name)
787      * @since Moodle 2.3
788      */
789     public static function get_groupings($groupingids, $returngroups = false) {
790         global $CFG, $DB;
791         require_once("$CFG->dirroot/group/lib.php");
792         require_once("$CFG->libdir/filelib.php");
794         $params = self::validate_parameters(self::get_groupings_parameters(),
795                                             array('groupingids' => $groupingids,
796                                                   'returngroups' => $returngroups));
798         $groupings = array();
799         foreach ($params['groupingids'] as $groupingid) {
800             // Validate params.
801             $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST);
803             // Now security checks.
804             $context = context_course::instance($grouping->courseid);
805             try {
806                 self::validate_context($context);
807             } catch (Exception $e) {
808                 $exceptionparam = new stdClass();
809                 $exceptionparam->message = $e->getMessage();
810                 $exceptionparam->courseid = $grouping->courseid;
811                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
812             }
813             require_capability('moodle/course:managegroups', $context);
815             list($grouping->description, $grouping->descriptionformat) =
816                 external_format_text($grouping->description, $grouping->descriptionformat,
817                         $context->id, 'grouping', 'description', $grouping->id);
819             $groupingarray = (array)$grouping;
821             if ($params['returngroups']) {
822                 $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ".
823                                                "ON g.id = gg.groupid WHERE gg.groupingid = ? ".
824                                                "ORDER BY groupid", array($groupingid));
825                 if ($grouprecords) {
826                     $groups = array();
827                     foreach ($grouprecords as $grouprecord) {
828                         list($grouprecord->description, $grouprecord->descriptionformat) =
829                         external_format_text($grouprecord->description, $grouprecord->descriptionformat,
830                         $context->id, 'group', 'description', $grouprecord->groupid);
831                         $groups[] = array('id' => $grouprecord->groupid,
832                                           'name' => $grouprecord->name,
833                                           'idnumber' => $grouprecord->idnumber,
834                                           'description' => $grouprecord->description,
835                                           'descriptionformat' => $grouprecord->descriptionformat,
836                                           'enrolmentkey' => $grouprecord->enrolmentkey,
837                                           'courseid' => $grouprecord->courseid
838                                           );
839                     }
840                     $groupingarray['groups'] = $groups;
841                 }
842             }
843             $groupings[] = $groupingarray;
844         }
846         return $groupings;
847     }
849     /**
850      * Returns description of method result value
851      *
852      * @return external_description
853      * @since Moodle 2.3
854      */
855     public static function get_groupings_returns() {
856         return new external_multiple_structure(
857             new external_single_structure(
858                 array(
859                     'id' => new external_value(PARAM_INT, 'grouping record id'),
860                     'courseid' => new external_value(PARAM_INT, 'id of course'),
861                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
862                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
863                     'descriptionformat' => new external_format_value('description'),
864                     'idnumber' => new external_value(PARAM_RAW, 'id number'),
865                     'groups' => new external_multiple_structure(
866                         new external_single_structure(
867                             array(
868                                 'id' => new external_value(PARAM_INT, 'group record id'),
869                                 'courseid' => new external_value(PARAM_INT, 'id of course'),
870                                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
871                                 'description' => new external_value(PARAM_RAW, 'group description text'),
872                                 'descriptionformat' => new external_format_value('description'),
873                                 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
874                                 'idnumber' => new external_value(PARAM_RAW, 'id number')
875                             )
876                         ),
877                     'optional groups', VALUE_OPTIONAL)
878                 )
879             )
880         );
881     }
883     /**
884      * Returns description of method parameters
885      *
886      * @return external_function_parameters
887      * @since Moodle 2.3
888      */
889     public static function get_course_groupings_parameters() {
890         return new external_function_parameters(
891             array(
892                 'courseid' => new external_value(PARAM_INT, 'id of course'),
893             )
894         );
895     }
897     /**
898      * Get all groupings in the specified course
899      *
900      * @param int $courseid id of course
901      * @return array of grouping objects (id, courseid, name, enrolmentkey)
902      * @since Moodle 2.3
903      */
904     public static function get_course_groupings($courseid) {
905         global $CFG;
906         require_once("$CFG->dirroot/group/lib.php");
907         require_once("$CFG->libdir/filelib.php");
909         $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid));
911         // Now security checks.
912         $context = context_course::instance($params['courseid']);
914         try {
915             self::validate_context($context);
916         } catch (Exception $e) {
917                 $exceptionparam = new stdClass();
918                 $exceptionparam->message = $e->getMessage();
919                 $exceptionparam->courseid = $params['courseid'];
920                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
921         }
922         require_capability('moodle/course:managegroups', $context);
924         $gs = groups_get_all_groupings($params['courseid']);
926         $groupings = array();
927         foreach ($gs as $grouping) {
928             list($grouping->description, $grouping->descriptionformat) =
929                 external_format_text($grouping->description, $grouping->descriptionformat,
930                         $context->id, 'grouping', 'description', $grouping->id);
931             $groupings[] = (array)$grouping;
932         }
934         return $groupings;
935     }
937     /**
938      * Returns description of method result value
939      *
940      * @return external_description
941      * @since Moodle 2.3
942      */
943     public static function get_course_groupings_returns() {
944         return new external_multiple_structure(
945             new external_single_structure(
946                 array(
947                     'id' => new external_value(PARAM_INT, 'grouping record id'),
948                     'courseid' => new external_value(PARAM_INT, 'id of course'),
949                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
950                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
951                     'descriptionformat' => new external_format_value('description'),
952                     'idnumber' => new external_value(PARAM_RAW, 'id number')
953                 )
954             )
955         );
956     }
958     /**
959      * Returns description of method parameters
960      *
961      * @return external_function_parameters
962      * @since Moodle 2.3
963      */
964     public static function delete_groupings_parameters() {
965         return new external_function_parameters(
966             array(
967                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')),
968             )
969         );
970     }
972     /**
973      * Delete groupings
974      *
975      * @param array $groupingids array of grouping ids
976      * @return void
977      * @since Moodle 2.3
978      */
979     public static function delete_groupings($groupingids) {
980         global $CFG, $DB;
981         require_once("$CFG->dirroot/group/lib.php");
983         $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids));
985         $transaction = $DB->start_delegated_transaction();
987         foreach ($params['groupingids'] as $groupingid) {
989             if (!$grouping = groups_get_grouping($groupingid, 'id, courseid', IGNORE_MISSING)) {
990                 // Silently ignore attempts to delete nonexisting groupings.
991                 continue;
992             }
994             // Now security checks.
995             $context = context_course::instance($grouping->courseid);
996             try {
997                 self::validate_context($context);
998             } catch (Exception $e) {
999                 $exceptionparam = new stdClass();
1000                 $exceptionparam->message = $e->getMessage();
1001                 $exceptionparam->courseid = $grouping->courseid;
1002                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1003             }
1004             require_capability('moodle/course:managegroups', $context);
1006             groups_delete_grouping($grouping);
1007         }
1009         $transaction->allow_commit();
1010     }
1012     /**
1013      * Returns description of method result value
1014      *
1015      * @return external_description
1016      * @since Moodle 2.3
1017      */
1018     public static function delete_groupings_returns() {
1019         return null;
1020     }
1022     /**
1023      * Returns description of method parameters
1024      *
1025      * @return external_function_parameters
1026      * @since Moodle 2.3
1027      */
1028     public static function assign_grouping_parameters() {
1029         return new external_function_parameters(
1030             array(
1031                 'assignments'=> new external_multiple_structure(
1032                     new external_single_structure(
1033                         array(
1034                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1035                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1036                         )
1037                     )
1038                 )
1039             )
1040         );
1041     }
1043     /**
1044      * Assign a group to a grouping
1045      *
1046      * @param array $assignments of arrays with keys groupid, groupingid
1047      * @return void
1048      * @since Moodle 2.3
1049      */
1050     public static function assign_grouping($assignments) {
1051         global $CFG, $DB;
1052         require_once("$CFG->dirroot/group/lib.php");
1054         $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments));
1056         $transaction = $DB->start_delegated_transaction();
1057         foreach ($params['assignments'] as $assignment) {
1058             // Validate params.
1059             $groupingid = $assignment['groupingid'];
1060             $groupid = $assignment['groupid'];
1062             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1063             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1065             if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1066                 // Continue silently if the group is yet assigned to the grouping.
1067                 continue;
1068             }
1070             // Now security checks.
1071             $context = context_course::instance($grouping->courseid);
1072             try {
1073                 self::validate_context($context);
1074             } catch (Exception $e) {
1075                 $exceptionparam = new stdClass();
1076                 $exceptionparam->message = $e->getMessage();
1077                 $exceptionparam->courseid = $group->courseid;
1078                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1079             }
1080             require_capability('moodle/course:managegroups', $context);
1082             groups_assign_grouping($groupingid, $groupid);
1083         }
1085         $transaction->allow_commit();
1086     }
1088     /**
1089      * Returns description of method result value
1090      *
1091      * @return null
1092      * @since Moodle 2.3
1093      */
1094     public static function assign_grouping_returns() {
1095         return null;
1096     }
1098     /**
1099      * Returns description of method parameters
1100      *
1101      * @return external_function_parameters
1102      * @since Moodle 2.3
1103      */
1104     public static function unassign_grouping_parameters() {
1105         return new external_function_parameters(
1106             array(
1107                 'unassignments'=> new external_multiple_structure(
1108                     new external_single_structure(
1109                         array(
1110                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1111                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1112                         )
1113                     )
1114                 )
1115             )
1116         );
1117     }
1119     /**
1120      * Unassign a group from a grouping
1121      *
1122      * @param array $unassignments of arrays with keys groupid, groupingid
1123      * @return void
1124      * @since Moodle 2.3
1125      */
1126     public static function unassign_grouping($unassignments) {
1127         global $CFG, $DB;
1128         require_once("$CFG->dirroot/group/lib.php");
1130         $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments));
1132         $transaction = $DB->start_delegated_transaction();
1133         foreach ($params['unassignments'] as $unassignment) {
1134             // Validate params.
1135             $groupingid = $unassignment['groupingid'];
1136             $groupid = $unassignment['groupid'];
1138             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1139             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1141             if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1142                 // Continue silently if the group is not assigned to the grouping.
1143                 continue;
1144             }
1146             // Now security checks.
1147             $context = context_course::instance($grouping->courseid);
1148             try {
1149                 self::validate_context($context);
1150             } catch (Exception $e) {
1151                 $exceptionparam = new stdClass();
1152                 $exceptionparam->message = $e->getMessage();
1153                 $exceptionparam->courseid = $group->courseid;
1154                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1155             }
1156             require_capability('moodle/course:managegroups', $context);
1158             groups_unassign_grouping($groupingid, $groupid);
1159         }
1161         $transaction->allow_commit();
1162     }
1164     /**
1165      * Returns description of method result value
1166      *
1167      * @return null
1168      * @since Moodle 2.3
1169      */
1170     public static function unassign_grouping_returns() {
1171         return null;
1172     }
1174     /**
1175      * Returns description of method parameters
1176      *
1177      * @return external_function_parameters
1178      * @since Moodle 2.9
1179      */
1180     public static function get_course_user_groups_parameters() {
1181         return new external_function_parameters(
1182             array(
1183                 'courseid' => new external_value(PARAM_INT, 'id of course'),
1184                 'userid' => new external_value(PARAM_INT, 'id of user'),
1185                 'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0)
1186             )
1187         );
1188     }
1190     /**
1191      * Get all groups in the specified course for the specified user.
1192      *
1193      * @throws moodle_exception
1194      * @param int $courseid id of course.
1195      * @param int $userid id of user.
1196      * @param int $groupingid optional returns only groups in the specified grouping.
1197      * @return array of group objects (id, name, description, format) and possible warnings.
1198      * @since Moodle 2.9
1199      */
1200     public static function get_course_user_groups($courseid, $userid, $groupingid = 0) {
1201         global $USER;
1203         // Warnings array, it can be empty at the end but is mandatory.
1204         $warnings = array();
1206         $params = array(
1207             'courseid' => $courseid,
1208             'userid' => $userid,
1209             'groupingid' => $groupingid
1210         );
1211         $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params);
1212         $courseid = $params['courseid'];
1213         $userid = $params['userid'];
1214         $groupingid = $params['groupingid'];
1216         // Validate course and user. get_course throws an exception if the course does not exists.
1217         $course = get_course($courseid);
1218         $user = core_user::get_user($userid, '*', MUST_EXIST);
1219         core_user::require_active_user($user);
1221         // Security checks.
1222         $context = context_course::instance($course->id);
1223         self::validate_context($context);
1225          // Check if we have permissions for retrieve the information.
1226         if ($user->id != $USER->id) {
1227             if (!has_capability('moodle/course:managegroups', $context)) {
1228                 throw new moodle_exception('accessdenied', 'admin');
1229             }
1230             // Validate if the user is enrolled in the course.
1231             if (!is_enrolled($context, $user->id)) {
1232                 // We return a warning because the function does not fail for not enrolled users.
1233                 $warning['item'] = 'course';
1234                 $warning['itemid'] = $course->id;
1235                 $warning['warningcode'] = '1';
1236                 $warning['message'] = "User $user->id is not enrolled in course $course->id";
1237                 $warnings[] = $warning;
1238             }
1239         }
1241         $usergroups = array();
1242         if (empty($warnings)) {
1243             $groups = groups_get_all_groups($course->id, $user->id, 0, 'g.id, g.name, g.description, g.descriptionformat, g.idnumber');
1245             foreach ($groups as $group) {
1246                 list($group->description, $group->descriptionformat) =
1247                     external_format_text($group->description, $group->descriptionformat,
1248                             $context->id, 'group', 'description', $group->id);
1249                 $group->courseid = $course->id;
1250                 $usergroups[] = $group;
1251             }
1252         }
1254         $results = array(
1255             'groups' => $usergroups,
1256             'warnings' => $warnings
1257         );
1258         return $results;
1259     }
1261     /**
1262      * Returns description of method result value.
1263      *
1264      * @return external_description A single structure containing groups and possible warnings.
1265      * @since Moodle 2.9
1266      */
1267     public static function get_course_user_groups_returns() {
1268         return new external_single_structure(
1269             array(
1270                 'groups' => new external_multiple_structure(self::group_description()),
1271                 'warnings' => new external_warnings(),
1272             )
1273         );
1274     }
1276     /**
1277      * Create group return value description.
1278      *
1279      * @return external_single_structure The group description
1280      */
1281     public static function group_description() {
1282         return new external_single_structure(
1283             array(
1284                 'id' => new external_value(PARAM_INT, 'group record id'),
1285                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1286                 'description' => new external_value(PARAM_RAW, 'group description text'),
1287                 'descriptionformat' => new external_format_value('description'),
1288                 'idnumber' => new external_value(PARAM_RAW, 'id number'),
1289                 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL),
1290             )
1291         );
1292     }
1294     /**
1295      * Returns description of method parameters
1296      *
1297      * @return external_function_parameters
1298      * @since Moodle 3.0
1299      */
1300     public static function get_activity_allowed_groups_parameters() {
1301         return new external_function_parameters(
1302             array(
1303                 'cmid' => new external_value(PARAM_INT, 'course module id'),
1304                 'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0)
1305             )
1306         );
1307     }
1309     /**
1310      * Gets a list of groups that the user is allowed to access within the specified activity.
1311      *
1312      * @throws moodle_exception
1313      * @param int $cmid course module id
1314      * @param int $userid id of user.
1315      * @return array of group objects (id, name, description, format) and possible warnings.
1316      * @since Moodle 3.0
1317      */
1318     public static function get_activity_allowed_groups($cmid, $userid = 0) {
1319         global $USER;
1321         // Warnings array, it can be empty at the end but is mandatory.
1322         $warnings = array();
1324         $params = array(
1325             'cmid' => $cmid,
1326             'userid' => $userid
1327         );
1328         $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params);
1329         $cmid = $params['cmid'];
1330         $userid = $params['userid'];
1332         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1334         // Security checks.
1335         $context = context_module::instance($cm->id);
1336         $coursecontext = context_course::instance($cm->course);
1337         self::validate_context($context);
1339         if (empty($userid)) {
1340             $userid = $USER->id;
1341         }
1343         $user = core_user::get_user($userid, '*', MUST_EXIST);
1344         core_user::require_active_user($user);
1346          // Check if we have permissions for retrieve the information.
1347         if ($user->id != $USER->id) {
1348             if (!has_capability('moodle/course:managegroups', $context)) {
1349                 throw new moodle_exception('accessdenied', 'admin');
1350             }
1352             // Validate if the user is enrolled in the course.
1353             $course = get_course($cm->course);
1354             if (!can_access_course($course, $user, '', true)) {
1355                 // We return a warning because the function does not fail for not enrolled users.
1356                 $warning = array();
1357                 $warning['item'] = 'course';
1358                 $warning['itemid'] = $cm->course;
1359                 $warning['warningcode'] = '1';
1360                 $warning['message'] = "User $user->id cannot access course $cm->course";
1361                 $warnings[] = $warning;
1362             }
1363         }
1365         $usergroups = array();
1366         if (empty($warnings)) {
1367             $groups = groups_get_activity_allowed_groups($cm, $user->id);
1369             foreach ($groups as $group) {
1370                 list($group->description, $group->descriptionformat) =
1371                     external_format_text($group->description, $group->descriptionformat,
1372                             $coursecontext->id, 'group', 'description', $group->id);
1373                 $group->courseid = $cm->course;
1374                 $usergroups[] = $group;
1375             }
1376         }
1378         $results = array(
1379             'groups' => $usergroups,
1380             'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
1381             'warnings' => $warnings
1382         );
1383         return $results;
1384     }
1386     /**
1387      * Returns description of method result value.
1388      *
1389      * @return external_description A single structure containing groups and possible warnings.
1390      * @since Moodle 3.0
1391      */
1392     public static function get_activity_allowed_groups_returns() {
1393         return new external_single_structure(
1394             array(
1395                 'groups' => new external_multiple_structure(self::group_description()),
1396                 'canaccessallgroups' => new external_value(PARAM_BOOL,
1397                     'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
1398                 'warnings' => new external_warnings(),
1399             )
1400         );
1401     }
1403     /**
1404      * Returns description of method parameters
1405      *
1406      * @return external_function_parameters
1407      * @since Moodle 3.0
1408      */
1409     public static function get_activity_groupmode_parameters() {
1410         return new external_function_parameters(
1411             array(
1412                 'cmid' => new external_value(PARAM_INT, 'course module id')
1413             )
1414         );
1415     }
1417     /**
1418      * Returns effective groupmode used in a given activity.
1419      *
1420      * @throws moodle_exception
1421      * @param int $cmid course module id.
1422      * @return array containing the group mode and possible warnings.
1423      * @since Moodle 3.0
1424      * @throws moodle_exception
1425      */
1426     public static function get_activity_groupmode($cmid) {
1427         global $USER;
1429         // Warnings array, it can be empty at the end but is mandatory.
1430         $warnings = array();
1432         $params = array(
1433             'cmid' => $cmid
1434         );
1435         $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params);
1436         $cmid = $params['cmid'];
1438         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1440         // Security checks.
1441         $context = context_module::instance($cm->id);
1442         self::validate_context($context);
1444         $groupmode = groups_get_activity_groupmode($cm);
1446         $results = array(
1447             'groupmode' => $groupmode,
1448             'warnings' => $warnings
1449         );
1450         return $results;
1451     }
1453     /**
1454      * Returns description of method result value.
1455      *
1456      * @return external_description
1457      * @since Moodle 3.0
1458      */
1459     public static function get_activity_groupmode_returns() {
1460         return new external_single_structure(
1461             array(
1462                 'groupmode' => new external_value(PARAM_INT, 'group mode:
1463                                                     0 for no groups, 1 for separate groups, 2 for visible groups'),
1464                 'warnings' => new external_warnings(),
1465             )
1466         );
1467     }