Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

use core_external\external_api;
use core_external\external_format_value;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\util;
use core_group\visibility;

> defined('MOODLE_INTERNAL') || die(); /** > * Group external functions > require_once($CFG->dirroot . '/group/lib.php'); * >
* @package core_group * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class core_group_external extends external_api { /** * Validate visibility. * * @param int $visibility Visibility string, must one of the visibility class constants. * @throws invalid_parameter_exception if visibility is not an allowed value. */ protected static function validate_visibility(int $visibility): void { $allowed = [ GROUPS_VISIBILITY_ALL, GROUPS_VISIBILITY_MEMBERS, GROUPS_VISIBILITY_OWN, GROUPS_VISIBILITY_NONE, ]; if (!array_key_exists($visibility, $allowed)) { throw new invalid_parameter_exception('Invalid group visibility provided. Must be one of ' . join(',', $allowed)); } } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function create_groups_parameters() { return new external_function_parameters( array( 'groups' => new external_multiple_structure( new external_single_structure( array( 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 'visibility' => new external_value(PARAM_INT, 'group visibility mode. 0 = Visible to all. 1 = Visible to members. ' . '2 = See own membership. 3 = Membership is hidden. default: 0', VALUE_DEFAULT, 0), 'participation' => new external_value(PARAM_BOOL, 'activity participation enabled? Only for "all" and "members" visibility. Default true.', VALUE_DEFAULT, true),
> 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of group object. A group has a courseid, a name, a description and an enrolment key.' ) ) ); } /** * Create groups * * @param array $groups array of group description arrays (with keys groupname and courseid) * @return array of newly created groups * @since Moodle 2.2 */ public static function create_groups($groups) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups)); $transaction = $DB->start_delegated_transaction(); $groups = array(); foreach ($params['groups'] as $group) { $group = (object)$group; if (trim($group->name) == '') { throw new invalid_parameter_exception('Invalid group name'); } if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) { throw new invalid_parameter_exception('Group with the same name already exists in the course'); } // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); // Validate format. $group->descriptionformat = util::validate_format($group->descriptionformat); // Validate visibility. self::validate_visibility($group->visibility);
> // Custom fields. // finally create the group > if (!empty($group->customfields)) { $group->id = groups_create_group($group, false); > foreach ($group->customfields as $field) { if (!isset($group->enrolmentkey)) { > $fieldname = self::build_custom_field_name($field['shortname']); $group->enrolmentkey = ''; > $group->{$fieldname} = $field['value']; } > } if (!isset($group->idnumber)) { > } $group->idnumber = ''; >
} $groups[] = (array)$group; } $transaction->allow_commit(); return $groups; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function create_groups_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description'), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'), 'idnumber' => new external_value(PARAM_RAW, 'id number'), 'visibility' => new external_value(PARAM_INT, 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. ' . '3 = Membership is hidden.'), 'participation' => new external_value(PARAM_BOOL, 'participation mode'),
> 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of group object. A group has an id, a courseid, a name, a description and an enrolment key.' ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_groups_parameters() { return new external_function_parameters( array( 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID') ,'List of group id. A group id is an integer.'), ) ); } /** * Get groups definition specified by ids * * @param array $groupids arrays of group ids * @return array of group objects (id, courseid, name, enrolmentkey) * @since Moodle 2.2 */ public static function get_groups($groupids) { $params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids)); $groups = array();
> $customfieldsdata = get_group_custom_fields_data($groupids);
foreach ($params['groupids'] as $groupid) { // validate params $group = groups_get_group($groupid, 'id, courseid, name, idnumber, description, descriptionformat, enrolmentkey, ' . 'visibility, participation', MUST_EXIST); // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); $group->name = \core_external\util::format_string($group->name, $context); [$group->description, $group->descriptionformat] = \core_external\util::format_text($group->description, $group->descriptionformat, $context, 'group', 'description', $group->id);
> $group->customfields = $customfieldsdata[$group->id] ?? [];
$groups[] = (array)$group; } return $groups; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function get_groups_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description'), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'), 'idnumber' => new external_value(PARAM_RAW, 'id number'), 'visibility' => new external_value(PARAM_INT, 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. ' . '3 = Membership is hidden.'), 'participation' => new external_value(PARAM_BOOL, 'participation mode'),
> 'customfields' => self::build_custom_fields_returns_structure(),
) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_course_groups_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'id of course'), ) ); } /** * Get all groups in the specified course * * @param int $courseid id of course * @return array of group objects (id, courseid, name, enrolmentkey) * @since Moodle 2.2 */ public static function get_course_groups($courseid) { $params = self::validate_parameters(self::get_course_groups_parameters(), array('courseid'=>$courseid)); // now security checks $context = context_course::instance($params['courseid'], IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); $gs = groups_get_all_groups($params['courseid'], 0, 0, 'g.id, g.courseid, g.name, g.idnumber, g.description, g.descriptionformat, g.enrolmentkey, ' . 'g.visibility, g.participation'); $groups = array(); foreach ($gs as $group) { $group->name = \core_external\util::format_string($group->name, $context); [$group->description, $group->descriptionformat] = \core_external\util::format_text($group->description, $group->descriptionformat, $context, 'group', 'description', $group->id); $groups[] = (array)$group; } return $groups; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function get_course_groups_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description'), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'), 'idnumber' => new external_value(PARAM_RAW, 'id number'), 'visibility' => new external_value(PARAM_INT, 'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. ' . '3 = Membership is hidden.'), 'participation' => new external_value(PARAM_BOOL, 'participation mode'), ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function delete_groups_parameters() { return new external_function_parameters( array( 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')), ) ); } /** * Delete groups * * @param array $groupids array of group ids * @since Moodle 2.2 */ public static function delete_groups($groupids) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids)); $transaction = $DB->start_delegated_transaction(); foreach ($params['groupids'] as $groupid) { // validate params $groupid = validate_param($groupid, PARAM_INT); if (!$group = groups_get_group($groupid, '*', IGNORE_MISSING)) { // silently ignore attempts to delete nonexisting groups continue; } // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); groups_delete_group($group); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null * @since Moodle 2.2 */ public static function delete_groups_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function get_group_members_parameters() { return new external_function_parameters( array( 'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')), ) ); } /** * Return all members for a group * * @param array $groupids array of group ids * @return array with group id keys containing arrays of user ids * @since Moodle 2.2 */ public static function get_group_members($groupids) { $members = array(); $params = self::validate_parameters(self::get_group_members_parameters(), array('groupids'=>$groupids)); foreach ($params['groupids'] as $groupid) { // validate params $group = groups_get_group($groupid, 'id, courseid, name, enrolmentkey', MUST_EXIST); // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); $groupmembers = groups_get_members($group->id, 'u.id', 'lastname ASC, firstname ASC'); $members[] = array('groupid'=>$groupid, 'userids'=>array_keys($groupmembers)); } return $members; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.2 */ public static function get_group_members_returns() { return new external_multiple_structure( new external_single_structure( array( 'groupid' => new external_value(PARAM_INT, 'group record id'), 'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')), ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function add_group_members_parameters() { return new external_function_parameters( array( 'members'=> new external_multiple_structure( new external_single_structure( array( 'groupid' => new external_value(PARAM_INT, 'group record id'), 'userid' => new external_value(PARAM_INT, 'user id'), ) ) ) ) ); } /** * Add group members * * @param array $members of arrays with keys userid, groupid * @since Moodle 2.2 */ public static function add_group_members($members) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::add_group_members_parameters(), array('members'=>$members)); $transaction = $DB->start_delegated_transaction(); foreach ($params['members'] as $member) { // validate params $groupid = $member['groupid']; $userid = $member['userid']; $group = groups_get_group($groupid, '*', MUST_EXIST); $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST); // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); // now make sure user is enrolled in course - this is mandatory requirement, // unfortunately this is slow if (!is_enrolled($context, $userid)) { throw new invalid_parameter_exception('Only enrolled users may be members of groups'); } groups_add_member($group, $user); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null * @since Moodle 2.2 */ public static function add_group_members_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.2 */ public static function delete_group_members_parameters() { return new external_function_parameters( array( 'members'=> new external_multiple_structure( new external_single_structure( array( 'groupid' => new external_value(PARAM_INT, 'group record id'), 'userid' => new external_value(PARAM_INT, 'user id'), ) ) ) ) ); } /** * Delete group members * * @param array $members of arrays with keys userid, groupid * @since Moodle 2.2 */ public static function delete_group_members($members) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::delete_group_members_parameters(), array('members'=>$members)); $transaction = $DB->start_delegated_transaction(); foreach ($params['members'] as $member) { // validate params $groupid = $member['groupid']; $userid = $member['userid']; $group = groups_get_group($groupid, '*', MUST_EXIST); $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST); // now security checks $context = context_course::instance($group->courseid, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); if (!groups_remove_member_allowed($group, $user)) { $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context)); throw new moodle_exception('errorremovenotpermitted', 'group', '', $fullname); } groups_remove_member($group, $user); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null * @since Moodle 2.2 */ public static function delete_group_members_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function create_groupings_parameters() { return new external_function_parameters( array( 'groupings' => new external_multiple_structure( new external_single_structure( array( 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'grouping description text'), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
< 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
> 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), > 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of grouping object. A grouping has a courseid, a name and a description.' ) ) ); } /** * Create groupings * * @param array $groupings array of grouping description arrays (with keys groupname and courseid) * @return array of newly created groupings * @since Moodle 2.3 */ public static function create_groupings($groupings) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings)); $transaction = $DB->start_delegated_transaction(); $groupings = array(); foreach ($params['groupings'] as $grouping) { $grouping = (object)$grouping; if (trim($grouping->name) == '') { throw new invalid_parameter_exception('Invalid grouping name'); } if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) { throw new invalid_parameter_exception('Grouping with the same name already exists in the course'); } // Now security checks . $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $grouping->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
> // Custom fields. // Finally create the grouping. > if (!empty($grouping->customfields)) { $grouping->id = groups_create_grouping($grouping); > foreach ($grouping->customfields as $field) { $groupings[] = (array)$grouping; > $fieldname = self::build_custom_field_name($field['shortname']); } > $grouping->{$fieldname} = $field['value']; > } $transaction->allow_commit(); > } >
return $groupings; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function create_groupings_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'grouping record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'grouping description text'), 'descriptionformat' => new external_format_value('description'),
< 'idnumber' => new external_value(PARAM_RAW, 'id number')
> 'idnumber' => new external_value(PARAM_RAW, 'id number'), > 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.' ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function update_groupings_parameters() { return new external_function_parameters( array( 'groupings' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'id of grouping'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'grouping description text'), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
< 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
> 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), > 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of grouping object. A grouping has a courseid, a name and a description.' ) ) ); } /** * Update groupings * * @param array $groupings array of grouping description arrays (with keys groupname and courseid) * @return array of newly updated groupings * @since Moodle 2.3 */ public static function update_groupings($groupings) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings)); $transaction = $DB->start_delegated_transaction(); foreach ($params['groupings'] as $grouping) { $grouping = (object)$grouping; if (trim($grouping->name) == '') { throw new invalid_parameter_exception('Invalid grouping name'); } if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) { throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course"); } // Check if the new modified grouping name already exists in the course. if ($grouping->name != $currentgrouping->name and $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) { throw new invalid_parameter_exception('A different grouping with the same name already exists in the course'); } $grouping->courseid = $currentgrouping->courseid; // Now security checks. $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $grouping->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); // We must force allways FORMAT_HTML. $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
> // Custom fields. // Finally update the grouping. > if (!empty($grouping->customfields)) { groups_update_grouping($grouping); > foreach ($grouping->customfields as $field) { } > $fieldname = self::build_custom_field_name($field['shortname']); > $grouping->{$fieldname} = $field['value']; $transaction->allow_commit(); > } > } return null; >
} /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function update_groupings_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function get_groupings_parameters() { return new external_function_parameters( array( 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID') , 'List of grouping id. A grouping id is an integer.'), 'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0) ) ); } /** * Get groupings definition specified by ids * * @param array $groupingids arrays of grouping ids * @param boolean $returngroups return the associated groups if true. The default is false. * @return array of grouping objects (id, courseid, name) * @since Moodle 2.3 */ public static function get_groupings($groupingids, $returngroups = false) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); require_once("$CFG->libdir/filelib.php"); $params = self::validate_parameters(self::get_groupings_parameters(), array('groupingids' => $groupingids, 'returngroups' => $returngroups)); $groupings = array();
> $groupingcustomfieldsdata = get_grouping_custom_fields_data($groupingids);
foreach ($params['groupingids'] as $groupingid) { // Validate params. $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST); // Now security checks. $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $grouping->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); list($grouping->description, $grouping->descriptionformat) = \core_external\util::format_text($grouping->description, $grouping->descriptionformat, $context, 'grouping', 'description', $grouping->id);
> $grouping->customfields = $groupingcustomfieldsdata[$grouping->id] ?? [];
$groupingarray = (array)$grouping; if ($params['returngroups']) { $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ". "ON g.id = gg.groupid WHERE gg.groupingid = ? ". "ORDER BY groupid", array($groupingid)); if ($grouprecords) { $groups = array();
> $groupids = [];
foreach ($grouprecords as $grouprecord) { list($grouprecord->description, $grouprecord->descriptionformat) = \core_external\util::format_text($grouprecord->description, $grouprecord->descriptionformat, $context, 'group', 'description', $grouprecord->groupid); $groups[] = array('id' => $grouprecord->groupid, 'name' => $grouprecord->name, 'idnumber' => $grouprecord->idnumber, 'description' => $grouprecord->description, 'descriptionformat' => $grouprecord->descriptionformat, 'enrolmentkey' => $grouprecord->enrolmentkey, 'courseid' => $grouprecord->courseid );
> $groupids[] = $grouprecord->groupid; } > } $groupingarray['groups'] = $groups; > $groupcustomfieldsdata = get_group_custom_fields_data($groupids); } > foreach ($groups as $i => $group) { } > $groups[$i]['customfields'] = $groupcustomfieldsdata[$group['id']] ?? [];
$groupings[] = $groupingarray; } return $groupings; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function get_groupings_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'grouping record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'grouping description text'), 'descriptionformat' => new external_format_value('description'), 'idnumber' => new external_value(PARAM_RAW, 'id number'),
> 'customfields' => self::build_custom_fields_returns_structure(),
'groups' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description'), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
< 'idnumber' => new external_value(PARAM_RAW, 'id number')
> 'idnumber' => new external_value(PARAM_RAW, 'id number'), > 'customfields' => self::build_custom_fields_returns_structure(),
) ), 'optional groups', VALUE_OPTIONAL) ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function get_course_groupings_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'id of course'), ) ); } /** * Get all groupings in the specified course * * @param int $courseid id of course * @return array of grouping objects (id, courseid, name, enrolmentkey) * @since Moodle 2.3 */ public static function get_course_groupings($courseid) { global $CFG; require_once("$CFG->dirroot/group/lib.php"); require_once("$CFG->libdir/filelib.php"); $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid)); // Now security checks. $context = context_course::instance($params['courseid']); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); $gs = groups_get_all_groupings($params['courseid']); $groupings = array(); foreach ($gs as $grouping) { list($grouping->description, $grouping->descriptionformat) = \core_external\util::format_text($grouping->description, $grouping->descriptionformat, $context, 'grouping', 'description', $grouping->id); $groupings[] = (array)$grouping; } return $groupings; } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function get_course_groupings_returns() { return new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'grouping record id'), 'courseid' => new external_value(PARAM_INT, 'id of course'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'grouping description text'), 'descriptionformat' => new external_format_value('description'), 'idnumber' => new external_value(PARAM_RAW, 'id number') ) ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function delete_groupings_parameters() { return new external_function_parameters( array( 'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')), ) ); } /** * Delete groupings * * @param array $groupingids array of grouping ids * @return void * @since Moodle 2.3 */ public static function delete_groupings($groupingids) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids)); $transaction = $DB->start_delegated_transaction(); foreach ($params['groupingids'] as $groupingid) { if (!$grouping = groups_get_grouping($groupingid)) { // Silently ignore attempts to delete nonexisting groupings. continue; } // Now security checks. $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $grouping->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); groups_delete_grouping($grouping); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return \core_external\external_description * @since Moodle 2.3 */ public static function delete_groupings_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function assign_grouping_parameters() { return new external_function_parameters( array( 'assignments'=> new external_multiple_structure( new external_single_structure( array( 'groupingid' => new external_value(PARAM_INT, 'grouping record id'), 'groupid' => new external_value(PARAM_INT, 'group record id'), ) ) ) ) ); } /** * Assign a group to a grouping * * @param array $assignments of arrays with keys groupid, groupingid * @return void * @since Moodle 2.3 */ public static function assign_grouping($assignments) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments)); $transaction = $DB->start_delegated_transaction(); foreach ($params['assignments'] as $assignment) { // Validate params. $groupingid = $assignment['groupingid']; $groupid = $assignment['groupid']; $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST); $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST); if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) { // Continue silently if the group is yet assigned to the grouping. continue; } // Now security checks. $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); groups_assign_grouping($groupingid, $groupid); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null * @since Moodle 2.3 */ public static function assign_grouping_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.3 */ public static function unassign_grouping_parameters() { return new external_function_parameters( array( 'unassignments'=> new external_multiple_structure( new external_single_structure( array( 'groupingid' => new external_value(PARAM_INT, 'grouping record id'), 'groupid' => new external_value(PARAM_INT, 'group record id'), ) ) ) ) ); } /** * Unassign a group from a grouping * * @param array $unassignments of arrays with keys groupid, groupingid * @return void * @since Moodle 2.3 */ public static function unassign_grouping($unassignments) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments)); $transaction = $DB->start_delegated_transaction(); foreach ($params['unassignments'] as $unassignment) { // Validate params. $groupingid = $unassignment['groupingid']; $groupid = $unassignment['groupid']; $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST); $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST); if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) { // Continue silently if the group is not assigned to the grouping. continue; } // Now security checks. $context = context_course::instance($grouping->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); groups_unassign_grouping($groupingid, $groupid); } $transaction->allow_commit(); } /** * Returns description of method result value * * @return null * @since Moodle 2.3 */ public static function unassign_grouping_returns() { return null; } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 2.9 */ public static function get_course_user_groups_parameters() { return new external_function_parameters( array( 'courseid' => new external_value(PARAM_INT, 'Id of course (empty or 0 for all the courses where the user is enrolled).', VALUE_DEFAULT, 0), 'userid' => new external_value(PARAM_INT, 'Id of user (empty or 0 for current user).', VALUE_DEFAULT, 0), 'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0) ) ); } /** * Get all groups in the specified course for the specified user. * * @throws moodle_exception * @param int $courseid id of course. * @param int $userid id of user. * @param int $groupingid optional returns only groups in the specified grouping. * @return array of group objects (id, name, description, format) and possible warnings. * @since Moodle 2.9 */ public static function get_course_user_groups($courseid = 0, $userid = 0, $groupingid = 0) { global $USER; // Warnings array, it can be empty at the end but is mandatory. $warnings = array(); $params = array( 'courseid' => $courseid, 'userid' => $userid, 'groupingid' => $groupingid ); $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params); $courseid = $params['courseid']; $userid = $params['userid']; $groupingid = $params['groupingid']; // Validate user. if (empty($userid)) { $userid = $USER->id; } else { $user = core_user::get_user($userid, '*', MUST_EXIST); core_user::require_active_user($user); } // Get courses. if (empty($courseid)) { $courses = enrol_get_users_courses($userid, true); $checkenrolments = false; // No need to check enrolments here since they are my courses. } else { $courses = array($courseid => get_course($courseid)); $checkenrolments = true; } // Security checks. list($courses, $warnings) = util::validate_courses(array_keys($courses), $courses, true); $usergroups = array(); foreach ($courses as $course) { // Check if we have permissions for retrieve the information. if ($userid != $USER->id && !has_capability('moodle/course:managegroups', $course->context)) { $warnings[] = array( 'item' => 'course', 'itemid' => $course->id, 'warningcode' => 'cannotmanagegroups', 'message' => "User $USER->id cannot manage groups in course $course->id", ); continue; } // Check if the user being check is enrolled in the given course. if ($checkenrolments && !is_enrolled($course->context, $userid)) { // We return a warning because the function does not fail for not enrolled users. $warnings[] = array( 'item' => 'course', 'itemid' => $course->id, 'warningcode' => 'notenrolled', 'message' => "User $userid is not enrolled in course $course->id", ); } $groups = groups_get_all_groups($course->id, $userid, $groupingid, 'g.id, g.name, g.description, g.descriptionformat, g.idnumber'); foreach ($groups as $group) { $group->name = \core_external\util::format_string($group->name, $course->context); [$group->description, $group->descriptionformat] = \core_external\util::format_text($group->description, $group->descriptionformat, $course->context, 'group', 'description', $group->id); $group->courseid = $course->id; $usergroups[] = $group; } } $results = array( 'groups' => $usergroups, 'warnings' => $warnings ); return $results; } /** * Returns description of method result value. * * @return \core_external\external_description A single structure containing groups and possible warnings. * @since Moodle 2.9 */ public static function get_course_user_groups_returns() { return new external_single_structure( array( 'groups' => new external_multiple_structure(self::group_description()), 'warnings' => new external_warnings(), ) ); } /** * Create group return value description. * * @return external_single_structure The group description */ public static function group_description() { return new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'group record id'), 'name' => new external_value(PARAM_TEXT, 'group name'), 'description' => new external_value(PARAM_RAW, 'group description text'), 'descriptionformat' => new external_format_value('description'), 'idnumber' => new external_value(PARAM_RAW, 'id number'), 'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function get_activity_allowed_groups_parameters() { return new external_function_parameters( array( 'cmid' => new external_value(PARAM_INT, 'course module id'), 'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0) ) ); } /** * Gets a list of groups that the user is allowed to access within the specified activity. * * @throws moodle_exception * @param int $cmid course module id * @param int $userid id of user. * @return array of group objects (id, name, description, format) and possible warnings. * @since Moodle 3.0 */ public static function get_activity_allowed_groups($cmid, $userid = 0) { global $USER; // Warnings array, it can be empty at the end but is mandatory. $warnings = array(); $params = array( 'cmid' => $cmid, 'userid' => $userid ); $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params); $cmid = $params['cmid']; $userid = $params['userid']; $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST); // Security checks. $context = context_module::instance($cm->id); $coursecontext = context_course::instance($cm->course); self::validate_context($context); if (empty($userid)) { $userid = $USER->id; } $user = core_user::get_user($userid, '*', MUST_EXIST); core_user::require_active_user($user); // Check if we have permissions for retrieve the information. if ($user->id != $USER->id) { if (!has_capability('moodle/course:managegroups', $context)) { throw new moodle_exception('accessdenied', 'admin'); } // Validate if the user is enrolled in the course. $course = get_course($cm->course); if (!can_access_course($course, $user, '', true)) { // We return a warning because the function does not fail for not enrolled users. $warning = array(); $warning['item'] = 'course'; $warning['itemid'] = $cm->course; $warning['warningcode'] = '1'; $warning['message'] = "User $user->id cannot access course $cm->course"; $warnings[] = $warning; } } $usergroups = array(); if (empty($warnings)) { $groups = groups_get_activity_allowed_groups($cm, $user->id); foreach ($groups as $group) { $group->name = \core_external\util::format_string($group->name, $coursecontext); [$group->description, $group->descriptionformat] = \core_external\util::format_text($group->description, $group->descriptionformat, $coursecontext, 'group', 'description', $group->id); $group->courseid = $cm->course; $usergroups[] = $group; } } $results = array( 'groups' => $usergroups, 'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user), 'warnings' => $warnings ); return $results; } /** * Returns description of method result value. * * @return \core_external\external_description A single structure containing groups and possible warnings. * @since Moodle 3.0 */ public static function get_activity_allowed_groups_returns() { return new external_single_structure( array( 'groups' => new external_multiple_structure(self::group_description()), 'canaccessallgroups' => new external_value(PARAM_BOOL, 'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.0 */ public static function get_activity_groupmode_parameters() { return new external_function_parameters( array( 'cmid' => new external_value(PARAM_INT, 'course module id') ) ); } /** * Returns effective groupmode used in a given activity. * * @throws moodle_exception * @param int $cmid course module id. * @return array containing the group mode and possible warnings. * @since Moodle 3.0 * @throws moodle_exception */ public static function get_activity_groupmode($cmid) { global $USER; // Warnings array, it can be empty at the end but is mandatory. $warnings = array(); $params = array( 'cmid' => $cmid ); $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params); $cmid = $params['cmid']; $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST); // Security checks. $context = context_module::instance($cm->id); self::validate_context($context); $groupmode = groups_get_activity_groupmode($cm); $results = array( 'groupmode' => $groupmode, 'warnings' => $warnings ); return $results; } /** * Returns description of method result value. * * @return \core_external\external_description * @since Moodle 3.0 */ public static function get_activity_groupmode_returns() { return new external_single_structure( array( 'groupmode' => new external_value(PARAM_INT, 'group mode: 0 for no groups, 1 for separate groups, 2 for visible groups'), 'warnings' => new external_warnings(), ) ); } /** * Returns description of method parameters * * @return external_function_parameters * @since Moodle 3.6 */ public static function update_groups_parameters() { return new external_function_parameters( array( 'groups' => new external_multiple_structure( new external_single_structure( array( 'id' => new external_value(PARAM_INT, 'ID of the group'), 'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'), 'description' => new external_value(PARAM_RAW, 'group description text', VALUE_OPTIONAL), 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT), 'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL), 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL), 'visibility' => new external_value(PARAM_TEXT, 'group visibility mode. 0 = Visible to all. 1 = Visible to members. ' . '2 = See own membership. 3 = Membership is hidden.', VALUE_OPTIONAL), 'participation' => new external_value(PARAM_BOOL, 'activity participation enabled? Only for "all" and "members" visibility', VALUE_OPTIONAL),
> 'customfields' => self::build_custom_fields_parameters_structure(),
) ), 'List of group objects. A group is found by the id, then all other details provided will be updated.' ) ) ); } /** * Update groups * * @param array $groups * @return null * @since Moodle 3.6 */ public static function update_groups($groups) { global $CFG, $DB; require_once("$CFG->dirroot/group/lib.php"); $params = self::validate_parameters(self::update_groups_parameters(), array('groups' => $groups)); $transaction = $DB->start_delegated_transaction(); foreach ($params['groups'] as $group) { $group = (object) $group; if (trim($group->name) == '') { throw new invalid_parameter_exception('Invalid group name'); } if (!$currentgroup = $DB->get_record('groups', array('id' => $group->id))) { throw new invalid_parameter_exception("Group $group->id does not exist"); } // Check if the modified group name already exists in the course. if ($group->name != $currentgroup->name and $DB->get_record('groups', array('courseid' => $currentgroup->courseid, 'name' => $group->name))) { throw new invalid_parameter_exception('A different group with the same name already exists in the course'); } if (isset($group->visibility) || isset($group->participation)) { $hasmembers = $DB->record_exists('groups_members', ['groupid' => $group->id]); if (isset($group->visibility)) { // Validate visibility. self::validate_visibility($group->visibility); if ($hasmembers && $group->visibility != $currentgroup->visibility) { throw new invalid_parameter_exception( 'The visibility of this group cannot be changed as it currently has members.'); } } else { $group->visibility = $currentgroup->visibility; } if (isset($group->participation) && $hasmembers && $group->participation != $currentgroup->participation) { throw new invalid_parameter_exception( 'The participation mode of this group cannot be changed as it currently has members.'); } } $group->courseid = $currentgroup->courseid; // Now security checks. $context = context_course::instance($group->courseid); try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $group->courseid; throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam); } require_capability('moodle/course:managegroups', $context); if (!empty($group->description)) { $group->descriptionformat = util::validate_format($group->descriptionformat); }
> // Custom fields. groups_update_group($group); > if (!empty($group->customfields)) { } > foreach ($group->customfields as $field) { > $fieldname = self::build_custom_field_name($field['shortname']); $transaction->allow_commit(); > $group->{$fieldname} = $field['value']; > } return null; > } } >
/** * Returns description of method result value * * @return null * @since Moodle 3.6 */ public static function update_groups_returns() { return null;
> } } > } > /** > * Builds a structure for custom fields parameters. > * > * @return \core_external\external_multiple_structure > */ > protected static function build_custom_fields_parameters_structure(): external_multiple_structure { > return new external_multiple_structure( > new external_single_structure([ > 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'), > 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), > ]), 'Custom fields', VALUE_OPTIONAL > ); > } > > /** > * Builds a structure for custom fields returns. > * > * @return \core_external\external_multiple_structure > */ > protected static function build_custom_fields_returns_structure(): external_multiple_structure { > return new external_multiple_structure( > new external_single_structure([ > 'name' => new external_value(PARAM_RAW, 'The name of the custom field'), > 'shortname' => new external_value(PARAM_RAW, > 'The shortname of the custom field - to be able to build the field class in the code'), > 'type' => new external_value(PARAM_ALPHANUMEXT, > 'The type of the custom field - text field, checkbox...'), > 'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'), > 'value' => new external_value(PARAM_RAW, 'The value of the custom field'), > ]), 'Custom fields', VALUE_OPTIONAL > ); > } > > /** > * Builds a suitable name of a custom field for a custom field handler based on provided shortname. > * > * @param string $shortname shortname to use. > * @return string > */ > protected static function build_custom_field_name(string $shortname): string { > return 'customfield_' . $shortname;