53d2dfc61ac265a1fea3a0a213ff6bcec238bb40
[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             }
93             if (!empty($group->idnumber) && $DB->count_records('groups', array('idnumber' => $group->idnumber))) {
94                 throw new invalid_parameter_exception('Group with the same idnumber already exists');
95             }
97             // now security checks
98             $context = context_course::instance($group->courseid, IGNORE_MISSING);
99             try {
100                 self::validate_context($context);
101             } catch (Exception $e) {
102                 $exceptionparam = new stdClass();
103                 $exceptionparam->message = $e->getMessage();
104                 $exceptionparam->courseid = $group->courseid;
105                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
106             }
107             require_capability('moodle/course:managegroups', $context);
109             // Validate format.
110             $group->descriptionformat = external_validate_format($group->descriptionformat);
112             // finally create the group
113             $group->id = groups_create_group($group, false);
114             if (!isset($group->enrolmentkey)) {
115                 $group->enrolmentkey = '';
116             }
117             if (!isset($group->idnumber)) {
118                 $group->idnumber = '';
119             }
121             $groups[] = (array)$group;
122         }
124         $transaction->allow_commit();
126         return $groups;
127     }
129     /**
130      * Returns description of method result value
131      *
132      * @return external_description
133      * @since Moodle 2.2
134      */
135     public static function create_groups_returns() {
136         return new external_multiple_structure(
137             new external_single_structure(
138                 array(
139                     'id' => new external_value(PARAM_INT, 'group record id'),
140                     'courseid' => new external_value(PARAM_INT, 'id of course'),
141                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
142                     'description' => new external_value(PARAM_RAW, 'group description text'),
143                     'descriptionformat' => new external_format_value('description'),
144                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
145                     'idnumber' => new external_value(PARAM_RAW, 'id number')
146                 )
147             ), 'List of group object. A group has an id, a courseid, a name, a description and an enrolment key.'
148         );
149     }
151     /**
152      * Returns description of method parameters
153      *
154      * @return external_function_parameters
155      * @since Moodle 2.2
156      */
157     public static function get_groups_parameters() {
158         return new external_function_parameters(
159             array(
160                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')
161                         ,'List of group id. A group id is an integer.'),
162             )
163         );
164     }
166     /**
167      * Get groups definition specified by ids
168      *
169      * @param array $groupids arrays of group ids
170      * @return array of group objects (id, courseid, name, enrolmentkey)
171      * @since Moodle 2.2
172      */
173     public static function get_groups($groupids) {
174         $params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids));
176         $groups = array();
177         foreach ($params['groupids'] as $groupid) {
178             // validate params
179             $group = groups_get_group($groupid, 'id, courseid, name, idnumber, description, descriptionformat, enrolmentkey', MUST_EXIST);
181             // now security checks
182             $context = context_course::instance($group->courseid, IGNORE_MISSING);
183             try {
184                 self::validate_context($context);
185             } catch (Exception $e) {
186                 $exceptionparam = new stdClass();
187                 $exceptionparam->message = $e->getMessage();
188                 $exceptionparam->courseid = $group->courseid;
189                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
190             }
191             require_capability('moodle/course:managegroups', $context);
193             list($group->description, $group->descriptionformat) =
194                 external_format_text($group->description, $group->descriptionformat,
195                         $context->id, 'group', 'description', $group->id);
197             $groups[] = (array)$group;
198         }
200         return $groups;
201     }
203     /**
204      * Returns description of method result value
205      *
206      * @return external_description
207      * @since Moodle 2.2
208      */
209     public static function get_groups_returns() {
210         return new external_multiple_structure(
211             new external_single_structure(
212                 array(
213                     'id' => new external_value(PARAM_INT, 'group record id'),
214                     'courseid' => new external_value(PARAM_INT, 'id of course'),
215                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
216                     'description' => new external_value(PARAM_RAW, 'group description text'),
217                     'descriptionformat' => new external_format_value('description'),
218                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
219                     'idnumber' => new external_value(PARAM_RAW, 'id number')
220                 )
221             )
222         );
223     }
225     /**
226      * Returns description of method parameters
227      *
228      * @return external_function_parameters
229      * @since Moodle 2.2
230      */
231     public static function get_course_groups_parameters() {
232         return new external_function_parameters(
233             array(
234                 'courseid' => new external_value(PARAM_INT, 'id of course'),
235             )
236         );
237     }
239     /**
240      * Get all groups in the specified course
241      *
242      * @param int $courseid id of course
243      * @return array of group objects (id, courseid, name, enrolmentkey)
244      * @since Moodle 2.2
245      */
246     public static function get_course_groups($courseid) {
247         $params = self::validate_parameters(self::get_course_groups_parameters(), array('courseid'=>$courseid));
249         // now security checks
250         $context = context_course::instance($params['courseid'], IGNORE_MISSING);
251         try {
252             self::validate_context($context);
253         } catch (Exception $e) {
254                 $exceptionparam = new stdClass();
255                 $exceptionparam->message = $e->getMessage();
256                 $exceptionparam->courseid = $params['courseid'];
257                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
258         }
259         require_capability('moodle/course:managegroups', $context);
261         $gs = groups_get_all_groups($params['courseid'], 0, 0,
262             'g.id, g.courseid, g.name, g.idnumber, g.description, g.descriptionformat, g.enrolmentkey');
264         $groups = array();
265         foreach ($gs as $group) {
266             list($group->description, $group->descriptionformat) =
267                 external_format_text($group->description, $group->descriptionformat,
268                         $context->id, 'group', 'description', $group->id);
269             $groups[] = (array)$group;
270         }
272         return $groups;
273     }
275     /**
276      * Returns description of method result value
277      *
278      * @return external_description
279      * @since Moodle 2.2
280      */
281     public static function get_course_groups_returns() {
282         return new external_multiple_structure(
283             new external_single_structure(
284                 array(
285                     'id' => new external_value(PARAM_INT, 'group record id'),
286                     'courseid' => new external_value(PARAM_INT, 'id of course'),
287                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
288                     'description' => new external_value(PARAM_RAW, 'group description text'),
289                     'descriptionformat' => new external_format_value('description'),
290                     'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
291                     'idnumber' => new external_value(PARAM_RAW, 'id number')
292                 )
293             )
294         );
295     }
297     /**
298      * Returns description of method parameters
299      *
300      * @return external_function_parameters
301      * @since Moodle 2.2
302      */
303     public static function delete_groups_parameters() {
304         return new external_function_parameters(
305             array(
306                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
307             )
308         );
309     }
311     /**
312      * Delete groups
313      *
314      * @param array $groupids array of group ids
315      * @since Moodle 2.2
316      */
317     public static function delete_groups($groupids) {
318         global $CFG, $DB;
319         require_once("$CFG->dirroot/group/lib.php");
321         $params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
323         $transaction = $DB->start_delegated_transaction();
325         foreach ($params['groupids'] as $groupid) {
326             // validate params
327             $groupid = validate_param($groupid, PARAM_INT);
328             if (!$group = groups_get_group($groupid, '*', IGNORE_MISSING)) {
329                 // silently ignore attempts to delete nonexisting groups
330                 continue;
331             }
333             // now security checks
334             $context = context_course::instance($group->courseid, IGNORE_MISSING);
335             try {
336                 self::validate_context($context);
337             } catch (Exception $e) {
338                 $exceptionparam = new stdClass();
339                 $exceptionparam->message = $e->getMessage();
340                 $exceptionparam->courseid = $group->courseid;
341                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
342             }
343             require_capability('moodle/course:managegroups', $context);
345             groups_delete_group($group);
346         }
348         $transaction->allow_commit();
349     }
351     /**
352      * Returns description of method result value
353      *
354      * @return null
355      * @since Moodle 2.2
356      */
357     public static function delete_groups_returns() {
358         return null;
359     }
362     /**
363      * Returns description of method parameters
364      *
365      * @return external_function_parameters
366      * @since Moodle 2.2
367      */
368     public static function get_group_members_parameters() {
369         return new external_function_parameters(
370             array(
371                 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
372             )
373         );
374     }
376     /**
377      * Return all members for a group
378      *
379      * @param array $groupids array of group ids
380      * @return array with  group id keys containing arrays of user ids
381      * @since Moodle 2.2
382      */
383     public static function get_group_members($groupids) {
384         $members = array();
386         $params = self::validate_parameters(self::get_group_members_parameters(), array('groupids'=>$groupids));
388         foreach ($params['groupids'] as $groupid) {
389             // validate params
390             $group = groups_get_group($groupid, 'id, courseid, name, enrolmentkey', MUST_EXIST);
391             // now security checks
392             $context = context_course::instance($group->courseid, IGNORE_MISSING);
393             try {
394                 self::validate_context($context);
395             } catch (Exception $e) {
396                 $exceptionparam = new stdClass();
397                 $exceptionparam->message = $e->getMessage();
398                 $exceptionparam->courseid = $group->courseid;
399                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
400             }
401             require_capability('moodle/course:managegroups', $context);
403             $groupmembers = groups_get_members($group->id, 'u.id', 'lastname ASC, firstname ASC');
405             $members[] = array('groupid'=>$groupid, 'userids'=>array_keys($groupmembers));
406         }
408         return $members;
409     }
411     /**
412      * Returns description of method result value
413      *
414      * @return external_description
415      * @since Moodle 2.2
416      */
417     public static function get_group_members_returns() {
418         return new external_multiple_structure(
419             new external_single_structure(
420                 array(
421                     'groupid' => new external_value(PARAM_INT, 'group record id'),
422                     'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')),
423                 )
424             )
425         );
426     }
429     /**
430      * Returns description of method parameters
431      *
432      * @return external_function_parameters
433      * @since Moodle 2.2
434      */
435     public static function add_group_members_parameters() {
436         return new external_function_parameters(
437             array(
438                 'members'=> new external_multiple_structure(
439                     new external_single_structure(
440                         array(
441                             'groupid' => new external_value(PARAM_INT, 'group record id'),
442                             'userid' => new external_value(PARAM_INT, 'user id'),
443                         )
444                     )
445                 )
446             )
447         );
448     }
450     /**
451      * Add group members
452      *
453      * @param array $members of arrays with keys userid, groupid
454      * @since Moodle 2.2
455      */
456     public static function add_group_members($members) {
457         global $CFG, $DB;
458         require_once("$CFG->dirroot/group/lib.php");
460         $params = self::validate_parameters(self::add_group_members_parameters(), array('members'=>$members));
462         $transaction = $DB->start_delegated_transaction();
463         foreach ($params['members'] as $member) {
464             // validate params
465             $groupid = $member['groupid'];
466             $userid = $member['userid'];
468             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
469             $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
471             // now security checks
472             $context = context_course::instance($group->courseid, IGNORE_MISSING);
473             try {
474                 self::validate_context($context);
475             } catch (Exception $e) {
476                 $exceptionparam = new stdClass();
477                 $exceptionparam->message = $e->getMessage();
478                 $exceptionparam->courseid = $group->courseid;
479                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
480             }
481             require_capability('moodle/course:managegroups', $context);
483             // now make sure user is enrolled in course - this is mandatory requirement,
484             // unfortunately this is slow
485             if (!is_enrolled($context, $userid)) {
486                 throw new invalid_parameter_exception('Only enrolled users may be members of groups');
487             }
489             groups_add_member($group, $user);
490         }
492         $transaction->allow_commit();
493     }
495     /**
496      * Returns description of method result value
497      *
498      * @return null
499      * @since Moodle 2.2
500      */
501     public static function add_group_members_returns() {
502         return null;
503     }
506     /**
507      * Returns description of method parameters
508      *
509      * @return external_function_parameters
510      * @since Moodle 2.2
511      */
512     public static function delete_group_members_parameters() {
513         return new external_function_parameters(
514             array(
515                 'members'=> new external_multiple_structure(
516                     new external_single_structure(
517                         array(
518                             'groupid' => new external_value(PARAM_INT, 'group record id'),
519                             'userid' => new external_value(PARAM_INT, 'user id'),
520                         )
521                     )
522                 )
523             )
524         );
525     }
527     /**
528      * Delete group members
529      *
530      * @param array $members of arrays with keys userid, groupid
531      * @since Moodle 2.2
532      */
533     public static function delete_group_members($members) {
534         global $CFG, $DB;
535         require_once("$CFG->dirroot/group/lib.php");
537         $params = self::validate_parameters(self::delete_group_members_parameters(), array('members'=>$members));
539         $transaction = $DB->start_delegated_transaction();
541         foreach ($params['members'] as $member) {
542             // validate params
543             $groupid = $member['groupid'];
544             $userid = $member['userid'];
546             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
547             $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
549             // now security checks
550             $context = context_course::instance($group->courseid, IGNORE_MISSING);
551             try {
552                 self::validate_context($context);
553             } catch (Exception $e) {
554                 $exceptionparam = new stdClass();
555                 $exceptionparam->message = $e->getMessage();
556                 $exceptionparam->courseid = $group->courseid;
557                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
558             }
559             require_capability('moodle/course:managegroups', $context);
561             if (!groups_remove_member_allowed($group, $user)) {
562                 throw new moodle_exception('errorremovenotpermitted', 'group', '', fullname($user));
563             }
564             groups_remove_member($group, $user);
565         }
567         $transaction->allow_commit();
568     }
570     /**
571      * Returns description of method result value
572      *
573      * @return null
574      * @since Moodle 2.2
575      */
576     public static function delete_group_members_returns() {
577         return null;
578     }
580     /**
581      * Returns description of method parameters
582      *
583      * @return external_function_parameters
584      * @since Moodle 2.3
585      */
586     public static function create_groupings_parameters() {
587         return new external_function_parameters(
588             array(
589                 'groupings' => new external_multiple_structure(
590                     new external_single_structure(
591                         array(
592                             'courseid' => new external_value(PARAM_INT, 'id of course'),
593                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
594                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
595                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
596                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
597                         )
598                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
599                 )
600             )
601         );
602     }
604     /**
605      * Create groupings
606      *
607      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
608      * @return array of newly created groupings
609      * @since Moodle 2.3
610      */
611     public static function create_groupings($groupings) {
612         global $CFG, $DB;
613         require_once("$CFG->dirroot/group/lib.php");
615         $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings));
617         $transaction = $DB->start_delegated_transaction();
619         $groupings = array();
621         foreach ($params['groupings'] as $grouping) {
622             $grouping = (object)$grouping;
624             if (trim($grouping->name) == '') {
625                 throw new invalid_parameter_exception('Invalid grouping name');
626             }
627             if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
628                 throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
629             }
630             if (!empty($grouping->idnumber) && $DB->count_records('groupings', array('idnumber' => $grouping->idnumber))) {
631                 throw new invalid_parameter_exception('Grouping with the same idnumber already exists');
632             }
634             // Now security checks            .
635             $context = context_course::instance($grouping->courseid);
636             try {
637                 self::validate_context($context);
638             } catch (Exception $e) {
639                 $exceptionparam = new stdClass();
640                 $exceptionparam->message = $e->getMessage();
641                 $exceptionparam->courseid = $grouping->courseid;
642                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
643             }
644             require_capability('moodle/course:managegroups', $context);
646             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
648             // Finally create the grouping.
649             $grouping->id = groups_create_grouping($grouping);
650             $groupings[] = (array)$grouping;
651         }
653         $transaction->allow_commit();
655         return $groupings;
656     }
658     /**
659      * Returns description of method result value
660      *
661      * @return external_description
662      * @since Moodle 2.3
663      */
664     public static function create_groupings_returns() {
665         return new external_multiple_structure(
666             new external_single_structure(
667                 array(
668                     'id' => new external_value(PARAM_INT, 'grouping record id'),
669                     'courseid' => new external_value(PARAM_INT, 'id of course'),
670                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
671                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
672                     'descriptionformat' => new external_format_value('description'),
673                     'idnumber' => new external_value(PARAM_RAW, 'id number')
674                 )
675             ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.'
676         );
677     }
679     /**
680      * Returns description of method parameters
681      *
682      * @return external_function_parameters
683      * @since Moodle 2.3
684      */
685     public static function update_groupings_parameters() {
686         return new external_function_parameters(
687             array(
688                 'groupings' => new external_multiple_structure(
689                     new external_single_structure(
690                         array(
691                             'id' => new external_value(PARAM_INT, 'id of grouping'),
692                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
693                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
694                             'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
695                             'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
696                         )
697                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
698                 )
699             )
700         );
701     }
703     /**
704      * Update groupings
705      *
706      * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
707      * @return array of newly updated groupings
708      * @since Moodle 2.3
709      */
710     public static function update_groupings($groupings) {
711         global $CFG, $DB;
712         require_once("$CFG->dirroot/group/lib.php");
714         $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings));
716         $transaction = $DB->start_delegated_transaction();
718         foreach ($params['groupings'] as $grouping) {
719             $grouping = (object)$grouping;
721             if (trim($grouping->name) == '') {
722                 throw new invalid_parameter_exception('Invalid grouping name');
723             }
725             if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) {
726                 throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course");
727             }
729             // Check if the new modified grouping name already exists in the course.
730             if ($grouping->name != $currentgrouping->name and
731                     $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
732                 throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
733             }
734             // Check if the new modified grouping idnumber already exists.
735             if (!empty($grouping->idnumber) && $grouping->idnumber != $currentgrouping->idnumber &&
736                     $DB->count_records('groupings', array('idnumber' => $grouping->idnumber))) {
737                 throw new invalid_parameter_exception('A different grouping with the same idnumber already exists');
738             }
740             $grouping->courseid = $currentgrouping->courseid;
742             // Now security checks.
743             $context = context_course::instance($grouping->courseid);
744             try {
745                 self::validate_context($context);
746             } catch (Exception $e) {
747                 $exceptionparam = new stdClass();
748                 $exceptionparam->message = $e->getMessage();
749                 $exceptionparam->courseid = $grouping->courseid;
750                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
751             }
752             require_capability('moodle/course:managegroups', $context);
754             // We must force allways FORMAT_HTML.
755             $grouping->descriptionformat = external_validate_format($grouping->descriptionformat);
757             // Finally update the grouping.
758             groups_update_grouping($grouping);
759         }
761         $transaction->allow_commit();
763         return null;
764     }
766     /**
767      * Returns description of method result value
768      *
769      * @return external_description
770      * @since Moodle 2.3
771      */
772     public static function update_groupings_returns() {
773         return null;
774     }
776     /**
777      * Returns description of method parameters
778      *
779      * @return external_function_parameters
780      * @since Moodle 2.3
781      */
782     public static function get_groupings_parameters() {
783         return new external_function_parameters(
784             array(
785                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')
786                         , 'List of grouping id. A grouping id is an integer.'),
787                 'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0)
788             )
789         );
790     }
792     /**
793      * Get groupings definition specified by ids
794      *
795      * @param array $groupingids arrays of grouping ids
796      * @param boolean $returngroups return the associated groups if true. The default is false.
797      * @return array of grouping objects (id, courseid, name)
798      * @since Moodle 2.3
799      */
800     public static function get_groupings($groupingids, $returngroups = false) {
801         global $CFG, $DB;
802         require_once("$CFG->dirroot/group/lib.php");
803         require_once("$CFG->libdir/filelib.php");
805         $params = self::validate_parameters(self::get_groupings_parameters(),
806                                             array('groupingids' => $groupingids,
807                                                   'returngroups' => $returngroups));
809         $groupings = array();
810         foreach ($params['groupingids'] as $groupingid) {
811             // Validate params.
812             $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST);
814             // Now security checks.
815             $context = context_course::instance($grouping->courseid);
816             try {
817                 self::validate_context($context);
818             } catch (Exception $e) {
819                 $exceptionparam = new stdClass();
820                 $exceptionparam->message = $e->getMessage();
821                 $exceptionparam->courseid = $grouping->courseid;
822                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
823             }
824             require_capability('moodle/course:managegroups', $context);
826             list($grouping->description, $grouping->descriptionformat) =
827                 external_format_text($grouping->description, $grouping->descriptionformat,
828                         $context->id, 'grouping', 'description', $grouping->id);
830             $groupingarray = (array)$grouping;
832             if ($params['returngroups']) {
833                 $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ".
834                                                "ON g.id = gg.groupid WHERE gg.groupingid = ? ".
835                                                "ORDER BY groupid", array($groupingid));
836                 if ($grouprecords) {
837                     $groups = array();
838                     foreach ($grouprecords as $grouprecord) {
839                         list($grouprecord->description, $grouprecord->descriptionformat) =
840                         external_format_text($grouprecord->description, $grouprecord->descriptionformat,
841                         $context->id, 'group', 'description', $grouprecord->groupid);
842                         $groups[] = array('id' => $grouprecord->groupid,
843                                           'name' => $grouprecord->name,
844                                           'idnumber' => $grouprecord->idnumber,
845                                           'description' => $grouprecord->description,
846                                           'descriptionformat' => $grouprecord->descriptionformat,
847                                           'enrolmentkey' => $grouprecord->enrolmentkey,
848                                           'courseid' => $grouprecord->courseid
849                                           );
850                     }
851                     $groupingarray['groups'] = $groups;
852                 }
853             }
854             $groupings[] = $groupingarray;
855         }
857         return $groupings;
858     }
860     /**
861      * Returns description of method result value
862      *
863      * @return external_description
864      * @since Moodle 2.3
865      */
866     public static function get_groupings_returns() {
867         return new external_multiple_structure(
868             new external_single_structure(
869                 array(
870                     'id' => new external_value(PARAM_INT, 'grouping record id'),
871                     'courseid' => new external_value(PARAM_INT, 'id of course'),
872                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
873                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
874                     'descriptionformat' => new external_format_value('description'),
875                     'idnumber' => new external_value(PARAM_RAW, 'id number'),
876                     'groups' => new external_multiple_structure(
877                         new external_single_structure(
878                             array(
879                                 'id' => new external_value(PARAM_INT, 'group record id'),
880                                 'courseid' => new external_value(PARAM_INT, 'id of course'),
881                                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
882                                 'description' => new external_value(PARAM_RAW, 'group description text'),
883                                 'descriptionformat' => new external_format_value('description'),
884                                 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
885                                 'idnumber' => new external_value(PARAM_RAW, 'id number')
886                             )
887                         ),
888                     'optional groups', VALUE_OPTIONAL)
889                 )
890             )
891         );
892     }
894     /**
895      * Returns description of method parameters
896      *
897      * @return external_function_parameters
898      * @since Moodle 2.3
899      */
900     public static function get_course_groupings_parameters() {
901         return new external_function_parameters(
902             array(
903                 'courseid' => new external_value(PARAM_INT, 'id of course'),
904             )
905         );
906     }
908     /**
909      * Get all groupings in the specified course
910      *
911      * @param int $courseid id of course
912      * @return array of grouping objects (id, courseid, name, enrolmentkey)
913      * @since Moodle 2.3
914      */
915     public static function get_course_groupings($courseid) {
916         global $CFG;
917         require_once("$CFG->dirroot/group/lib.php");
918         require_once("$CFG->libdir/filelib.php");
920         $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid));
922         // Now security checks.
923         $context = context_course::instance($params['courseid']);
925         try {
926             self::validate_context($context);
927         } catch (Exception $e) {
928                 $exceptionparam = new stdClass();
929                 $exceptionparam->message = $e->getMessage();
930                 $exceptionparam->courseid = $params['courseid'];
931                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
932         }
933         require_capability('moodle/course:managegroups', $context);
935         $gs = groups_get_all_groupings($params['courseid']);
937         $groupings = array();
938         foreach ($gs as $grouping) {
939             list($grouping->description, $grouping->descriptionformat) =
940                 external_format_text($grouping->description, $grouping->descriptionformat,
941                         $context->id, 'grouping', 'description', $grouping->id);
942             $groupings[] = (array)$grouping;
943         }
945         return $groupings;
946     }
948     /**
949      * Returns description of method result value
950      *
951      * @return external_description
952      * @since Moodle 2.3
953      */
954     public static function get_course_groupings_returns() {
955         return new external_multiple_structure(
956             new external_single_structure(
957                 array(
958                     'id' => new external_value(PARAM_INT, 'grouping record id'),
959                     'courseid' => new external_value(PARAM_INT, 'id of course'),
960                     'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
961                     'description' => new external_value(PARAM_RAW, 'grouping description text'),
962                     'descriptionformat' => new external_format_value('description'),
963                     'idnumber' => new external_value(PARAM_RAW, 'id number')
964                 )
965             )
966         );
967     }
969     /**
970      * Returns description of method parameters
971      *
972      * @return external_function_parameters
973      * @since Moodle 2.3
974      */
975     public static function delete_groupings_parameters() {
976         return new external_function_parameters(
977             array(
978                 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')),
979             )
980         );
981     }
983     /**
984      * Delete groupings
985      *
986      * @param array $groupingids array of grouping ids
987      * @return void
988      * @since Moodle 2.3
989      */
990     public static function delete_groupings($groupingids) {
991         global $CFG, $DB;
992         require_once("$CFG->dirroot/group/lib.php");
994         $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids));
996         $transaction = $DB->start_delegated_transaction();
998         foreach ($params['groupingids'] as $groupingid) {
1000             if (!$grouping = groups_get_grouping($groupingid, 'id, courseid', IGNORE_MISSING)) {
1001                 // Silently ignore attempts to delete nonexisting groupings.
1002                 continue;
1003             }
1005             // Now security checks.
1006             $context = context_course::instance($grouping->courseid);
1007             try {
1008                 self::validate_context($context);
1009             } catch (Exception $e) {
1010                 $exceptionparam = new stdClass();
1011                 $exceptionparam->message = $e->getMessage();
1012                 $exceptionparam->courseid = $grouping->courseid;
1013                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1014             }
1015             require_capability('moodle/course:managegroups', $context);
1017             groups_delete_grouping($grouping);
1018         }
1020         $transaction->allow_commit();
1021     }
1023     /**
1024      * Returns description of method result value
1025      *
1026      * @return external_description
1027      * @since Moodle 2.3
1028      */
1029     public static function delete_groupings_returns() {
1030         return null;
1031     }
1033     /**
1034      * Returns description of method parameters
1035      *
1036      * @return external_function_parameters
1037      * @since Moodle 2.3
1038      */
1039     public static function assign_grouping_parameters() {
1040         return new external_function_parameters(
1041             array(
1042                 'assignments'=> new external_multiple_structure(
1043                     new external_single_structure(
1044                         array(
1045                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1046                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1047                         )
1048                     )
1049                 )
1050             )
1051         );
1052     }
1054     /**
1055      * Assign a group to a grouping
1056      *
1057      * @param array $assignments of arrays with keys groupid, groupingid
1058      * @return void
1059      * @since Moodle 2.3
1060      */
1061     public static function assign_grouping($assignments) {
1062         global $CFG, $DB;
1063         require_once("$CFG->dirroot/group/lib.php");
1065         $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments));
1067         $transaction = $DB->start_delegated_transaction();
1068         foreach ($params['assignments'] as $assignment) {
1069             // Validate params.
1070             $groupingid = $assignment['groupingid'];
1071             $groupid = $assignment['groupid'];
1073             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1074             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1076             if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1077                 // Continue silently if the group is yet assigned to the grouping.
1078                 continue;
1079             }
1081             // Now security checks.
1082             $context = context_course::instance($grouping->courseid);
1083             try {
1084                 self::validate_context($context);
1085             } catch (Exception $e) {
1086                 $exceptionparam = new stdClass();
1087                 $exceptionparam->message = $e->getMessage();
1088                 $exceptionparam->courseid = $group->courseid;
1089                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1090             }
1091             require_capability('moodle/course:managegroups', $context);
1093             groups_assign_grouping($groupingid, $groupid);
1094         }
1096         $transaction->allow_commit();
1097     }
1099     /**
1100      * Returns description of method result value
1101      *
1102      * @return null
1103      * @since Moodle 2.3
1104      */
1105     public static function assign_grouping_returns() {
1106         return null;
1107     }
1109     /**
1110      * Returns description of method parameters
1111      *
1112      * @return external_function_parameters
1113      * @since Moodle 2.3
1114      */
1115     public static function unassign_grouping_parameters() {
1116         return new external_function_parameters(
1117             array(
1118                 'unassignments'=> new external_multiple_structure(
1119                     new external_single_structure(
1120                         array(
1121                             'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1122                             'groupid' => new external_value(PARAM_INT, 'group record id'),
1123                         )
1124                     )
1125                 )
1126             )
1127         );
1128     }
1130     /**
1131      * Unassign a group from a grouping
1132      *
1133      * @param array $unassignments of arrays with keys groupid, groupingid
1134      * @return void
1135      * @since Moodle 2.3
1136      */
1137     public static function unassign_grouping($unassignments) {
1138         global $CFG, $DB;
1139         require_once("$CFG->dirroot/group/lib.php");
1141         $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments));
1143         $transaction = $DB->start_delegated_transaction();
1144         foreach ($params['unassignments'] as $unassignment) {
1145             // Validate params.
1146             $groupingid = $unassignment['groupingid'];
1147             $groupid = $unassignment['groupid'];
1149             $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1150             $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1152             if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1153                 // Continue silently if the group is not assigned to the grouping.
1154                 continue;
1155             }
1157             // Now security checks.
1158             $context = context_course::instance($grouping->courseid);
1159             try {
1160                 self::validate_context($context);
1161             } catch (Exception $e) {
1162                 $exceptionparam = new stdClass();
1163                 $exceptionparam->message = $e->getMessage();
1164                 $exceptionparam->courseid = $group->courseid;
1165                 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1166             }
1167             require_capability('moodle/course:managegroups', $context);
1169             groups_unassign_grouping($groupingid, $groupid);
1170         }
1172         $transaction->allow_commit();
1173     }
1175     /**
1176      * Returns description of method result value
1177      *
1178      * @return null
1179      * @since Moodle 2.3
1180      */
1181     public static function unassign_grouping_returns() {
1182         return null;
1183     }
1185     /**
1186      * Returns description of method parameters
1187      *
1188      * @return external_function_parameters
1189      * @since Moodle 2.9
1190      */
1191     public static function get_course_user_groups_parameters() {
1192         return new external_function_parameters(
1193             array(
1194                 'courseid' => new external_value(PARAM_INT, 'id of course'),
1195                 'userid' => new external_value(PARAM_INT, 'id of user'),
1196                 'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0)
1197             )
1198         );
1199     }
1201     /**
1202      * Get all groups in the specified course for the specified user.
1203      *
1204      * @throws moodle_exception
1205      * @param int $courseid id of course.
1206      * @param int $userid id of user.
1207      * @param int $groupingid optional returns only groups in the specified grouping.
1208      * @return array of group objects (id, name, description, format) and possible warnings.
1209      * @since Moodle 2.9
1210      */
1211     public static function get_course_user_groups($courseid, $userid, $groupingid = 0) {
1212         global $USER;
1214         // Warnings array, it can be empty at the end but is mandatory.
1215         $warnings = array();
1217         $params = array(
1218             'courseid' => $courseid,
1219             'userid' => $userid,
1220             'groupingid' => $groupingid
1221         );
1222         $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params);
1223         $courseid = $params['courseid'];
1224         $userid = $params['userid'];
1225         $groupingid = $params['groupingid'];
1227         // Validate course and user. get_course throws an exception if the course does not exists.
1228         $course = get_course($courseid);
1229         $user = core_user::get_user($userid, '*', MUST_EXIST);
1230         core_user::require_active_user($user);
1232         // Security checks.
1233         $context = context_course::instance($course->id);
1234         self::validate_context($context);
1236          // Check if we have permissions for retrieve the information.
1237         if ($user->id != $USER->id) {
1238             if (!has_capability('moodle/course:managegroups', $context)) {
1239                 throw new moodle_exception('accessdenied', 'admin');
1240             }
1241             // Validate if the user is enrolled in the course.
1242             if (!is_enrolled($context, $user->id)) {
1243                 // We return a warning because the function does not fail for not enrolled users.
1244                 $warning['item'] = 'course';
1245                 $warning['itemid'] = $course->id;
1246                 $warning['warningcode'] = '1';
1247                 $warning['message'] = "User $user->id is not enrolled in course $course->id";
1248                 $warnings[] = $warning;
1249             }
1250         }
1252         $usergroups = array();
1253         if (empty($warnings)) {
1254             $groups = groups_get_all_groups($course->id, $user->id, 0, 'g.id, g.name, g.description, g.descriptionformat, g.idnumber');
1256             foreach ($groups as $group) {
1257                 list($group->description, $group->descriptionformat) =
1258                     external_format_text($group->description, $group->descriptionformat,
1259                             $context->id, 'group', 'description', $group->id);
1260                 $group->courseid = $course->id;
1261                 $usergroups[] = $group;
1262             }
1263         }
1265         $results = array(
1266             'groups' => $usergroups,
1267             'warnings' => $warnings
1268         );
1269         return $results;
1270     }
1272     /**
1273      * Returns description of method result value.
1274      *
1275      * @return external_description A single structure containing groups and possible warnings.
1276      * @since Moodle 2.9
1277      */
1278     public static function get_course_user_groups_returns() {
1279         return new external_single_structure(
1280             array(
1281                 'groups' => new external_multiple_structure(self::group_description()),
1282                 'warnings' => new external_warnings(),
1283             )
1284         );
1285     }
1287     /**
1288      * Create group return value description.
1289      *
1290      * @return external_single_structure The group description
1291      */
1292     public static function group_description() {
1293         return new external_single_structure(
1294             array(
1295                 'id' => new external_value(PARAM_INT, 'group record id'),
1296                 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1297                 'description' => new external_value(PARAM_RAW, 'group description text'),
1298                 'descriptionformat' => new external_format_value('description'),
1299                 'idnumber' => new external_value(PARAM_RAW, 'id number'),
1300                 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL),
1301             )
1302         );
1303     }
1305     /**
1306      * Returns description of method parameters
1307      *
1308      * @return external_function_parameters
1309      * @since Moodle 3.0
1310      */
1311     public static function get_activity_allowed_groups_parameters() {
1312         return new external_function_parameters(
1313             array(
1314                 'cmid' => new external_value(PARAM_INT, 'course module id'),
1315                 'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0)
1316             )
1317         );
1318     }
1320     /**
1321      * Gets a list of groups that the user is allowed to access within the specified activity.
1322      *
1323      * @throws moodle_exception
1324      * @param int $cmid course module id
1325      * @param int $userid id of user.
1326      * @return array of group objects (id, name, description, format) and possible warnings.
1327      * @since Moodle 3.0
1328      */
1329     public static function get_activity_allowed_groups($cmid, $userid = 0) {
1330         global $USER;
1332         // Warnings array, it can be empty at the end but is mandatory.
1333         $warnings = array();
1335         $params = array(
1336             'cmid' => $cmid,
1337             'userid' => $userid
1338         );
1339         $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params);
1340         $cmid = $params['cmid'];
1341         $userid = $params['userid'];
1343         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1345         // Security checks.
1346         $context = context_module::instance($cm->id);
1347         $coursecontext = context_course::instance($cm->course);
1348         self::validate_context($context);
1350         if (empty($userid)) {
1351             $userid = $USER->id;
1352         }
1354         $user = core_user::get_user($userid, '*', MUST_EXIST);
1355         core_user::require_active_user($user);
1357          // Check if we have permissions for retrieve the information.
1358         if ($user->id != $USER->id) {
1359             if (!has_capability('moodle/course:managegroups', $context)) {
1360                 throw new moodle_exception('accessdenied', 'admin');
1361             }
1363             // Validate if the user is enrolled in the course.
1364             $course = get_course($cm->course);
1365             if (!can_access_course($course, $user, '', true)) {
1366                 // We return a warning because the function does not fail for not enrolled users.
1367                 $warning = array();
1368                 $warning['item'] = 'course';
1369                 $warning['itemid'] = $cm->course;
1370                 $warning['warningcode'] = '1';
1371                 $warning['message'] = "User $user->id cannot access course $cm->course";
1372                 $warnings[] = $warning;
1373             }
1374         }
1376         $usergroups = array();
1377         if (empty($warnings)) {
1378             $groups = groups_get_activity_allowed_groups($cm, $user->id);
1380             foreach ($groups as $group) {
1381                 list($group->description, $group->descriptionformat) =
1382                     external_format_text($group->description, $group->descriptionformat,
1383                             $coursecontext->id, 'group', 'description', $group->id);
1384                 $group->courseid = $cm->course;
1385                 $usergroups[] = $group;
1386             }
1387         }
1389         $results = array(
1390             'groups' => $usergroups,
1391             'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
1392             'warnings' => $warnings
1393         );
1394         return $results;
1395     }
1397     /**
1398      * Returns description of method result value.
1399      *
1400      * @return external_description A single structure containing groups and possible warnings.
1401      * @since Moodle 3.0
1402      */
1403     public static function get_activity_allowed_groups_returns() {
1404         return new external_single_structure(
1405             array(
1406                 'groups' => new external_multiple_structure(self::group_description()),
1407                 'canaccessallgroups' => new external_value(PARAM_BOOL,
1408                     'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
1409                 'warnings' => new external_warnings(),
1410             )
1411         );
1412     }
1414     /**
1415      * Returns description of method parameters
1416      *
1417      * @return external_function_parameters
1418      * @since Moodle 3.0
1419      */
1420     public static function get_activity_groupmode_parameters() {
1421         return new external_function_parameters(
1422             array(
1423                 'cmid' => new external_value(PARAM_INT, 'course module id')
1424             )
1425         );
1426     }
1428     /**
1429      * Returns effective groupmode used in a given activity.
1430      *
1431      * @throws moodle_exception
1432      * @param int $cmid course module id.
1433      * @return array containing the group mode and possible warnings.
1434      * @since Moodle 3.0
1435      * @throws moodle_exception
1436      */
1437     public static function get_activity_groupmode($cmid) {
1438         global $USER;
1440         // Warnings array, it can be empty at the end but is mandatory.
1441         $warnings = array();
1443         $params = array(
1444             'cmid' => $cmid
1445         );
1446         $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params);
1447         $cmid = $params['cmid'];
1449         $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1451         // Security checks.
1452         $context = context_module::instance($cm->id);
1453         self::validate_context($context);
1455         $groupmode = groups_get_activity_groupmode($cm);
1457         $results = array(
1458             'groupmode' => $groupmode,
1459             'warnings' => $warnings
1460         );
1461         return $results;
1462     }
1464     /**
1465      * Returns description of method result value.
1466      *
1467      * @return external_description
1468      * @since Moodle 3.0
1469      */
1470     public static function get_activity_groupmode_returns() {
1471         return new external_single_structure(
1472             array(
1473                 'groupmode' => new external_value(PARAM_INT, 'group mode:
1474                                                     0 for no groups, 1 for separate groups, 2 for visible groups'),
1475                 'warnings' => new external_warnings(),
1476             )
1477         );
1478     }