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]

   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  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once($CFG->dirroot . '/cohort/lib.php');
  29  
  30  /**
  31   * External cohort API
  32   *
  33   * @package    core_cohort
  34   * @category   external
  35   * @copyright  MediaTouch 2000 srl
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class core_cohort_external extends external_api {
  39  
  40      /**
  41       * Returns description of method parameters
  42       *
  43       * @return external_function_parameters
  44       * @since Moodle 2.5
  45       */
  46      public static function create_cohorts_parameters() {
  47          return new external_function_parameters(
  48              array(
  49                  'cohorts' => new external_multiple_structure(
  50                      new external_single_structure(
  51                          array(
  52                              'categorytype' => new external_single_structure(
  53                                  array(
  54                                      'type' => new external_value(PARAM_TEXT, 'the name of the field: id (numeric value
  55                                          of course category id) or idnumber (alphanumeric value of idnumber course category)
  56                                          or system (value ignored)'),
  57                                      'value' => new external_value(PARAM_RAW, 'the value of the categorytype')
  58                                  )
  59                              ),
  60                              'name' => new external_value(PARAM_RAW, 'cohort name'),
  61                              'idnumber' => new external_value(PARAM_RAW, 'cohort idnumber'),
  62                              'description' => new external_value(PARAM_RAW, 'cohort description', VALUE_OPTIONAL),
  63                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
  64                              'visible' => new external_value(PARAM_BOOL, 'cohort visible', VALUE_OPTIONAL, true),
  65                              'theme' => new external_value(PARAM_THEME,
  66                                  'the cohort theme. The allowcohortthemes setting must be enabled on Moodle',
  67                                  VALUE_OPTIONAL
  68                              ),
  69                              'customfields' => self::build_custom_fields_parameters_structure(),
  70                          )
  71                      )
  72                  )
  73              )
  74          );
  75      }
  76  
  77      /**
  78       * Create one or more cohorts
  79       *
  80       * @param array $cohorts An array of cohorts to create.
  81       * @return array An array of arrays
  82       * @since Moodle 2.5
  83       */
  84      public static function create_cohorts($cohorts) {
  85          global $CFG, $DB;
  86          require_once("$CFG->dirroot/cohort/lib.php");
  87  
  88          $params = self::validate_parameters(self::create_cohorts_parameters(), array('cohorts' => $cohorts));
  89  
  90          $availablethemes = cohort_get_list_of_themes();
  91  
  92          $transaction = $DB->start_delegated_transaction();
  93  
  94          $syscontext = context_system::instance();
  95          $cohortids = array();
  96  
  97          foreach ($params['cohorts'] as $cohort) {
  98              $cohort = (object)$cohort;
  99  
 100              // Category type (context id).
 101              $categorytype = $cohort->categorytype;
 102              if (!in_array($categorytype['type'], array('idnumber', 'id', 'system'))) {
 103                  throw new invalid_parameter_exception('category type must be id, idnumber or system:' . $categorytype['type']);
 104              }
 105              if ($categorytype['type'] === 'system') {
 106                  $cohort->contextid = $syscontext->id;
 107              } else if ($catid = $DB->get_field('course_categories', 'id', array($categorytype['type'] => $categorytype['value']))) {
 108                  $catcontext = context_coursecat::instance($catid);
 109                  $cohort->contextid = $catcontext->id;
 110              } else {
 111                  throw new invalid_parameter_exception('category not exists: category '
 112                      .$categorytype['type'].' = '.$categorytype['value']);
 113              }
 114              // Make sure that the idnumber doesn't already exist.
 115              if ($DB->record_exists('cohort', array('idnumber' => $cohort->idnumber))) {
 116                  throw new invalid_parameter_exception('record already exists: idnumber='.$cohort->idnumber);
 117              }
 118              $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 119              if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 120                  throw new invalid_parameter_exception('Invalid context');
 121              }
 122              self::validate_context($context);
 123              require_capability('moodle/cohort:manage', $context);
 124  
 125              // Make sure theme is valid.
 126              if (isset($cohort->theme)) {
 127                  if (!empty($CFG->allowcohortthemes)) {
 128                      if (empty($availablethemes[$cohort->theme])) {
 129                          throw new moodle_exception('errorinvalidparam', 'webservice', '', 'theme');
 130                      }
 131                  }
 132              }
 133  
 134              // Validate format.
 135              $cohort->descriptionformat = util::validate_format($cohort->descriptionformat);
 136  
 137              // Custom fields.
 138              if (!empty($cohort->customfields)) {
 139                  foreach ($cohort->customfields as $field) {
 140                      $fieldname = self::build_custom_field_name($field['shortname']);
 141                      $cohort->{$fieldname} = $field['value'];
 142                  }
 143                  unset($cohort->customfields);
 144              }
 145  
 146              $cohort->id = cohort_add_cohort($cohort);
 147  
 148              list($cohort->description, $cohort->descriptionformat) =
 149                  \core_external\util::format_text($cohort->description, $cohort->descriptionformat,
 150                          $context, 'cohort', 'description', $cohort->id);
 151              $cohortids[] = (array)$cohort;
 152          }
 153          $transaction->allow_commit();
 154  
 155          return $cohortids;
 156      }
 157  
 158      /**
 159       * Returns description of method result value
 160       *
 161       * @return \core_external\external_description
 162       * @since Moodle 2.5
 163       */
 164      public static function create_cohorts_returns() {
 165          return new external_multiple_structure(
 166              new external_single_structure(
 167                  array(
 168                      'id' => new external_value(PARAM_INT, 'cohort id'),
 169                      'name' => new external_value(PARAM_RAW, 'cohort name'),
 170                      'idnumber' => new external_value(PARAM_RAW, 'cohort idnumber'),
 171                      'description' => new external_value(PARAM_RAW, 'cohort description'),
 172                      'descriptionformat' => new external_format_value('description'),
 173                      'visible' => new external_value(PARAM_BOOL, 'cohort visible'),
 174                      'theme' => new external_value(PARAM_THEME, 'cohort theme', VALUE_OPTIONAL),
 175                  )
 176              )
 177          );
 178      }
 179  
 180      /**
 181       * Returns description of method parameters
 182       *
 183       * @return external_function_parameters
 184       * @since Moodle 2.5
 185       */
 186      public static function delete_cohorts_parameters() {
 187          return new external_function_parameters(
 188              array(
 189                  'cohortids' => new external_multiple_structure(new external_value(PARAM_INT, 'cohort ID')),
 190              )
 191          );
 192      }
 193  
 194      /**
 195       * Delete cohorts
 196       *
 197       * @param array $cohortids
 198       * @return null
 199       * @since Moodle 2.5
 200       */
 201      public static function delete_cohorts($cohortids) {
 202          global $CFG, $DB;
 203          require_once("$CFG->dirroot/cohort/lib.php");
 204  
 205          $params = self::validate_parameters(self::delete_cohorts_parameters(), array('cohortids' => $cohortids));
 206  
 207          $transaction = $DB->start_delegated_transaction();
 208  
 209          foreach ($params['cohortids'] as $cohortid) {
 210              // Validate params.
 211              $cohortid = validate_param($cohortid, PARAM_INT);
 212              $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
 213  
 214              // Now security checks.
 215              $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 216              if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 217                  throw new invalid_parameter_exception('Invalid context');
 218              }
 219              self::validate_context($context);
 220              require_capability('moodle/cohort:manage', $context);
 221              cohort_delete_cohort($cohort);
 222          }
 223          $transaction->allow_commit();
 224  
 225          return null;
 226      }
 227  
 228      /**
 229       * Returns description of method result value
 230       *
 231       * @return null
 232       * @since Moodle 2.5
 233       */
 234      public static function delete_cohorts_returns() {
 235          return null;
 236      }
 237  
 238      /**
 239       * Returns description of method parameters
 240       *
 241       * @return external_function_parameters
 242       * @since Moodle 2.5
 243       */
 244      public static function get_cohorts_parameters() {
 245          return new external_function_parameters(
 246              array(
 247                  'cohortids' => new external_multiple_structure(new external_value(PARAM_INT, 'Cohort ID')
 248                      , 'List of cohort id. A cohort id is an integer.', VALUE_DEFAULT, array()),
 249              )
 250          );
 251      }
 252  
 253      /**
 254       * Get cohorts definition specified by ids
 255       *
 256       * @param array $cohortids array of cohort ids
 257       * @return array of cohort objects (id, courseid, name)
 258       * @since Moodle 2.5
 259       */
 260      public static function get_cohorts($cohortids = array()) {
 261          global $DB, $CFG;
 262  
 263          $params = self::validate_parameters(self::get_cohorts_parameters(), array('cohortids' => $cohortids));
 264  
 265          if (empty($cohortids)) {
 266              $cohorts = $DB->get_records('cohort');
 267              if (!empty($cohorts)) {
 268                  $cohortids = array_keys($cohorts);
 269              }
 270          } else {
 271              $cohorts = $DB->get_records_list('cohort', 'id', $params['cohortids']);
 272          }
 273  
 274          $customfieldsdata = self::get_custom_fields_data($cohortids);
 275          $cohortsinfo = array();
 276          foreach ($cohorts as $cohort) {
 277              // Now security checks.
 278              $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 279              if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 280                  throw new invalid_parameter_exception('Invalid context');
 281              }
 282              self::validate_context($context);
 283              if (!has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), $context)) {
 284                  throw new required_capability_exception($context, 'moodle/cohort:view', 'nopermissions', '');
 285              }
 286  
 287              // Only return theme when $CFG->allowcohortthemes is enabled.
 288              if (!empty($cohort->theme) && empty($CFG->allowcohortthemes)) {
 289                  $cohort->theme = null;
 290              }
 291  
 292              list($cohort->description, $cohort->descriptionformat) =
 293                  \core_external\util::format_text($cohort->description, $cohort->descriptionformat,
 294                          $context, 'cohort', 'description', $cohort->id);
 295  
 296              $cohort->customfields = !empty($customfieldsdata[$cohort->id]) ? $customfieldsdata[$cohort->id] : [];
 297              $cohortsinfo[] = (array) $cohort;
 298          }
 299          return $cohortsinfo;
 300      }
 301  
 302  
 303      /**
 304       * Returns description of method result value
 305       *
 306       * @return \core_external\external_description
 307       * @since Moodle 2.5
 308       */
 309      public static function get_cohorts_returns() {
 310          return new external_multiple_structure(
 311              new external_single_structure(
 312                  array(
 313                      'id' => new external_value(PARAM_INT, 'ID of the cohort'),
 314                      'name' => new external_value(PARAM_RAW, 'cohort name'),
 315                      'idnumber' => new external_value(PARAM_RAW, 'cohort idnumber'),
 316                      'description' => new external_value(PARAM_RAW, 'cohort description'),
 317                      'descriptionformat' => new external_format_value('description'),
 318                      'visible' => new external_value(PARAM_BOOL, 'cohort visible'),
 319                      'theme' => new external_value(PARAM_THEME, 'cohort theme', VALUE_OPTIONAL),
 320                      'customfields' => self::build_custom_fields_returns_structure(),
 321                  )
 322              )
 323          );
 324      }
 325  
 326      /**
 327       * Returns the description of external function parameters.
 328       *
 329       * @return external_function_parameters
 330       */
 331      public static function search_cohorts_parameters() {
 332          $query = new external_value(
 333              PARAM_RAW,
 334              'Query string'
 335          );
 336          $includes = new external_value(
 337              PARAM_ALPHA,
 338              'What other contexts to fetch the frameworks from. (all, parents, self)',
 339              VALUE_DEFAULT,
 340              'parents'
 341          );
 342          $limitfrom = new external_value(
 343              PARAM_INT,
 344              'limitfrom we are fetching the records from',
 345              VALUE_DEFAULT,
 346              0
 347          );
 348          $limitnum = new external_value(
 349              PARAM_INT,
 350              'Number of records to fetch',
 351              VALUE_DEFAULT,
 352              25
 353          );
 354          return new external_function_parameters(array(
 355              'query' => $query,
 356              'context' => self::get_context_parameters(),
 357              'includes' => $includes,
 358              'limitfrom' => $limitfrom,
 359              'limitnum' => $limitnum
 360          ));
 361      }
 362  
 363      /**
 364       * Search cohorts.
 365       *
 366       * @param string $query
 367       * @param array $context
 368       * @param string $includes
 369       * @param int $limitfrom
 370       * @param int $limitnum
 371       * @return array
 372       */
 373      public static function search_cohorts($query, $context, $includes = 'parents', $limitfrom = 0, $limitnum = 25) {
 374          global $CFG;
 375          require_once($CFG->dirroot . '/cohort/lib.php');
 376  
 377          $params = self::validate_parameters(self::search_cohorts_parameters(), array(
 378              'query' => $query,
 379              'context' => $context,
 380              'includes' => $includes,
 381              'limitfrom' => $limitfrom,
 382              'limitnum' => $limitnum,
 383          ));
 384          $query = $params['query'];
 385          $includes = $params['includes'];
 386          $context = self::get_context_from_params($params['context']);
 387          $limitfrom = $params['limitfrom'];
 388          $limitnum = $params['limitnum'];
 389  
 390          self::validate_context($context);
 391  
 392          $manager = has_capability('moodle/cohort:manage', $context);
 393          if (!$manager) {
 394              require_capability('moodle/cohort:view', $context);
 395          }
 396  
 397          // TODO Make this more efficient.
 398          if ($includes == 'self') {
 399              $results = cohort_get_cohorts($context->id, $limitfrom, $limitnum, $query);
 400              $results = $results['cohorts'];
 401          } else if ($includes == 'parents') {
 402              $results = cohort_get_cohorts($context->id, $limitfrom, $limitnum, $query);
 403              $results = $results['cohorts'];
 404              if (!$context instanceof context_system) {
 405                  $results = array_merge($results, cohort_get_available_cohorts($context, COHORT_ALL, $limitfrom, $limitnum, $query));
 406              }
 407          } else if ($includes == 'all') {
 408              $results = cohort_get_all_cohorts($limitfrom, $limitnum, $query);
 409              $results = $results['cohorts'];
 410          } else {
 411              throw new coding_exception('Invalid parameter value for \'includes\'.');
 412          }
 413  
 414          $cohorts = array();
 415  
 416          if (!empty($results)) {
 417              $cohortids = array_keys($results);
 418              $customfieldsdata = self::get_custom_fields_data($cohortids);
 419          }
 420  
 421          foreach ($results as $key => $cohort) {
 422              $cohortcontext = context::instance_by_id($cohort->contextid);
 423  
 424              // Only return theme when $CFG->allowcohortthemes is enabled.
 425              if (!empty($cohort->theme) && empty($CFG->allowcohortthemes)) {
 426                  $cohort->theme = null;
 427              }
 428  
 429              if (!isset($cohort->description)) {
 430                  $cohort->description = '';
 431              }
 432              if (!isset($cohort->descriptionformat)) {
 433                  $cohort->descriptionformat = FORMAT_PLAIN;
 434              }
 435  
 436              list($cohort->description, $cohort->descriptionformat) =
 437                  \core_external\util::format_text($cohort->description, $cohort->descriptionformat,
 438                          $cohortcontext, 'cohort', 'description', $cohort->id);
 439  
 440              $cohort->customfields = !empty($customfieldsdata[$cohort->id]) ? $customfieldsdata[$cohort->id] : [];
 441  
 442              $cohorts[$key] = $cohort;
 443          }
 444  
 445          return array('cohorts' => $cohorts);
 446      }
 447  
 448      /**
 449       * Returns description of external function result value.
 450       *
 451       * @return \core_external\external_description
 452       */
 453      public static function search_cohorts_returns() {
 454          return new external_single_structure(array(
 455              'cohorts' => new external_multiple_structure(
 456                  new external_single_structure(array(
 457                      'id' => new external_value(PARAM_INT, 'ID of the cohort'),
 458                      'name' => new external_value(PARAM_RAW, 'cohort name'),
 459                      'idnumber' => new external_value(PARAM_RAW, 'cohort idnumber'),
 460                      'description' => new external_value(PARAM_RAW, 'cohort description'),
 461                      'descriptionformat' => new external_format_value('description'),
 462                      'visible' => new external_value(PARAM_BOOL, 'cohort visible'),
 463                      'theme' => new external_value(PARAM_THEME, 'cohort theme', VALUE_OPTIONAL),
 464                      'customfields' => self::build_custom_fields_returns_structure(),
 465                  ))
 466              )
 467          ));
 468      }
 469  
 470  
 471  
 472      /**
 473       * Returns description of method parameters
 474       *
 475       * @return external_function_parameters
 476       * @since Moodle 2.5
 477       */
 478      public static function update_cohorts_parameters() {
 479          return new external_function_parameters(
 480              array(
 481                  'cohorts' => new external_multiple_structure(
 482                      new external_single_structure(
 483                          array(
 484                              'id' => new external_value(PARAM_INT, 'ID of the cohort'),
 485                              'categorytype' => new external_single_structure(
 486                                  array(
 487                                      'type' => new external_value(PARAM_TEXT, 'the name of the field: id (numeric value
 488                                          of course category id) or idnumber (alphanumeric value of idnumber course category)
 489                                          or system (value ignored)'),
 490                                      'value' => new external_value(PARAM_RAW, 'the value of the categorytype')
 491                                  )
 492                              ),
 493                              'name' => new external_value(PARAM_RAW, 'cohort name'),
 494                              'idnumber' => new external_value(PARAM_RAW, 'cohort idnumber'),
 495                              'description' => new external_value(PARAM_RAW, 'cohort description', VALUE_OPTIONAL),
 496                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
 497                              'visible' => new external_value(PARAM_BOOL, 'cohort visible', VALUE_OPTIONAL),
 498                              'theme' => new external_value(PARAM_THEME,
 499                                  'the cohort theme. The allowcohortthemes setting must be enabled on Moodle',
 500                                  VALUE_OPTIONAL
 501                              ),
 502                              'customfields' => self::build_custom_fields_parameters_structure(),
 503                          )
 504                      )
 505                  )
 506              )
 507          );
 508      }
 509  
 510      /**
 511       * Update cohorts
 512       *
 513       * @param array $cohorts
 514       * @return null
 515       * @since Moodle 2.5
 516       */
 517      public static function update_cohorts($cohorts) {
 518          global $CFG, $DB;
 519          require_once("$CFG->dirroot/cohort/lib.php");
 520  
 521          $params = self::validate_parameters(self::update_cohorts_parameters(), array('cohorts' => $cohorts));
 522  
 523          $availablethemes = cohort_get_list_of_themes();
 524  
 525          $transaction = $DB->start_delegated_transaction();
 526          $syscontext = context_system::instance();
 527  
 528          foreach ($params['cohorts'] as $cohort) {
 529              $cohort = (object) $cohort;
 530  
 531              if (trim($cohort->name) == '') {
 532                  throw new invalid_parameter_exception('Invalid cohort name');
 533              }
 534  
 535              $oldcohort = $DB->get_record('cohort', array('id' => $cohort->id), '*', MUST_EXIST);
 536              $oldcontext = context::instance_by_id($oldcohort->contextid, MUST_EXIST);
 537              require_capability('moodle/cohort:manage', $oldcontext);
 538  
 539              // Category type (context id).
 540              $categorytype = $cohort->categorytype;
 541              if (!in_array($categorytype['type'], array('idnumber', 'id', 'system'))) {
 542                  throw new invalid_parameter_exception('category type must be id, idnumber or system:' . $categorytype['type']);
 543              }
 544              if ($categorytype['type'] === 'system') {
 545                  $cohort->contextid = $syscontext->id;
 546              } else if ($catid = $DB->get_field('course_categories', 'id', array($categorytype['type'] => $categorytype['value']))) {
 547                  $cohort->contextid = $DB->get_field('context', 'id', array('instanceid' => $catid,
 548                      'contextlevel' => CONTEXT_COURSECAT));
 549              } else {
 550                  throw new invalid_parameter_exception('category not exists: category='.$categorytype['value']);
 551              }
 552  
 553              if ($cohort->contextid != $oldcohort->contextid) {
 554                  $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 555                  if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 556                      throw new invalid_parameter_exception('Invalid context');
 557                  }
 558  
 559                  self::validate_context($context);
 560                  require_capability('moodle/cohort:manage', $context);
 561              }
 562  
 563              // Make sure theme is valid.
 564              if (!empty($cohort->theme) && !empty($CFG->allowcohortthemes)) {
 565                  if (empty($availablethemes[$cohort->theme])) {
 566                      $debuginfo = 'The following cohort theme is not installed on this site: '.$cohort->theme;
 567                      throw new moodle_exception('errorinvalidparam', 'webservice', '', 'theme', $debuginfo);
 568                  }
 569              }
 570  
 571              if (!empty($cohort->description)) {
 572                  $cohort->descriptionformat = util::validate_format($cohort->descriptionformat);
 573              }
 574  
 575              // Custom fields.
 576              if (!empty($cohort->customfields)) {
 577                  foreach ($cohort->customfields as $field) {
 578                      $fieldname = self::build_custom_field_name($field['shortname']);
 579                      $cohort->{$fieldname} = $field['value'];
 580                  }
 581                  unset($cohort->customfields);
 582              }
 583  
 584              cohort_update_cohort($cohort);
 585          }
 586  
 587          $transaction->allow_commit();
 588  
 589          return null;
 590      }
 591  
 592      /**
 593       * Returns description of method result value
 594       *
 595       * @return null
 596       * @since Moodle 2.5
 597       */
 598      public static function update_cohorts_returns() {
 599          return null;
 600      }
 601  
 602      /**
 603       * Returns description of method parameters
 604       *
 605       * @return external_function_parameters
 606       * @since Moodle 2.5
 607       */
 608      public static function add_cohort_members_parameters() {
 609          return new external_function_parameters (
 610              array(
 611                  'members' => new external_multiple_structure (
 612                      new external_single_structure (
 613                          array (
 614                              'cohorttype' => new external_single_structure (
 615                                  array(
 616                                      'type' => new external_value(PARAM_ALPHANUMEXT, 'The name of the field: id
 617                                          (numeric value of cohortid) or idnumber (alphanumeric value of idnumber) '),
 618                                      'value' => new external_value(PARAM_RAW, 'The value of the cohort')
 619                                  )
 620                              ),
 621                              'usertype' => new external_single_structure (
 622                                  array(
 623                                      'type' => new external_value(PARAM_ALPHANUMEXT, 'The name of the field: id
 624                                          (numeric value of id) or username (alphanumeric value of username) '),
 625                                      'value' => new external_value(PARAM_RAW, 'The value of the cohort')
 626                                  )
 627                              )
 628                          )
 629                      )
 630                  )
 631              )
 632          );
 633      }
 634  
 635      /**
 636       * Add cohort members
 637       *
 638       * @param array $members of arrays with keys userid, cohortid
 639       * @since Moodle 2.5
 640       */
 641      public static function add_cohort_members($members) {
 642          global $CFG, $DB;
 643          require_once($CFG->dirroot."/cohort/lib.php");
 644  
 645          $params = self::validate_parameters(self::add_cohort_members_parameters(), array('members' => $members));
 646  
 647          $transaction = $DB->start_delegated_transaction();
 648          $warnings = array();
 649          foreach ($params['members'] as $member) {
 650              // Cohort parameters.
 651              $cohorttype = $member['cohorttype'];
 652              $cohortparam = array($cohorttype['type'] => $cohorttype['value']);
 653              // User parameters.
 654              $usertype = $member['usertype'];
 655              $userparam = array($usertype['type'] => $usertype['value']);
 656              try {
 657                  // Check parameters.
 658                  if ($cohorttype['type'] != 'id' && $cohorttype['type'] != 'idnumber') {
 659                      $warning = array();
 660                      $warning['warningcode'] = '1';
 661                      $warning['message'] = 'invalid parameter: cohortype='.$cohorttype['type'];
 662                      $warnings[] = $warning;
 663                      continue;
 664                  }
 665                  if ($usertype['type'] != 'id' && $usertype['type'] != 'username') {
 666                      $warning = array();
 667                      $warning['warningcode'] = '1';
 668                      $warning['message'] = 'invalid parameter: usertype='.$usertype['type'];
 669                      $warnings[] = $warning;
 670                      continue;
 671                  }
 672                  // Extract parameters.
 673                  if (!$cohortid = $DB->get_field('cohort', 'id', $cohortparam)) {
 674                      $warning = array();
 675                      $warning['warningcode'] = '2';
 676                      $warning['message'] = 'cohort '.$cohorttype['type'].'='.$cohorttype['value'].' not exists';
 677                      $warnings[] = $warning;
 678                      continue;
 679                  }
 680                  if (!$userid = $DB->get_field('user', 'id', array_merge($userparam, array('deleted' => 0,
 681                      'mnethostid' => $CFG->mnet_localhost_id)))) {
 682                      $warning = array();
 683                      $warning['warningcode'] = '2';
 684                      $warning['message'] = 'user '.$usertype['type'].'='.$usertype['value'].' not exists';
 685                      $warnings[] = $warning;
 686                      continue;
 687                  }
 688                  if ($DB->record_exists('cohort_members', array('cohortid' => $cohortid, 'userid' => $userid))) {
 689                      $warning = array();
 690                      $warning['warningcode'] = '3';
 691                      $warning['message'] = 'record already exists: cohort('.$cohorttype['type'].'='.$cohorttype['value'].' '.
 692                          $usertype['type'].'='.$usertype['value'].')';
 693                      $warnings[] = $warning;
 694                      continue;
 695                  }
 696                  $cohort = $DB->get_record('cohort', array('id'=>$cohortid), '*', MUST_EXIST);
 697                  $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 698                  if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 699                      $warning = array();
 700                      $warning['warningcode'] = '1';
 701                      $warning['message'] = 'Invalid context: '.$context->contextlevel;
 702                      $warnings[] = $warning;
 703                      continue;
 704                  }
 705                  self::validate_context($context);
 706              } catch (Exception $e) {
 707                  throw new moodle_exception('Error', 'cohort', '', $e->getMessage());
 708              }
 709              if (!has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:assign'), $context)) {
 710                  throw new required_capability_exception($context, 'moodle/cohort:assign', 'nopermissions', '');
 711              }
 712              cohort_add_member($cohortid, $userid);
 713          }
 714          $transaction->allow_commit();
 715          // Return.
 716          $result = array();
 717          $result['warnings'] = $warnings;
 718          return $result;
 719      }
 720  
 721      /**
 722       * Returns description of method result value
 723       *
 724       * @return null
 725       * @since Moodle 2.5
 726       */
 727      public static function add_cohort_members_returns() {
 728          return new external_single_structure(
 729              array(
 730                  'warnings' => new external_warnings()
 731              )
 732          );
 733      }
 734  
 735      /**
 736       * Returns description of method parameters
 737       *
 738       * @return external_function_parameters
 739       * @since Moodle 2.5
 740       */
 741      public static function delete_cohort_members_parameters() {
 742          return new external_function_parameters(
 743              array(
 744                  'members' => new external_multiple_structure(
 745                      new external_single_structure(
 746                          array(
 747                              'cohortid' => new external_value(PARAM_INT, 'cohort record id'),
 748                              'userid' => new external_value(PARAM_INT, 'user id'),
 749                          )
 750                      )
 751                  )
 752              )
 753          );
 754      }
 755  
 756      /**
 757       * Delete cohort members
 758       *
 759       * @param array $members of arrays with keys userid, cohortid
 760       * @since Moodle 2.5
 761       */
 762      public static function delete_cohort_members($members) {
 763          global $CFG, $DB;
 764          require_once("$CFG->dirroot/cohort/lib.php");
 765  
 766          // Validate parameters.
 767          $params = self::validate_parameters(self::delete_cohort_members_parameters(), array('members' => $members));
 768  
 769          $transaction = $DB->start_delegated_transaction();
 770  
 771          foreach ($params['members'] as $member) {
 772              $cohortid = $member['cohortid'];
 773              $userid = $member['userid'];
 774  
 775              $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
 776              $user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0, 'mnethostid' => $CFG->mnet_localhost_id),
 777                  '*', MUST_EXIST);
 778  
 779              // Now security checks.
 780              $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 781              if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 782                  throw new invalid_parameter_exception('Invalid context');
 783              }
 784              self::validate_context($context);
 785              if (!has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:assign'), $context)) {
 786                  throw new required_capability_exception($context, 'moodle/cohort:assign', 'nopermissions', '');
 787              }
 788  
 789              cohort_remove_member($cohort->id, $user->id);
 790          }
 791          $transaction->allow_commit();
 792      }
 793  
 794      /**
 795       * Returns description of method result value
 796       *
 797       * @return null
 798       * @since Moodle 2.5
 799       */
 800      public static function delete_cohort_members_returns() {
 801          return null;
 802      }
 803  
 804      /**
 805       * Returns description of method parameters
 806       *
 807       * @return external_function_parameters
 808       * @since Moodle 2.5
 809       */
 810      public static function get_cohort_members_parameters() {
 811          return new external_function_parameters(
 812              array(
 813                  'cohortids' => new external_multiple_structure(new external_value(PARAM_INT, 'Cohort ID')),
 814              )
 815          );
 816      }
 817  
 818      /**
 819       * Return all members for a cohort
 820       *
 821       * @param array $cohortids array of cohort ids
 822       * @return array with cohort id keys containing arrays of user ids
 823       * @since Moodle 2.5
 824       */
 825      public static function get_cohort_members($cohortids) {
 826          global $DB;
 827          $params = self::validate_parameters(self::get_cohort_members_parameters(), array('cohortids' => $cohortids));
 828  
 829          $members = array();
 830  
 831          foreach ($params['cohortids'] as $cohortid) {
 832              // Validate params.
 833              $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
 834              // Now security checks.
 835              $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 836              if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
 837                  throw new invalid_parameter_exception('Invalid context');
 838              }
 839              self::validate_context($context);
 840              if (!has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), $context)) {
 841                  throw new required_capability_exception($context, 'moodle/cohort:view', 'nopermissions', '');
 842              }
 843  
 844              $cohortmembers = $DB->get_records_sql("SELECT u.id FROM {user} u, {cohort_members} cm
 845                  WHERE u.id = cm.userid AND cm.cohortid = ?
 846                  ORDER BY lastname ASC, firstname ASC", array($cohort->id));
 847              $members[] = array('cohortid' => $cohortid, 'userids' => array_keys($cohortmembers));
 848          }
 849          return $members;
 850      }
 851  
 852      /**
 853       * Returns description of method result value
 854       *
 855       * @return \core_external\external_description
 856       * @since Moodle 2.5
 857       */
 858      public static function get_cohort_members_returns() {
 859          return new external_multiple_structure(
 860              new external_single_structure(
 861                  array(
 862                      'cohortid' => new external_value(PARAM_INT, 'cohort record id'),
 863                      'userids' => new external_multiple_structure(new external_value(PARAM_INT, 'user id')),
 864                  )
 865              )
 866          );
 867      }
 868  
 869      /**
 870       * Builds a structure for custom fields parameters.
 871       *
 872       * @return \core_external\external_multiple_structure
 873       */
 874      protected static function build_custom_fields_parameters_structure(): external_multiple_structure {
 875          return new external_multiple_structure(
 876              new external_single_structure(
 877                  array(
 878                      'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
 879                      'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
 880                  )
 881              ), 'Custom fields for the cohort', VALUE_OPTIONAL
 882          );
 883      }
 884  
 885      /**
 886       * Builds a structure for custom fields returns.
 887       *
 888       * @return \core_external\external_multiple_structure
 889       */
 890      protected static function build_custom_fields_returns_structure(): external_multiple_structure {
 891          return new external_multiple_structure(
 892              new external_single_structure(
 893                  array(
 894                      'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
 895                      'shortname' => new external_value(PARAM_RAW,
 896                          'The shortname of the custom field - to be able to build the field class in the code'),
 897                      'type' => new external_value(PARAM_ALPHANUMEXT,
 898                          'The type of the custom field - text field, checkbox...'),
 899                      'valueraw' => new external_value(PARAM_RAW, 'The raw value of the custom field'),
 900                      'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
 901                  )
 902              ), 'Custom fields', VALUE_OPTIONAL
 903          );
 904      }
 905  
 906      /**
 907       * Returns custom fields data for provided cohorts.
 908       *
 909       * @param array $cohortids a list of cohort IDs to provide data for.
 910       * @return array
 911       */
 912      protected static function get_custom_fields_data(array $cohortids): array {
 913          $result = [];
 914  
 915          $customfieldsdata = cohort_get_custom_fields_data($cohortids);
 916  
 917          foreach ($customfieldsdata as $cohortid => $fieldcontrollers) {
 918              foreach ($fieldcontrollers as $fieldcontroller) {
 919                  $result[$cohortid][] = [
 920                      'type' => $fieldcontroller->get_field()->get('type'),
 921                      'value' => $fieldcontroller->export_value(),
 922                      'valueraw' => $fieldcontroller->get_value(),
 923                      'name' => $fieldcontroller->get_field()->get('name'),
 924                      'shortname' => $fieldcontroller->get_field()->get('shortname'),
 925                  ];
 926              }
 927          }
 928  
 929          return $result;
 930      }
 931  
 932      /**
 933       * Builds a suitable name of a custom field for a custom field handler based on provided shortname.
 934       *
 935       * @param string $shortname shortname to use.
 936       * @return string
 937       */
 938      protected static function build_custom_field_name(string $shortname): string {
 939          return 'customfield_' . $shortname;
 940      }
 941  }