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.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [Versions 402 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  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;
  26  
  27  /**
  28   * Group external functions
  29   *
  30   * @package    core_group
  31   * @category   external
  32   * @copyright  2011 Jerome Mouneyrac
  33   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   * @since Moodle 2.2
  35   */
  36  class core_group_external extends external_api {
  37  
  38  
  39      /**
  40       * Validate visibility.
  41       *
  42       * @param int $visibility Visibility string, must one of the visibility class constants.
  43       * @throws invalid_parameter_exception if visibility is not an allowed value.
  44       */
  45      protected static function validate_visibility(int $visibility): void {
  46          $allowed = [
  47              GROUPS_VISIBILITY_ALL,
  48              GROUPS_VISIBILITY_MEMBERS,
  49              GROUPS_VISIBILITY_OWN,
  50              GROUPS_VISIBILITY_NONE,
  51          ];
  52          if (!array_key_exists($visibility, $allowed)) {
  53              throw new invalid_parameter_exception('Invalid group visibility provided. Must be one of '
  54                      . join(',', $allowed));
  55          }
  56      }
  57  
  58      /**
  59       * Returns description of method parameters
  60       *
  61       * @return external_function_parameters
  62       * @since Moodle 2.2
  63       */
  64      public static function create_groups_parameters() {
  65          return new external_function_parameters(
  66              array(
  67                  'groups' => new external_multiple_structure(
  68                      new external_single_structure(
  69                          array(
  70                              'courseid' => new external_value(PARAM_INT, 'id of course'),
  71                              'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
  72                              'description' => new external_value(PARAM_RAW, 'group description text'),
  73                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
  74                              'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
  75                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
  76                              'visibility' => new external_value(PARAM_INT,
  77                                      'group visibility mode. 0 = Visible to all. 1 = Visible to members. '
  78                                      . '2 = See own membership. 3 = Membership is hidden. default: 0',
  79                                      VALUE_DEFAULT, 0),
  80                              'participation' => new external_value(PARAM_BOOL,
  81                                      'activity participation enabled? Only for "all" and "members" visibility. Default true.',
  82                                      VALUE_DEFAULT, true),
  83                          )
  84                      ), 'List of group object. A group has a courseid, a name, a description and an enrolment key.'
  85                  )
  86              )
  87          );
  88      }
  89  
  90      /**
  91       * Create groups
  92       *
  93       * @param array $groups array of group description arrays (with keys groupname and courseid)
  94       * @return array of newly created groups
  95       * @since Moodle 2.2
  96       */
  97      public static function create_groups($groups) {
  98          global $CFG, $DB;
  99          require_once("$CFG->dirroot/group/lib.php");
 100  
 101          $params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));
 102  
 103          $transaction = $DB->start_delegated_transaction();
 104  
 105          $groups = array();
 106  
 107          foreach ($params['groups'] as $group) {
 108              $group = (object)$group;
 109  
 110              if (trim($group->name) == '') {
 111                  throw new invalid_parameter_exception('Invalid group name');
 112              }
 113              if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) {
 114                  throw new invalid_parameter_exception('Group with the same name already exists in the course');
 115              }
 116  
 117              // now security checks
 118              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 119              try {
 120                  self::validate_context($context);
 121              } catch (Exception $e) {
 122                  $exceptionparam = new stdClass();
 123                  $exceptionparam->message = $e->getMessage();
 124                  $exceptionparam->courseid = $group->courseid;
 125                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 126              }
 127              require_capability('moodle/course:managegroups', $context);
 128  
 129              // Validate format.
 130              $group->descriptionformat = util::validate_format($group->descriptionformat);
 131  
 132              // Validate visibility.
 133              self::validate_visibility($group->visibility);
 134  
 135              // finally create the group
 136              $group->id = groups_create_group($group, false);
 137              if (!isset($group->enrolmentkey)) {
 138                  $group->enrolmentkey = '';
 139              }
 140              if (!isset($group->idnumber)) {
 141                  $group->idnumber = '';
 142              }
 143  
 144              $groups[] = (array)$group;
 145          }
 146  
 147          $transaction->allow_commit();
 148  
 149          return $groups;
 150      }
 151  
 152      /**
 153       * Returns description of method result value
 154       *
 155       * @return \core_external\external_description
 156       * @since Moodle 2.2
 157       */
 158      public static function create_groups_returns() {
 159          return new external_multiple_structure(
 160              new external_single_structure(
 161                  array(
 162                      'id' => new external_value(PARAM_INT, 'group record id'),
 163                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 164                      'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 165                      'description' => new external_value(PARAM_RAW, 'group description text'),
 166                      'descriptionformat' => new external_format_value('description'),
 167                      'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
 168                      'idnumber' => new external_value(PARAM_RAW, 'id number'),
 169                      'visibility' => new external_value(PARAM_INT,
 170                              'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
 171                              . '3 = Membership is hidden.'),
 172                      'participation' => new external_value(PARAM_BOOL, 'participation mode'),
 173                  )
 174              ), 'List of group object. A group has an id, a courseid, a name, a description and an enrolment key.'
 175          );
 176      }
 177  
 178      /**
 179       * Returns description of method parameters
 180       *
 181       * @return external_function_parameters
 182       * @since Moodle 2.2
 183       */
 184      public static function get_groups_parameters() {
 185          return new external_function_parameters(
 186              array(
 187                  'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')
 188                          ,'List of group id. A group id is an integer.'),
 189              )
 190          );
 191      }
 192  
 193      /**
 194       * Get groups definition specified by ids
 195       *
 196       * @param array $groupids arrays of group ids
 197       * @return array of group objects (id, courseid, name, enrolmentkey)
 198       * @since Moodle 2.2
 199       */
 200      public static function get_groups($groupids) {
 201          $params = self::validate_parameters(self::get_groups_parameters(), array('groupids'=>$groupids));
 202  
 203          $groups = array();
 204          foreach ($params['groupids'] as $groupid) {
 205              // validate params
 206              $group = groups_get_group($groupid, 'id, courseid, name, idnumber, description, descriptionformat, enrolmentkey, '
 207                      . 'visibility, participation', MUST_EXIST);
 208  
 209              // now security checks
 210              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 211              try {
 212                  self::validate_context($context);
 213              } catch (Exception $e) {
 214                  $exceptionparam = new stdClass();
 215                  $exceptionparam->message = $e->getMessage();
 216                  $exceptionparam->courseid = $group->courseid;
 217                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 218              }
 219              require_capability('moodle/course:managegroups', $context);
 220  
 221              $group->name = \core_external\util::format_string($group->name, $context);
 222              [$group->description, $group->descriptionformat] =
 223                  \core_external\util::format_text($group->description, $group->descriptionformat,
 224                          $context, 'group', 'description', $group->id);
 225  
 226              $groups[] = (array)$group;
 227          }
 228  
 229          return $groups;
 230      }
 231  
 232      /**
 233       * Returns description of method result value
 234       *
 235       * @return \core_external\external_description
 236       * @since Moodle 2.2
 237       */
 238      public static function get_groups_returns() {
 239          return new external_multiple_structure(
 240              new external_single_structure(
 241                  array(
 242                      'id' => new external_value(PARAM_INT, 'group record id'),
 243                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 244                      'name' => new external_value(PARAM_TEXT, 'group name'),
 245                      'description' => new external_value(PARAM_RAW, 'group description text'),
 246                      'descriptionformat' => new external_format_value('description'),
 247                      'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
 248                      'idnumber' => new external_value(PARAM_RAW, 'id number'),
 249                      'visibility' => new external_value(PARAM_INT,
 250                              'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
 251                              . '3 = Membership is hidden.'),
 252                      'participation' => new external_value(PARAM_BOOL, 'participation mode'),
 253                  )
 254              )
 255          );
 256      }
 257  
 258      /**
 259       * Returns description of method parameters
 260       *
 261       * @return external_function_parameters
 262       * @since Moodle 2.2
 263       */
 264      public static function get_course_groups_parameters() {
 265          return new external_function_parameters(
 266              array(
 267                  'courseid' => new external_value(PARAM_INT, 'id of course'),
 268              )
 269          );
 270      }
 271  
 272      /**
 273       * Get all groups in the specified course
 274       *
 275       * @param int $courseid id of course
 276       * @return array of group objects (id, courseid, name, enrolmentkey)
 277       * @since Moodle 2.2
 278       */
 279      public static function get_course_groups($courseid) {
 280          $params = self::validate_parameters(self::get_course_groups_parameters(), array('courseid'=>$courseid));
 281  
 282          // now security checks
 283          $context = context_course::instance($params['courseid'], IGNORE_MISSING);
 284          try {
 285              self::validate_context($context);
 286          } catch (Exception $e) {
 287                  $exceptionparam = new stdClass();
 288                  $exceptionparam->message = $e->getMessage();
 289                  $exceptionparam->courseid = $params['courseid'];
 290                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 291          }
 292          require_capability('moodle/course:managegroups', $context);
 293  
 294          $gs = groups_get_all_groups($params['courseid'], 0, 0,
 295              'g.id, g.courseid, g.name, g.idnumber, g.description, g.descriptionformat, g.enrolmentkey, '
 296              . 'g.visibility, g.participation');
 297  
 298          $groups = array();
 299          foreach ($gs as $group) {
 300              $group->name = \core_external\util::format_string($group->name, $context);
 301              [$group->description, $group->descriptionformat] =
 302                  \core_external\util::format_text($group->description, $group->descriptionformat,
 303                          $context, 'group', 'description', $group->id);
 304              $groups[] = (array)$group;
 305          }
 306  
 307          return $groups;
 308      }
 309  
 310      /**
 311       * Returns description of method result value
 312       *
 313       * @return \core_external\external_description
 314       * @since Moodle 2.2
 315       */
 316      public static function get_course_groups_returns() {
 317          return new external_multiple_structure(
 318              new external_single_structure(
 319                  array(
 320                      'id' => new external_value(PARAM_INT, 'group record id'),
 321                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 322                      'name' => new external_value(PARAM_TEXT, 'group name'),
 323                      'description' => new external_value(PARAM_RAW, 'group description text'),
 324                      'descriptionformat' => new external_format_value('description'),
 325                      'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
 326                      'idnumber' => new external_value(PARAM_RAW, 'id number'),
 327                      'visibility' => new external_value(PARAM_INT,
 328                              'group visibility mode. 0 = Visible to all. 1 = Visible to members. 2 = See own membership. '
 329                              . '3 = Membership is hidden.'),
 330                      'participation' => new external_value(PARAM_BOOL, 'participation mode'),
 331                  )
 332              )
 333          );
 334      }
 335  
 336      /**
 337       * Returns description of method parameters
 338       *
 339       * @return external_function_parameters
 340       * @since Moodle 2.2
 341       */
 342      public static function delete_groups_parameters() {
 343          return new external_function_parameters(
 344              array(
 345                  'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
 346              )
 347          );
 348      }
 349  
 350      /**
 351       * Delete groups
 352       *
 353       * @param array $groupids array of group ids
 354       * @since Moodle 2.2
 355       */
 356      public static function delete_groups($groupids) {
 357          global $CFG, $DB;
 358          require_once("$CFG->dirroot/group/lib.php");
 359  
 360          $params = self::validate_parameters(self::delete_groups_parameters(), array('groupids'=>$groupids));
 361  
 362          $transaction = $DB->start_delegated_transaction();
 363  
 364          foreach ($params['groupids'] as $groupid) {
 365              // validate params
 366              $groupid = validate_param($groupid, PARAM_INT);
 367              if (!$group = groups_get_group($groupid, '*', IGNORE_MISSING)) {
 368                  // silently ignore attempts to delete nonexisting groups
 369                  continue;
 370              }
 371  
 372              // now security checks
 373              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 374              try {
 375                  self::validate_context($context);
 376              } catch (Exception $e) {
 377                  $exceptionparam = new stdClass();
 378                  $exceptionparam->message = $e->getMessage();
 379                  $exceptionparam->courseid = $group->courseid;
 380                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 381              }
 382              require_capability('moodle/course:managegroups', $context);
 383  
 384              groups_delete_group($group);
 385          }
 386  
 387          $transaction->allow_commit();
 388      }
 389  
 390      /**
 391       * Returns description of method result value
 392       *
 393       * @return null
 394       * @since Moodle 2.2
 395       */
 396      public static function delete_groups_returns() {
 397          return null;
 398      }
 399  
 400  
 401      /**
 402       * Returns description of method parameters
 403       *
 404       * @return external_function_parameters
 405       * @since Moodle 2.2
 406       */
 407      public static function get_group_members_parameters() {
 408          return new external_function_parameters(
 409              array(
 410                  'groupids' => new external_multiple_structure(new external_value(PARAM_INT, 'Group ID')),
 411              )
 412          );
 413      }
 414  
 415      /**
 416       * Return all members for a group
 417       *
 418       * @param array $groupids array of group ids
 419       * @return array with  group id keys containing arrays of user ids
 420       * @since Moodle 2.2
 421       */
 422      public static function get_group_members($groupids) {
 423          $members = array();
 424  
 425          $params = self::validate_parameters(self::get_group_members_parameters(), array('groupids'=>$groupids));
 426  
 427          foreach ($params['groupids'] as $groupid) {
 428              // validate params
 429              $group = groups_get_group($groupid, 'id, courseid, name, enrolmentkey', MUST_EXIST);
 430              // now security checks
 431              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 432              try {
 433                  self::validate_context($context);
 434              } catch (Exception $e) {
 435                  $exceptionparam = new stdClass();
 436                  $exceptionparam->message = $e->getMessage();
 437                  $exceptionparam->courseid = $group->courseid;
 438                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 439              }
 440              require_capability('moodle/course:managegroups', $context);
 441  
 442              $groupmembers = groups_get_members($group->id, 'u.id', 'lastname ASC, firstname ASC');
 443  
 444              $members[] = array('groupid'=>$groupid, 'userids'=>array_keys($groupmembers));
 445          }
 446  
 447          return $members;
 448      }
 449  
 450      /**
 451       * Returns description of method result value
 452       *
 453       * @return \core_external\external_description
 454       * @since Moodle 2.2
 455       */
 456      public static function get_group_members_returns() {
 457          return new external_multiple_structure(
 458              new external_single_structure(
 459                  array(
 460                      'groupid' => new external_value(PARAM_INT, 'group record id'),
 461                      'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')),
 462                  )
 463              )
 464          );
 465      }
 466  
 467  
 468      /**
 469       * Returns description of method parameters
 470       *
 471       * @return external_function_parameters
 472       * @since Moodle 2.2
 473       */
 474      public static function add_group_members_parameters() {
 475          return new external_function_parameters(
 476              array(
 477                  'members'=> new external_multiple_structure(
 478                      new external_single_structure(
 479                          array(
 480                              'groupid' => new external_value(PARAM_INT, 'group record id'),
 481                              'userid' => new external_value(PARAM_INT, 'user id'),
 482                          )
 483                      )
 484                  )
 485              )
 486          );
 487      }
 488  
 489      /**
 490       * Add group members
 491       *
 492       * @param array $members of arrays with keys userid, groupid
 493       * @since Moodle 2.2
 494       */
 495      public static function add_group_members($members) {
 496          global $CFG, $DB;
 497          require_once("$CFG->dirroot/group/lib.php");
 498  
 499          $params = self::validate_parameters(self::add_group_members_parameters(), array('members'=>$members));
 500  
 501          $transaction = $DB->start_delegated_transaction();
 502          foreach ($params['members'] as $member) {
 503              // validate params
 504              $groupid = $member['groupid'];
 505              $userid = $member['userid'];
 506  
 507              $group = groups_get_group($groupid, '*', MUST_EXIST);
 508              $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
 509  
 510              // now security checks
 511              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 512              try {
 513                  self::validate_context($context);
 514              } catch (Exception $e) {
 515                  $exceptionparam = new stdClass();
 516                  $exceptionparam->message = $e->getMessage();
 517                  $exceptionparam->courseid = $group->courseid;
 518                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 519              }
 520              require_capability('moodle/course:managegroups', $context);
 521  
 522              // now make sure user is enrolled in course - this is mandatory requirement,
 523              // unfortunately this is slow
 524              if (!is_enrolled($context, $userid)) {
 525                  throw new invalid_parameter_exception('Only enrolled users may be members of groups');
 526              }
 527  
 528              groups_add_member($group, $user);
 529          }
 530  
 531          $transaction->allow_commit();
 532      }
 533  
 534      /**
 535       * Returns description of method result value
 536       *
 537       * @return null
 538       * @since Moodle 2.2
 539       */
 540      public static function add_group_members_returns() {
 541          return null;
 542      }
 543  
 544  
 545      /**
 546       * Returns description of method parameters
 547       *
 548       * @return external_function_parameters
 549       * @since Moodle 2.2
 550       */
 551      public static function delete_group_members_parameters() {
 552          return new external_function_parameters(
 553              array(
 554                  'members'=> new external_multiple_structure(
 555                      new external_single_structure(
 556                          array(
 557                              'groupid' => new external_value(PARAM_INT, 'group record id'),
 558                              'userid' => new external_value(PARAM_INT, 'user id'),
 559                          )
 560                      )
 561                  )
 562              )
 563          );
 564      }
 565  
 566      /**
 567       * Delete group members
 568       *
 569       * @param array $members of arrays with keys userid, groupid
 570       * @since Moodle 2.2
 571       */
 572      public static function delete_group_members($members) {
 573          global $CFG, $DB;
 574          require_once("$CFG->dirroot/group/lib.php");
 575  
 576          $params = self::validate_parameters(self::delete_group_members_parameters(), array('members'=>$members));
 577  
 578          $transaction = $DB->start_delegated_transaction();
 579  
 580          foreach ($params['members'] as $member) {
 581              // validate params
 582              $groupid = $member['groupid'];
 583              $userid = $member['userid'];
 584  
 585              $group = groups_get_group($groupid, '*', MUST_EXIST);
 586              $user = $DB->get_record('user', array('id'=>$userid, 'deleted'=>0, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
 587  
 588              // now security checks
 589              $context = context_course::instance($group->courseid, IGNORE_MISSING);
 590              try {
 591                  self::validate_context($context);
 592              } catch (Exception $e) {
 593                  $exceptionparam = new stdClass();
 594                  $exceptionparam->message = $e->getMessage();
 595                  $exceptionparam->courseid = $group->courseid;
 596                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 597              }
 598              require_capability('moodle/course:managegroups', $context);
 599  
 600              if (!groups_remove_member_allowed($group, $user)) {
 601                  $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
 602                  throw new moodle_exception('errorremovenotpermitted', 'group', '', $fullname);
 603              }
 604              groups_remove_member($group, $user);
 605          }
 606  
 607          $transaction->allow_commit();
 608      }
 609  
 610      /**
 611       * Returns description of method result value
 612       *
 613       * @return null
 614       * @since Moodle 2.2
 615       */
 616      public static function delete_group_members_returns() {
 617          return null;
 618      }
 619  
 620      /**
 621       * Returns description of method parameters
 622       *
 623       * @return external_function_parameters
 624       * @since Moodle 2.3
 625       */
 626      public static function create_groupings_parameters() {
 627          return new external_function_parameters(
 628              array(
 629                  'groupings' => new external_multiple_structure(
 630                      new external_single_structure(
 631                          array(
 632                              'courseid' => new external_value(PARAM_INT, 'id of course'),
 633                              'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 634                              'description' => new external_value(PARAM_RAW, 'grouping description text'),
 635                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
 636                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
 637                          )
 638                      ), 'List of grouping object. A grouping has a courseid, a name and a description.'
 639                  )
 640              )
 641          );
 642      }
 643  
 644      /**
 645       * Create groupings
 646       *
 647       * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
 648       * @return array of newly created groupings
 649       * @since Moodle 2.3
 650       */
 651      public static function create_groupings($groupings) {
 652          global $CFG, $DB;
 653          require_once("$CFG->dirroot/group/lib.php");
 654  
 655          $params = self::validate_parameters(self::create_groupings_parameters(), array('groupings'=>$groupings));
 656  
 657          $transaction = $DB->start_delegated_transaction();
 658  
 659          $groupings = array();
 660  
 661          foreach ($params['groupings'] as $grouping) {
 662              $grouping = (object)$grouping;
 663  
 664              if (trim($grouping->name) == '') {
 665                  throw new invalid_parameter_exception('Invalid grouping name');
 666              }
 667              if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
 668                  throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
 669              }
 670  
 671              // Now security checks            .
 672              $context = context_course::instance($grouping->courseid);
 673              try {
 674                  self::validate_context($context);
 675              } catch (Exception $e) {
 676                  $exceptionparam = new stdClass();
 677                  $exceptionparam->message = $e->getMessage();
 678                  $exceptionparam->courseid = $grouping->courseid;
 679                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 680              }
 681              require_capability('moodle/course:managegroups', $context);
 682  
 683              $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
 684  
 685              // Finally create the grouping.
 686              $grouping->id = groups_create_grouping($grouping);
 687              $groupings[] = (array)$grouping;
 688          }
 689  
 690          $transaction->allow_commit();
 691  
 692          return $groupings;
 693      }
 694  
 695      /**
 696       * Returns description of method result value
 697       *
 698       * @return \core_external\external_description
 699       * @since Moodle 2.3
 700       */
 701      public static function create_groupings_returns() {
 702          return new external_multiple_structure(
 703              new external_single_structure(
 704                  array(
 705                      'id' => new external_value(PARAM_INT, 'grouping record id'),
 706                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 707                      'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 708                      'description' => new external_value(PARAM_RAW, 'grouping description text'),
 709                      'descriptionformat' => new external_format_value('description'),
 710                      'idnumber' => new external_value(PARAM_RAW, 'id number')
 711                  )
 712              ), 'List of grouping object. A grouping has an id, a courseid, a name and a description.'
 713          );
 714      }
 715  
 716      /**
 717       * Returns description of method parameters
 718       *
 719       * @return external_function_parameters
 720       * @since Moodle 2.3
 721       */
 722      public static function update_groupings_parameters() {
 723          return new external_function_parameters(
 724              array(
 725                  'groupings' => new external_multiple_structure(
 726                      new external_single_structure(
 727                          array(
 728                              'id' => new external_value(PARAM_INT, 'id of grouping'),
 729                              'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 730                              'description' => new external_value(PARAM_RAW, 'grouping description text'),
 731                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
 732                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL)
 733                          )
 734                      ), 'List of grouping object. A grouping has a courseid, a name and a description.'
 735                  )
 736              )
 737          );
 738      }
 739  
 740      /**
 741       * Update groupings
 742       *
 743       * @param array $groupings array of grouping description arrays (with keys groupname and courseid)
 744       * @return array of newly updated groupings
 745       * @since Moodle 2.3
 746       */
 747      public static function update_groupings($groupings) {
 748          global $CFG, $DB;
 749          require_once("$CFG->dirroot/group/lib.php");
 750  
 751          $params = self::validate_parameters(self::update_groupings_parameters(), array('groupings'=>$groupings));
 752  
 753          $transaction = $DB->start_delegated_transaction();
 754  
 755          foreach ($params['groupings'] as $grouping) {
 756              $grouping = (object)$grouping;
 757  
 758              if (trim($grouping->name) == '') {
 759                  throw new invalid_parameter_exception('Invalid grouping name');
 760              }
 761  
 762              if (! $currentgrouping = $DB->get_record('groupings', array('id'=>$grouping->id))) {
 763                  throw new invalid_parameter_exception("Grouping $grouping->id does not exist in the course");
 764              }
 765  
 766              // Check if the new modified grouping name already exists in the course.
 767              if ($grouping->name != $currentgrouping->name and
 768                      $DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
 769                  throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
 770              }
 771  
 772              $grouping->courseid = $currentgrouping->courseid;
 773  
 774              // Now security checks.
 775              $context = context_course::instance($grouping->courseid);
 776              try {
 777                  self::validate_context($context);
 778              } catch (Exception $e) {
 779                  $exceptionparam = new stdClass();
 780                  $exceptionparam->message = $e->getMessage();
 781                  $exceptionparam->courseid = $grouping->courseid;
 782                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 783              }
 784              require_capability('moodle/course:managegroups', $context);
 785  
 786              // We must force allways FORMAT_HTML.
 787              $grouping->descriptionformat = util::validate_format($grouping->descriptionformat);
 788  
 789              // Finally update the grouping.
 790              groups_update_grouping($grouping);
 791          }
 792  
 793          $transaction->allow_commit();
 794  
 795          return null;
 796      }
 797  
 798      /**
 799       * Returns description of method result value
 800       *
 801       * @return \core_external\external_description
 802       * @since Moodle 2.3
 803       */
 804      public static function update_groupings_returns() {
 805          return null;
 806      }
 807  
 808      /**
 809       * Returns description of method parameters
 810       *
 811       * @return external_function_parameters
 812       * @since Moodle 2.3
 813       */
 814      public static function get_groupings_parameters() {
 815          return new external_function_parameters(
 816              array(
 817                  'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')
 818                          , 'List of grouping id. A grouping id is an integer.'),
 819                  'returngroups' => new external_value(PARAM_BOOL, 'return associated groups', VALUE_DEFAULT, 0)
 820              )
 821          );
 822      }
 823  
 824      /**
 825       * Get groupings definition specified by ids
 826       *
 827       * @param array $groupingids arrays of grouping ids
 828       * @param boolean $returngroups return the associated groups if true. The default is false.
 829       * @return array of grouping objects (id, courseid, name)
 830       * @since Moodle 2.3
 831       */
 832      public static function get_groupings($groupingids, $returngroups = false) {
 833          global $CFG, $DB;
 834          require_once("$CFG->dirroot/group/lib.php");
 835          require_once("$CFG->libdir/filelib.php");
 836  
 837          $params = self::validate_parameters(self::get_groupings_parameters(),
 838                                              array('groupingids' => $groupingids,
 839                                                    'returngroups' => $returngroups));
 840  
 841          $groupings = array();
 842          foreach ($params['groupingids'] as $groupingid) {
 843              // Validate params.
 844              $grouping = groups_get_grouping($groupingid, '*', MUST_EXIST);
 845  
 846              // Now security checks.
 847              $context = context_course::instance($grouping->courseid);
 848              try {
 849                  self::validate_context($context);
 850              } catch (Exception $e) {
 851                  $exceptionparam = new stdClass();
 852                  $exceptionparam->message = $e->getMessage();
 853                  $exceptionparam->courseid = $grouping->courseid;
 854                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 855              }
 856              require_capability('moodle/course:managegroups', $context);
 857  
 858              list($grouping->description, $grouping->descriptionformat) =
 859                  \core_external\util::format_text($grouping->description, $grouping->descriptionformat,
 860                          $context, 'grouping', 'description', $grouping->id);
 861  
 862              $groupingarray = (array)$grouping;
 863  
 864              if ($params['returngroups']) {
 865                  $grouprecords = $DB->get_records_sql("SELECT * FROM {groups} g INNER JOIN {groupings_groups} gg ".
 866                                                 "ON g.id = gg.groupid WHERE gg.groupingid = ? ".
 867                                                 "ORDER BY groupid", array($groupingid));
 868                  if ($grouprecords) {
 869                      $groups = array();
 870                      foreach ($grouprecords as $grouprecord) {
 871                          list($grouprecord->description, $grouprecord->descriptionformat) =
 872                          \core_external\util::format_text($grouprecord->description, $grouprecord->descriptionformat,
 873                          $context, 'group', 'description', $grouprecord->groupid);
 874                          $groups[] = array('id' => $grouprecord->groupid,
 875                                            'name' => $grouprecord->name,
 876                                            'idnumber' => $grouprecord->idnumber,
 877                                            'description' => $grouprecord->description,
 878                                            'descriptionformat' => $grouprecord->descriptionformat,
 879                                            'enrolmentkey' => $grouprecord->enrolmentkey,
 880                                            'courseid' => $grouprecord->courseid
 881                                            );
 882                      }
 883                      $groupingarray['groups'] = $groups;
 884                  }
 885              }
 886              $groupings[] = $groupingarray;
 887          }
 888  
 889          return $groupings;
 890      }
 891  
 892      /**
 893       * Returns description of method result value
 894       *
 895       * @return \core_external\external_description
 896       * @since Moodle 2.3
 897       */
 898      public static function get_groupings_returns() {
 899          return new external_multiple_structure(
 900              new external_single_structure(
 901                  array(
 902                      'id' => new external_value(PARAM_INT, 'grouping record id'),
 903                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 904                      'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 905                      'description' => new external_value(PARAM_RAW, 'grouping description text'),
 906                      'descriptionformat' => new external_format_value('description'),
 907                      'idnumber' => new external_value(PARAM_RAW, 'id number'),
 908                      'groups' => new external_multiple_structure(
 909                          new external_single_structure(
 910                              array(
 911                                  'id' => new external_value(PARAM_INT, 'group record id'),
 912                                  'courseid' => new external_value(PARAM_INT, 'id of course'),
 913                                  'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 914                                  'description' => new external_value(PARAM_RAW, 'group description text'),
 915                                  'descriptionformat' => new external_format_value('description'),
 916                                  'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
 917                                  'idnumber' => new external_value(PARAM_RAW, 'id number')
 918                              )
 919                          ),
 920                      'optional groups', VALUE_OPTIONAL)
 921                  )
 922              )
 923          );
 924      }
 925  
 926      /**
 927       * Returns description of method parameters
 928       *
 929       * @return external_function_parameters
 930       * @since Moodle 2.3
 931       */
 932      public static function get_course_groupings_parameters() {
 933          return new external_function_parameters(
 934              array(
 935                  'courseid' => new external_value(PARAM_INT, 'id of course'),
 936              )
 937          );
 938      }
 939  
 940      /**
 941       * Get all groupings in the specified course
 942       *
 943       * @param int $courseid id of course
 944       * @return array of grouping objects (id, courseid, name, enrolmentkey)
 945       * @since Moodle 2.3
 946       */
 947      public static function get_course_groupings($courseid) {
 948          global $CFG;
 949          require_once("$CFG->dirroot/group/lib.php");
 950          require_once("$CFG->libdir/filelib.php");
 951  
 952          $params = self::validate_parameters(self::get_course_groupings_parameters(), array('courseid'=>$courseid));
 953  
 954          // Now security checks.
 955          $context = context_course::instance($params['courseid']);
 956  
 957          try {
 958              self::validate_context($context);
 959          } catch (Exception $e) {
 960                  $exceptionparam = new stdClass();
 961                  $exceptionparam->message = $e->getMessage();
 962                  $exceptionparam->courseid = $params['courseid'];
 963                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
 964          }
 965          require_capability('moodle/course:managegroups', $context);
 966  
 967          $gs = groups_get_all_groupings($params['courseid']);
 968  
 969          $groupings = array();
 970          foreach ($gs as $grouping) {
 971              list($grouping->description, $grouping->descriptionformat) =
 972                  \core_external\util::format_text($grouping->description, $grouping->descriptionformat,
 973                          $context, 'grouping', 'description', $grouping->id);
 974              $groupings[] = (array)$grouping;
 975          }
 976  
 977          return $groupings;
 978      }
 979  
 980      /**
 981       * Returns description of method result value
 982       *
 983       * @return \core_external\external_description
 984       * @since Moodle 2.3
 985       */
 986      public static function get_course_groupings_returns() {
 987          return new external_multiple_structure(
 988              new external_single_structure(
 989                  array(
 990                      'id' => new external_value(PARAM_INT, 'grouping record id'),
 991                      'courseid' => new external_value(PARAM_INT, 'id of course'),
 992                      'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
 993                      'description' => new external_value(PARAM_RAW, 'grouping description text'),
 994                      'descriptionformat' => new external_format_value('description'),
 995                      'idnumber' => new external_value(PARAM_RAW, 'id number')
 996                  )
 997              )
 998          );
 999      }
1000  
1001      /**
1002       * Returns description of method parameters
1003       *
1004       * @return external_function_parameters
1005       * @since Moodle 2.3
1006       */
1007      public static function delete_groupings_parameters() {
1008          return new external_function_parameters(
1009              array(
1010                  'groupingids' => new external_multiple_structure(new external_value(PARAM_INT, 'grouping ID')),
1011              )
1012          );
1013      }
1014  
1015      /**
1016       * Delete groupings
1017       *
1018       * @param array $groupingids array of grouping ids
1019       * @return void
1020       * @since Moodle 2.3
1021       */
1022      public static function delete_groupings($groupingids) {
1023          global $CFG, $DB;
1024          require_once("$CFG->dirroot/group/lib.php");
1025  
1026          $params = self::validate_parameters(self::delete_groupings_parameters(), array('groupingids'=>$groupingids));
1027  
1028          $transaction = $DB->start_delegated_transaction();
1029  
1030          foreach ($params['groupingids'] as $groupingid) {
1031  
1032              if (!$grouping = groups_get_grouping($groupingid)) {
1033                  // Silently ignore attempts to delete nonexisting groupings.
1034                  continue;
1035              }
1036  
1037              // Now security checks.
1038              $context = context_course::instance($grouping->courseid);
1039              try {
1040                  self::validate_context($context);
1041              } catch (Exception $e) {
1042                  $exceptionparam = new stdClass();
1043                  $exceptionparam->message = $e->getMessage();
1044                  $exceptionparam->courseid = $grouping->courseid;
1045                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1046              }
1047              require_capability('moodle/course:managegroups', $context);
1048  
1049              groups_delete_grouping($grouping);
1050          }
1051  
1052          $transaction->allow_commit();
1053      }
1054  
1055      /**
1056       * Returns description of method result value
1057       *
1058       * @return \core_external\external_description
1059       * @since Moodle 2.3
1060       */
1061      public static function delete_groupings_returns() {
1062          return null;
1063      }
1064  
1065      /**
1066       * Returns description of method parameters
1067       *
1068       * @return external_function_parameters
1069       * @since Moodle 2.3
1070       */
1071      public static function assign_grouping_parameters() {
1072          return new external_function_parameters(
1073              array(
1074                  'assignments'=> new external_multiple_structure(
1075                      new external_single_structure(
1076                          array(
1077                              'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1078                              'groupid' => new external_value(PARAM_INT, 'group record id'),
1079                          )
1080                      )
1081                  )
1082              )
1083          );
1084      }
1085  
1086      /**
1087       * Assign a group to a grouping
1088       *
1089       * @param array $assignments of arrays with keys groupid, groupingid
1090       * @return void
1091       * @since Moodle 2.3
1092       */
1093      public static function assign_grouping($assignments) {
1094          global $CFG, $DB;
1095          require_once("$CFG->dirroot/group/lib.php");
1096  
1097          $params = self::validate_parameters(self::assign_grouping_parameters(), array('assignments'=>$assignments));
1098  
1099          $transaction = $DB->start_delegated_transaction();
1100          foreach ($params['assignments'] as $assignment) {
1101              // Validate params.
1102              $groupingid = $assignment['groupingid'];
1103              $groupid = $assignment['groupid'];
1104  
1105              $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1106              $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1107  
1108              if ($DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1109                  // Continue silently if the group is yet assigned to the grouping.
1110                  continue;
1111              }
1112  
1113              // Now security checks.
1114              $context = context_course::instance($grouping->courseid);
1115              try {
1116                  self::validate_context($context);
1117              } catch (Exception $e) {
1118                  $exceptionparam = new stdClass();
1119                  $exceptionparam->message = $e->getMessage();
1120                  $exceptionparam->courseid = $group->courseid;
1121                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1122              }
1123              require_capability('moodle/course:managegroups', $context);
1124  
1125              groups_assign_grouping($groupingid, $groupid);
1126          }
1127  
1128          $transaction->allow_commit();
1129      }
1130  
1131      /**
1132       * Returns description of method result value
1133       *
1134       * @return null
1135       * @since Moodle 2.3
1136       */
1137      public static function assign_grouping_returns() {
1138          return null;
1139      }
1140  
1141      /**
1142       * Returns description of method parameters
1143       *
1144       * @return external_function_parameters
1145       * @since Moodle 2.3
1146       */
1147      public static function unassign_grouping_parameters() {
1148          return new external_function_parameters(
1149              array(
1150                  'unassignments'=> new external_multiple_structure(
1151                      new external_single_structure(
1152                          array(
1153                              'groupingid' => new external_value(PARAM_INT, 'grouping record id'),
1154                              'groupid' => new external_value(PARAM_INT, 'group record id'),
1155                          )
1156                      )
1157                  )
1158              )
1159          );
1160      }
1161  
1162      /**
1163       * Unassign a group from a grouping
1164       *
1165       * @param array $unassignments of arrays with keys groupid, groupingid
1166       * @return void
1167       * @since Moodle 2.3
1168       */
1169      public static function unassign_grouping($unassignments) {
1170          global $CFG, $DB;
1171          require_once("$CFG->dirroot/group/lib.php");
1172  
1173          $params = self::validate_parameters(self::unassign_grouping_parameters(), array('unassignments'=>$unassignments));
1174  
1175          $transaction = $DB->start_delegated_transaction();
1176          foreach ($params['unassignments'] as $unassignment) {
1177              // Validate params.
1178              $groupingid = $unassignment['groupingid'];
1179              $groupid = $unassignment['groupid'];
1180  
1181              $grouping = groups_get_grouping($groupingid, 'id, courseid', MUST_EXIST);
1182              $group = groups_get_group($groupid, 'id, courseid', MUST_EXIST);
1183  
1184              if (!$DB->record_exists('groupings_groups', array('groupingid'=>$groupingid, 'groupid'=>$groupid))) {
1185                  // Continue silently if the group is not assigned to the grouping.
1186                  continue;
1187              }
1188  
1189              // Now security checks.
1190              $context = context_course::instance($grouping->courseid);
1191              try {
1192                  self::validate_context($context);
1193              } catch (Exception $e) {
1194                  $exceptionparam = new stdClass();
1195                  $exceptionparam->message = $e->getMessage();
1196                  $exceptionparam->courseid = $group->courseid;
1197                  throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
1198              }
1199              require_capability('moodle/course:managegroups', $context);
1200  
1201              groups_unassign_grouping($groupingid, $groupid);
1202          }
1203  
1204          $transaction->allow_commit();
1205      }
1206  
1207      /**
1208       * Returns description of method result value
1209       *
1210       * @return null
1211       * @since Moodle 2.3
1212       */
1213      public static function unassign_grouping_returns() {
1214          return null;
1215      }
1216  
1217      /**
1218       * Returns description of method parameters
1219       *
1220       * @return external_function_parameters
1221       * @since Moodle 2.9
1222       */
1223      public static function get_course_user_groups_parameters() {
1224          return new external_function_parameters(
1225              array(
1226                  'courseid' => new external_value(PARAM_INT,
1227                      'Id of course (empty or 0 for all the courses where the user is enrolled).', VALUE_DEFAULT, 0),
1228                  'userid' => new external_value(PARAM_INT, 'Id of user (empty or 0 for current user).', VALUE_DEFAULT, 0),
1229                  'groupingid' => new external_value(PARAM_INT, 'returns only groups in the specified grouping', VALUE_DEFAULT, 0)
1230              )
1231          );
1232      }
1233  
1234      /**
1235       * Get all groups in the specified course for the specified user.
1236       *
1237       * @throws moodle_exception
1238       * @param int $courseid id of course.
1239       * @param int $userid id of user.
1240       * @param int $groupingid optional returns only groups in the specified grouping.
1241       * @return array of group objects (id, name, description, format) and possible warnings.
1242       * @since Moodle 2.9
1243       */
1244      public static function get_course_user_groups($courseid = 0, $userid = 0, $groupingid = 0) {
1245          global $USER;
1246  
1247          // Warnings array, it can be empty at the end but is mandatory.
1248          $warnings = array();
1249  
1250          $params = array(
1251              'courseid' => $courseid,
1252              'userid' => $userid,
1253              'groupingid' => $groupingid
1254          );
1255          $params = self::validate_parameters(self::get_course_user_groups_parameters(), $params);
1256  
1257          $courseid = $params['courseid'];
1258          $userid = $params['userid'];
1259          $groupingid = $params['groupingid'];
1260  
1261          // Validate user.
1262          if (empty($userid)) {
1263              $userid = $USER->id;
1264          } else {
1265              $user = core_user::get_user($userid, '*', MUST_EXIST);
1266              core_user::require_active_user($user);
1267          }
1268  
1269          // Get courses.
1270          if (empty($courseid)) {
1271              $courses = enrol_get_users_courses($userid, true);
1272              $checkenrolments = false;   // No need to check enrolments here since they are my courses.
1273          } else {
1274              $courses = array($courseid => get_course($courseid));
1275              $checkenrolments = true;
1276          }
1277  
1278          // Security checks.
1279          list($courses, $warnings) = util::validate_courses(array_keys($courses), $courses, true);
1280  
1281          $usergroups = array();
1282          foreach ($courses as $course) {
1283               // Check if we have permissions for retrieve the information.
1284              if ($userid != $USER->id && !has_capability('moodle/course:managegroups', $course->context)) {
1285                  $warnings[] = array(
1286                      'item' => 'course',
1287                      'itemid' => $course->id,
1288                      'warningcode' => 'cannotmanagegroups',
1289                      'message' => "User $USER->id cannot manage groups in course $course->id",
1290                  );
1291                  continue;
1292              }
1293  
1294              // Check if the user being check is enrolled in the given course.
1295              if ($checkenrolments && !is_enrolled($course->context, $userid)) {
1296                  // We return a warning because the function does not fail for not enrolled users.
1297                  $warnings[] = array(
1298                      'item' => 'course',
1299                      'itemid' => $course->id,
1300                      'warningcode' => 'notenrolled',
1301                      'message' => "User $userid is not enrolled in course $course->id",
1302                  );
1303              }
1304  
1305              $groups = groups_get_all_groups($course->id, $userid, $groupingid,
1306                  'g.id, g.name, g.description, g.descriptionformat, g.idnumber');
1307  
1308              foreach ($groups as $group) {
1309                  $group->name = \core_external\util::format_string($group->name, $course->context);
1310                  [$group->description, $group->descriptionformat] =
1311                      \core_external\util::format_text($group->description, $group->descriptionformat,
1312                              $course->context, 'group', 'description', $group->id);
1313                  $group->courseid = $course->id;
1314                  $usergroups[] = $group;
1315              }
1316          }
1317  
1318          $results = array(
1319              'groups' => $usergroups,
1320              'warnings' => $warnings
1321          );
1322          return $results;
1323      }
1324  
1325      /**
1326       * Returns description of method result value.
1327       *
1328       * @return \core_external\external_description A single structure containing groups and possible warnings.
1329       * @since Moodle 2.9
1330       */
1331      public static function get_course_user_groups_returns() {
1332          return new external_single_structure(
1333              array(
1334                  'groups' => new external_multiple_structure(self::group_description()),
1335                  'warnings' => new external_warnings(),
1336              )
1337          );
1338      }
1339  
1340      /**
1341       * Create group return value description.
1342       *
1343       * @return external_single_structure The group description
1344       */
1345      public static function group_description() {
1346          return new external_single_structure(
1347              array(
1348                  'id' => new external_value(PARAM_INT, 'group record id'),
1349                  'name' => new external_value(PARAM_TEXT, 'group name'),
1350                  'description' => new external_value(PARAM_RAW, 'group description text'),
1351                  'descriptionformat' => new external_format_value('description'),
1352                  'idnumber' => new external_value(PARAM_RAW, 'id number'),
1353                  'courseid' => new external_value(PARAM_INT, 'course id', VALUE_OPTIONAL),
1354              )
1355          );
1356      }
1357  
1358      /**
1359       * Returns description of method parameters
1360       *
1361       * @return external_function_parameters
1362       * @since Moodle 3.0
1363       */
1364      public static function get_activity_allowed_groups_parameters() {
1365          return new external_function_parameters(
1366              array(
1367                  'cmid' => new external_value(PARAM_INT, 'course module id'),
1368                  'userid' => new external_value(PARAM_INT, 'id of user, empty for current user', VALUE_DEFAULT, 0)
1369              )
1370          );
1371      }
1372  
1373      /**
1374       * Gets a list of groups that the user is allowed to access within the specified activity.
1375       *
1376       * @throws moodle_exception
1377       * @param int $cmid course module id
1378       * @param int $userid id of user.
1379       * @return array of group objects (id, name, description, format) and possible warnings.
1380       * @since Moodle 3.0
1381       */
1382      public static function get_activity_allowed_groups($cmid, $userid = 0) {
1383          global $USER;
1384  
1385          // Warnings array, it can be empty at the end but is mandatory.
1386          $warnings = array();
1387  
1388          $params = array(
1389              'cmid' => $cmid,
1390              'userid' => $userid
1391          );
1392          $params = self::validate_parameters(self::get_activity_allowed_groups_parameters(), $params);
1393          $cmid = $params['cmid'];
1394          $userid = $params['userid'];
1395  
1396          $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1397  
1398          // Security checks.
1399          $context = context_module::instance($cm->id);
1400          $coursecontext = context_course::instance($cm->course);
1401          self::validate_context($context);
1402  
1403          if (empty($userid)) {
1404              $userid = $USER->id;
1405          }
1406  
1407          $user = core_user::get_user($userid, '*', MUST_EXIST);
1408          core_user::require_active_user($user);
1409  
1410           // Check if we have permissions for retrieve the information.
1411          if ($user->id != $USER->id) {
1412              if (!has_capability('moodle/course:managegroups', $context)) {
1413                  throw new moodle_exception('accessdenied', 'admin');
1414              }
1415  
1416              // Validate if the user is enrolled in the course.
1417              $course = get_course($cm->course);
1418              if (!can_access_course($course, $user, '', true)) {
1419                  // We return a warning because the function does not fail for not enrolled users.
1420                  $warning = array();
1421                  $warning['item'] = 'course';
1422                  $warning['itemid'] = $cm->course;
1423                  $warning['warningcode'] = '1';
1424                  $warning['message'] = "User $user->id cannot access course $cm->course";
1425                  $warnings[] = $warning;
1426              }
1427          }
1428  
1429          $usergroups = array();
1430          if (empty($warnings)) {
1431              $groups = groups_get_activity_allowed_groups($cm, $user->id);
1432  
1433              foreach ($groups as $group) {
1434                  $group->name = \core_external\util::format_string($group->name, $coursecontext);
1435                  [$group->description, $group->descriptionformat] =
1436                      \core_external\util::format_text($group->description, $group->descriptionformat,
1437                              $coursecontext, 'group', 'description', $group->id);
1438                  $group->courseid = $cm->course;
1439                  $usergroups[] = $group;
1440              }
1441          }
1442  
1443          $results = array(
1444              'groups' => $usergroups,
1445              'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
1446              'warnings' => $warnings
1447          );
1448          return $results;
1449      }
1450  
1451      /**
1452       * Returns description of method result value.
1453       *
1454       * @return \core_external\external_description A single structure containing groups and possible warnings.
1455       * @since Moodle 3.0
1456       */
1457      public static function get_activity_allowed_groups_returns() {
1458          return new external_single_structure(
1459              array(
1460                  'groups' => new external_multiple_structure(self::group_description()),
1461                  'canaccessallgroups' => new external_value(PARAM_BOOL,
1462                      'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
1463                  'warnings' => new external_warnings(),
1464              )
1465          );
1466      }
1467  
1468      /**
1469       * Returns description of method parameters
1470       *
1471       * @return external_function_parameters
1472       * @since Moodle 3.0
1473       */
1474      public static function get_activity_groupmode_parameters() {
1475          return new external_function_parameters(
1476              array(
1477                  'cmid' => new external_value(PARAM_INT, 'course module id')
1478              )
1479          );
1480      }
1481  
1482      /**
1483       * Returns effective groupmode used in a given activity.
1484       *
1485       * @throws moodle_exception
1486       * @param int $cmid course module id.
1487       * @return array containing the group mode and possible warnings.
1488       * @since Moodle 3.0
1489       * @throws moodle_exception
1490       */
1491      public static function get_activity_groupmode($cmid) {
1492          global $USER;
1493  
1494          // Warnings array, it can be empty at the end but is mandatory.
1495          $warnings = array();
1496  
1497          $params = array(
1498              'cmid' => $cmid
1499          );
1500          $params = self::validate_parameters(self::get_activity_groupmode_parameters(), $params);
1501          $cmid = $params['cmid'];
1502  
1503          $cm = get_coursemodule_from_id(null, $cmid, 0, false, MUST_EXIST);
1504  
1505          // Security checks.
1506          $context = context_module::instance($cm->id);
1507          self::validate_context($context);
1508  
1509          $groupmode = groups_get_activity_groupmode($cm);
1510  
1511          $results = array(
1512              'groupmode' => $groupmode,
1513              'warnings' => $warnings
1514          );
1515          return $results;
1516      }
1517  
1518      /**
1519       * Returns description of method result value.
1520       *
1521       * @return \core_external\external_description
1522       * @since Moodle 3.0
1523       */
1524      public static function get_activity_groupmode_returns() {
1525          return new external_single_structure(
1526              array(
1527                  'groupmode' => new external_value(PARAM_INT, 'group mode:
1528                                                      0 for no groups, 1 for separate groups, 2 for visible groups'),
1529                  'warnings' => new external_warnings(),
1530              )
1531          );
1532      }
1533  
1534      /**
1535       * Returns description of method parameters
1536       *
1537       * @return external_function_parameters
1538       * @since Moodle 3.6
1539       */
1540      public static function update_groups_parameters() {
1541          return new external_function_parameters(
1542              array(
1543                  'groups' => new external_multiple_structure(
1544                      new external_single_structure(
1545                          array(
1546                              'id' => new external_value(PARAM_INT, 'ID of the group'),
1547                              'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
1548                              'description' => new external_value(PARAM_RAW, 'group description text', VALUE_OPTIONAL),
1549                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1550                              'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase', VALUE_OPTIONAL),
1551                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
1552                              'visibility' => new external_value(PARAM_TEXT,
1553                                      'group visibility mode. 0 = Visible to all. 1 = Visible to members. '
1554                                      . '2 = See own membership. 3 = Membership is hidden.', VALUE_OPTIONAL),
1555                              'participation' => new external_value(PARAM_BOOL,
1556                                      'activity participation enabled? Only for "all" and "members" visibility', VALUE_OPTIONAL),
1557                          )
1558                      ), 'List of group objects. A group is found by the id, then all other details provided will be updated.'
1559                  )
1560              )
1561          );
1562      }
1563  
1564      /**
1565       * Update groups
1566       *
1567       * @param array $groups
1568       * @return null
1569       * @since Moodle 3.6
1570       */
1571      public static function update_groups($groups) {
1572          global $CFG, $DB;
1573          require_once("$CFG->dirroot/group/lib.php");
1574  
1575          $params = self::validate_parameters(self::update_groups_parameters(), array('groups' => $groups));
1576  
1577          $transaction = $DB->start_delegated_transaction();
1578  
1579          foreach ($params['groups'] as $group) {
1580              $group = (object) $group;
1581  
1582              if (trim($group->name) == '') {
1583                  throw new invalid_parameter_exception('Invalid group name');
1584              }
1585  
1586              if (!$currentgroup = $DB->get_record('groups', array('id' => $group->id))) {
1587                  throw new invalid_parameter_exception("Group $group->id does not exist");
1588              }
1589  
1590              // Check if the modified group name already exists in the course.
1591              if ($group->name != $currentgroup->name and
1592                      $DB->get_record('groups', array('courseid' => $currentgroup->courseid, 'name' => $group->name))) {
1593                  throw new invalid_parameter_exception('A different group with the same name already exists in the course');
1594              }
1595  
1596              if (isset($group->visibility) || isset($group->participation)) {
1597                  $hasmembers = $DB->record_exists('groups_members', ['groupid' => $group->id]);
1598                  if (isset($group->visibility)) {
1599                      // Validate visibility.
1600                      self::validate_visibility($group->visibility);
1601                      if ($hasmembers && $group->visibility != $currentgroup->visibility) {
1602                          throw new invalid_parameter_exception(
1603                                  'The visibility of this group cannot be changed as it currently has members.');
1604                      }
1605                  } else {
1606                      $group->visibility = $currentgroup->visibility;
1607                  }
1608                  if (isset($group->participation) && $hasmembers && $group->participation != $currentgroup->participation) {
1609                      throw new invalid_parameter_exception(
1610                              'The participation mode of this group cannot be changed as it currently has members.');
1611                  }
1612              }
1613  
1614              $group->courseid = $currentgroup->courseid;
1615  
1616              // Now security checks.
1617              $context = context_course::instance($group->courseid);
1618              try {
1619                  self::validate_context($context);
1620              } catch (Exception $e) {
1621                  $exceptionparam = new stdClass();
1622                  $exceptionparam->message = $e->getMessage();
1623                  $exceptionparam->courseid = $group->courseid;
1624                  throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
1625              }
1626              require_capability('moodle/course:managegroups', $context);
1627  
1628              if (!empty($group->description)) {
1629                  $group->descriptionformat = util::validate_format($group->descriptionformat);
1630              }
1631  
1632              groups_update_group($group);
1633          }
1634  
1635          $transaction->allow_commit();
1636  
1637          return null;
1638      }
1639  
1640      /**
1641       * Returns description of method result value
1642       *
1643       * @return null
1644       * @since Moodle 3.6
1645       */
1646      public static function update_groups_returns() {
1647          return null;
1648      }
1649  }