weekly release 3.10.1+
[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, '*', 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, '*', 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                 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
560                 throw new moodle_exception('errorremovenotpermitted', 'group', '', $fullname);
561             }
562             groups_remove_member($group, $user);
563         }
565         $transaction->allow_commit();
566     }
568     /**
569      * Returns description of method result value
570      *
571      * @return null
572      * @since Moodle 2.2
573      */
574     public static function delete_group_members_returns() {
575         return null;
576     }
578     /**
579      * Returns description of method parameters
580      *
581      * @return external_function_parameters
582      * @since Moodle 2.3
583      */
584     public static function create_groupings_parameters() {
585         return new external_function_parameters(
586             array(
587                 'groupings' => new external_multiple_structure(
588                     new external_single_structure(
589                         array(
590                             'courseid' => new external_value(PARAM_INT, 'id of course'),
591                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
592                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
593                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
594                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
595                         )
596                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
597                 )
598             )
599         );
600     }
602     /**
603      * Create groupings
604      *
605      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
606      * @return array of newly created groupings
607      * @since Moodle 2.3
608      */
609     public static function create_groupings($groupings) {
610         global $CFG, $DB;
611         require_once("$CFG->dirroot/group/lib.php");
613         $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings));
615         $transaction = $DB->start_delegated_transaction();
617         $groupings = array();
619         foreach ($params['groupings'] as $grouping) {
620             $grouping = (object)$grouping;
622             if (trim($grouping->name) == '') {
623                 throw new invalid_parameter_exception('Invalid grouping name');
624             }
625             if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
626                 throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
627             }
629             // Now security checks            .
630             $context = context_course::instance($grouping->courseid);
631             try {
632                 self::validate_context($context);
633             } catch (Exception $e) {
634                 $exceptionparam = new stdClass();
635                 $exceptionparam->message = $e->getMessage();
636                 $exceptionparam->courseid = $grouping->courseid;
637                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
638             }
639             require_capability('moodle/course:managegroups', $context);
641             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
643             // Finally create the grouping.
644             $grouping->id = groups_create_grouping($grouping);
645             $groupings[] = (array)$grouping;
646         }
648         $transaction->allow_commit();
650         return $groupings;
651     }
653     /**
654      * Returns description of method result value
655      *
656      * @return external_description
657      * @since Moodle 2.3
658      */
659     public static function create_groupings_returns() {
660         return new external_multiple_structure(
661             new external_single_structure(
662                 array(
663                     'id' => new external_value(PARAM_INT, 'grouping record id'),
664                     'courseid' => new external_value(PARAM_INT, 'id of course'),
665                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
666                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
667                     'descriptionformat' => new external_format_value('description'),
668                     'idnumber' => new external_value(PARAM_RAW, 'id number')
669                 )
670             ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.'
671         );
672     }
674     /**
675      * Returns description of method parameters
676      *
677      * @return external_function_parameters
678      * @since Moodle 2.3
679      */
680     public static function update_groupings_parameters() {
681         return new external_function_parameters(
682             array(
683                 'groupings' => new external_multiple_structure(
684                     new external_single_structure(
685                         array(
686                             'id' => new external_value(PARAM_INT, 'id of grouping'),
687                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
688                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
689                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
690                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
691                         )
692                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
693                 )
694             )
695         );
696     }
698     /**
699      * Update groupings
700      *
701      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
702      * @return array of newly updated groupings
703      * @since Moodle 2.3
704      */
705     public static function update_groupings($groupings) {
706         global $CFG, $DB;
707         require_once("$CFG->dirroot/group/lib.php");
709         $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings));
711         $transaction = $DB->start_delegated_transaction();
713         foreach ($params['groupings'] as $grouping) {
714             $grouping = (object)$grouping;
716             if (trim($grouping->name) == '') {
717                 throw new invalid_parameter_exception('Invalid grouping name');
718             }
720             if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) {
721                 throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course");
722             }
724             // Check if the new modified grouping name already exists in the course.
725             if ($grouping->name != $currentgrouping->name and
726                     $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
727                 throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
728             }
730             $grouping->courseid = $currentgrouping->courseid;
732             // Now security checks.
733             $context = context_course::instance($grouping->courseid);
734             try {
735                 self::validate_context($context);
736             } catch (Exception $e) {
737                 $exceptionparam = new stdClass();
738                 $exceptionparam->message = $e->getMessage();
739                 $exceptionparam->courseid = $grouping->courseid;
740                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
741             }
742             require_capability('moodle/course:managegroups', $context);
744             // We must force allways FORMAT_HTML.
745             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
747             // Finally update the grouping.
748             groups_update_grouping($grouping);
749         }
751         $transaction->allow_commit();
753         return null;
754     }
756     /**
757      * Returns description of method result value
758      *
759      * @return external_description
760      * @since Moodle 2.3
761      */
762     public static function update_groupings_returns() {
763         return null;
764     }
766     /**
767      * Returns description of method parameters
768      *
769      * @return external_function_parameters
770      * @since Moodle 2.3
771      */
772     public static function get_groupings_parameters() {
773         return new external_function_parameters(
774             array(
775                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')
776                         , 'List of grouping id. A grouping id is an integer.'),
777                 'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0)
778             )
779         );
780     }
782     /**
783      * Get groupings definition specified by ids
784      *
785      * @param array $groupingids arrays of grouping ids
786      * @param boolean $returngroups return the associated groups if true. The default is false.
787      * @return array of grouping objects (id, courseid, name)
788      * @since Moodle 2.3
789      */
790     public static function get_groupings($groupingids, $returngroups = false) {
791         global $CFG, $DB;
792         require_once("$CFG->dirroot/group/lib.php");
793         require_once("$CFG->libdir/filelib.php");
795         $params = self::validate_parameters(self::get_groupings_parameters(),
796                                             array('groupingids' => $groupingids,
797                                                   'returngroups' => $returngroups));
799         $groupings = array();
800         foreach ($params['groupingids'] as $groupingid) {
801             // Validate params.
802             $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST);
804             // Now security checks.
805             $context = context_course::instance($grouping->courseid);
806             try {
807                 self::validate_context($context);
808             } catch (Exception $e) {
809                 $exceptionparam = new stdClass();
810                 $exceptionparam->message = $e->getMessage();
811                 $exceptionparam->courseid = $grouping->courseid;
812                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
813             }
814             require_capability('moodle/course:managegroups', $context);
816             list($grouping->description, $grouping->descriptionformat) =
817                 external_format_text($grouping->description, $grouping->descriptionformat,
818                         $context->id, 'grouping', 'description', $grouping->id);
820             $groupingarray = (array)$grouping;
822             if ($params['returngroups']) {
823                 $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ".
824                                                "ON g.id = gg.groupid WHERE gg.groupingid = ? ".
825                                                "ORDER BY groupid", array($groupingid));
826                 if ($grouprecords) {
827                     $groups = array();
828                     foreach ($grouprecords as $grouprecord) {
829                         list($grouprecord->description, $grouprecord->descriptionformat) =
830                         external_format_text($grouprecord->description, $grouprecord->descriptionformat,
831                         $context->id, 'group', 'description', $grouprecord->groupid);
832                         $groups[] = array('id' => $grouprecord->groupid,
833                                           'name' => $grouprecord->name,
834                                           'idnumber' => $grouprecord->idnumber,
835                                           'description' => $grouprecord->description,
836                                           'descriptionformat' => $grouprecord->descriptionformat,
837                                           'enrolmentkey' => $grouprecord->enrolmentkey,
838                                           'courseid' => $grouprecord->courseid
839                                           );
840                     }
841                     $groupingarray['groups'] = $groups;
842                 }
843             }
844             $groupings[] = $groupingarray;
845         }
847         return $groupings;
848     }
850     /**
851      * Returns description of method result value
852      *
853      * @return external_description
854      * @since Moodle 2.3
855      */
856     public static function get_groupings_returns() {
857         return new external_multiple_structure(
858             new external_single_structure(
859                 array(
860                     'id' => new external_value(PARAM_INT, 'grouping record id'),
861                     'courseid' => new external_value(PARAM_INT, 'id of course'),
862                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
863                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
864                     'descriptionformat' => new external_format_value('description'),
865                     'idnumber' => new external_value(PARAM_RAW, 'id number'),
866                     'groups' => new external_multiple_structure(
867                         new external_single_structure(
868                             array(
869                                 'id' => new external_value(PARAM_INT, 'group record id'),
870                                 'courseid' => new external_value(PARAM_INT, 'id of course'),
871                                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
872                                 'description' => new external_value(PARAM_RAW, 'group description text'),
873                                 'descriptionformat' => new external_format_value('description'),
874                                 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
875                                 'idnumber' => new external_value(PARAM_RAW, 'id number')
876                             )
877                         ),
878                     'optional groups', VALUE_OPTIONAL)
879                 )
880             )
881         );
882     }
884     /**
885      * Returns description of method parameters
886      *
887      * @return external_function_parameters
888      * @since Moodle 2.3
889      */
890     public static function get_course_groupings_parameters() {
891         return new external_function_parameters(
892             array(
893                 'courseid' => new external_value(PARAM_INT, 'id of course'),
894             )
895         );
896     }
898     /**
899      * Get all groupings in the specified course
900      *
901      * @param int $courseid id of course
902      * @return array of grouping objects (id, courseid, name, enrolmentkey)
903      * @since Moodle 2.3
904      */
905     public static function get_course_groupings($courseid) {
906         global $CFG;
907         require_once("$CFG->dirroot/group/lib.php");
908         require_once("$CFG->libdir/filelib.php");
910         $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid));
912         // Now security checks.
913         $context = context_course::instance($params['courseid']);
915         try {
916             self::validate_context($context);
917         } catch (Exception $e) {
918                 $exceptionparam = new stdClass();
919                 $exceptionparam->message = $e->getMessage();
920                 $exceptionparam->courseid = $params['courseid'];
921                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
922         }
923         require_capability('moodle/course:managegroups', $context);
925         $gs = groups_get_all_groupings($params['courseid']);
927         $groupings = array();
928         foreach ($gs as $grouping) {
929             list($grouping->description, $grouping->descriptionformat) =
930                 external_format_text($grouping->description, $grouping->descriptionformat,
931                         $context->id, 'grouping', 'description', $grouping->id);
932             $groupings[] = (array)$grouping;
933         }
935         return $groupings;
936     }
938     /**
939      * Returns description of method result value
940      *
941      * @return external_description
942      * @since Moodle 2.3
943      */
944     public static function get_course_groupings_returns() {
945         return new external_multiple_structure(
946             new external_single_structure(
947                 array(
948                     'id' => new external_value(PARAM_INT, 'grouping record id'),
949                     'courseid' => new external_value(PARAM_INT, 'id of course'),
950                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
951                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
952                     'descriptionformat' => new external_format_value('description'),
953                     'idnumber' => new external_value(PARAM_RAW, 'id number')
954                 )
955             )
956         );
957     }
959     /**
960      * Returns description of method parameters
961      *
962      * @return external_function_parameters
963      * @since Moodle 2.3
964      */
965     public static function delete_groupings_parameters() {
966         return new external_function_parameters(
967             array(
968                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')),
969             )
970         );
971     }
973     /**
974      * Delete groupings
975      *
976      * @param array $groupingids array of grouping ids
977      * @return void
978      * @since Moodle 2.3
979      */
980     public static function delete_groupings($groupingids) {
981         global $CFG, $DB;
982         require_once("$CFG->dirroot/group/lib.php");
984         $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids));
986         $transaction = $DB->start_delegated_transaction();
988         foreach ($params['groupingids'] as $groupingid) {
990             if (!$grouping = groups_get_grouping($groupingid)) {
991                 // Silently ignore attempts to delete nonexisting groupings.
992                 continue;
993             }
995             // Now security checks.
996             $context = context_course::instance($grouping->courseid);
997             try {
998                 self::validate_context($context);
999             } catch (Exception $e) {
1000                 $exceptionparam = new stdClass();
1001                 $exceptionparam->message = $e->getMessage();
1002                 $exceptionparam->courseid = $grouping->courseid;
1003                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1004             }
1005             require_capability('moodle/course:managegroups', $context);
1007             groups_delete_grouping($grouping);
1008         }
1010         $transaction->allow_commit();
1011     }
1013     /**
1014      * Returns description of method result value
1015      *
1016      * @return external_description
1017      * @since Moodle 2.3
1018      */
1019     public static function delete_groupings_returns() {
1020         return null;
1021     }
1023     /**
1024      * Returns description of method parameters
1025      *
1026      * @return external_function_parameters
1027      * @since Moodle 2.3
1028      */
1029     public static function assign_grouping_parameters() {
1030         return new external_function_parameters(
1031             array(
1032                 'assignments'=> new external_multiple_structure(
1033                     new external_single_structure(
1034                         array(
1035                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1036                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1037                         )
1038                     )
1039                 )
1040             )
1041         );
1042     }
1044     /**
1045      * Assign a group to a grouping
1046      *
1047      * @param array $assignments of arrays with keys groupid, groupingid
1048      * @return void
1049      * @since Moodle 2.3
1050      */
1051     public static function assign_grouping($assignments) {
1052         global $CFG, $DB;
1053         require_once("$CFG->dirroot/group/lib.php");
1055         $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments));
1057         $transaction = $DB->start_delegated_transaction();
1058         foreach ($params['assignments'] as $assignment) {
1059             // Validate params.
1060             $groupingid = $assignment['groupingid'];
1061             $groupid = $assignment['groupid'];
1063             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1064             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1066             if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1067                 // Continue silently if the group is yet assigned to the grouping.
1068                 continue;
1069             }
1071             // Now security checks.
1072             $context = context_course::instance($grouping->courseid);
1073             try {
1074                 self::validate_context($context);
1075             } catch (Exception $e) {
1076                 $exceptionparam = new stdClass();
1077                 $exceptionparam->message = $e->getMessage();
1078                 $exceptionparam->courseid = $group->courseid;
1079                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1080             }
1081             require_capability('moodle/course:managegroups', $context);
1083             groups_assign_grouping($groupingid, $groupid);
1084         }
1086         $transaction->allow_commit();
1087     }
1089     /**
1090      * Returns description of method result value
1091      *
1092      * @return null
1093      * @since Moodle 2.3
1094      */
1095     public static function assign_grouping_returns() {
1096         return null;
1097     }
1099     /**
1100      * Returns description of method parameters
1101      *
1102      * @return external_function_parameters
1103      * @since Moodle 2.3
1104      */
1105     public static function unassign_grouping_parameters() {
1106         return new external_function_parameters(
1107             array(
1108                 'unassignments'=> new external_multiple_structure(
1109                     new external_single_structure(
1110                         array(
1111                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1112                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1113                         )
1114                     )
1115                 )
1116             )
1117         );
1118     }
1120     /**
1121      * Unassign a group from a grouping
1122      *
1123      * @param array $unassignments of arrays with keys groupid, groupingid
1124      * @return void
1125      * @since Moodle 2.3
1126      */
1127     public static function unassign_grouping($unassignments) {
1128         global $CFG, $DB;
1129         require_once("$CFG->dirroot/group/lib.php");
1131         $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments));
1133         $transaction = $DB->start_delegated_transaction();
1134         foreach ($params['unassignments'] as $unassignment) {
1135             // Validate params.
1136             $groupingid = $unassignment['groupingid'];
1137             $groupid = $unassignment['groupid'];
1139             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1140             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1142             if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1143                 // Continue silently if the group is not assigned to the grouping.
1144                 continue;
1145             }
1147             // Now security checks.
1148             $context = context_course::instance($grouping->courseid);
1149             try {
1150                 self::validate_context($context);
1151             } catch (Exception $e) {
1152                 $exceptionparam = new stdClass();
1153                 $exceptionparam->message = $e->getMessage();
1154                 $exceptionparam->courseid = $group->courseid;
1155                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1156             }
1157             require_capability('moodle/course:managegroups', $context);
1159             groups_unassign_grouping($groupingid, $groupid);
1160         }
1162         $transaction->allow_commit();
1163     }
1165     /**
1166      * Returns description of method result value
1167      *
1168      * @return null
1169      * @since Moodle 2.3
1170      */
1171     public static function unassign_grouping_returns() {
1172         return null;
1173     }
1175     /**
1176      * Returns description of method parameters
1177      *
1178      * @return external_function_parameters
1179      * @since Moodle 2.9
1180      */
1181     public static function get_course_user_groups_parameters() {
1182         return new external_function_parameters(
1183             array(
1184                 'courseid' => new external_value(PARAM_INT,
1185                     'Id of course (empty or 0 for all the courses where the user is enrolled).', VALUE_DEFAULT, 0),
1186                 'userid' => new external_value(PARAM_INT, 'Id of user (empty or 0 for current user).', VALUE_DEFAULT, 0),
1187                 'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0)
1188             )
1189         );
1190     }
1192     /**
1193      * Get all groups in the specified course for the specified user.
1194      *
1195      * @throws moodle_exception
1196      * @param int $courseid id of course.
1197      * @param int $userid id of user.
1198      * @param int $groupingid optional returns only groups in the specified grouping.
1199      * @return array of group objects (id, name, description, format) and possible warnings.
1200      * @since Moodle 2.9
1201      */
1202     public static function get_course_user_groups($courseid = 0, $userid = 0, $groupingid = 0) {
1203         global $USER;
1205         // Warnings array, it can be empty at the end but is mandatory.
1206         $warnings = array();
1208         $params = array(
1209             'courseid' => $courseid,
1210             'userid' => $userid,
1211             'groupingid' => $groupingid
1212         );
1213         $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params);
1215         $courseid = $params['courseid'];
1216         $userid = $params['userid'];
1217         $groupingid = $params['groupingid'];
1219         // Validate user.
1220         if (empty($userid)) {
1221             $userid = $USER->id;
1222         } else {
1223             $user = core_user::get_user($userid, '*', MUST_EXIST);
1224             core_user::require_active_user($user);
1225         }
1227         // Get courses.
1228         if (empty($courseid)) {
1229             $courses = enrol_get_users_courses($userid, true);
1230             $checkenrolments = false;   // No need to check enrolments here since they are my courses.
1231         } else {
1232             $courses = array($courseid => get_course($courseid));
1233             $checkenrolments = true;
1234         }
1236         // Security checks.
1237         list($courses, $warnings) = external_util::validate_courses(array_keys($courses), $courses, true);
1239         $usergroups = array();
1240         foreach ($courses as $course) {
1241              // Check if we have permissions for retrieve the information.
1242             if ($userid != $USER->id && !has_capability('moodle/course:managegroups', $course->context)) {
1243                 $warnings[] = array(
1244                     'item' => 'course',
1245                     'itemid' => $course->id,
1246                     'warningcode' => 'cannotmanagegroups',
1247                     'message' => "User $USER->id cannot manage groups in course $course->id",
1248                 );
1249                 continue;
1250             }
1252             // Check if the user being check is enrolled in the given course.
1253             if ($checkenrolments && !is_enrolled($course->context, $userid)) {
1254                 // We return a warning because the function does not fail for not enrolled users.
1255                 $warnings[] = array(
1256                     'item' => 'course',
1257                     'itemid' => $course->id,
1258                     'warningcode' => 'notenrolled',
1259                     'message' => "User $userid is not enrolled in course $course->id",
1260                 );
1261             }
1263             $groups = groups_get_all_groups($course->id, $userid, $groupingid,
1264                 'g.id, g.name, g.description, g.descriptionformat, g.idnumber');
1266             foreach ($groups as $group) {
1267                 list($group->description, $group->descriptionformat) =
1268                     external_format_text($group->description, $group->descriptionformat,
1269                             $course->context->id, 'group', 'description', $group->id);
1270                 $group->courseid = $course->id;
1271                 $usergroups[] = $group;
1272             }
1273         }
1275         $results = array(
1276             'groups' => $usergroups,
1277             'warnings' => $warnings
1278         );
1279         return $results;
1280     }
1282     /**
1283      * Returns description of method result value.
1284      *
1285      * @return external_description A single structure containing groups and possible warnings.
1286      * @since Moodle 2.9
1287      */
1288     public static function get_course_user_groups_returns() {
1289         return new external_single_structure(
1290             array(
1291                 'groups' => new external_multiple_structure(self::group_description()),
1292                 'warnings' => new external_warnings(),
1293             )
1294         );
1295     }
1297     /**
1298      * Create group return value description.
1299      *
1300      * @return external_single_structure The group description
1301      */
1302     public static function group_description() {
1303         return new external_single_structure(
1304             array(
1305                 'id' => new external_value(PARAM_INT, 'group record id'),
1306                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1307                 'description' => new external_value(PARAM_RAW, 'group description text'),
1308                 'descriptionformat' => new external_format_value('description'),
1309                 'idnumber' => new external_value(PARAM_RAW, 'id number'),
1310                 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL),
1311             )
1312         );
1313     }
1315     /**
1316      * Returns description of method parameters
1317      *
1318      * @return external_function_parameters
1319      * @since Moodle 3.0
1320      */
1321     public static function get_activity_allowed_groups_parameters() {
1322         return new external_function_parameters(
1323             array(
1324                 'cmid' => new external_value(PARAM_INT, 'course module id'),
1325                 'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0)
1326             )
1327         );
1328     }
1330     /**
1331      * Gets a list of groups that the user is allowed to access within the specified activity.
1332      *
1333      * @throws moodle_exception
1334      * @param int $cmid course module id
1335      * @param int $userid id of user.
1336      * @return array of group objects (id, name, description, format) and possible warnings.
1337      * @since Moodle 3.0
1338      */
1339     public static function get_activity_allowed_groups($cmid, $userid = 0) {
1340         global $USER;
1342         // Warnings array, it can be empty at the end but is mandatory.
1343         $warnings = array();
1345         $params = array(
1346             'cmid' => $cmid,
1347             'userid' => $userid
1348         );
1349         $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params);
1350         $cmid = $params['cmid'];
1351         $userid = $params['userid'];
1353         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1355         // Security checks.
1356         $context = context_module::instance($cm->id);
1357         $coursecontext = context_course::instance($cm->course);
1358         self::validate_context($context);
1360         if (empty($userid)) {
1361             $userid = $USER->id;
1362         }
1364         $user = core_user::get_user($userid, '*', MUST_EXIST);
1365         core_user::require_active_user($user);
1367          // Check if we have permissions for retrieve the information.
1368         if ($user->id != $USER->id) {
1369             if (!has_capability('moodle/course:managegroups', $context)) {
1370                 throw new moodle_exception('accessdenied', 'admin');
1371             }
1373             // Validate if the user is enrolled in the course.
1374             $course = get_course($cm->course);
1375             if (!can_access_course($course, $user, '', true)) {
1376                 // We return a warning because the function does not fail for not enrolled users.
1377                 $warning = array();
1378                 $warning['item'] = 'course';
1379                 $warning['itemid'] = $cm->course;
1380                 $warning['warningcode'] = '1';
1381                 $warning['message'] = "User $user->id cannot access course $cm->course";
1382                 $warnings[] = $warning;
1383             }
1384         }
1386         $usergroups = array();
1387         if (empty($warnings)) {
1388             $groups = groups_get_activity_allowed_groups($cm, $user->id);
1390             foreach ($groups as $group) {
1391                 list($group->description, $group->descriptionformat) =
1392                     external_format_text($group->description, $group->descriptionformat,
1393                             $coursecontext->id, 'group', 'description', $group->id);
1394                 $group->courseid = $cm->course;
1395                 $usergroups[] = $group;
1396             }
1397         }
1399         $results = array(
1400             'groups' => $usergroups,
1401             'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
1402             'warnings' => $warnings
1403         );
1404         return $results;
1405     }
1407     /**
1408      * Returns description of method result value.
1409      *
1410      * @return external_description A single structure containing groups and possible warnings.
1411      * @since Moodle 3.0
1412      */
1413     public static function get_activity_allowed_groups_returns() {
1414         return new external_single_structure(
1415             array(
1416                 'groups' => new external_multiple_structure(self::group_description()),
1417                 'canaccessallgroups' => new external_value(PARAM_BOOL,
1418                     'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
1419                 'warnings' => new external_warnings(),
1420             )
1421         );
1422     }
1424     /**
1425      * Returns description of method parameters
1426      *
1427      * @return external_function_parameters
1428      * @since Moodle 3.0
1429      */
1430     public static function get_activity_groupmode_parameters() {
1431         return new external_function_parameters(
1432             array(
1433                 'cmid' => new external_value(PARAM_INT, 'course module id')
1434             )
1435         );
1436     }
1438     /**
1439      * Returns effective groupmode used in a given activity.
1440      *
1441      * @throws moodle_exception
1442      * @param int $cmid course module id.
1443      * @return array containing the group mode and possible warnings.
1444      * @since Moodle 3.0
1445      * @throws moodle_exception
1446      */
1447     public static function get_activity_groupmode($cmid) {
1448         global $USER;
1450         // Warnings array, it can be empty at the end but is mandatory.
1451         $warnings = array();
1453         $params = array(
1454             'cmid' => $cmid
1455         );
1456         $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params);
1457         $cmid = $params['cmid'];
1459         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1461         // Security checks.
1462         $context = context_module::instance($cm->id);
1463         self::validate_context($context);
1465         $groupmode = groups_get_activity_groupmode($cm);
1467         $results = array(
1468             'groupmode' => $groupmode,
1469             'warnings' => $warnings
1470         );
1471         return $results;
1472     }
1474     /**
1475      * Returns description of method result value.
1476      *
1477      * @return external_description
1478      * @since Moodle 3.0
1479      */
1480     public static function get_activity_groupmode_returns() {
1481         return new external_single_structure(
1482             array(
1483                 'groupmode' => new external_value(PARAM_INT, 'group mode:
1484                                                     0 for no groups, 1 for separate groups, 2 for visible groups'),
1485                 'warnings' => new external_warnings(),
1486             )
1487         );
1488     }
1490     /**
1491      * Returns description of method parameters
1492      *
1493      * @return external_function_parameters
1494      * @since Moodle 3.6
1495      */
1496     public static function update_groups_parameters() {
1497         return new external_function_parameters(
1498             array(
1499                 'groups' => new external_multiple_structure(
1500                     new external_single_structure(
1501                         array(
1502                             'id' => new external_value(PARAM_INT, 'ID of the group'),
1503                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1504                             'description' => new external_value(PARAM_RAW, 'group description text', VALUE_OPTIONAL),
1505                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1506                             'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
1507                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
1508                         )
1509                     ), 'List of group objects. A group is found by the id, then all other details provided will be updated.'
1510                 )
1511             )
1512         );
1513     }
1515     /**
1516      * Update groups
1517      *
1518      * @param array $groups
1519      * @return null
1520      * @since Moodle 3.6
1521      */
1522     public static function update_groups($groups) {
1523         global $CFG, $DB;
1524         require_once("$CFG->dirroot/group/lib.php");
1526         $params = self::validate_parameters(self::update_groups_parameters(), array('groups' => $groups));
1528         $transaction = $DB->start_delegated_transaction();
1530         foreach ($params['groups'] as $group) {
1531             $group = (object)$group;
1533             if (trim($group->name) == '') {
1534                 throw new invalid_parameter_exception('Invalid group name');
1535             }
1537             if (! $currentgroup = $DB->get_record('groups', array('id' => $group->id))) {
1538                 throw new invalid_parameter_exception("Group $group->id does not exist");
1539             }
1541             // Check if the modified group name already exists in the course.
1542             if ($group->name != $currentgroup->name and
1543                     $DB->get_record('groups', array('courseid' => $currentgroup->courseid, 'name' => $group->name))) {
1544                 throw new invalid_parameter_exception('A different group with the same name already exists in the course');
1545             }
1547             $group->courseid = $currentgroup->courseid;
1549             // Now security checks.
1550             $context = context_course::instance($group->courseid);
1551             try {
1552                 self::validate_context($context);
1553             } catch (Exception $e) {
1554                 $exceptionparam = new stdClass();
1555                 $exceptionparam->message = $e->getMessage();
1556                 $exceptionparam->courseid = $group->courseid;
1557                 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
1558             }
1559             require_capability('moodle/course:managegroups', $context);
1561             if (!empty($group->description)) {
1562                 $group->descriptionformat = external_validate_format($group->descriptionformat);
1563             }
1565             groups_update_group($group);
1566         }
1568         $transaction->allow_commit();
1570         return null;
1571     }
1573     /**
1574      * Returns description of method result value
1575      *
1576      * @return null
1577      * @since Moodle 3.6
1578      */
1579     public static function update_groups_returns() {
1580         return null;
1581     }