MDL-80880 quiz: change display of previous attempts summary
[moodle.git] / group / externallib.php
blob0d2d2713f74f747673393f6879aa304228761399
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.
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/>.
17 use core_external\external_api;
18 use core_external\external_format_value;
19 use core_external\external_function_parameters;
20 use core_external\external_multiple_structure;
21 use core_external\external_single_structure;
22 use core_external\external_value;
23 use core_external\external_warnings;
24 use core_external\util;
25 use core_group\visibility;
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->dirroot . '/group/lib.php');
31 /**
32 * Group external functions
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
40 class core_group_external extends external_api {
43 /**
44 * Validate visibility.
46 * @param int $visibility Visibility string, must one of the visibility class constants.
47 * @throws invalid_parameter_exception if visibility is not an allowed value.
49 protected static function validate_visibility(int $visibility): void {
50 $allowed = [
51 GROUPS_VISIBILITY_ALL,
52 GROUPS_VISIBILITY_MEMBERS,
53 GROUPS_VISIBILITY_OWN,
54 GROUPS_VISIBILITY_NONE,
56 if (!array_key_exists($visibility, $allowed)) {
57 throw new invalid_parameter_exception('Invalid group visibility provided. Must be one of '
58 . join(',', $allowed));
62 /**
63 * Returns description of method parameters
65 * @return external_function_parameters
66 * @since Moodle 2.2
68 public static function create_groups_parameters() {
69 return new external_function_parameters(
70 array(
71 'groups' => new external_multiple_structure(
72 new external_single_structure(
73 array(
74 'courseid' => new external_value(PARAM_INT, 'id of course'),
75 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
76 'description' => new external_value(PARAM_RAW, 'group description text'),
77 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
78 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
79 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
80 'visibility' => new external_value(PARAM_INT,
81 'group visibility mode. 0 = Visible to all. 1 = Visible to members. '
82 . '2 = See own membership. 3 = Membership is hidden. default: 0',
83 VALUE_DEFAULT, 0),
84 'participation' => new external_value(PARAM_BOOL,
85 'activity participation enabled? Only for "all" and "members" visibility. Default true.',
86 VALUE_DEFAULT, true),
87 'customfields' => self::build_custom_fields_parameters_structure(),
89 ), 'List of group object. A group has a courseid, a name, a description and an enrolment key.'
95 /**
96 * Create groups
98 * @param array $groups array of group description arrays (with keys groupname and courseid)
99 * @return array of newly created groups
100 * @since Moodle 2.2
102 public static function create_groups($groups) {
103 global $CFG, $DB;
104 require_once("$CFG->dirroot/group/lib.php");
106 $params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));
108 $transaction = $DB->start_delegated_transaction();
110 $groups = array();
112 foreach ($params['groups'] as $group) {
113 $group = (object)$group;
115 if (trim($group->name) == '') {
116 throw new invalid_parameter_exception('Invalid group name');
118 if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) {
119 throw new invalid_parameter_exception('Group with the same name already exists in the course');
122 // now security checks
123 $context = context_course::instance($group->courseid, IGNORE_MISSING);
124 try {
125 self::validate_context($context);
126 } catch (Exception $e) {
127 $exceptionparam = new stdClass();
128 $exceptionparam->message = $e->getMessage();
129 $exceptionparam->courseid = $group->courseid;
130 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
132 require_capability('moodle/course:managegroups', $context);
134 // Validate format.
135 $group->descriptionformat = util::validate_format($group->descriptionformat);
137 // Validate visibility.
138 self::validate_visibility($group->visibility);
140 // Custom fields.
141 if (!empty($group->customfields)) {
142 foreach ($group->customfields as $field) {
143 $fieldname = self::build_custom_field_name($field['shortname']);
144 $group->{$fieldname} = $field['value'];
148 // finally create the group
149 $group->id = groups_create_group($group, false);
150 if (!isset($group->enrolmentkey)) {
151 $group->enrolmentkey = '';
153 if (!isset($group->idnumber)) {
154 $group->idnumber = '';
157 $groups[] = (array)$group;
160 $transaction->allow_commit();
162 return $groups;
166 * Returns description of method result value
168 * @return \core_external\external_description
169 * @since Moodle 2.2
171 public static function create_groups_returns() {
172 return new external_multiple_structure(
173 new external_single_structure(
174 array(
175 'id' => new external_value(PARAM_INT, 'group record id'),
176 'courseid' => new external_value(PARAM_INT, 'id of course'),
177 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
178 'description' => new external_value(PARAM_RAW, 'group description text'),
179 'descriptionformat' => new external_format_value('description'),
180 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
181 'idnumber' => new external_value(PARAM_RAW, 'id number'),
182 'visibility' => new external_value(PARAM_INT,
183 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
184 . '3 = Membership is hidden.'),
185 'participation' => new external_value(PARAM_BOOL, 'participation mode'),
186 'customfields' => self::build_custom_fields_parameters_structure(),
188 ), 'List of group object. A group has an id, a courseid, a name, a description and an enrolment key.'
193 * Returns description of method parameters
195 * @return external_function_parameters
196 * @since Moodle 2.2
198 public static function get_groups_parameters() {
199 return new external_function_parameters(
200 array(
201 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')
202 ,'List of group id. A group id is an integer.'),
208 * Get groups definition specified by ids
210 * @param array $groupids arrays of group ids
211 * @return array of group objects (id, courseid, name, enrolmentkey)
212 * @since Moodle 2.2
214 public static function get_groups($groupids) {
215 $params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids));
217 $groups = array();
218 $customfieldsdata = get_group_custom_fields_data($groupids);
219 foreach ($params['groupids'] as $groupid) {
220 // validate params
221 $group = groups_get_group($groupid, 'id, courseid, name, idnumber, description, descriptionformat, enrolmentkey, '
222 . 'visibility, participation', MUST_EXIST);
224 // now security checks
225 $context = context_course::instance($group->courseid, IGNORE_MISSING);
226 try {
227 self::validate_context($context);
228 } catch (Exception $e) {
229 $exceptionparam = new stdClass();
230 $exceptionparam->message = $e->getMessage();
231 $exceptionparam->courseid = $group->courseid;
232 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
234 require_capability('moodle/course:managegroups', $context);
236 $group->name = \core_external\util::format_string($group->name, $context);
237 [$group->description, $group->descriptionformat] =
238 \core_external\util::format_text($group->description, $group->descriptionformat,
239 $context, 'group', 'description', $group->id);
241 $group->customfields = $customfieldsdata[$group->id] ?? [];
242 $groups[] = (array)$group;
245 return $groups;
249 * Returns description of method result value
251 * @return \core_external\external_description
252 * @since Moodle 2.2
254 public static function get_groups_returns() {
255 return new external_multiple_structure(
256 new external_single_structure(
257 array(
258 'id' => new external_value(PARAM_INT, 'group record id'),
259 'courseid' => new external_value(PARAM_INT, 'id of course'),
260 'name' => new external_value(PARAM_TEXT, 'group name'),
261 'description' => new external_value(PARAM_RAW, 'group description text'),
262 'descriptionformat' => new external_format_value('description'),
263 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
264 'idnumber' => new external_value(PARAM_RAW, 'id number'),
265 'visibility' => new external_value(PARAM_INT,
266 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
267 . '3 = Membership is hidden.'),
268 'participation' => new external_value(PARAM_BOOL, 'participation mode'),
269 'customfields' => self::build_custom_fields_returns_structure(),
276 * Returns description of method parameters
278 * @return external_function_parameters
279 * @since Moodle 2.2
281 public static function get_course_groups_parameters() {
282 return new external_function_parameters(
283 array(
284 'courseid' => new external_value(PARAM_INT, 'id of course'),
290 * Get all groups in the specified course
292 * @param int $courseid id of course
293 * @return array of group objects (id, courseid, name, enrolmentkey)
294 * @since Moodle 2.2
296 public static function get_course_groups($courseid) {
297 $params = self::validate_parameters(self::get_course_groups_parameters(), array('courseid'=>$courseid));
299 // now security checks
300 $context = context_course::instance($params['courseid'], IGNORE_MISSING);
301 try {
302 self::validate_context($context);
303 } catch (Exception $e) {
304 $exceptionparam = new stdClass();
305 $exceptionparam->message = $e->getMessage();
306 $exceptionparam->courseid = $params['courseid'];
307 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
309 require_capability('moodle/course:managegroups', $context);
311 $gs = groups_get_all_groups($params['courseid'], 0, 0,
312 'g.id, g.courseid, g.name, g.idnumber, g.description, g.descriptionformat, g.enrolmentkey, '
313 . 'g.visibility, g.participation');
315 $groups = array();
316 foreach ($gs as $group) {
317 $group->name = \core_external\util::format_string($group->name, $context);
318 [$group->description, $group->descriptionformat] =
319 \core_external\util::format_text($group->description, $group->descriptionformat,
320 $context, 'group', 'description', $group->id);
321 $groups[] = (array)$group;
324 return $groups;
328 * Returns description of method result value
330 * @return \core_external\external_description
331 * @since Moodle 2.2
333 public static function get_course_groups_returns() {
334 return new external_multiple_structure(
335 new external_single_structure(
336 array(
337 'id' => new external_value(PARAM_INT, 'group record id'),
338 'courseid' => new external_value(PARAM_INT, 'id of course'),
339 'name' => new external_value(PARAM_TEXT, 'group name'),
340 'description' => new external_value(PARAM_RAW, 'group description text'),
341 'descriptionformat' => new external_format_value('description'),
342 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
343 'idnumber' => new external_value(PARAM_RAW, 'id number'),
344 'visibility' => new external_value(PARAM_INT,
345 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
346 . '3 = Membership is hidden.'),
347 'participation' => new external_value(PARAM_BOOL, 'participation mode'),
354 * Returns description of method parameters
356 * @return external_function_parameters
357 * @since Moodle 2.2
359 public static function delete_groups_parameters() {
360 return new external_function_parameters(
361 array(
362 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
368 * Delete groups
370 * @param array $groupids array of group ids
371 * @since Moodle 2.2
373 public static function delete_groups($groupids) {
374 global $CFG, $DB;
375 require_once("$CFG->dirroot/group/lib.php");
377 $params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
379 $transaction = $DB->start_delegated_transaction();
381 foreach ($params['groupids'] as $groupid) {
382 // validate params
383 $groupid = validate_param($groupid, PARAM_INT);
384 if (!$group = groups_get_group($groupid, '*', IGNORE_MISSING)) {
385 // silently ignore attempts to delete nonexisting groups
386 continue;
389 // now security checks
390 $context = context_course::instance($group->courseid, IGNORE_MISSING);
391 try {
392 self::validate_context($context);
393 } catch (Exception $e) {
394 $exceptionparam = new stdClass();
395 $exceptionparam->message = $e->getMessage();
396 $exceptionparam->courseid = $group->courseid;
397 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
399 require_capability('moodle/course:managegroups', $context);
401 groups_delete_group($group);
404 $transaction->allow_commit();
408 * Returns description of method result value
410 * @return null
411 * @since Moodle 2.2
413 public static function delete_groups_returns() {
414 return null;
419 * Returns description of method parameters
421 * @return external_function_parameters
422 * @since Moodle 2.2
424 public static function get_group_members_parameters() {
425 return new external_function_parameters(
426 array(
427 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
433 * Return all members for a group
435 * @param array $groupids array of group ids
436 * @return array with group id keys containing arrays of user ids
437 * @since Moodle 2.2
439 public static function get_group_members($groupids) {
440 $members = array();
442 $params = self::validate_parameters(self::get_group_members_parameters(), array('groupids'=>$groupids));
444 foreach ($params['groupids'] as $groupid) {
445 // validate params
446 $group = groups_get_group($groupid, 'id, courseid, name, enrolmentkey', MUST_EXIST);
447 // now security checks
448 $context = context_course::instance($group->courseid, IGNORE_MISSING);
449 try {
450 self::validate_context($context);
451 } catch (Exception $e) {
452 $exceptionparam = new stdClass();
453 $exceptionparam->message = $e->getMessage();
454 $exceptionparam->courseid = $group->courseid;
455 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
457 require_capability('moodle/course:managegroups', $context);
459 $groupmembers = groups_get_members($group->id, 'u.id', 'lastname ASC, firstname ASC');
461 $members[] = array('groupid'=>$groupid, 'userids'=>array_keys($groupmembers));
464 return $members;
468 * Returns description of method result value
470 * @return \core_external\external_description
471 * @since Moodle 2.2
473 public static function get_group_members_returns() {
474 return new external_multiple_structure(
475 new external_single_structure(
476 array(
477 'groupid' => new external_value(PARAM_INT, 'group record id'),
478 'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')),
486 * Returns description of method parameters
488 * @return external_function_parameters
489 * @since Moodle 2.2
491 public static function add_group_members_parameters() {
492 return new external_function_parameters(
493 array(
494 'members'=> new external_multiple_structure(
495 new external_single_structure(
496 array(
497 'groupid' => new external_value(PARAM_INT, 'group record id'),
498 'userid' => new external_value(PARAM_INT, 'user id'),
507 * Add group members
509 * @param array $members of arrays with keys userid, groupid
510 * @since Moodle 2.2
512 public static function add_group_members($members) {
513 global $CFG, $DB;
514 require_once("$CFG->dirroot/group/lib.php");
516 $params = self::validate_parameters(self::add_group_members_parameters(), array('members'=>$members));
518 $transaction = $DB->start_delegated_transaction();
519 foreach ($params['members'] as $member) {
520 // validate params
521 $groupid = $member['groupid'];
522 $userid = $member['userid'];
524 $group = groups_get_group($groupid, '*', MUST_EXIST);
525 $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
527 // now security checks
528 $context = context_course::instance($group->courseid, IGNORE_MISSING);
529 try {
530 self::validate_context($context);
531 } catch (Exception $e) {
532 $exceptionparam = new stdClass();
533 $exceptionparam->message = $e->getMessage();
534 $exceptionparam->courseid = $group->courseid;
535 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
537 require_capability('moodle/course:managegroups', $context);
539 // now make sure user is enrolled in course - this is mandatory requirement,
540 // unfortunately this is slow
541 if (!is_enrolled($context, $userid)) {
542 throw new invalid_parameter_exception('Only enrolled users may be members of groups');
545 groups_add_member($group, $user);
548 $transaction->allow_commit();
552 * Returns description of method result value
554 * @return null
555 * @since Moodle 2.2
557 public static function add_group_members_returns() {
558 return null;
563 * Returns description of method parameters
565 * @return external_function_parameters
566 * @since Moodle 2.2
568 public static function delete_group_members_parameters() {
569 return new external_function_parameters(
570 array(
571 'members'=> new external_multiple_structure(
572 new external_single_structure(
573 array(
574 'groupid' => new external_value(PARAM_INT, 'group record id'),
575 'userid' => new external_value(PARAM_INT, 'user id'),
584 * Delete group members
586 * @param array $members of arrays with keys userid, groupid
587 * @since Moodle 2.2
589 public static function delete_group_members($members) {
590 global $CFG, $DB;
591 require_once("$CFG->dirroot/group/lib.php");
593 $params = self::validate_parameters(self::delete_group_members_parameters(), array('members'=>$members));
595 $transaction = $DB->start_delegated_transaction();
597 foreach ($params['members'] as $member) {
598 // validate params
599 $groupid = $member['groupid'];
600 $userid = $member['userid'];
602 $group = groups_get_group($groupid, '*', MUST_EXIST);
603 $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
605 // now security checks
606 $context = context_course::instance($group->courseid, IGNORE_MISSING);
607 try {
608 self::validate_context($context);
609 } catch (Exception $e) {
610 $exceptionparam = new stdClass();
611 $exceptionparam->message = $e->getMessage();
612 $exceptionparam->courseid = $group->courseid;
613 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
615 require_capability('moodle/course:managegroups', $context);
617 if (!groups_remove_member_allowed($group, $user)) {
618 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
619 throw new moodle_exception('errorremovenotpermitted', 'group', '', $fullname);
621 groups_remove_member($group, $user);
624 $transaction->allow_commit();
628 * Returns description of method result value
630 * @return null
631 * @since Moodle 2.2
633 public static function delete_group_members_returns() {
634 return null;
638 * Returns description of method parameters
640 * @return external_function_parameters
641 * @since Moodle 2.3
643 public static function create_groupings_parameters() {
644 return new external_function_parameters(
645 array(
646 'groupings' => new external_multiple_structure(
647 new external_single_structure(
648 array(
649 'courseid' => new external_value(PARAM_INT, 'id of course'),
650 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
651 'description' => new external_value(PARAM_RAW, 'grouping description text'),
652 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
653 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
654 'customfields' => self::build_custom_fields_parameters_structure(),
656 ), 'List of grouping object. A grouping has a courseid, a name and a description.'
663 * Create groupings
665 * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
666 * @return array of newly created groupings
667 * @since Moodle 2.3
669 public static function create_groupings($groupings) {
670 global $CFG, $DB;
671 require_once("$CFG->dirroot/group/lib.php");
673 $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings));
675 $transaction = $DB->start_delegated_transaction();
677 $groupings = array();
679 foreach ($params['groupings'] as $grouping) {
680 $grouping = (object)$grouping;
682 if (trim($grouping->name) == '') {
683 throw new invalid_parameter_exception('Invalid grouping name');
685 if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
686 throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
689 // Now security checks .
690 $context = context_course::instance($grouping->courseid);
691 try {
692 self::validate_context($context);
693 } catch (Exception $e) {
694 $exceptionparam = new stdClass();
695 $exceptionparam->message = $e->getMessage();
696 $exceptionparam->courseid = $grouping->courseid;
697 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
699 require_capability('moodle/course:managegroups', $context);
701 $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
703 // Custom fields.
704 if (!empty($grouping->customfields)) {
705 foreach ($grouping->customfields as $field) {
706 $fieldname = self::build_custom_field_name($field['shortname']);
707 $grouping->{$fieldname} = $field['value'];
711 // Finally create the grouping.
712 $grouping->id = groups_create_grouping($grouping);
713 $groupings[] = (array)$grouping;
716 $transaction->allow_commit();
718 return $groupings;
722 * Returns description of method result value
724 * @return \core_external\external_description
725 * @since Moodle 2.3
727 public static function create_groupings_returns() {
728 return new external_multiple_structure(
729 new external_single_structure(
730 array(
731 'id' => new external_value(PARAM_INT, 'grouping record id'),
732 'courseid' => new external_value(PARAM_INT, 'id of course'),
733 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
734 'description' => new external_value(PARAM_RAW, 'grouping description text'),
735 'descriptionformat' => new external_format_value('description'),
736 'idnumber' => new external_value(PARAM_RAW, 'id number'),
737 'customfields' => self::build_custom_fields_parameters_structure(),
739 ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.'
744 * Returns description of method parameters
746 * @return external_function_parameters
747 * @since Moodle 2.3
749 public static function update_groupings_parameters() {
750 return new external_function_parameters(
751 array(
752 'groupings' => new external_multiple_structure(
753 new external_single_structure(
754 array(
755 'id' => new external_value(PARAM_INT, 'id of grouping'),
756 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
757 'description' => new external_value(PARAM_RAW, 'grouping description text'),
758 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
759 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
760 'customfields' => self::build_custom_fields_parameters_structure(),
762 ), 'List of grouping object. A grouping has a courseid, a name and a description.'
769 * Update groupings
771 * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
772 * @return array of newly updated groupings
773 * @since Moodle 2.3
775 public static function update_groupings($groupings) {
776 global $CFG, $DB;
777 require_once("$CFG->dirroot/group/lib.php");
779 $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings));
781 $transaction = $DB->start_delegated_transaction();
783 foreach ($params['groupings'] as $grouping) {
784 $grouping = (object)$grouping;
786 if (trim($grouping->name) == '') {
787 throw new invalid_parameter_exception('Invalid grouping name');
790 if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) {
791 throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course");
794 // Check if the new modified grouping name already exists in the course.
795 if ($grouping->name != $currentgrouping->name and
796 $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
797 throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
800 $grouping->courseid = $currentgrouping->courseid;
802 // Now security checks.
803 $context = context_course::instance($grouping->courseid);
804 try {
805 self::validate_context($context);
806 } catch (Exception $e) {
807 $exceptionparam = new stdClass();
808 $exceptionparam->message = $e->getMessage();
809 $exceptionparam->courseid = $grouping->courseid;
810 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
812 require_capability('moodle/course:managegroups', $context);
814 // We must force allways FORMAT_HTML.
815 $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
817 // Custom fields.
818 if (!empty($grouping->customfields)) {
819 foreach ($grouping->customfields as $field) {
820 $fieldname = self::build_custom_field_name($field['shortname']);
821 $grouping->{$fieldname} = $field['value'];
825 // Finally update the grouping.
826 groups_update_grouping($grouping);
829 $transaction->allow_commit();
831 return null;
835 * Returns description of method result value
837 * @return \core_external\external_description
838 * @since Moodle 2.3
840 public static function update_groupings_returns() {
841 return null;
845 * Returns description of method parameters
847 * @return external_function_parameters
848 * @since Moodle 2.3
850 public static function get_groupings_parameters() {
851 return new external_function_parameters(
852 array(
853 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')
854 , 'List of grouping id. A grouping id is an integer.'),
855 'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0)
861 * Get groupings definition specified by ids
863 * @param array $groupingids arrays of grouping ids
864 * @param boolean $returngroups return the associated groups if true. The default is false.
865 * @return array of grouping objects (id, courseid, name)
866 * @since Moodle 2.3
868 public static function get_groupings($groupingids, $returngroups = false) {
869 global $CFG, $DB;
870 require_once("$CFG->dirroot/group/lib.php");
871 require_once("$CFG->libdir/filelib.php");
873 $params = self::validate_parameters(self::get_groupings_parameters(),
874 array('groupingids' => $groupingids,
875 'returngroups' => $returngroups));
877 $groupings = array();
878 $groupingcustomfieldsdata = get_grouping_custom_fields_data($groupingids);
879 foreach ($params['groupingids'] as $groupingid) {
880 // Validate params.
881 $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST);
883 // Now security checks.
884 $context = context_course::instance($grouping->courseid);
885 try {
886 self::validate_context($context);
887 } catch (Exception $e) {
888 $exceptionparam = new stdClass();
889 $exceptionparam->message = $e->getMessage();
890 $exceptionparam->courseid = $grouping->courseid;
891 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
893 require_capability('moodle/course:managegroups', $context);
895 list($grouping->description, $grouping->descriptionformat) =
896 \core_external\util::format_text($grouping->description, $grouping->descriptionformat,
897 $context, 'grouping', 'description', $grouping->id);
899 $grouping->customfields = $groupingcustomfieldsdata[$grouping->id] ?? [];
900 $groupingarray = (array)$grouping;
902 if ($params['returngroups']) {
903 $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ".
904 "ON g.id = gg.groupid WHERE gg.groupingid = ? ".
905 "ORDER BY groupid", array($groupingid));
906 if ($grouprecords) {
907 $groups = array();
908 $groupids = [];
909 foreach ($grouprecords as $grouprecord) {
910 list($grouprecord->description, $grouprecord->descriptionformat) =
911 \core_external\util::format_text($grouprecord->description, $grouprecord->descriptionformat,
912 $context, 'group', 'description', $grouprecord->groupid);
913 $groups[] = array('id' => $grouprecord->groupid,
914 'name' => $grouprecord->name,
915 'idnumber' => $grouprecord->idnumber,
916 'description' => $grouprecord->description,
917 'descriptionformat' => $grouprecord->descriptionformat,
918 'enrolmentkey' => $grouprecord->enrolmentkey,
919 'courseid' => $grouprecord->courseid
921 $groupids[] = $grouprecord->groupid;
923 $groupcustomfieldsdata = get_group_custom_fields_data($groupids);
924 foreach ($groups as $i => $group) {
925 $groups[$i]['customfields'] = $groupcustomfieldsdata[$group['id']] ?? [];
927 $groupingarray['groups'] = $groups;
930 $groupings[] = $groupingarray;
933 return $groupings;
937 * Returns description of method result value
939 * @return \core_external\external_description
940 * @since Moodle 2.3
942 public static function get_groupings_returns() {
943 return new external_multiple_structure(
944 new external_single_structure(
945 array(
946 'id' => new external_value(PARAM_INT, 'grouping record id'),
947 'courseid' => new external_value(PARAM_INT, 'id of course'),
948 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
949 'description' => new external_value(PARAM_RAW, 'grouping description text'),
950 'descriptionformat' => new external_format_value('description'),
951 'idnumber' => new external_value(PARAM_RAW, 'id number'),
952 'customfields' => self::build_custom_fields_returns_structure(),
953 'groups' => new external_multiple_structure(
954 new external_single_structure(
955 array(
956 'id' => new external_value(PARAM_INT, 'group record id'),
957 'courseid' => new external_value(PARAM_INT, 'id of course'),
958 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
959 'description' => new external_value(PARAM_RAW, 'group description text'),
960 'descriptionformat' => new external_format_value('description'),
961 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
962 'idnumber' => new external_value(PARAM_RAW, 'id number'),
963 'customfields' => self::build_custom_fields_returns_structure(),
966 'optional groups', VALUE_OPTIONAL)
973 * Returns description of method parameters
975 * @return external_function_parameters
976 * @since Moodle 2.3
978 public static function get_course_groupings_parameters() {
979 return new external_function_parameters(
980 array(
981 'courseid' => new external_value(PARAM_INT, 'id of course'),
987 * Get all groupings in the specified course
989 * @param int $courseid id of course
990 * @return array of grouping objects (id, courseid, name, enrolmentkey)
991 * @since Moodle 2.3
993 public static function get_course_groupings($courseid) {
994 global $CFG;
995 require_once("$CFG->dirroot/group/lib.php");
996 require_once("$CFG->libdir/filelib.php");
998 $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid));
1000 // Now security checks.
1001 $context = context_course::instance($params['courseid']);
1003 try {
1004 self::validate_context($context);
1005 } catch (Exception $e) {
1006 $exceptionparam = new stdClass();
1007 $exceptionparam->message = $e->getMessage();
1008 $exceptionparam->courseid = $params['courseid'];
1009 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1011 require_capability('moodle/course:managegroups', $context);
1013 $gs = groups_get_all_groupings($params['courseid']);
1015 $groupings = array();
1016 foreach ($gs as $grouping) {
1017 list($grouping->description, $grouping->descriptionformat) =
1018 \core_external\util::format_text($grouping->description, $grouping->descriptionformat,
1019 $context, 'grouping', 'description', $grouping->id);
1020 $groupings[] = (array)$grouping;
1023 return $groupings;
1027 * Returns description of method result value
1029 * @return \core_external\external_description
1030 * @since Moodle 2.3
1032 public static function get_course_groupings_returns() {
1033 return new external_multiple_structure(
1034 new external_single_structure(
1035 array(
1036 'id' => new external_value(PARAM_INT, 'grouping record id'),
1037 'courseid' => new external_value(PARAM_INT, 'id of course'),
1038 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1039 'description' => new external_value(PARAM_RAW, 'grouping description text'),
1040 'descriptionformat' => new external_format_value('description'),
1041 'idnumber' => new external_value(PARAM_RAW, 'id number')
1048 * Returns description of method parameters
1050 * @return external_function_parameters
1051 * @since Moodle 2.3
1053 public static function delete_groupings_parameters() {
1054 return new external_function_parameters(
1055 array(
1056 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')),
1062 * Delete groupings
1064 * @param array $groupingids array of grouping ids
1065 * @return void
1066 * @since Moodle 2.3
1068 public static function delete_groupings($groupingids) {
1069 global $CFG, $DB;
1070 require_once("$CFG->dirroot/group/lib.php");
1072 $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids));
1074 $transaction = $DB->start_delegated_transaction();
1076 foreach ($params['groupingids'] as $groupingid) {
1078 if (!$grouping = groups_get_grouping($groupingid)) {
1079 // Silently ignore attempts to delete nonexisting groupings.
1080 continue;
1083 // Now security checks.
1084 $context = context_course::instance($grouping->courseid);
1085 try {
1086 self::validate_context($context);
1087 } catch (Exception $e) {
1088 $exceptionparam = new stdClass();
1089 $exceptionparam->message = $e->getMessage();
1090 $exceptionparam->courseid = $grouping->courseid;
1091 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1093 require_capability('moodle/course:managegroups', $context);
1095 groups_delete_grouping($grouping);
1098 $transaction->allow_commit();
1102 * Returns description of method result value
1104 * @return \core_external\external_description
1105 * @since Moodle 2.3
1107 public static function delete_groupings_returns() {
1108 return null;
1112 * Returns description of method parameters
1114 * @return external_function_parameters
1115 * @since Moodle 2.3
1117 public static function assign_grouping_parameters() {
1118 return new external_function_parameters(
1119 array(
1120 'assignments'=> new external_multiple_structure(
1121 new external_single_structure(
1122 array(
1123 'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1124 'groupid' => new external_value(PARAM_INT, 'group record id'),
1133 * Assign a group to a grouping
1135 * @param array $assignments of arrays with keys groupid, groupingid
1136 * @return void
1137 * @since Moodle 2.3
1139 public static function assign_grouping($assignments) {
1140 global $CFG, $DB;
1141 require_once("$CFG->dirroot/group/lib.php");
1143 $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments));
1145 $transaction = $DB->start_delegated_transaction();
1146 foreach ($params['assignments'] as $assignment) {
1147 // Validate params.
1148 $groupingid = $assignment['groupingid'];
1149 $groupid = $assignment['groupid'];
1151 $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1152 $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1154 if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1155 // Continue silently if the group is yet assigned to the grouping.
1156 continue;
1159 // Now security checks.
1160 $context = context_course::instance($grouping->courseid);
1161 try {
1162 self::validate_context($context);
1163 } catch (Exception $e) {
1164 $exceptionparam = new stdClass();
1165 $exceptionparam->message = $e->getMessage();
1166 $exceptionparam->courseid = $group->courseid;
1167 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1169 require_capability('moodle/course:managegroups', $context);
1171 groups_assign_grouping($groupingid, $groupid);
1174 $transaction->allow_commit();
1178 * Returns description of method result value
1180 * @return null
1181 * @since Moodle 2.3
1183 public static function assign_grouping_returns() {
1184 return null;
1188 * Returns description of method parameters
1190 * @return external_function_parameters
1191 * @since Moodle 2.3
1193 public static function unassign_grouping_parameters() {
1194 return new external_function_parameters(
1195 array(
1196 'unassignments'=> new external_multiple_structure(
1197 new external_single_structure(
1198 array(
1199 'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1200 'groupid' => new external_value(PARAM_INT, 'group record id'),
1209 * Unassign a group from a grouping
1211 * @param array $unassignments of arrays with keys groupid, groupingid
1212 * @return void
1213 * @since Moodle 2.3
1215 public static function unassign_grouping($unassignments) {
1216 global $CFG, $DB;
1217 require_once("$CFG->dirroot/group/lib.php");
1219 $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments));
1221 $transaction = $DB->start_delegated_transaction();
1222 foreach ($params['unassignments'] as $unassignment) {
1223 // Validate params.
1224 $groupingid = $unassignment['groupingid'];
1225 $groupid = $unassignment['groupid'];
1227 $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1228 $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1230 if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1231 // Continue silently if the group is not assigned to the grouping.
1232 continue;
1235 // Now security checks.
1236 $context = context_course::instance($grouping->courseid);
1237 try {
1238 self::validate_context($context);
1239 } catch (Exception $e) {
1240 $exceptionparam = new stdClass();
1241 $exceptionparam->message = $e->getMessage();
1242 $exceptionparam->courseid = $group->courseid;
1243 throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1245 require_capability('moodle/course:managegroups', $context);
1247 groups_unassign_grouping($groupingid, $groupid);
1250 $transaction->allow_commit();
1254 * Returns description of method result value
1256 * @return null
1257 * @since Moodle 2.3
1259 public static function unassign_grouping_returns() {
1260 return null;
1264 * Returns description of method parameters
1266 * @return external_function_parameters
1267 * @since Moodle 2.9
1269 public static function get_course_user_groups_parameters() {
1270 return new external_function_parameters(
1271 array(
1272 'courseid' => new external_value(PARAM_INT,
1273 'Id of course (empty or 0 for all the courses where the user is enrolled).', VALUE_DEFAULT, 0),
1274 'userid' => new external_value(PARAM_INT, 'Id of user (empty or 0 for current user).', VALUE_DEFAULT, 0),
1275 'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0)
1281 * Get all groups in the specified course for the specified user.
1283 * @throws moodle_exception
1284 * @param int $courseid id of course.
1285 * @param int $userid id of user.
1286 * @param int $groupingid optional returns only groups in the specified grouping.
1287 * @return array of group objects (id, name, description, format) and possible warnings.
1288 * @since Moodle 2.9
1290 public static function get_course_user_groups($courseid = 0, $userid = 0, $groupingid = 0) {
1291 global $USER;
1293 // Warnings array, it can be empty at the end but is mandatory.
1294 $warnings = array();
1296 $params = array(
1297 'courseid' => $courseid,
1298 'userid' => $userid,
1299 'groupingid' => $groupingid
1301 $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params);
1303 $courseid = $params['courseid'];
1304 $userid = $params['userid'];
1305 $groupingid = $params['groupingid'];
1307 // Validate user.
1308 if (empty($userid)) {
1309 $userid = $USER->id;
1310 } else {
1311 $user = core_user::get_user($userid, '*', MUST_EXIST);
1312 core_user::require_active_user($user);
1315 // Get courses.
1316 if (empty($courseid)) {
1317 $courses = enrol_get_users_courses($userid, true);
1318 $checkenrolments = false; // No need to check enrolments here since they are my courses.
1319 } else {
1320 $courses = array($courseid => get_course($courseid));
1321 $checkenrolments = true;
1324 // Security checks.
1325 list($courses, $warnings) = util::validate_courses(array_keys($courses), $courses, true);
1327 $usergroups = array();
1328 foreach ($courses as $course) {
1329 // Check if we have permissions for retrieve the information.
1330 if ($userid != $USER->id && !has_capability('moodle/course:managegroups', $course->context)) {
1331 $warnings[] = array(
1332 'item' => 'course',
1333 'itemid' => $course->id,
1334 'warningcode' => 'cannotmanagegroups',
1335 'message' => "User $USER->id cannot manage groups in course $course->id",
1337 continue;
1340 // Check if the user being check is enrolled in the given course.
1341 if ($checkenrolments && !is_enrolled($course->context, $userid)) {
1342 // We return a warning because the function does not fail for not enrolled users.
1343 $warnings[] = array(
1344 'item' => 'course',
1345 'itemid' => $course->id,
1346 'warningcode' => 'notenrolled',
1347 'message' => "User $userid is not enrolled in course $course->id",
1351 $groups = groups_get_all_groups($course->id, $userid, $groupingid,
1352 'g.id, g.name, g.description, g.descriptionformat, g.idnumber');
1354 foreach ($groups as $group) {
1355 $group->name = \core_external\util::format_string($group->name, $course->context);
1356 [$group->description, $group->descriptionformat] =
1357 \core_external\util::format_text($group->description, $group->descriptionformat,
1358 $course->context, 'group', 'description', $group->id);
1359 $group->courseid = $course->id;
1360 $usergroups[] = $group;
1364 $results = array(
1365 'groups' => $usergroups,
1366 'warnings' => $warnings
1368 return $results;
1372 * Returns description of method result value.
1374 * @return \core_external\external_description A single structure containing groups and possible warnings.
1375 * @since Moodle 2.9
1377 public static function get_course_user_groups_returns() {
1378 return new external_single_structure(
1379 array(
1380 'groups' => new external_multiple_structure(self::group_description()),
1381 'warnings' => new external_warnings(),
1387 * Create group return value description.
1389 * @return external_single_structure The group description
1391 public static function group_description() {
1392 return new external_single_structure(
1393 array(
1394 'id' => new external_value(PARAM_INT, 'group record id'),
1395 'name' => new external_value(PARAM_TEXT, 'group name'),
1396 'description' => new external_value(PARAM_RAW, 'group description text'),
1397 'descriptionformat' => new external_format_value('description'),
1398 'idnumber' => new external_value(PARAM_RAW, 'id number'),
1399 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL),
1405 * Returns description of method parameters
1407 * @return external_function_parameters
1408 * @since Moodle 3.0
1410 public static function get_activity_allowed_groups_parameters() {
1411 return new external_function_parameters(
1412 array(
1413 'cmid' => new external_value(PARAM_INT, 'course module id'),
1414 'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0)
1420 * Gets a list of groups that the user is allowed to access within the specified activity.
1422 * @throws moodle_exception
1423 * @param int $cmid course module id
1424 * @param int $userid id of user.
1425 * @return array of group objects (id, name, description, format) and possible warnings.
1426 * @since Moodle 3.0
1428 public static function get_activity_allowed_groups($cmid, $userid = 0) {
1429 global $USER;
1431 // Warnings array, it can be empty at the end but is mandatory.
1432 $warnings = array();
1434 $params = array(
1435 'cmid' => $cmid,
1436 'userid' => $userid
1438 $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params);
1439 $cmid = $params['cmid'];
1440 $userid = $params['userid'];
1442 $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1444 // Security checks.
1445 $context = context_module::instance($cm->id);
1446 $coursecontext = context_course::instance($cm->course);
1447 self::validate_context($context);
1449 if (empty($userid)) {
1450 $userid = $USER->id;
1453 $user = core_user::get_user($userid, '*', MUST_EXIST);
1454 core_user::require_active_user($user);
1456 // Check if we have permissions for retrieve the information.
1457 if ($user->id != $USER->id) {
1458 if (!has_capability('moodle/course:managegroups', $context)) {
1459 throw new moodle_exception('accessdenied', 'admin');
1462 // Validate if the user is enrolled in the course.
1463 $course = get_course($cm->course);
1464 if (!can_access_course($course, $user, '', true)) {
1465 // We return a warning because the function does not fail for not enrolled users.
1466 $warning = array();
1467 $warning['item'] = 'course';
1468 $warning['itemid'] = $cm->course;
1469 $warning['warningcode'] = '1';
1470 $warning['message'] = "User $user->id cannot access course $cm->course";
1471 $warnings[] = $warning;
1475 $usergroups = array();
1476 if (empty($warnings)) {
1477 $groups = groups_get_activity_allowed_groups($cm, $user->id);
1479 foreach ($groups as $group) {
1480 $group->name = \core_external\util::format_string($group->name, $coursecontext);
1481 [$group->description, $group->descriptionformat] =
1482 \core_external\util::format_text($group->description, $group->descriptionformat,
1483 $coursecontext, 'group', 'description', $group->id);
1484 $group->courseid = $cm->course;
1485 $usergroups[] = $group;
1489 $results = array(
1490 'groups' => $usergroups,
1491 'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
1492 'warnings' => $warnings
1494 return $results;
1498 * Returns description of method result value.
1500 * @return \core_external\external_description A single structure containing groups and possible warnings.
1501 * @since Moodle 3.0
1503 public static function get_activity_allowed_groups_returns() {
1504 return new external_single_structure(
1505 array(
1506 'groups' => new external_multiple_structure(self::group_description()),
1507 'canaccessallgroups' => new external_value(PARAM_BOOL,
1508 'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
1509 'warnings' => new external_warnings(),
1515 * Returns description of method parameters
1517 * @return external_function_parameters
1518 * @since Moodle 3.0
1520 public static function get_activity_groupmode_parameters() {
1521 return new external_function_parameters(
1522 array(
1523 'cmid' => new external_value(PARAM_INT, 'course module id')
1529 * Returns effective groupmode used in a given activity.
1531 * @throws moodle_exception
1532 * @param int $cmid course module id.
1533 * @return array containing the group mode and possible warnings.
1534 * @since Moodle 3.0
1535 * @throws moodle_exception
1537 public static function get_activity_groupmode($cmid) {
1538 global $USER;
1540 // Warnings array, it can be empty at the end but is mandatory.
1541 $warnings = array();
1543 $params = array(
1544 'cmid' => $cmid
1546 $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params);
1547 $cmid = $params['cmid'];
1549 $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1551 // Security checks.
1552 $context = context_module::instance($cm->id);
1553 self::validate_context($context);
1555 $groupmode = groups_get_activity_groupmode($cm);
1557 $results = array(
1558 'groupmode' => $groupmode,
1559 'warnings' => $warnings
1561 return $results;
1565 * Returns description of method result value.
1567 * @return \core_external\external_description
1568 * @since Moodle 3.0
1570 public static function get_activity_groupmode_returns() {
1571 return new external_single_structure(
1572 array(
1573 'groupmode' => new external_value(PARAM_INT, 'group mode:
1574 0 for no groups, 1 for separate groups, 2 for visible groups'),
1575 'warnings' => new external_warnings(),
1581 * Returns description of method parameters
1583 * @return external_function_parameters
1584 * @since Moodle 3.6
1586 public static function update_groups_parameters() {
1587 return new external_function_parameters(
1588 array(
1589 'groups' => new external_multiple_structure(
1590 new external_single_structure(
1591 array(
1592 'id' => new external_value(PARAM_INT, 'ID of the group'),
1593 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1594 'description' => new external_value(PARAM_RAW, 'group description text', VALUE_OPTIONAL),
1595 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1596 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
1597 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
1598 'visibility' => new external_value(PARAM_TEXT,
1599 'group visibility mode. 0 = Visible to all. 1 = Visible to members. '
1600 . '2 = See own membership. 3 = Membership is hidden.', VALUE_OPTIONAL),
1601 'participation' => new external_value(PARAM_BOOL,
1602 'activity participation enabled? Only for "all" and "members" visibility', VALUE_OPTIONAL),
1603 'customfields' => self::build_custom_fields_parameters_structure(),
1605 ), 'List of group objects. A group is found by the id, then all other details provided will be updated.'
1612 * Update groups
1614 * @param array $groups
1615 * @return null
1616 * @since Moodle 3.6
1618 public static function update_groups($groups) {
1619 global $CFG, $DB;
1620 require_once("$CFG->dirroot/group/lib.php");
1622 $params = self::validate_parameters(self::update_groups_parameters(), array('groups' => $groups));
1624 $transaction = $DB->start_delegated_transaction();
1626 foreach ($params['groups'] as $group) {
1627 $group = (object) $group;
1629 if (trim($group->name) == '') {
1630 throw new invalid_parameter_exception('Invalid group name');
1633 if (!$currentgroup = $DB->get_record('groups', array('id' => $group->id))) {
1634 throw new invalid_parameter_exception("Group $group->id does not exist");
1637 // Check if the modified group name already exists in the course.
1638 if ($group->name != $currentgroup->name and
1639 $DB->get_record('groups', array('courseid' => $currentgroup->courseid, 'name' => $group->name))) {
1640 throw new invalid_parameter_exception('A different group with the same name already exists in the course');
1643 if (isset($group->visibility) || isset($group->participation)) {
1644 $hasmembers = $DB->record_exists('groups_members', ['groupid' => $group->id]);
1645 if (isset($group->visibility)) {
1646 // Validate visibility.
1647 self::validate_visibility($group->visibility);
1648 if ($hasmembers && $group->visibility != $currentgroup->visibility) {
1649 throw new invalid_parameter_exception(
1650 'The visibility of this group cannot be changed as it currently has members.');
1652 } else {
1653 $group->visibility = $currentgroup->visibility;
1655 if (isset($group->participation) && $hasmembers && $group->participation != $currentgroup->participation) {
1656 throw new invalid_parameter_exception(
1657 'The participation mode of this group cannot be changed as it currently has members.');
1661 $group->courseid = $currentgroup->courseid;
1663 // Now security checks.
1664 $context = context_course::instance($group->courseid);
1665 try {
1666 self::validate_context($context);
1667 } catch (Exception $e) {
1668 $exceptionparam = new stdClass();
1669 $exceptionparam->message = $e->getMessage();
1670 $exceptionparam->courseid = $group->courseid;
1671 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
1673 require_capability('moodle/course:managegroups', $context);
1675 if (!empty($group->description)) {
1676 $group->descriptionformat = util::validate_format($group->descriptionformat);
1679 // Custom fields.
1680 if (!empty($group->customfields)) {
1681 foreach ($group->customfields as $field) {
1682 $fieldname = self::build_custom_field_name($field['shortname']);
1683 $group->{$fieldname} = $field['value'];
1687 groups_update_group($group);
1690 $transaction->allow_commit();
1692 return null;
1696 * Returns description of method result value
1698 * @return null
1699 * @since Moodle 3.6
1701 public static function update_groups_returns() {
1702 return null;
1706 * Builds a structure for custom fields parameters.
1708 * @return \core_external\external_multiple_structure
1710 protected static function build_custom_fields_parameters_structure(): external_multiple_structure {
1711 return new external_multiple_structure(
1712 new external_single_structure([
1713 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
1714 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
1715 ]), 'Custom fields', VALUE_OPTIONAL
1720 * Builds a structure for custom fields returns.
1722 * @return \core_external\external_multiple_structure
1724 protected static function build_custom_fields_returns_structure(): external_multiple_structure {
1725 return new external_multiple_structure(
1726 new external_single_structure([
1727 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
1728 'shortname' => new external_value(PARAM_RAW,
1729 'The shortname of the custom field - to be able to build the field class in the code'),
1730 'type' => new external_value(PARAM_ALPHANUMEXT,
1731 'The type of the custom field - text field, checkbox...'),
1732 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'),
1733 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
1734 ]), 'Custom fields', VALUE_OPTIONAL
1739 * Builds a suitable name of a custom field for a custom field handler based on provided shortname.
1741 * @param string $shortname shortname to use.
1742 * @return string
1744 protected static function build_custom_field_name(string $shortname): string {
1745 return 'customfield_' . $shortname;