Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [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  namespace core_group;
  18  
  19  use core_customfield\field_controller;
  20  use core_external\external_api;
  21  use core_group\customfield\group_handler;
  22  use core_group\customfield\grouping_handler;
  23  use core_group_external;
  24  use externallib_advanced_testcase;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  
  30  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  31  require_once($CFG->dirroot . '/group/externallib.php');
  32  require_once($CFG->dirroot . '/group/lib.php');
  33  
  34  /**
  35   * Group external PHPunit tests
  36   *
  37   * @package    core_group
  38   * @category   external
  39   * @copyright  2012 Jerome Mouneyrac
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   * @since Moodle 2.4
  42   * @covers \core_group_external
  43   */
  44  class externallib_test extends externallib_advanced_testcase {
  45  
  46      /**
  47       * Create group custom field for testing.
  48       *
  49       * @return field_controller
  50       */
  51      protected function create_group_custom_field(): field_controller {
  52          $fieldcategory = self::getDataGenerator()->create_custom_field_category([
  53              'component' => 'core_group',
  54              'area' => 'group',
  55          ]);
  56  
  57          return self::getDataGenerator()->create_custom_field([
  58              'shortname' => 'testgroupcustomfield1',
  59              'type' => 'text',
  60              'categoryid' => $fieldcategory->get('id'),
  61          ]);
  62      }
  63      /**
  64       * Create grouping custom field for testing.
  65       *
  66       * @return field_controller
  67       */
  68      protected function create_grouping_custom_field(): field_controller {
  69          $fieldcategory = self::getDataGenerator()->create_custom_field_category([
  70              'component' => 'core_group',
  71              'area' => 'grouping',
  72          ]);
  73  
  74          return self::getDataGenerator()->create_custom_field([
  75              'shortname' => 'testgroupingcustomfield1',
  76              'type' => 'text',
  77              'categoryid' => $fieldcategory->get('id'),
  78          ]);
  79      }
  80  
  81      /**
  82       * Test create_groups
  83       */
  84      public function test_create_groups() {
  85          global $DB;
  86  
  87          $this->resetAfterTest(true);
  88  
  89          $course  = self::getDataGenerator()->create_course();
  90  
  91          $group1 = array();
  92          $group1['courseid'] = $course->id;
  93          $group1['name'] = 'Group Test 1';
  94          $group1['description'] = 'Group Test 1 description';
  95          $group1['descriptionformat'] = FORMAT_MOODLE;
  96          $group1['enrolmentkey'] = 'Test group enrol secret phrase';
  97          $group1['idnumber'] = 'TEST1';
  98          $group2 = array();
  99          $group2['courseid'] = $course->id;
 100          $group2['name'] = 'Group Test 2';
 101          $group2['description'] = 'Group Test 2 description';
 102          $group2['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 103          $group2['participation'] = false;
 104          $group3 = array();
 105          $group3['courseid'] = $course->id;
 106          $group3['name'] = 'Group Test 3';
 107          $group3['description'] = 'Group Test 3 description';
 108          $group3['idnumber'] = 'TEST1';
 109          $group4 = array();
 110          $group4['courseid'] = $course->id;
 111          $group4['name'] = 'Group Test 4';
 112          $group4['description'] = 'Group Test 4 description';
 113  
 114          // Set the required capabilities by the external function
 115          $context = \context_course::instance($course->id);
 116          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 117          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 118  
 119          // Call the external function.
 120          $groups = core_group_external::create_groups(array($group1, $group2));
 121  
 122          // We need to execute the return values cleaning process to simulate the web service server.
 123          $groups = external_api::clean_returnvalue(core_group_external::create_groups_returns(), $groups);
 124  
 125          // Checks against DB values
 126          $this->assertEquals(2, count($groups));
 127          foreach ($groups as $group) {
 128              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
 129              switch ($dbgroup->name) {
 130                  case $group1['name']:
 131                      $groupdescription = $group1['description'];
 132                      $groupcourseid = $group1['courseid'];
 133                      $this->assertEquals($dbgroup->descriptionformat, $group1['descriptionformat']);
 134                      $this->assertEquals($dbgroup->enrolmentkey, $group1['enrolmentkey']);
 135                      $this->assertEquals($dbgroup->idnumber, $group1['idnumber']);
 136                      // The visibility and participation attributes were not specified, so should match the default values.
 137                      $groupvisibility = GROUPS_VISIBILITY_ALL;
 138                      $groupparticipation = true;
 139                      break;
 140                  case $group2['name']:
 141                      $groupdescription = $group2['description'];
 142                      $groupcourseid = $group2['courseid'];
 143                      $groupvisibility = $group2['visibility'];
 144                      $groupparticipation = $group2['participation'];
 145                      break;
 146                  default:
 147                      throw new \moodle_exception('unknowgroupname');
 148                      break;
 149              }
 150              $this->assertEquals($dbgroup->description, $groupdescription);
 151              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 152              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 153              $this->assertEquals($dbgroup->participation, $groupparticipation);
 154          }
 155  
 156          try {
 157              $froups = core_group_external::create_groups(array($group3));
 158              $this->fail('Exception expected due to already existing idnumber.');
 159          } catch (\moodle_exception $e) {
 160              $this->assertInstanceOf('moodle_exception', $e);
 161              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 162          }
 163  
 164          // Call without required capability
 165          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 166  
 167          $this->expectException(\required_capability_exception::class);
 168          $froups = core_group_external::create_groups(array($group4));
 169      }
 170  
 171      /**
 172       * Test create_groups with custom fields.
 173       */
 174      public function test_create_groups_with_customfields() {
 175          global $DB;
 176  
 177          $this->resetAfterTest();
 178          $this->setAdminUser();
 179  
 180          $course = self::getDataGenerator()->create_course();
 181          $this->create_group_custom_field();
 182          $group = [
 183              'courseid' => $course->id,
 184              'name' => 'Create groups test (with custom fields)',
 185              'description' => 'Description for create groups test with custom fields',
 186              'customfields' => [
 187                  [
 188                      'shortname' => 'testgroupcustomfield1',
 189                      'value' => 'Test group value 1',
 190                  ],
 191              ],
 192          ];
 193          $createdgroups = core_group_external::create_groups([$group]);
 194          $createdgroups = external_api::clean_returnvalue(core_group_external::create_groups_returns(), $createdgroups);
 195  
 196          $this->assertCount(1, $createdgroups);
 197          $createdgroup = reset($createdgroups);
 198          $dbgroup = $DB->get_record('groups', ['id' => $createdgroup['id']], '*', MUST_EXIST);
 199          $this->assertEquals($group['name'], $dbgroup->name);
 200          $this->assertEquals($group['description'], $dbgroup->description);
 201  
 202          $data = group_handler::create()->export_instance_data_object($createdgroup['id'], true);
 203          $this->assertEquals('Test group value 1', $data->testgroupcustomfield1);
 204      }
 205  
 206      /**
 207       * Test that creating a group with an invalid visibility value throws an exception.
 208       *
 209       * @covers \core_group_external::create_groups
 210       * @return void
 211       */
 212      public function test_create_group_invalid_visibility(): void {
 213          $this->resetAfterTest(true);
 214  
 215          $course = self::getDataGenerator()->create_course();
 216  
 217          $group1 = array();
 218          $group1['courseid'] = $course->id;
 219          $group1['name'] = 'Group Test 1';
 220          $group1['description'] = 'Group Test 1 description';
 221          $group1['visibility'] = 1000;
 222  
 223          // Set the required capabilities by the external function.
 224          $context = \context_course::instance($course->id);
 225          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 226          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 227  
 228          // Call the external function.
 229          $this->expectException('invalid_parameter_exception');
 230          core_group_external::create_groups([$group1]);
 231      }
 232  
 233      /**
 234       * Test update_groups
 235       */
 236      public function test_update_groups() {
 237          global $DB;
 238  
 239          $this->resetAfterTest(true);
 240  
 241          $course = self::getDataGenerator()->create_course();
 242  
 243          $group1data = array();
 244          $group1data['courseid'] = $course->id;
 245          $group1data['name'] = 'Group Test 1';
 246          $group1data['description'] = 'Group Test 1 description';
 247          $group1data['descriptionformat'] = FORMAT_MOODLE;
 248          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 249          $group1data['idnumber'] = 'TEST1';
 250          $group2data = array();
 251          $group2data['courseid'] = $course->id;
 252          $group2data['name'] = 'Group Test 2';
 253          $group2data['description'] = 'Group Test 2 description';
 254          $group2data['idnumber'] = 'TEST2';
 255  
 256          // Set the required capabilities by the external function.
 257          $context = \context_course::instance($course->id);
 258          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 259          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 260  
 261          // Create the test groups.
 262          $group1 = self::getDataGenerator()->create_group($group1data);
 263          $group2 = self::getDataGenerator()->create_group($group2data);
 264  
 265          $group1data['id'] = $group1->id;
 266          unset($group1data['courseid']);
 267          $group2data['id'] = $group2->id;
 268          unset($group2data['courseid']);
 269  
 270          // No exceptions should be triggered.
 271          $group1data['idnumber'] = 'CHANGED';
 272          core_group_external::update_groups(array($group1data));
 273          $group2data['description'] = 'Group Test 2 description CHANGED';
 274          $group2data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 275          core_group_external::update_groups(array($group2data));
 276  
 277          foreach ([$group1, $group2] as $group) {
 278              $dbgroup = $DB->get_record('groups', array('id' => $group->id), '*', MUST_EXIST);
 279              switch ($dbgroup->name) {
 280                  case $group1data['name']:
 281                      $this->assertEquals($dbgroup->idnumber, $group1data['idnumber']);
 282                      $groupdescription = $group1data['description'];
 283                      // Visibility was not specified, so should match the default value.
 284                      $groupvisibility = GROUPS_VISIBILITY_ALL;
 285                      break;
 286                  case $group2data['name']:
 287                      $this->assertEquals($dbgroup->idnumber, $group2data['idnumber']);
 288                      $groupdescription = $group2data['description'];
 289                      $groupvisibility = $group2data['visibility'];
 290                      break;
 291                  default:
 292                      throw new \moodle_exception('unknowngroupname');
 293                      break;
 294              }
 295              $this->assertEquals($dbgroup->description, $groupdescription);
 296              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 297          }
 298  
 299          // Taken idnumber exception.
 300          $group1data['idnumber'] = 'TEST2';
 301          try {
 302              $groups = core_group_external::update_groups(array($group1data));
 303              $this->fail('Exception expected due to already existing idnumber.');
 304          } catch (\moodle_exception $e) {
 305              $this->assertInstanceOf('moodle_exception', $e);
 306              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 307          }
 308  
 309          // Call without required capability.
 310          $group1data['idnumber'] = 'TEST1';
 311          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 312  
 313          $this->expectException(\required_capability_exception::class);
 314          $groups = core_group_external::update_groups(array($group1data));
 315      }
 316  
 317      /**
 318       * Test update_groups with custom fields.
 319       */
 320      public function test_update_groups_with_customfields() {
 321          $this->resetAfterTest();
 322          $this->setAdminUser();
 323  
 324          $course = self::getDataGenerator()->create_course();
 325          $this->create_group_custom_field();
 326          $group = self::getDataGenerator()->create_group(['courseid' => $course->id]);
 327  
 328          $data = group_handler::create()->export_instance_data_object($group->id, true);
 329          $this->assertNull($data->testgroupcustomfield1);
 330  
 331          $updategroup = [
 332              'id' => $group->id,
 333              'name' => $group->name,
 334              'customfields' => [
 335                  [
 336                      'shortname' => 'testgroupcustomfield1',
 337                      'value' => 'Test value 1',
 338                  ],
 339              ],
 340          ];
 341          core_group_external::update_groups([$updategroup]);
 342          $data = group_handler::create()->export_instance_data_object($group->id, true);
 343          $this->assertEquals('Test value 1', $data->testgroupcustomfield1);
 344      }
 345  
 346      /**
 347       * Test an exception is thrown when an invalid visibility value is passed in an update.
 348       *
 349       * @covers \core_group_external::update_groups
 350       * @return void
 351       */
 352      public function test_update_groups_invalid_visibility(): void {
 353          $this->resetAfterTest(true);
 354  
 355          $course = self::getDataGenerator()->create_course();
 356  
 357          $group1data = array();
 358          $group1data['courseid'] = $course->id;
 359          $group1data['name'] = 'Group Test 1';
 360  
 361          // Set the required capabilities by the external function.
 362          $context = \context_course::instance($course->id);
 363          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 364          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 365  
 366          // Create the test group.
 367          $group1 = self::getDataGenerator()->create_group($group1data);
 368  
 369          $group1data['id'] = $group1->id;
 370          unset($group1data['courseid']);
 371          $group1data['visibility'] = 1000;
 372  
 373          $this->expectException('invalid_parameter_exception');
 374          core_group_external::update_groups(array($group1data));
 375      }
 376  
 377      /**
 378       * Attempting to change the visibility of a group with members should throw an exception.
 379       *
 380       * @covers \core_group_external::update_groups
 381       * @return void
 382       */
 383      public function test_update_groups_visibility_with_members(): void {
 384          $this->resetAfterTest(true);
 385  
 386          $course = self::getDataGenerator()->create_course();
 387  
 388          $group1data = array();
 389          $group1data['courseid'] = $course->id;
 390          $group1data['name'] = 'Group Test 1';
 391  
 392          // Set the required capabilities by the external function.
 393          $context = \context_course::instance($course->id);
 394          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 395          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 396  
 397          // Create the test group and add a member.
 398          $group1 = self::getDataGenerator()->create_group($group1data);
 399          $user1 = self::getDataGenerator()->create_and_enrol($course);
 400          self::getDataGenerator()->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
 401  
 402          $group1data['id'] = $group1->id;
 403          unset($group1data['courseid']);
 404          $group1data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 405  
 406          $this->expectExceptionMessage('The visibility of this group cannot be changed as it currently has members.');
 407          core_group_external::update_groups(array($group1data));
 408      }
 409  
 410      /**
 411       * Attempting to change the participation field of a group with members should throw an exception.
 412       *
 413       * @covers \core_group_external::update_groups
 414       * @return void
 415       */
 416      public function test_update_groups_participation_with_members(): void {
 417          $this->resetAfterTest(true);
 418  
 419          $course = self::getDataGenerator()->create_course();
 420  
 421          $group1data = array();
 422          $group1data['courseid'] = $course->id;
 423          $group1data['name'] = 'Group Test 1';
 424  
 425          // Set the required capabilities by the external function.
 426          $context = \context_course::instance($course->id);
 427          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 428          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 429  
 430          // Create the test group and add a member.
 431          $group1 = self::getDataGenerator()->create_group($group1data);
 432          $user1 = self::getDataGenerator()->create_and_enrol($course);
 433          self::getDataGenerator()->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
 434  
 435          $group1data['id'] = $group1->id;
 436          unset($group1data['courseid']);
 437          $group1data['participation'] = false;
 438  
 439          $this->expectExceptionMessage('The participation mode of this group cannot be changed as it currently has members.');
 440          core_group_external::update_groups(array($group1data));
 441      }
 442  
 443      /**
 444       * Test get_groups
 445       */
 446      public function test_get_groups() {
 447          global $DB;
 448  
 449          $this->resetAfterTest(true);
 450  
 451          $course = self::getDataGenerator()->create_course();
 452          $group1data = array();
 453          $group1data['courseid'] = $course->id;
 454          $group1data['name'] = 'Group Test 1';
 455          $group1data['description'] = 'Group Test 1 description';
 456          $group1data['descriptionformat'] = FORMAT_MOODLE;
 457          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 458          $group1data['idnumber'] = 'TEST1';
 459          $group2data = array();
 460          $group2data['courseid'] = $course->id;
 461          $group2data['name'] = 'Group Test 2';
 462          $group2data['description'] = 'Group Test 2 description';
 463          $group2data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 464          $group2data['participation'] = false;
 465          $group1 = self::getDataGenerator()->create_group($group1data);
 466          $group2 = self::getDataGenerator()->create_group($group2data);
 467  
 468          // Set the required capabilities by the external function
 469          $context = \context_course::instance($course->id);
 470          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 471          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 472  
 473          // Call the external function.
 474          $groups = core_group_external::get_groups(array($group1->id, $group2->id));
 475  
 476          // We need to execute the return values cleaning process to simulate the web service server.
 477          $groups = external_api::clean_returnvalue(core_group_external::get_groups_returns(), $groups);
 478  
 479          // Checks against DB values
 480          $this->assertEquals(2, count($groups));
 481          foreach ($groups as $group) {
 482              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
 483              switch ($dbgroup->name) {
 484                  case $group1->name:
 485                      $groupdescription = $group1->description;
 486                      $groupcourseid = $group1->courseid;
 487                      // The visibility and participation attributes were not specified, so should match the default values.
 488                      $groupvisibility = GROUPS_VISIBILITY_ALL;
 489                      $groupparticipation = true;
 490                      $this->assertEquals($dbgroup->descriptionformat, $group1->descriptionformat);
 491                      $this->assertEquals($dbgroup->enrolmentkey, $group1->enrolmentkey);
 492                      $this->assertEquals($dbgroup->idnumber, $group1->idnumber);
 493                      break;
 494                  case $group2->name:
 495                      $groupdescription = $group2->description;
 496                      $groupcourseid = $group2->courseid;
 497                      $groupvisibility = $group2->visibility;
 498                      $groupparticipation = $group2->participation;
 499                      break;
 500                  default:
 501                      throw new \moodle_exception('unknowgroupname');
 502                      break;
 503              }
 504              $this->assertEquals($dbgroup->description, $groupdescription);
 505              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 506              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 507              $this->assertEquals($dbgroup->participation, $groupparticipation);
 508          }
 509  
 510          // Call without required capability
 511          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 512  
 513          $this->expectException(\required_capability_exception::class);
 514          $groups = core_group_external::get_groups(array($group1->id, $group2->id));
 515      }
 516  
 517      /**
 518       * Test get_groups with customfields.
 519       */
 520      public function test_get_groups_with_customfields() {
 521          $this->resetAfterTest();
 522          $this->setAdminUser();
 523  
 524          $course = self::getDataGenerator()->create_course();
 525          $this->create_group_custom_field();
 526          $group = self::getDataGenerator()->create_group([
 527              'courseid' => $course->id,
 528              'customfield_testgroupcustomfield1' => 'Test group value 1',
 529          ]);
 530  
 531          // Call the external function.
 532          $groups = core_group_external::get_groups([$group->id]);
 533          // We need to execute the return values cleaning process to simulate the web service server.
 534          $groups = external_api::clean_returnvalue(core_group_external::get_groups_returns(), $groups);
 535  
 536          $this->assertEquals(1, count($groups));
 537          $groupresult = reset($groups);
 538          $this->assertEquals(1, count($groupresult['customfields']));
 539          $customfield = reset($groupresult['customfields']);
 540          $this->assertEquals('testgroupcustomfield1', $customfield['shortname']);
 541          $this->assertEquals('Test group value 1', $customfield['value']);
 542      }
 543  
 544      /**
 545       * Test delete_groups
 546       */
 547      public function test_delete_groups() {
 548          global $DB;
 549  
 550          $this->resetAfterTest(true);
 551  
 552          $course = self::getDataGenerator()->create_course();
 553          $group1data = array();
 554          $group1data['courseid'] = $course->id;
 555          $group1data['name'] = 'Group Test 1';
 556          $group1data['description'] = 'Group Test 1 description';
 557          $group1data['descriptionformat'] = FORMAT_MOODLE;
 558          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 559          $group2data = array();
 560          $group2data['courseid'] = $course->id;
 561          $group2data['name'] = 'Group Test 2';
 562          $group2data['description'] = 'Group Test 2 description';
 563          $group3data['courseid'] = $course->id;
 564          $group3data['name'] = 'Group Test 3';
 565          $group3data['description'] = 'Group Test 3 description';
 566          $group1 = self::getDataGenerator()->create_group($group1data);
 567          $group2 = self::getDataGenerator()->create_group($group2data);
 568          $group3 = self::getDataGenerator()->create_group($group3data);
 569  
 570          // Set the required capabilities by the external function
 571          $context = \context_course::instance($course->id);
 572          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 573          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 574  
 575          // Checks against DB values
 576          $groupstotal = $DB->count_records('groups', array());
 577          $this->assertEquals(3, $groupstotal);
 578  
 579          // Call the external function.
 580          core_group_external::delete_groups(array($group1->id, $group2->id));
 581  
 582          // Checks against DB values
 583          $groupstotal = $DB->count_records('groups', array());
 584          $this->assertEquals(1, $groupstotal);
 585  
 586          // Call without required capability
 587          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 588  
 589          $this->expectException(\required_capability_exception::class);
 590          $froups = core_group_external::delete_groups(array($group3->id));
 591      }
 592  
 593      /**
 594       * Test create and update groupings.
 595       * @return void
 596       */
 597      public function test_create_update_groupings() {
 598          global $DB;
 599  
 600          $this->resetAfterTest(true);
 601  
 602          $this->setAdminUser();
 603  
 604          $course = self::getDataGenerator()->create_course();
 605  
 606          $grouping1data = array();
 607          $grouping1data['courseid'] = $course->id;
 608          $grouping1data['name'] = 'Grouping 1 Test';
 609          $grouping1data['description'] = 'Grouping 1 Test description';
 610          $grouping1data['descriptionformat'] = FORMAT_MOODLE;
 611          $grouping1data['idnumber'] = 'TEST';
 612  
 613          $grouping1 = self::getDataGenerator()->create_grouping($grouping1data);
 614  
 615          $grouping1data['name'] = 'Another group';
 616  
 617          try {
 618              $groupings = core_group_external::create_groupings(array($grouping1data));
 619              $this->fail('Exception expected due to already existing idnumber.');
 620          } catch (\moodle_exception $e) {
 621              $this->assertInstanceOf('moodle_exception', $e);
 622              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 623          }
 624  
 625          // No exception should be triggered.
 626          $grouping1data['id'] = $grouping1->id;
 627          $grouping1data['idnumber'] = 'CHANGED';
 628          unset($grouping1data['courseid']);
 629          core_group_external::update_groupings(array($grouping1data));
 630  
 631          $grouping2data = array();
 632          $grouping2data['courseid'] = $course->id;
 633          $grouping2data['name'] = 'Grouping 2 Test';
 634          $grouping2data['description'] = 'Grouping 2 Test description';
 635          $grouping2data['descriptionformat'] = FORMAT_MOODLE;
 636          $grouping2data['idnumber'] = 'TEST';
 637  
 638          $grouping2 = self::getDataGenerator()->create_grouping($grouping2data);
 639  
 640          $grouping2data['id'] = $grouping2->id;
 641          $grouping2data['idnumber'] = 'CHANGED';
 642          unset($grouping2data['courseid']);
 643          try {
 644              $groupings = core_group_external::update_groupings(array($grouping2data));
 645              $this->fail('Exception expected due to already existing idnumber.');
 646          } catch (\moodle_exception $e) {
 647              $this->assertInstanceOf('moodle_exception', $e);
 648              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 649          }
 650      }
 651  
 652      /**
 653       * Test create_groupings with custom fields.
 654       */
 655      public function test_create_groupings_with_customfields() {
 656          global $DB;
 657  
 658          $this->resetAfterTest();
 659          $this->setAdminUser();
 660  
 661          $course = self::getDataGenerator()->create_course();
 662          $this->create_grouping_custom_field();
 663          $grouping = [
 664              'courseid' => $course->id,
 665              'name' => 'Create groupings test (with custom fields)',
 666              'description' => 'Description for create groupings test with custom fields',
 667              'idnumber' => 'groupingidnumber1',
 668              'customfields' => [
 669                  [
 670                      'shortname' => 'testgroupingcustomfield1',
 671                      'value' => 'Test grouping value 1',
 672                  ],
 673              ],
 674          ];
 675          $createdgroupings = core_group_external::create_groupings([$grouping]);
 676          $createdgroupings = external_api::clean_returnvalue(core_group_external::create_groupings_returns(), $createdgroupings);
 677  
 678          $this->assertCount(1, $createdgroupings);
 679          $createdgrouping = reset($createdgroupings);
 680          $dbgroup = $DB->get_record('groupings', ['id' => $createdgrouping['id']], '*', MUST_EXIST);
 681          $this->assertEquals($grouping['name'], $dbgroup->name);
 682          $this->assertEquals($grouping['description'], $dbgroup->description);
 683          $this->assertEquals($grouping['idnumber'], $dbgroup->idnumber);
 684  
 685          $data = grouping_handler::create()->export_instance_data_object($createdgrouping['id'], true);
 686          $this->assertEquals('Test grouping value 1', $data->testgroupingcustomfield1);
 687      }
 688  
 689      /**
 690       * Test update_groups with custom fields.
 691       */
 692      public function test_update_groupings_with_customfields() {
 693          $this->resetAfterTest();
 694          $this->setAdminUser();
 695  
 696          $course = self::getDataGenerator()->create_course();
 697          $this->create_grouping_custom_field();
 698          $grouping = self::getDataGenerator()->create_grouping(['courseid' => $course->id]);
 699  
 700          $data = grouping_handler::create()->export_instance_data_object($grouping->id, true);
 701          $this->assertNull($data->testgroupingcustomfield1);
 702  
 703          $updategroup = [
 704              'id' => $grouping->id,
 705              'name' => $grouping->name,
 706              'description' => $grouping->description,
 707              'customfields' => [
 708                  [
 709                      'shortname' => 'testgroupingcustomfield1',
 710                      'value' => 'Test grouping value 1',
 711                  ],
 712              ],
 713          ];
 714          core_group_external::update_groupings([$updategroup]);
 715          $data = grouping_handler::create()->export_instance_data_object($grouping->id, true);
 716          $this->assertEquals('Test grouping value 1', $data->testgroupingcustomfield1);
 717      }
 718  
 719      /**
 720       * Test get_groupings
 721       */
 722      public function test_get_groupings() {
 723          global $DB;
 724  
 725          $this->resetAfterTest(true);
 726  
 727          $course = self::getDataGenerator()->create_course();
 728  
 729          $groupingdata = array();
 730          $groupingdata['courseid'] = $course->id;
 731          $groupingdata['name'] = 'Grouping Test';
 732          $groupingdata['description'] = 'Grouping Test description';
 733          $groupingdata['descriptionformat'] = FORMAT_MOODLE;
 734  
 735          $grouping = self::getDataGenerator()->create_grouping($groupingdata);
 736  
 737          // Set the required capabilities by the external function.
 738          $context = \context_course::instance($course->id);
 739          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 740          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 741  
 742          // Call the external function without specifying the optional parameter.
 743          $groupings = core_group_external::get_groupings(array($grouping->id));
 744          // We need to execute the return values cleaning process to simulate the web service server.
 745          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 746  
 747          $this->assertEquals(1, count($groupings));
 748  
 749          $group1data = array();
 750          $group1data['courseid'] = $course->id;
 751          $group1data['name'] = 'Group Test 1';
 752          $group1data['description'] = 'Group Test 1 description';
 753          $group1data['descriptionformat'] = FORMAT_MOODLE;
 754          $group2data = array();
 755          $group2data['courseid'] = $course->id;
 756          $group2data['name'] = 'Group Test 2';
 757          $group2data['description'] = 'Group Test 2 description';
 758          $group2data['descriptionformat'] = FORMAT_MOODLE;
 759  
 760          $group1 = self::getDataGenerator()->create_group($group1data);
 761          $group2 = self::getDataGenerator()->create_group($group2data);
 762  
 763          groups_assign_grouping($grouping->id, $group1->id);
 764          groups_assign_grouping($grouping->id, $group2->id);
 765  
 766          // Call the external function specifying that groups are returned.
 767          $groupings = core_group_external::get_groupings(array($grouping->id), true);
 768          // We need to execute the return values cleaning process to simulate the web service server.
 769          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 770          $this->assertEquals(1, count($groupings));
 771          $this->assertEquals(2, count($groupings[0]['groups']));
 772          foreach ($groupings[0]['groups'] as $group) {
 773              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
 774              $dbgroupinggroups = $DB->get_record('groupings_groups',
 775                                                  array('groupingid' => $groupings[0]['id'],
 776                                                        'groupid' => $group['id']),
 777                                                  '*', MUST_EXIST);
 778              switch ($dbgroup->name) {
 779                  case $group1->name:
 780                      $groupdescription = $group1->description;
 781                      $groupcourseid = $group1->courseid;
 782                      break;
 783                  case $group2->name:
 784                      $groupdescription = $group2->description;
 785                      $groupcourseid = $group2->courseid;
 786                      break;
 787                  default:
 788                      throw new \moodle_exception('unknowgroupname');
 789                      break;
 790              }
 791              $this->assertEquals($dbgroup->description, $groupdescription);
 792              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 793          }
 794      }
 795  
 796      /**
 797       * Test get_groupings with customfields.
 798       */
 799      public function test_get_groupings_with_customfields() {
 800          $this->resetAfterTest();
 801          $this->setAdminUser();
 802  
 803          $course = self::getDataGenerator()->create_course();
 804          $this->create_grouping_custom_field();
 805          $grouping = self::getDataGenerator()->create_grouping([
 806              'courseid' => $course->id,
 807              'customfield_testgroupingcustomfield1' => 'Test grouping value 1',
 808          ]);
 809          $this->create_group_custom_field();
 810          $group = self::getDataGenerator()->create_group([
 811              'courseid' => $course->id,
 812              'customfield_testgroupcustomfield1' => 'Test group value 1',
 813          ]);
 814          groups_assign_grouping($grouping->id, $group->id);
 815  
 816          // Call the external function.
 817          $groupings = core_group_external::get_groupings([$grouping->id]);
 818          // We need to execute the return values cleaning process to simulate the web service server.
 819          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 820  
 821          $this->assertEquals(1, count($groupings));
 822          $groupingresult = reset($groupings);
 823          $this->assertEquals(1, count($groupingresult['customfields']));
 824          $customfield = reset($groupingresult['customfields']);
 825          $this->assertEquals('testgroupingcustomfield1', $customfield['shortname']);
 826          $this->assertEquals('Test grouping value 1', $customfield['value']);
 827          $this->assertArrayNotHasKey('groups', $groupingresult);
 828  
 829          // Call the external function with return group parameter.
 830          $groupings = core_group_external::get_groupings([$grouping->id], true);
 831          // We need to execute the return values cleaning process to simulate the web service server.
 832          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 833  
 834          $this->assertEquals(1, count($groupings));
 835          $groupingresult = reset($groupings);
 836          $this->assertEquals(1, count($groupingresult['customfields']));
 837          $this->assertArrayHasKey('groups', $groupingresult);
 838          $this->assertEquals(1, count($groupingresult['groups']));
 839          $groupresult = reset($groupingresult['groups']);
 840          $this->assertEquals(1, count($groupresult['customfields']));
 841          $customfield = reset($groupresult['customfields']);
 842          $this->assertEquals('testgroupcustomfield1', $customfield['shortname']);
 843          $this->assertEquals('Test group value 1', $customfield['value']);
 844      }
 845  
 846      /**
 847       * Test delete_groupings.
 848       */
 849      public function test_delete_groupings() {
 850          global $DB;
 851  
 852          $this->resetAfterTest(true);
 853  
 854          $course = self::getDataGenerator()->create_course();
 855  
 856          $groupingdata1 = array();
 857          $groupingdata1['courseid'] = $course->id;
 858          $groupingdata1['name'] = 'Grouping Test';
 859          $groupingdata1['description'] = 'Grouping Test description';
 860          $groupingdata1['descriptionformat'] = FORMAT_MOODLE;
 861          $groupingdata2 = array();
 862          $groupingdata2['courseid'] = $course->id;
 863          $groupingdata2['name'] = 'Grouping Test';
 864          $groupingdata2['description'] = 'Grouping Test description';
 865          $groupingdata2['descriptionformat'] = FORMAT_MOODLE;
 866          $groupingdata3 = array();
 867          $groupingdata3['courseid'] = $course->id;
 868          $groupingdata3['name'] = 'Grouping Test';
 869          $groupingdata3['description'] = 'Grouping Test description';
 870          $groupingdata3['descriptionformat'] = FORMAT_MOODLE;
 871  
 872          $grouping1 = self::getDataGenerator()->create_grouping($groupingdata1);
 873          $grouping2 = self::getDataGenerator()->create_grouping($groupingdata2);
 874          $grouping3 = self::getDataGenerator()->create_grouping($groupingdata3);
 875  
 876          // Set the required capabilities by the external function.
 877          $context = \context_course::instance($course->id);
 878          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 879          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 880  
 881          // Checks against DB values.
 882          $groupingstotal = $DB->count_records('groupings', array());
 883          $this->assertEquals(3, $groupingstotal);
 884  
 885          // Call the external function.
 886          core_group_external::delete_groupings(array($grouping1->id, $grouping2->id));
 887  
 888          // Checks against DB values.
 889          $groupingstotal = $DB->count_records('groupings', array());
 890          $this->assertEquals(1, $groupingstotal);
 891  
 892          // Call without required capability.
 893          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 894  
 895          $this->expectException(\required_capability_exception::class);
 896          core_group_external::delete_groupings(array($grouping3->id));
 897      }
 898  
 899      /**
 900       * Test get_groups
 901       */
 902      public function test_get_course_user_groups() {
 903          global $DB;
 904  
 905          $this->resetAfterTest(true);
 906  
 907          $student1 = self::getDataGenerator()->create_user();
 908          $student2 = self::getDataGenerator()->create_user();
 909          $teacher = self::getDataGenerator()->create_user();
 910  
 911          $course = self::getDataGenerator()->create_course();
 912          $anothercourse = self::getDataGenerator()->create_course();
 913          $emptycourse = self::getDataGenerator()->create_course();
 914  
 915          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 916          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
 917          $this->getDataGenerator()->enrol_user($student1->id, $anothercourse->id, $studentrole->id);
 918          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
 919  
 920          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 921          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 922          $this->getDataGenerator()->enrol_user($teacher->id, $emptycourse->id, $teacherrole->id);
 923  
 924          $group1data = array();
 925          $group1data['courseid'] = $course->id;
 926          $group1data['name'] = 'Group Test 1';
 927          $group1data['description'] = 'Group Test 1 description';
 928          $group1data['idnumber'] = 'TEST1';
 929          $group2data = array();
 930          $group2data['courseid'] = $course->id;
 931          $group2data['name'] = 'Group Test 2';
 932          $group2data['description'] = 'Group Test 2 description';
 933          $group3data = array();
 934          $group3data['courseid'] = $anothercourse->id;
 935          $group3data['name'] = 'Group Test 3';
 936          $group3data['description'] = 'Group Test 3 description';
 937          $group3data['idnumber'] = 'TEST3';
 938          $group1 = self::getDataGenerator()->create_group($group1data);
 939          $group2 = self::getDataGenerator()->create_group($group2data);
 940          $group3 = self::getDataGenerator()->create_group($group3data);
 941  
 942          groups_add_member($group1->id, $student1->id);
 943          groups_add_member($group1->id, $student2->id);
 944          groups_add_member($group2->id, $student1->id);
 945          groups_add_member($group3->id, $student1->id);
 946  
 947          // Create a grouping.
 948          $groupingdata = array();
 949          $groupingdata['courseid'] = $course->id;
 950          $groupingdata['name'] = 'Grouping Test';
 951          $groupingdata['description'] = 'Grouping Test description';
 952          $groupingdata['descriptionformat'] = FORMAT_MOODLE;
 953  
 954          $grouping = self::getDataGenerator()->create_grouping($groupingdata);
 955          // Grouping only containing group1.
 956          groups_assign_grouping($grouping->id, $group1->id);
 957  
 958          $this->setUser($student1);
 959  
 960          $groups = core_group_external::get_course_user_groups($course->id, $student1->id);
 961          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 962          // Check that I see my groups.
 963          $this->assertCount(2, $groups['groups']);
 964          $this->assertEquals($course->id, $groups['groups'][0]['courseid']);
 965          $this->assertEquals($course->id, $groups['groups'][1]['courseid']);
 966  
 967          // Check that I only see my groups inside the given grouping.
 968          $groups = core_group_external::get_course_user_groups($course->id, $student1->id, $grouping->id);
 969          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 970          // Check that I see my groups in the grouping.
 971          $this->assertCount(1, $groups['groups']);
 972          $this->assertEquals($group1->id, $groups['groups'][0]['id']);
 973  
 974  
 975          // Check optional parameters (all student 1 courses and current user).
 976          $groups = core_group_external::get_course_user_groups();
 977          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 978          // Check that I see my groups in all my courses.
 979          $this->assertCount(3, $groups['groups']);
 980  
 981          $this->setUser($student2);
 982          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
 983          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 984          // Check that I see my groups.
 985          $this->assertCount(1, $groups['groups']);
 986  
 987          $this->assertEquals($group1data['name'], $groups['groups'][0]['name']);
 988          $this->assertEquals($group1data['description'], $groups['groups'][0]['description']);
 989          $this->assertEquals($group1data['idnumber'], $groups['groups'][0]['idnumber']);
 990  
 991          $this->setUser($teacher);
 992          $groups = core_group_external::get_course_user_groups($course->id, $student1->id);
 993          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 994          // Check that a teacher can see student groups in given course.
 995          $this->assertCount(2, $groups['groups']);
 996  
 997          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
 998          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 999          // Check that a teacher can see student groups in given course.
1000          $this->assertCount(1, $groups['groups']);
1001  
1002          $groups = core_group_external::get_course_user_groups(0, $student1->id);
1003          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
1004          // Check that a teacher can see student groups in all the user courses if the teacher is enrolled in the course.
1005          $this->assertCount(2, $groups['groups']); // Teacher only see groups in first course.
1006          $this->assertCount(1, $groups['warnings']); // Enrolment warnings.
1007          $this->assertEquals('1', $groups['warnings'][0]['warningcode']);
1008  
1009          // Enrol teacher in second course.
1010          $this->getDataGenerator()->enrol_user($teacher->id, $anothercourse->id, $teacherrole->id);
1011          $groups = core_group_external::get_course_user_groups(0, $student1->id);
1012          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
1013          // Check that a teacher can see student groups in all the user courses if the teacher is enrolled in the course.
1014          $this->assertCount(3, $groups['groups']);
1015  
1016          // Check permissions.
1017          $this->setUser($student1);
1018  
1019          // Student can's see other students group.
1020          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
1021          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
1022          $this->assertCount(1, $groups['warnings']);
1023          $this->assertEquals('cannotmanagegroups', $groups['warnings'][0]['warningcode']);
1024  
1025          // Not enrolled course.
1026          $groups = core_group_external::get_course_user_groups($emptycourse->id, $student2->id);
1027          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
1028          $this->assertCount(1, $groups['warnings']);
1029          $this->assertEquals('1', $groups['warnings'][0]['warningcode']);
1030  
1031          $this->setUser($teacher);
1032          // Check user checking not enrolled in given course.
1033          $groups = core_group_external::get_course_user_groups($emptycourse->id, $student1->id);
1034          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
1035          $this->assertCount(1, $groups['warnings']);
1036          $this->assertEquals('notenrolled', $groups['warnings'][0]['warningcode']);
1037      }
1038  
1039      /**
1040       * Test get_activity_allowed_groups
1041       */
1042      public function test_get_activity_allowed_groups() {
1043          global $DB;
1044  
1045          $this->resetAfterTest(true);
1046  
1047          $generator = self::getDataGenerator();
1048  
1049          $student = $generator->create_user();
1050          $otherstudent = $generator->create_user();
1051          $teacher = $generator->create_user();
1052          $course = $generator->create_course();
1053          $othercourse = $generator->create_course();
1054  
1055          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1056          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
1057          $generator->enrol_user($student->id, $course->id, $studentrole->id);
1058          $generator->enrol_user($otherstudent->id, $othercourse->id, $studentrole->id);
1059          $generator->enrol_user($teacher->id, $course->id, $teacherrole->id);
1060  
1061          $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS));
1062          $forum2 = $generator->create_module("forum", array('course' => $othercourse->id));
1063          $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0));
1064  
1065          // Request data for tests.
1066          $cm1 = get_coursemodule_from_instance("forum", $forum1->id);
1067          $cm2 = get_coursemodule_from_instance("forum", $forum2->id);
1068          $cm3 = get_coursemodule_from_instance("forum", $forum3->id);
1069  
1070          $group1data = array();
1071          $group1data['courseid'] = $course->id;
1072          $group1data['name'] = 'Group Test 1';
1073          $group1data['description'] = 'Group Test 1 description';
1074          $group1data['idnumber'] = 'TEST1';
1075          $group2data = array();
1076          $group2data['courseid'] = $course->id;
1077          $group2data['name'] = 'Group Test 2';
1078          $group2data['description'] = 'Group Test 2 description';
1079          $group2data['idnumber'] = 'TEST2';
1080          $group1 = $generator->create_group($group1data);
1081          $group2 = $generator->create_group($group2data);
1082  
1083          groups_add_member($group1->id, $student->id);
1084          groups_add_member($group2->id, $student->id);
1085  
1086          $this->setUser($student);
1087  
1088          // First try possible errors.
1089          try {
1090              $data = core_group_external::get_activity_allowed_groups($cm2->id);
1091          } catch (\moodle_exception $e) {
1092              $this->assertEquals('requireloginerror', $e->errorcode);
1093          }
1094  
1095          try {
1096              $data = core_group_external::get_activity_allowed_groups($cm3->id);
1097          } catch (\moodle_exception $e) {
1098              $this->assertEquals('requireloginerror', $e->errorcode);
1099          }
1100  
1101          // Retrieve my groups.
1102          $groups = core_group_external::get_activity_allowed_groups($cm1->id);
1103          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
1104          $this->assertCount(2, $groups['groups']);
1105          $this->assertFalse($groups['canaccessallgroups']);
1106  
1107          foreach ($groups['groups'] as $group) {
1108              if ($group['name'] == $group1data['name']) {
1109                  $this->assertEquals($group1data['description'], $group['description']);
1110                  $this->assertEquals($group1data['idnumber'], $group['idnumber']);
1111              } else {
1112                  $this->assertEquals($group2data['description'], $group['description']);
1113                  $this->assertEquals($group2data['idnumber'], $group['idnumber']);
1114              }
1115          }
1116  
1117          $this->setUser($teacher);
1118          // Retrieve other users groups.
1119          $groups = core_group_external::get_activity_allowed_groups($cm1->id, $student->id);
1120          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
1121          $this->assertCount(2, $groups['groups']);
1122          // We are checking the $student passed as parameter so this will return false.
1123          $this->assertFalse($groups['canaccessallgroups']);
1124  
1125          // Check warnings. Trying to get groups for a user not enrolled in course.
1126          $groups = core_group_external::get_activity_allowed_groups($cm1->id, $otherstudent->id);
1127          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
1128          $this->assertCount(1, $groups['warnings']);
1129          $this->assertFalse($groups['canaccessallgroups']);
1130  
1131          // Checking teacher groups.
1132          $groups = core_group_external::get_activity_allowed_groups($cm1->id);
1133          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
1134          $this->assertCount(2, $groups['groups']);
1135          // Teachers by default can access all groups.
1136          $this->assertTrue($groups['canaccessallgroups']);
1137      }
1138  
1139      /**
1140       * Test get_activity_groupmode
1141       */
1142      public function test_get_activity_groupmode() {
1143          global $DB;
1144  
1145          $this->resetAfterTest(true);
1146  
1147          $generator = self::getDataGenerator();
1148  
1149          $student = $generator->create_user();
1150          $course = $generator->create_course();
1151          $othercourse = $generator->create_course();
1152  
1153          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1154          $generator->enrol_user($student->id, $course->id, $studentrole->id);
1155  
1156          $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS));
1157          $forum2 = $generator->create_module("forum", array('course' => $othercourse->id));
1158          $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0));
1159  
1160          // Request data for tests.
1161          $cm1 = get_coursemodule_from_instance("forum", $forum1->id);
1162          $cm2 = get_coursemodule_from_instance("forum", $forum2->id);
1163          $cm3 = get_coursemodule_from_instance("forum", $forum3->id);
1164  
1165          $this->setUser($student);
1166  
1167          $data = core_group_external::get_activity_groupmode($cm1->id);
1168          $data = external_api::clean_returnvalue(core_group_external::get_activity_groupmode_returns(), $data);
1169          $this->assertEquals(VISIBLEGROUPS, $data['groupmode']);
1170  
1171          try {
1172              $data = core_group_external::get_activity_groupmode($cm2->id);
1173          } catch (\moodle_exception $e) {
1174              $this->assertEquals('requireloginerror', $e->errorcode);
1175          }
1176  
1177          try {
1178              $data = core_group_external::get_activity_groupmode($cm3->id);
1179          } catch (\moodle_exception $e) {
1180              $this->assertEquals('requireloginerror', $e->errorcode);
1181          }
1182  
1183      }
1184  
1185      /**
1186       * Test add_group_members.
1187       */
1188      public function test_add_group_members() {
1189          global $DB;
1190  
1191          $this->resetAfterTest(true);
1192  
1193          $student1 = self::getDataGenerator()->create_user();
1194          $student2 = self::getDataGenerator()->create_user();
1195          $student3 = self::getDataGenerator()->create_user();
1196  
1197          $course = self::getDataGenerator()->create_course();
1198  
1199          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1200          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1201          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1202          $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
1203  
1204          $group1data = array();
1205          $group1data['courseid'] = $course->id;
1206          $group1data['name'] = 'Group Test 1';
1207          $group1data['description'] = 'Group Test 1 description';
1208          $group1data['idnumber'] = 'TEST1';
1209          $group1 = self::getDataGenerator()->create_group($group1data);
1210  
1211          // Checks against DB values.
1212          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1213          $this->assertEquals(0, $memberstotal);
1214  
1215          // Set the required capabilities by the external function.
1216          $context = \context_course::instance($course->id);
1217          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
1218          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
1219  
1220          core_group_external::add_group_members([
1221              'members' => [
1222                  'groupid' => $group1->id,
1223                  'userid' => $student1->id,
1224              ]
1225          ]);
1226          core_group_external::add_group_members([
1227              'members' => [
1228                  'groupid' => $group1->id,
1229                  'userid' => $student2->id,
1230              ]
1231          ]);
1232          core_group_external::add_group_members([
1233              'members' => [
1234                  'groupid' => $group1->id,
1235                  'userid' => $student3->id,
1236              ]
1237          ]);
1238  
1239          // Checks against DB values.
1240          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1241          $this->assertEquals(3, $memberstotal);
1242      }
1243  
1244      /**
1245       * Test delete_group_members.
1246       */
1247      public function test_delete_group_members() {
1248          global $DB;
1249  
1250          $this->resetAfterTest(true);
1251  
1252          $student1 = self::getDataGenerator()->create_user();
1253          $student2 = self::getDataGenerator()->create_user();
1254          $student3 = self::getDataGenerator()->create_user();
1255  
1256          $course = self::getDataGenerator()->create_course();
1257  
1258          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1259          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1260          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1261          $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
1262  
1263          $group1data = array();
1264          $group1data['courseid'] = $course->id;
1265          $group1data['name'] = 'Group Test 1';
1266          $group1data['description'] = 'Group Test 1 description';
1267          $group1data['idnumber'] = 'TEST1';
1268          $group1 = self::getDataGenerator()->create_group($group1data);
1269  
1270          groups_add_member($group1->id, $student1->id);
1271          groups_add_member($group1->id, $student2->id);
1272          groups_add_member($group1->id, $student3->id);
1273  
1274          // Checks against DB values.
1275          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1276          $this->assertEquals(3, $memberstotal);
1277  
1278          // Set the required capabilities by the external function.
1279          $context = \context_course::instance($course->id);
1280          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
1281          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
1282  
1283          core_group_external::delete_group_members([
1284              'members' => [
1285                  'groupid' => $group1->id,
1286                  'userid' => $student2->id,
1287              ]
1288          ]);
1289  
1290          // Checks against DB values.
1291          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1292          $this->assertEquals(2, $memberstotal);
1293      }
1294  }