Search moodle.org's
Developer Documentation

   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  
  18  /**
  19   * External course API
  20   *
  21   * @package    core_course
  22   * @category   external
  23   * @copyright  2009 Petr Skodak
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die;
  28  
  29  require_once("$CFG->libdir/externallib.php");
  30  
  31  /**
  32   * Course external functions
  33   *
  34   * @package    core_course
  35   * @category   external
  36   * @copyright  2011 Jerome Mouneyrac
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   * @since Moodle 2.2
  39   */
  40  class core_course_external extends external_api {
  41  
  42      /**
  43       * Returns description of method parameters
  44       *
  45       * @return external_function_parameters
  46       * @since Moodle 2.9 Options available
  47       * @since Moodle 2.2
  48       */
  49      public static function get_course_contents_parameters() {
  50          return new external_function_parameters(
  51                  array('courseid' => new external_value(PARAM_INT, 'course id'),
  52                        'options' => new external_multiple_structure (
  53                                new external_single_structure(
  54                                  array(
  55                                      'name' => new external_value(PARAM_ALPHANUM,
  56                                                  'The expected keys (value format) are:
  57                                                  excludemodules (bool) Do not return modules, return only the sections structure
  58                                                  excludecontents (bool) Do not return module contents (i.e: files inside a resource)
  59                                                  sectionid (int) Return only this section
  60                                                  sectionnumber (int) Return only this section with number (order)
  61                                                  cmid (int) Return only this module information (among the whole sections structure)
  62                                                  modname (string) Return only modules with this name "label, forum, etc..."
  63                                                  modid (int) Return only the module with this id (to be used with modname'),
  64                                      'value' => new external_value(PARAM_RAW, 'the value of the option,
  65                                                                      this param is personaly validated in the external function.')
  66                                )
  67                        ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
  68                  )
  69          );
  70      }
  71  
  72      /**
  73       * Get course contents
  74       *
  75       * @param int $courseid course id
  76       * @param array $options Options for filtering the results, used since Moodle 2.9
  77       * @return array
  78       * @since Moodle 2.9 Options available
  79       * @since Moodle 2.2
  80       */
  81      public static function get_course_contents($courseid, $options = array()) {
  82          global $CFG, $DB;
  83          require_once($CFG->dirroot . "/course/lib.php");
  84  
  85          //validate parameter
  86          $params = self::validate_parameters(self::get_course_contents_parameters(),
  87                          array('courseid' => $courseid, 'options' => $options));
  88  
  89          $filters = array();
  90          if (!empty($params['options'])) {
  91  
  92              foreach ($params['options'] as $option) {
  93                  $name = trim($option['name']);
  94                  // Avoid duplicated options.
  95                  if (!isset($filters[$name])) {
  96                      switch ($name) {
  97                          case 'excludemodules':
  98                          case 'excludecontents':
  99                              $value = clean_param($option['value'], PARAM_BOOL);
 100                              $filters[$name] = $value;
 101                              break;
 102                          case 'sectionid':
 103                          case 'sectionnumber':
 104                          case 'cmid':
 105                          case 'modid':
 106                              $value = clean_param($option['value'], PARAM_INT);
 107                              if (is_numeric($value)) {
 108                                  $filters[$name] = $value;
 109                              } else {
 110                                  throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
 111                              }
 112                              break;
 113                          case 'modname':
 114                              $value = clean_param($option['value'], PARAM_PLUGIN);
 115                              if ($value) {
 116                                  $filters[$name] = $value;
 117                              } else {
 118                                  throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
 119                              }
 120                              break;
 121                          default:
 122                              throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
 123                      }
 124                  }
 125              }
 126          }
 127  
 128          //retrieve the course
 129          $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
 130  
 131          if ($course->id != SITEID) {
 132              // Check course format exist.
 133              if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
 134                  throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null,
 135                                              get_string('courseformatnotfound', 'error', $course->format));
 136              } else {
 137                  require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
 138              }
 139          }
 140  
 141          // now security checks
 142          $context = context_course::instance($course->id, IGNORE_MISSING);
 143          try {
 144              self::validate_context($context);
 145          } catch (Exception $e) {
 146              $exceptionparam = new stdClass();
 147              $exceptionparam->message = $e->getMessage();
 148              $exceptionparam->courseid = $course->id;
 149              throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
 150          }
 151  
 152          $canupdatecourse = has_capability('moodle/course:update', $context);
 153  
 154          //create return value
 155          $coursecontents = array();
 156  
 157          if ($canupdatecourse or $course->visible
 158                  or has_capability('moodle/course:viewhiddencourses', $context)) {
 159  
 160              //retrieve sections
 161              $modinfo = get_fast_modinfo($course);
 162              $sections = $modinfo->get_section_info_all();
 163  
 164              //for each sections (first displayed to last displayed)
 165              $modinfosections = $modinfo->get_sections();
 166              foreach ($sections as $key => $section) {
 167  
 168                  if (!$section->uservisible) {
 169                      continue;
 170                  }
 171  
 172                  // This becomes true when we are filtering and we found the value to filter with.
 173                  $sectionfound = false;
 174  
 175                  // Filter by section id.
 176                  if (!empty($filters['sectionid'])) {
 177                      if ($section->id != $filters['sectionid']) {
 178                          continue;
 179                      } else {
 180                          $sectionfound = true;
 181                      }
 182                  }
 183  
 184                  // Filter by section number. Note that 0 is a valid section number.
 185                  if (isset($filters['sectionnumber'])) {
 186                      if ($key != $filters['sectionnumber']) {
 187                          continue;
 188                      } else {
 189                          $sectionfound = true;
 190                      }
 191                  }
 192  
 193                  // reset $sectioncontents
 194                  $sectionvalues = array();
 195                  $sectionvalues['id'] = $section->id;
 196                  $sectionvalues['name'] = get_section_name($course, $section);
 197                  $sectionvalues['visible'] = $section->visible;
 198  
 199                  $options = (object) array('noclean' => true);
 200                  list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
 201                          external_format_text($section->summary, $section->summaryformat,
 202                                  $context->id, 'course', 'section', $section->id, $options);
 203                  $sectioncontents = array();
 204  
 205                  //for each module of the section
 206                  if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
 207                      foreach ($modinfosections[$section->section] as $cmid) {
 208                          $cm = $modinfo->cms[$cmid];
 209  
 210                          // stop here if the module is not visible to the user
 211                          if (!$cm->uservisible) {
 212                              continue;
 213                          }
 214  
 215                          // This becomes true when we are filtering and we found the value to filter with.
 216                          $modfound = false;
 217  
 218                          // Filter by cmid.
 219                          if (!empty($filters['cmid'])) {
 220                              if ($cmid != $filters['cmid']) {
 221                                  continue;
 222                              } else {
 223                                  $modfound = true;
 224                              }
 225                          }
 226  
 227                          // Filter by module name and id.
 228                          if (!empty($filters['modname'])) {
 229                              if ($cm->modname != $filters['modname']) {
 230                                  continue;
 231                              } else if (!empty($filters['modid'])) {
 232                                  if ($cm->instance != $filters['modid']) {
 233                                      continue;
 234                                  } else {
 235                                      // Note that if we are only filtering by modname we don't break the loop.
 236                                      $modfound = true;
 237                                  }
 238                              }
 239                          }
 240  
 241                          $module = array();
 242  
 243                          $modcontext = context_module::instance($cm->id);
 244  
 245                          //common info (for people being able to see the module or availability dates)
 246                          $module['id'] = $cm->id;
 247                          $module['name'] = external_format_string($cm->name, $modcontext->id);
 248                          $module['instance'] = $cm->instance;
 249                          $module['modname'] = $cm->modname;
 250                          $module['modplural'] = $cm->modplural;
 251                          $module['modicon'] = $cm->get_icon_url()->out(false);
 252                          $module['indent'] = $cm->indent;
 253  
 254                          if (!empty($cm->showdescription) or $cm->modname == 'label') {
 255                              // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
 256                              list($module['description'], $descriptionformat) = external_format_text($cm->content,
 257                                  FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
 258                          }
 259  
 260                          //url of the module
 261                          $url = $cm->url;
 262                          if ($url) { //labels don't have url
 263                              $module['url'] = $url->out(false);
 264                          }
 265  
 266                          $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
 267                                              context_module::instance($cm->id));
 268                          //user that can view hidden module should know about the visibility
 269                          $module['visible'] = $cm->visible;
 270  
 271                          // Availability date (also send to user who can see hidden module).
 272                          if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
 273                              $module['availability'] = $cm->availability;
 274                          }
 275  
 276                          $baseurl = 'webservice/pluginfile.php';
 277  
 278                          //call $modulename_export_contents
 279                          //(each module callback take care about checking the capabilities)
 280  
 281                          require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
 282                          $getcontentfunction = $cm->modname.'_export_contents';
 283                          if (function_exists($getcontentfunction)) {
 284                              if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
 285                                  $module['contents'] = $contents;
 286                              } else {
 287                                  $module['contents'] = array();
 288                              }
 289                          }
 290  
 291                          //assign result to $sectioncontents
 292                          $sectioncontents[] = $module;
 293  
 294                          // If we just did a filtering, break the loop.
 295                          if ($modfound) {
 296                              break;
 297                          }
 298  
 299                      }
 300                  }
 301                  $sectionvalues['modules'] = $sectioncontents;
 302  
 303                  // assign result to $coursecontents
 304                  $coursecontents[] = $sectionvalues;
 305  
 306                  // Break the loop if we are filtering.
 307                  if ($sectionfound) {
 308                      break;
 309                  }
 310              }
 311          }
 312          return $coursecontents;
 313      }
 314  
 315      /**
 316       * Returns description of method result value
 317       *
 318       * @return external_description
 319       * @since Moodle 2.2
 320       */
 321      public static function get_course_contents_returns() {
 322          return new external_multiple_structure(
 323              new external_single_structure(
 324                  array(
 325                      'id' => new external_value(PARAM_INT, 'Section ID'),
 326                      'name' => new external_value(PARAM_TEXT, 'Section name'),
 327                      'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
 328                      'summary' => new external_value(PARAM_RAW, 'Section description'),
 329                      'summaryformat' => new external_format_value('summary'),
 330                      'modules' => new external_multiple_structure(
 331                              new external_single_structure(
 332                                  array(
 333                                      'id' => new external_value(PARAM_INT, 'activity id'),
 334                                      'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
 335                                      'name' => new external_value(PARAM_RAW, 'activity module name'),
 336                                      'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
 337                                      'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
 338                                      'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
 339                                      'modicon' => new external_value(PARAM_URL, 'activity icon url'),
 340                                      'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
 341                                      'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
 342                                      'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
 343                                      'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
 344                                      'contents' => new external_multiple_structure(
 345                                            new external_single_structure(
 346                                                array(
 347                                                    // content info
 348                                                    'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
 349                                                    'filename'=> new external_value(PARAM_FILE, 'filename'),
 350                                                    'filepath'=> new external_value(PARAM_PATH, 'filepath'),
 351                                                    'filesize'=> new external_value(PARAM_INT, 'filesize'),
 352                                                    'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
 353                                                    'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
 354                                                    'timecreated' => new external_value(PARAM_INT, 'Time created'),
 355                                                    'timemodified' => new external_value(PARAM_INT, 'Time modified'),
 356                                                    'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
 357  
 358                                                    // copyright related info
 359                                                    'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
 360                                                    'author' => new external_value(PARAM_TEXT, 'Content owner'),
 361                                                    'license' => new external_value(PARAM_TEXT, 'Content license'),
 362                                                )
 363                                            ), VALUE_DEFAULT, array()
 364                                        )
 365                                  )
 366                              ), 'list of module'
 367                      )
 368                  )
 369              )
 370          );
 371      }
 372  
 373      /**
 374       * Returns description of method parameters
 375       *
 376       * @return external_function_parameters
 377       * @since Moodle 2.3
 378       */
 379      public static function get_courses_parameters() {
 380          return new external_function_parameters(
 381                  array('options' => new external_single_structure(
 382                              array('ids' => new external_multiple_structure(
 383                                          new external_value(PARAM_INT, 'Course id')
 384                                          , 'List of course id. If empty return all courses
 385                                              except front page course.',
 386                                          VALUE_OPTIONAL)
 387                              ), 'options - operator OR is used', VALUE_DEFAULT, array())
 388                  )
 389          );
 390      }
 391  
 392      /**
 393       * Get courses
 394       *
 395       * @param array $options It contains an array (list of ids)
 396       * @return array
 397       * @since Moodle 2.2
 398       */
 399      public static function get_courses($options = array()) {
 400          global $CFG, $DB;
 401          require_once($CFG->dirroot . "/course/lib.php");
 402  
 403          //validate parameter
 404          $params = self::validate_parameters(self::get_courses_parameters(),
 405                          array('options' => $options));
 406  
 407          //retrieve courses
 408          if (!array_key_exists('ids', $params['options'])
 409                  or empty($params['options']['ids'])) {
 410              $courses = $DB->get_records('course');
 411          } else {
 412              $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
 413          }
 414  
 415          //create return value
 416          $coursesinfo = array();
 417          foreach ($courses as $course) {
 418  
 419              // now security checks
 420              $context = context_course::instance($course->id, IGNORE_MISSING);
 421              $courseformatoptions = course_get_format($course)->get_format_options();
 422              try {
 423                  self::validate_context($context);
 424              } catch (Exception $e) {
 425                  $exceptionparam = new stdClass();
 426                  $exceptionparam->message = $e->getMessage();
 427                  $exceptionparam->courseid = $course->id;
 428                  throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
 429              }
 430              require_capability('moodle/course:view', $context);
 431  
 432              $courseinfo = array();
 433              $courseinfo['id'] = $course->id;
 434              $courseinfo['fullname'] = external_format_string($course->fullname, $context->id);
 435              $courseinfo['shortname'] = external_format_string($course->shortname, $context->id);
 436              $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id);
 437              $courseinfo['categoryid'] = $course->category;
 438              list($courseinfo['summary'], $courseinfo['summaryformat']) =
 439                  external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
 440              $courseinfo['format'] = $course->format;
 441              $courseinfo['startdate'] = $course->startdate;
 442              if (array_key_exists('numsections', $courseformatoptions)) {
 443                  // For backward-compartibility
 444                  $courseinfo['numsections'] = $courseformatoptions['numsections'];
 445              }
 446  
 447              //some field should be returned only if the user has update permission
 448              $courseadmin = has_capability('moodle/course:update', $context);
 449              if ($courseadmin) {
 450                  $courseinfo['categorysortorder'] = $course->sortorder;
 451                  $courseinfo['idnumber'] = $course->idnumber;
 452                  $courseinfo['showgrades'] = $course->showgrades;
 453                  $courseinfo['showreports'] = $course->showreports;
 454                  $courseinfo['newsitems'] = $course->newsitems;
 455                  $courseinfo['visible'] = $course->visible;
 456                  $courseinfo['maxbytes'] = $course->maxbytes;
 457                  if (array_key_exists('hiddensections', $courseformatoptions)) {
 458                      // For backward-compartibility
 459                      $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
 460                  }
 461                  $courseinfo['groupmode'] = $course->groupmode;
 462                  $courseinfo['groupmodeforce'] = $course->groupmodeforce;
 463                  $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
 464                  $courseinfo['lang'] = $course->lang;
 465                  $courseinfo['timecreated'] = $course->timecreated;
 466                  $courseinfo['timemodified'] = $course->timemodified;
 467                  $courseinfo['forcetheme'] = $course->theme;
 468                  $courseinfo['enablecompletion'] = $course->enablecompletion;
 469                  $courseinfo['completionnotify'] = $course->completionnotify;
 470                  $courseinfo['courseformatoptions'] = array();
 471                  foreach ($courseformatoptions as $key => $value) {
 472                      $courseinfo['courseformatoptions'][] = array(
 473                          'name' => $key,
 474                          'value' => $value
 475                      );
 476                  }
 477              }
 478  
 479              if ($courseadmin or $course->visible
 480                      or has_capability('moodle/course:viewhiddencourses', $context)) {
 481                  $coursesinfo[] = $courseinfo;
 482              }
 483          }
 484  
 485          return $coursesinfo;
 486      }
 487  
 488      /**
 489       * Returns description of method result value
 490       *
 491       * @return external_description
 492       * @since Moodle 2.2
 493       */
 494      public static function get_courses_returns() {
 495          return new external_multiple_structure(
 496                  new external_single_structure(
 497                          array(
 498                              'id' => new external_value(PARAM_INT, 'course id'),
 499                              'shortname' => new external_value(PARAM_TEXT, 'course short name'),
 500                              'categoryid' => new external_value(PARAM_INT, 'category id'),
 501                              'categorysortorder' => new external_value(PARAM_INT,
 502                                      'sort order into the category', VALUE_OPTIONAL),
 503                              'fullname' => new external_value(PARAM_TEXT, 'full name'),
 504                              'displayname' => new external_value(PARAM_TEXT, 'course display name'),
 505                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
 506                              'summary' => new external_value(PARAM_RAW, 'summary'),
 507                              'summaryformat' => new external_format_value('summary'),
 508                              'format' => new external_value(PARAM_PLUGIN,
 509                                      'course format: weeks, topics, social, site,..'),
 510                              'showgrades' => new external_value(PARAM_INT,
 511                                      '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
 512                              'newsitems' => new external_value(PARAM_INT,
 513                                      'number of recent items appearing on the course page', VALUE_OPTIONAL),
 514                              'startdate' => new external_value(PARAM_INT,
 515                                      'timestamp when the course start'),
 516                              'numsections' => new external_value(PARAM_INT,
 517                                      '(deprecated, use courseformatoptions) number of weeks/topics',
 518                                      VALUE_OPTIONAL),
 519                              'maxbytes' => new external_value(PARAM_INT,
 520                                      'largest size of file that can be uploaded into the course',
 521                                      VALUE_OPTIONAL),
 522                              'showreports' => new external_value(PARAM_INT,
 523                                      'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
 524                              'visible' => new external_value(PARAM_INT,
 525                                      '1: available to student, 0:not available', VALUE_OPTIONAL),
 526                              'hiddensections' => new external_value(PARAM_INT,
 527                                      '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
 528                                      VALUE_OPTIONAL),
 529                              'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
 530                                      VALUE_OPTIONAL),
 531                              'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
 532                                      VALUE_OPTIONAL),
 533                              'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
 534                                      VALUE_OPTIONAL),
 535                              'timecreated' => new external_value(PARAM_INT,
 536                                      'timestamp when the course have been created', VALUE_OPTIONAL),
 537                              'timemodified' => new external_value(PARAM_INT,
 538                                      'timestamp when the course have been modified', VALUE_OPTIONAL),
 539                              'enablecompletion' => new external_value(PARAM_INT,
 540                                      'Enabled, control via completion and activity settings. Disbaled,
 541                                          not shown in activity settings.',
 542                                      VALUE_OPTIONAL),
 543                              'completionnotify' => new external_value(PARAM_INT,
 544                                      '1: yes 0: no', VALUE_OPTIONAL),
 545                              'lang' => new external_value(PARAM_SAFEDIR,
 546                                      'forced course language', VALUE_OPTIONAL),
 547                              'forcetheme' => new external_value(PARAM_PLUGIN,
 548                                      'name of the force theme', VALUE_OPTIONAL),
 549                              'courseformatoptions' => new external_multiple_structure(
 550                                  new external_single_structure(
 551                                      array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
 552                                          'value' => new external_value(PARAM_RAW, 'course format option value')
 553                                  )),
 554                                      'additional options for particular course format', VALUE_OPTIONAL
 555                               ),
 556                          ), 'course'
 557                  )
 558          );
 559      }
 560  
 561      /**
 562       * Returns description of method parameters
 563       *
 564       * @return external_function_parameters
 565       * @since Moodle 2.2
 566       */
 567      public static function create_courses_parameters() {
 568          $courseconfig = get_config('moodlecourse'); //needed for many default values
 569          return new external_function_parameters(
 570              array(
 571                  'courses' => new external_multiple_structure(
 572                      new external_single_structure(
 573                          array(
 574                              'fullname' => new external_value(PARAM_TEXT, 'full name'),
 575                              'shortname' => new external_value(PARAM_TEXT, 'course short name'),
 576                              'categoryid' => new external_value(PARAM_INT, 'category id'),
 577                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
 578                              'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
 579                              'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
 580                              'format' => new external_value(PARAM_PLUGIN,
 581                                      'course format: weeks, topics, social, site,..',
 582                                      VALUE_DEFAULT, $courseconfig->format),
 583                              'showgrades' => new external_value(PARAM_INT,
 584                                      '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
 585                                      $courseconfig->showgrades),
 586                              'newsitems' => new external_value(PARAM_INT,
 587                                      'number of recent items appearing on the course page',
 588                                      VALUE_DEFAULT, $courseconfig->newsitems),
 589                              'startdate' => new external_value(PARAM_INT,
 590                                      'timestamp when the course start', VALUE_OPTIONAL),
 591                              'numsections' => new external_value(PARAM_INT,
 592                                      '(deprecated, use courseformatoptions) number of weeks/topics',
 593                                      VALUE_OPTIONAL),
 594                              'maxbytes' => new external_value(PARAM_INT,
 595                                      'largest size of file that can be uploaded into the course',
 596                                      VALUE_DEFAULT, $courseconfig->maxbytes),
 597                              'showreports' => new external_value(PARAM_INT,
 598                                      'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
 599                                      $courseconfig->showreports),
 600                              'visible' => new external_value(PARAM_INT,
 601                                      '1: available to student, 0:not available', VALUE_OPTIONAL),
 602                              'hiddensections' => new external_value(PARAM_INT,
 603                                      '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
 604                                      VALUE_OPTIONAL),
 605                              'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
 606                                      VALUE_DEFAULT, $courseconfig->groupmode),
 607                              'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
 608                                      VALUE_DEFAULT, $courseconfig->groupmodeforce),
 609                              'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
 610                                      VALUE_DEFAULT, 0),
 611                              'enablecompletion' => new external_value(PARAM_INT,
 612                                      'Enabled, control via completion and activity settings. Disabled,
 613                                          not shown in activity settings.',
 614                                      VALUE_OPTIONAL),
 615                              'completionnotify' => new external_value(PARAM_INT,
 616                                      '1: yes 0: no', VALUE_OPTIONAL),
 617                              'lang' => new external_value(PARAM_SAFEDIR,
 618                                      'forced course language', VALUE_OPTIONAL),
 619                              'forcetheme' => new external_value(PARAM_PLUGIN,
 620                                      'name of the force theme', VALUE_OPTIONAL),
 621                              'courseformatoptions' => new external_multiple_structure(
 622                                  new external_single_structure(
 623                                      array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
 624                                          'value' => new external_value(PARAM_RAW, 'course format option value')
 625                                  )),
 626                                      'additional options for particular course format', VALUE_OPTIONAL),
 627                          )
 628                      ), 'courses to create'
 629                  )
 630              )
 631          );
 632      }
 633  
 634      /**
 635       * Create  courses
 636       *
 637       * @param array $courses
 638       * @return array courses (id and shortname only)
 639       * @since Moodle 2.2
 640       */
 641      public static function create_courses($courses) {
 642          global $CFG, $DB;
 643          require_once($CFG->dirroot . "/course/lib.php");
 644          require_once($CFG->libdir . '/completionlib.php');
 645  
 646          $params = self::validate_parameters(self::create_courses_parameters(),
 647                          array('courses' => $courses));
 648  
 649          $availablethemes = core_component::get_plugin_list('theme');
 650          $availablelangs = get_string_manager()->get_list_of_translations();
 651  
 652          $transaction = $DB->start_delegated_transaction();
 653  
 654          foreach ($params['courses'] as $course) {
 655  
 656              // Ensure the current user is allowed to run this function
 657              $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
 658              try {
 659                  self::validate_context($context);
 660              } catch (Exception $e) {
 661                  $exceptionparam = new stdClass();
 662                  $exceptionparam->message = $e->getMessage();
 663                  $exceptionparam->catid = $course['categoryid'];
 664                  throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
 665              }
 666              require_capability('moodle/course:create', $context);
 667  
 668              // Make sure lang is valid
 669              if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
 670                  throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
 671              }
 672  
 673              // Make sure theme is valid
 674              if (array_key_exists('forcetheme', $course)) {
 675                  if (!empty($CFG->allowcoursethemes)) {
 676                      if (empty($availablethemes[$course['forcetheme']])) {
 677                          throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
 678                      } else {
 679                          $course['theme'] = $course['forcetheme'];
 680                      }
 681                  }
 682              }
 683  
 684              //force visibility if ws user doesn't have the permission to set it
 685              $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
 686              if (!has_capability('moodle/course:visibility', $context)) {
 687                  $course['visible'] = $category->visible;
 688              }
 689  
 690              //set default value for completion
 691              $courseconfig = get_config('moodlecourse');
 692              if (completion_info::is_enabled_for_site()) {
 693                  if (!array_key_exists('enablecompletion', $course)) {
 694                      $course['enablecompletion'] = $courseconfig->enablecompletion;
 695                  }
 696              } else {
 697                  $course['enablecompletion'] = 0;
 698              }
 699  
 700              $course['category'] = $course['categoryid'];
 701  
 702              // Summary format.
 703              $course['summaryformat'] = external_validate_format($course['summaryformat']);
 704  
 705              if (!empty($course['courseformatoptions'])) {
 706                  foreach ($course['courseformatoptions'] as $option) {
 707                      $course[$option['name']] = $option['value'];
 708                  }
 709              }
 710  
 711              //Note: create_course() core function check shortname, idnumber, category
 712              $course['id'] = create_course((object) $course)->id;
 713  
 714              $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
 715          }
 716  
 717          $transaction->allow_commit();
 718  
 719          return $resultcourses;
 720      }
 721  
 722      /**
 723       * Returns description of method result value
 724       *
 725       * @return external_description
 726       * @since Moodle 2.2
 727       */
 728      public static function create_courses_returns() {
 729          return new external_multiple_structure(
 730              new external_single_structure(
 731                  array(
 732                      'id'       => new external_value(PARAM_INT, 'course id'),
 733                      'shortname' => new external_value(PARAM_TEXT, 'short name'),
 734                  )
 735              )
 736          );
 737      }
 738  
 739      /**
 740       * Update courses
 741       *
 742       * @return external_function_parameters
 743       * @since Moodle 2.5
 744       */
 745      public static function update_courses_parameters() {
 746          return new external_function_parameters(
 747              array(
 748                  'courses' => new external_multiple_structure(
 749                      new external_single_structure(
 750                          array(
 751                              'id' => new external_value(PARAM_INT, 'ID of the course'),
 752                              'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
 753                              'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
 754                              'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
 755                              'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
 756                              'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
 757                              'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
 758                              'format' => new external_value(PARAM_PLUGIN,
 759                                      'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
 760                              'showgrades' => new external_value(PARAM_INT,
 761                                      '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
 762                              'newsitems' => new external_value(PARAM_INT,
 763                                      'number of recent items appearing on the course page', VALUE_OPTIONAL),
 764                              'startdate' => new external_value(PARAM_INT,
 765                                      'timestamp when the course start', VALUE_OPTIONAL),
 766                              'numsections' => new external_value(PARAM_INT,
 767                                      '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
 768                              'maxbytes' => new external_value(PARAM_INT,
 769                                      'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
 770                              'showreports' => new external_value(PARAM_INT,
 771                                      'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
 772                              'visible' => new external_value(PARAM_INT,
 773                                      '1: available to student, 0:not available', VALUE_OPTIONAL),
 774                              'hiddensections' => new external_value(PARAM_INT,
 775                                      '(deprecated, use courseformatoptions) How the hidden sections in the course are
 776                                          displayed to students', VALUE_OPTIONAL),
 777                              'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
 778                              'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
 779                              'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
 780                              'enablecompletion' => new external_value(PARAM_INT,
 781                                      'Enabled, control via completion and activity settings. Disabled,
 782                                          not shown in activity settings.', VALUE_OPTIONAL),
 783                              'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
 784                              'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
 785                              'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
 786                              'courseformatoptions' => new external_multiple_structure(
 787                                  new external_single_structure(
 788                                      array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
 789                                          'value' => new external_value(PARAM_RAW, 'course format option value')
 790                                  )),
 791                                      'additional options for particular course format', VALUE_OPTIONAL),
 792                          )
 793                      ), 'courses to update'
 794                  )
 795              )
 796          );
 797      }
 798  
 799      /**
 800       * Update courses
 801       *
 802       * @param array $courses
 803       * @since Moodle 2.5
 804       */
 805      public static function update_courses($courses) {
 806          global $CFG, $DB;
 807          require_once($CFG->dirroot . "/course/lib.php");
 808          $warnings = array();
 809  
 810          $params = self::validate_parameters(self::update_courses_parameters(),
 811                          array('courses' => $courses));
 812  
 813          $availablethemes = core_component::get_plugin_list('theme');
 814          $availablelangs = get_string_manager()->get_list_of_translations();
 815  
 816          foreach ($params['courses'] as $course) {
 817              // Catch any exception while updating course and return as warning to user.
 818              try {
 819                  // Ensure the current user is allowed to run this function.
 820                  $context = context_course::instance($course['id'], MUST_EXIST);
 821                  self::validate_context($context);
 822  
 823                  $oldcourse = course_get_format($course['id'])->get_course();
 824  
 825                  require_capability('moodle/course:update', $context);
 826  
 827                  // Check if user can change category.
 828                  if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
 829                      require_capability('moodle/course:changecategory', $context);
 830                      $course['category'] = $course['categoryid'];
 831                  }
 832  
 833                  // Check if the user can change fullname.
 834                  if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
 835                      require_capability('moodle/course:changefullname', $context);
 836                  }
 837  
 838                  // Check if the user can change shortname.
 839                  if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
 840                      require_capability('moodle/course:changeshortname', $context);
 841                  }
 842  
 843                  // Check if the user can change the idnumber.
 844                  if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
 845                      require_capability('moodle/course:changeidnumber', $context);
 846                  }
 847  
 848                  // Check if user can change summary.
 849                  if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
 850                      require_capability('moodle/course:changesummary', $context);
 851                  }
 852  
 853                  // Summary format.
 854                  if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
 855                      require_capability('moodle/course:changesummary', $context);
 856                      $course['summaryformat'] = external_validate_format($course['summaryformat']);
 857                  }
 858  
 859                  // Check if user can change visibility.
 860                  if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
 861                      require_capability('moodle/course:visibility', $context);
 862                  }
 863  
 864                  // Make sure lang is valid.
 865                  if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
 866                      throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
 867                  }
 868  
 869                  // Make sure theme is valid.
 870                  if (array_key_exists('forcetheme', $course)) {
 871                      if (!empty($CFG->allowcoursethemes)) {
 872                          if (empty($availablethemes[$course['forcetheme']])) {
 873                              throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
 874                          } else {
 875                              $course['theme'] = $course['forcetheme'];
 876                          }
 877                      }
 878                  }
 879  
 880                  // Make sure completion is enabled before setting it.
 881                  if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
 882                      $course['enabledcompletion'] = 0;
 883                  }
 884  
 885                  // Make sure maxbytes are less then CFG->maxbytes.
 886                  if (array_key_exists('maxbytes', $course)) {
 887                      $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
 888                  }
 889  
 890                  if (!empty($course['courseformatoptions'])) {
 891                      foreach ($course['courseformatoptions'] as $option) {
 892                          if (isset($option['name']) && isset($option['value'])) {
 893                              $course[$option['name']] = $option['value'];
 894                          }
 895                      }
 896                  }
 897  
 898                  // Update course if user has all required capabilities.
 899                  update_course((object) $course);
 900              } catch (Exception $e) {
 901                  $warning = array();
 902                  $warning['item'] = 'course';
 903                  $warning['itemid'] = $course['id'];
 904                  if ($e instanceof moodle_exception) {
 905                      $warning['warningcode'] = $e->errorcode;
 906                  } else {
 907                      $warning['warningcode'] = $e->getCode();
 908                  }
 909                  $warning['message'] = $e->getMessage();
 910                  $warnings[] = $warning;
 911              }
 912          }
 913  
 914          $result = array();
 915          $result['warnings'] = $warnings;
 916          return $result;
 917      }
 918  
 919      /**
 920       * Returns description of method result value
 921       *
 922       * @return external_description
 923       * @since Moodle 2.5
 924       */
 925      public static function update_courses_returns() {
 926          return new external_single_structure(
 927              array(
 928                  'warnings' => new external_warnings()
 929              )
 930          );
 931      }
 932  
 933      /**
 934       * Returns description of method parameters
 935       *
 936       * @return external_function_parameters
 937       * @since Moodle 2.2
 938       */
 939      public static function delete_courses_parameters() {
 940          return new external_function_parameters(
 941              array(
 942                  'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
 943              )
 944          );
 945      }
 946  
 947      /**
 948       * Delete courses
 949       *
 950       * @param array $courseids A list of course ids
 951       * @since Moodle 2.2
 952       */
 953      public static function delete_courses($courseids) {
 954          global $CFG, $DB;
 955          require_once($CFG->dirroot."/course/lib.php");
 956  
 957          // Parameter validation.
 958          $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
 959  
 960          $warnings = array();
 961  
 962          foreach ($params['courseids'] as $courseid) {
 963              $course = $DB->get_record('course', array('id' => $courseid));
 964  
 965              if ($course === false) {
 966                  $warnings[] = array(
 967                                  'item' => 'course',
 968                                  'itemid' => $courseid,
 969                                  'warningcode' => 'unknowncourseidnumber',
 970                                  'message' => 'Unknown course ID ' . $courseid
 971                              );
 972                  continue;
 973              }
 974  
 975              // Check if the context is valid.
 976              $coursecontext = context_course::instance($course->id);
 977              self::validate_context($coursecontext);
 978  
 979              // Check if the current user has permission.
 980              if (!can_delete_course($courseid)) {
 981                  $warnings[] = array(
 982                                  'item' => 'course',
 983                                  'itemid' => $courseid,
 984                                  'warningcode' => 'cannotdeletecourse',
 985                                  'message' => 'You do not have the permission to delete this course' . $courseid
 986                              );
 987                  continue;
 988              }
 989  
 990              if (delete_course($course, false) === false) {
 991                  $warnings[] = array(
 992                                  'item' => 'course',
 993                                  'itemid' => $courseid,
 994                                  'warningcode' => 'cannotdeletecategorycourse',
 995                                  'message' => 'Course ' . $courseid . ' failed to be deleted'
 996                              );
 997                  continue;
 998              }
 999          }
1000  
1001          fix_course_sortorder();
1002  
1003          return array('warnings' => $warnings);
1004      }
1005  
1006      /**
1007       * Returns description of method result value
1008       *
1009       * @return external_description
1010       * @since Moodle 2.2
1011       */
1012      public static function delete_courses_returns() {
1013          return new external_single_structure(
1014              array(
1015                  'warnings' => new external_warnings()
1016              )
1017          );
1018      }
1019  
1020      /**
1021       * Returns description of method parameters
1022       *
1023       * @return external_function_parameters
1024       * @since Moodle 2.3
1025       */
1026      public static function duplicate_course_parameters() {
1027          return new external_function_parameters(
1028              array(
1029                  'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1030                  'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1031                  'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1032                  'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1033                  'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1034                  'options' => new external_multiple_structure(
1035                      new external_single_structure(
1036                          array(
1037                                  'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
1038                                              "activities" (int) Include course activites (default to 1 that is equal to yes),
1039                                              "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1040                                              "filters" (int) Include course filters  (default to 1 that is equal to yes),
1041                                              "users" (int) Include users (default to 0 that is equal to no),
1042                                              "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
1043                                              "comments" (int) Include user comments  (default to 0 that is equal to no),
1044                                              "userscompletion" (int) Include user course completion information  (default to 0 that is equal to no),
1045                                              "logs" (int) Include course logs  (default to 0 that is equal to no),
1046                                              "grade_histories" (int) Include histories  (default to 0 that is equal to no)'
1047                                              ),
1048                                  'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1049                              )
1050                          )
1051                      ), VALUE_DEFAULT, array()
1052                  ),
1053              )
1054          );
1055      }
1056  
1057      /**
1058       * Duplicate a course
1059       *
1060       * @param int $courseid
1061       * @param string $fullname Duplicated course fullname
1062       * @param string $shortname Duplicated course shortname
1063       * @param int $categoryid Duplicated course parent category id
1064       * @param int $visible Duplicated course availability
1065       * @param array $options List of backup options
1066       * @return array New course info
1067       * @since Moodle 2.3
1068       */
1069      public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
1070          global $CFG, $USER, $DB;
1071          require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1072          require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1073  
1074          // Parameter validation.
1075          $params = self::validate_parameters(
1076                  self::duplicate_course_parameters(),
1077                  array(
1078                        'courseid' => $courseid,
1079                        'fullname' => $fullname,
1080                        'shortname' => $shortname,
1081                        'categoryid' => $categoryid,
1082                        'visible' => $visible,
1083                        'options' => $options
1084                  )
1085          );
1086  
1087          // Context validation.
1088  
1089          if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
1090              throw new moodle_exception('invalidcourseid', 'error');
1091          }
1092  
1093          // Category where duplicated course is going to be created.
1094          $categorycontext = context_coursecat::instance($params['categoryid']);
1095          self::validate_context($categorycontext);
1096  
1097          // Course to be duplicated.
1098          $coursecontext = context_course::instance($course->id);
1099          self::validate_context($coursecontext);
1100  
1101          $backupdefaults = array(
1102              'activities' => 1,
1103              'blocks' => 1,
1104              'filters' => 1,
1105              'users' => 0,
1106              'role_assignments' => 0,
1107              'comments' => 0,
1108              'userscompletion' => 0,
1109              'logs' => 0,
1110              'grade_histories' => 0
1111          );
1112  
1113          $backupsettings = array();
1114          // Check for backup and restore options.
1115          if (!empty($params['options'])) {
1116              foreach ($params['options'] as $option) {
1117  
1118                  // Strict check for a correct value (allways 1 or 0, true or false).
1119                  $value = clean_param($option['value'], PARAM_INT);
1120  
1121                  if ($value !== 0 and $value !== 1) {
1122                      throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1123                  }
1124  
1125                  if (!isset($backupdefaults[$option['name']])) {
1126                      throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1127                  }
1128  
1129                  $backupsettings[$option['name']] = $value;
1130              }
1131          }
1132  
1133          // Capability checking.
1134  
1135          // The backup controller check for this currently, this may be redundant.
1136          require_capability('moodle/course:create', $categorycontext);
1137          require_capability('moodle/restore:restorecourse', $categorycontext);
1138          require_capability('moodle/backup:backupcourse', $coursecontext);
1139  
1140          if (!empty($backupsettings['users'])) {
1141              require_capability('moodle/backup:userinfo', $coursecontext);
1142              require_capability('moodle/restore:userinfo', $categorycontext);
1143          }
1144  
1145          // Check if the shortname is used.
1146          if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1147              foreach ($foundcourses as $foundcourse) {
1148                  $foundcoursenames[] = $foundcourse->fullname;
1149              }
1150  
1151              $foundcoursenamestring = implode(',', $foundcoursenames);
1152              throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1153          }
1154  
1155          // Backup the course.
1156  
1157          $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1158          backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1159  
1160          foreach ($backupsettings as $name => $value) {
1161              $bc->get_plan()->get_setting($name)->set_value($value);
1162          }
1163  
1164          $backupid       = $bc->get_backupid();
1165          $backupbasepath = $bc->get_plan()->get_basepath();
1166  
1167          $bc->execute_plan();
1168          $results = $bc->get_results();
1169          $file = $results['backup_destination'];
1170  
1171          $bc->destroy();
1172  
1173          // Restore the backup immediately.
1174  
1175          // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1176          if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1177              $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1178          }
1179  
1180          // Create new course.
1181          $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1182  
1183          $rc = new restore_controller($backupid, $newcourseid,
1184                  backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1185  
1186          foreach ($backupsettings as $name => $value) {
1187              $setting = $rc->get_plan()->get_setting($name);
1188              if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1189                  $setting->set_value($value);
1190              }
1191          }
1192  
1193          if (!$rc->execute_precheck()) {
1194              $precheckresults = $rc->get_precheck_results();
1195              if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1196                  if (empty($CFG->keeptempdirectoriesonbackup)) {
1197                      fulldelete($backupbasepath);
1198                  }
1199  
1200                  $errorinfo = '';
1201  
1202                  foreach ($precheckresults['errors'] as $error) {
1203                      $errorinfo .= $error;
1204                  }
1205  
1206                  if (array_key_exists('warnings', $precheckresults)) {
1207                      foreach ($precheckresults['warnings'] as $warning) {
1208                          $errorinfo .= $warning;
1209                      }
1210                  }
1211  
1212                  throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1213              }
1214          }
1215  
1216          $rc->execute_plan();
1217          $rc->destroy();
1218  
1219          $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1220          $course->fullname = $params['fullname'];
1221          $course->shortname = $params['shortname'];
1222          $course->visible = $params['visible'];
1223  
1224          // Set shortname and fullname back.
1225          $DB->update_record('course', $course);
1226  
1227          if (empty($CFG->keeptempdirectoriesonbackup)) {
1228              fulldelete($backupbasepath);
1229          }
1230  
1231          // Delete the course backup file created by this WebService. Originally located in the course backups area.
1232          $file->delete();
1233  
1234          return array('id' => $course->id, 'shortname' => $course->shortname);
1235      }
1236  
1237      /**
1238       * Returns description of method result value
1239       *
1240       * @return external_description
1241       * @since Moodle 2.3
1242       */
1243      public static function duplicate_course_returns() {
1244          return new external_single_structure(
1245              array(
1246                  'id'       => new external_value(PARAM_INT, 'course id'),
1247                  'shortname' => new external_value(PARAM_TEXT, 'short name'),
1248              )
1249          );
1250      }
1251  
1252      /**
1253       * Returns description of method parameters for import_course
1254       *
1255       * @return external_function_parameters
1256       * @since Moodle 2.4
1257       */
1258      public static function import_course_parameters() {
1259          return new external_function_parameters(
1260              array(
1261                  'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1262                  'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1263                  'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1264                  'options' => new external_multiple_structure(
1265                      new external_single_structure(
1266                          array(
1267                                  'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1268                                              "activities" (int) Include course activites (default to 1 that is equal to yes),
1269                                              "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1270                                              "filters" (int) Include course filters  (default to 1 that is equal to yes)'
1271                                              ),
1272                                  'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1273                              )
1274                          )
1275                      ), VALUE_DEFAULT, array()
1276                  ),
1277              )
1278          );
1279      }
1280  
1281      /**
1282       * Imports a course
1283       *
1284       * @param int $importfrom The id of the course we are importing from
1285       * @param int $importto The id of the course we are importing to
1286       * @param bool $deletecontent Whether to delete the course we are importing to content
1287       * @param array $options List of backup options
1288       * @return null
1289       * @since Moodle 2.4
1290       */
1291      public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1292          global $CFG, $USER, $DB;
1293          require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1294          require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1295  
1296          // Parameter validation.
1297          $params = self::validate_parameters(
1298              self::import_course_parameters(),
1299              array(
1300                  'importfrom' => $importfrom,
1301                  'importto' => $importto,
1302                  'deletecontent' => $deletecontent,
1303                  'options' => $options
1304              )
1305          );
1306  
1307          if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1308              throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1309          }
1310  
1311          // Context validation.
1312  
1313          if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1314              throw new moodle_exception('invalidcourseid', 'error');
1315          }
1316  
1317          if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1318              throw new moodle_exception('invalidcourseid', 'error');
1319          }
1320  
1321          $importfromcontext = context_course::instance($importfrom->id);
1322          self::validate_context($importfromcontext);
1323  
1324          $importtocontext = context_course::instance($importto->id);
1325          self::validate_context($importtocontext);
1326  
1327          $backupdefaults = array(
1328              'activities' => 1,
1329              'blocks' => 1,
1330              'filters' => 1
1331          );
1332  
1333          $backupsettings = array();
1334  
1335          // Check for backup and restore options.
1336          if (!empty($params['options'])) {
1337              foreach ($params['options'] as $option) {
1338  
1339                  // Strict check for a correct value (allways 1 or 0, true or false).
1340                  $value = clean_param($option['value'], PARAM_INT);
1341  
1342                  if ($value !== 0 and $value !== 1) {
1343                      throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1344                  }
1345  
1346                  if (!isset($backupdefaults[$option['name']])) {
1347                      throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1348                  }
1349  
1350                  $backupsettings[$option['name']] = $value;
1351              }
1352          }
1353  
1354          // Capability checking.
1355  
1356          require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1357          require_capability('moodle/restore:restoretargetimport', $importtocontext);
1358  
1359          $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1360                  backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1361  
1362          foreach ($backupsettings as $name => $value) {
1363              $bc->get_plan()->get_setting($name)->set_value($value);
1364          }
1365  
1366          $backupid       = $bc->get_backupid();
1367          $backupbasepath = $bc->get_plan()->get_basepath();
1368  
1369          $bc->execute_plan();
1370          $bc->destroy();
1371  
1372          // Restore the backup immediately.
1373  
1374          // Check if we must delete the contents of the destination course.
1375          if ($params['deletecontent']) {
1376              $restoretarget = backup::TARGET_EXISTING_DELETING;
1377          } else {
1378              $restoretarget = backup::TARGET_EXISTING_ADDING;
1379          }
1380  
1381          $rc = new restore_controller($backupid, $importto->id,
1382                  backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1383  
1384          foreach ($backupsettings as $name => $value) {
1385              $rc->get_plan()->get_setting($name)->set_value($value);
1386          }
1387  
1388          if (!$rc->execute_precheck()) {
1389              $precheckresults = $rc->get_precheck_results();
1390              if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1391                  if (empty($CFG->keeptempdirectoriesonbackup)) {
1392                      fulldelete($backupbasepath);
1393                  }
1394  
1395                  $errorinfo = '';
1396  
1397                  foreach ($precheckresults['errors'] as $error) {
1398                      $errorinfo .= $error;
1399                  }
1400  
1401                  if (array_key_exists('warnings', $precheckresults)) {
1402                      foreach ($precheckresults['warnings'] as $warning) {
1403                          $errorinfo .= $warning;
1404                      }
1405                  }
1406  
1407                  throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1408              }
1409          } else {
1410              if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1411                  restore_dbops::delete_course_content($importto->id);
1412              }
1413          }
1414  
1415          $rc->execute_plan();
1416          $rc->destroy();
1417  
1418          if (empty($CFG->keeptempdirectoriesonbackup)) {
1419              fulldelete($backupbasepath);
1420          }
1421  
1422          return null;
1423      }
1424  
1425      /**
1426       * Returns description of method result value
1427       *
1428       * @return external_description
1429       * @since Moodle 2.4
1430       */
1431      public static function import_course_returns() {
1432          return null;
1433      }
1434  
1435      /**
1436       * Returns description of method parameters
1437       *
1438       * @return external_function_parameters
1439       * @since Moodle 2.3
1440       */
1441      public static function get_categories_parameters() {
1442          return new external_function_parameters(
1443              array(
1444                  'criteria' => new external_multiple_structure(
1445                      new external_single_structure(
1446                          array(
1447                              'key' => new external_value(PARAM_ALPHA,
1448                                           'The category column to search, expected keys (value format) are:'.
1449                                           '"id" (int) the category id,'.
1450                                           '"name" (string) the category name,'.
1451                                           '"parent" (int) the parent category id,'.
1452                                           '"idnumber" (string) category idnumber'.
1453                                           ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1454                                           '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1455                                               then the function return all categories that the user can see.'.
1456                                           ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1457                                           '"theme" (string) only return the categories having this theme'.
1458                                           ' - user must have \'moodle/category:manage\' to search on theme'),
1459                              'value' => new external_value(PARAM_RAW, 'the value to match')
1460                          )
1461                      ), 'criteria', VALUE_DEFAULT, array()
1462                  ),
1463                  'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1464                                            (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1465              )
1466          );
1467      }
1468  
1469      /**
1470       * Get categories
1471       *
1472       * @param array $criteria Criteria to match the results
1473       * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1474       * @return array list of categories
1475       * @since Moodle 2.3
1476       */
1477      public static function get_categories($criteria = array(), $addsubcategories = true) {
1478          global $CFG, $DB;
1479          require_once($CFG->dirroot . "/course/lib.php");
1480  
1481          // Validate parameters.
1482          $params = self::validate_parameters(self::get_categories_parameters(),
1483                  array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1484  
1485          // Retrieve the categories.
1486          $categories = array();
1487          if (!empty($params['criteria'])) {
1488  
1489              $conditions = array();
1490              $wheres = array();
1491              foreach ($params['criteria'] as $crit) {
1492                  $key = trim($crit['key']);
1493  
1494                  // Trying to avoid duplicate keys.
1495                  if (!isset($conditions[$key])) {
1496  
1497                      $context = context_system::instance();
1498                      $value = null;
1499                      switch ($key) {
1500                          case 'id':
1501                              $value = clean_param($crit['value'], PARAM_INT);
1502                              break;
1503  
1504                          case 'idnumber':
1505                              if (has_capability('moodle/category:manage', $context)) {
1506                                  $value = clean_param($crit['value'], PARAM_RAW);
1507                              } else {
1508                                  // We must throw an exception.
1509                                  // Otherwise the dev client would think no idnumber exists.
1510                                  throw new moodle_exception('criteriaerror',
1511                                          'webservice', '', null,
1512                                          'You don\'t have the permissions to search on the "idnumber" field.');
1513                              }
1514                              break;
1515  
1516                          case 'name':
1517                              $value = clean_param($crit['value'], PARAM_TEXT);
1518                              break;
1519  
1520                          case 'parent':
1521                              $value = clean_param($crit['value'], PARAM_INT);
1522                              break;
1523  
1524                          case 'visible':
1525                              if (has_capability('moodle/category:manage', $context)
1526                                  or has_capability('moodle/category:viewhiddencategories',
1527                                          context_system::instance())) {
1528                                  $value = clean_param($crit['value'], PARAM_INT);
1529                              } else {
1530                                  throw new moodle_exception('criteriaerror',
1531                                          'webservice', '', null,
1532                                          'You don\'t have the permissions to search on the "visible" field.');
1533                              }
1534                              break;
1535  
1536                          case 'theme':
1537                              if (has_capability('moodle/category:manage', $context)) {
1538                                  $value = clean_param($crit['value'], PARAM_THEME);
1539                              } else {
1540                                  throw new moodle_exception('criteriaerror',
1541                                          'webservice', '', null,
1542                                          'You don\'t have the permissions to search on the "theme" field.');
1543                              }
1544                              break;
1545  
1546                          default:
1547                              throw new moodle_exception('criteriaerror',
1548                                      'webservice', '', null,
1549                                      'You can not search on this criteria: ' . $key);
1550                      }
1551  
1552                      if (isset($value)) {
1553                          $conditions[$key] = $value;
1554                          $wheres[] = $key . " = :" . $key;
1555                      }
1556                  }
1557              }
1558  
1559              if (!empty($wheres)) {
1560                  $wheres = implode(" AND ", $wheres);
1561  
1562                  $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1563  
1564                  // Retrieve its sub subcategories (all levels).
1565                  if ($categories and !empty($params['addsubcategories'])) {
1566                      $newcategories = array();
1567  
1568                      // Check if we required visible/theme checks.
1569                      $additionalselect = '';
1570                      $additionalparams = array();
1571                      if (isset($conditions['visible'])) {
1572                          $additionalselect .= ' AND visible = :visible';
1573                          $additionalparams['visible'] = $conditions['visible'];
1574                      }
1575                      if (isset($conditions['theme'])) {
1576                          $additionalselect .= ' AND theme= :theme';
1577                          $additionalparams['theme'] = $conditions['theme'];
1578                      }
1579  
1580                      foreach ($categories as $category) {
1581                          $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1582                          $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1583                          $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1584                          $newcategories = $newcategories + $subcategories;   // Both arrays have integer as keys.
1585                      }
1586                      $categories = $categories + $newcategories;
1587                  }
1588              }
1589  
1590          } else {
1591              // Retrieve all categories in the database.
1592              $categories = $DB->get_records('course_categories');
1593          }
1594  
1595          // The not returned categories. key => category id, value => reason of exclusion.
1596          $excludedcats = array();
1597  
1598          // The returned categories.
1599          $categoriesinfo = array();
1600  
1601          // We need to sort the categories by path.
1602          // The parent cats need to be checked by the algo first.
1603          usort($categories, "core_course_external::compare_categories_by_path");
1604  
1605          foreach ($categories as $category) {
1606  
1607              // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1608              $parents = explode('/', $category->path);
1609              unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1610              foreach ($parents as $parentid) {
1611                  // Note: when the parent exclusion was due to the context,
1612                  // the sub category could still be returned.
1613                  if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1614                      $excludedcats[$category->id] = 'parent';
1615                  }
1616              }
1617  
1618              // Check the user can use the category context.
1619              $context = context_coursecat::instance($category->id);
1620              try {
1621                  self::validate_context($context);
1622              } catch (Exception $e) {
1623                  $excludedcats[$category->id] = 'context';
1624  
1625                  // If it was the requested category then throw an exception.
1626                  if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1627                      $exceptionparam = new stdClass();
1628                      $exceptionparam->message = $e->getMessage();
1629                      $exceptionparam->catid = $category->id;
1630                      throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1631                  }
1632              }
1633  
1634              // Return the category information.
1635              if (!isset($excludedcats[$category->id])) {
1636  
1637                  // Final check to see if the category is visible to the user.
1638                  if ($category->visible
1639                          or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1640                          or has_capability('moodle/category:manage', $context)) {
1641  
1642                      $categoryinfo = array();
1643                      $categoryinfo['id'] = $category->id;
1644                      $categoryinfo['name'] = $category->name;
1645                      list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1646                          external_format_text($category->description, $category->descriptionformat,
1647                                  $context->id, 'coursecat', 'description', null);
1648                      $categoryinfo['parent'] = $category->parent;
1649                      $categoryinfo['sortorder'] = $category->sortorder;
1650                      $categoryinfo['coursecount'] = $category->coursecount;
1651                      $categoryinfo['depth'] = $category->depth;
1652                      $categoryinfo['path'] = $category->path;
1653  
1654                      // Some fields only returned for admin.
1655                      if (has_capability('moodle/category:manage', $context)) {
1656                          $categoryinfo['idnumber'] = $category->idnumber;
1657                          $categoryinfo['visible'] = $category->visible;
1658                          $categoryinfo['visibleold'] = $category->visibleold;
1659                          $categoryinfo['timemodified'] = $category->timemodified;
1660                          $categoryinfo['theme'] = $category->theme;
1661                      }
1662  
1663                      $categoriesinfo[] = $categoryinfo;
1664                  } else {
1665                      $excludedcats[$category->id] = 'visibility';
1666                  }
1667              }
1668          }
1669  
1670          // Sorting the resulting array so it looks a bit better for the client developer.
1671          usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1672  
1673          return $categoriesinfo;
1674      }
1675  
1676      /**
1677       * Sort categories array by path
1678       * private function: only used by get_categories
1679       *
1680       * @param array $category1
1681       * @param array $category2
1682       * @return int result of strcmp
1683       * @since Moodle 2.3
1684       */
1685      private static function compare_categories_by_path($category1, $category2) {
1686          return strcmp($category1->path, $category2->path);
1687      }
1688  
1689      /**
1690       * Sort categories array by sortorder
1691       * private function: only used by get_categories
1692       *
1693       * @param array $category1
1694       * @param array $category2
1695       * @return int result of strcmp
1696       * @since Moodle 2.3
1697       */
1698      private static function compare_categories_by_sortorder($category1, $category2) {
1699          return strcmp($category1['sortorder'], $category2['sortorder']);
1700      }
1701  
1702      /**
1703       * Returns description of method result value
1704       *
1705       * @return external_description
1706       * @since Moodle 2.3
1707       */
1708      public static function get_categories_returns() {
1709          return new external_multiple_structure(
1710              new external_single_structure(
1711                  array(
1712                      'id' => new external_value(PARAM_INT, 'category id'),
1713                      'name' => new external_value(PARAM_TEXT, 'category name'),
1714                      'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1715                      'description' => new external_value(PARAM_RAW, 'category description'),
1716                      'descriptionformat' => new external_format_value('description'),
1717                      'parent' => new external_value(PARAM_INT, 'parent category id'),
1718                      'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1719                      'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1720                      'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1721                      'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1722                      'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1723                      'depth' => new external_value(PARAM_INT, 'category depth'),
1724                      'path' => new external_value(PARAM_TEXT, 'category path'),
1725                      'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1726                  ), 'List of categories'
1727              )
1728          );
1729      }
1730  
1731      /**
1732       * Returns description of method parameters
1733       *
1734       * @return external_function_parameters
1735       * @since Moodle 2.3
1736       */
1737      public static function create_categories_parameters() {
1738          return new external_function_parameters(
1739              array(
1740                  'categories' => new external_multiple_structure(
1741                          new external_single_structure(
1742                              array(
1743                                  'name' => new external_value(PARAM_TEXT, 'new category name'),
1744                                  'parent' => new external_value(PARAM_INT,
1745                                          'the parent category id inside which the new category will be created
1746                                           - set to 0 for a root category',
1747                                          VALUE_DEFAULT, 0),
1748                                  'idnumber' => new external_value(PARAM_RAW,
1749                                          'the new category idnumber', VALUE_OPTIONAL),
1750                                  'description' => new external_value(PARAM_RAW,
1751                                          'the new category description', VALUE_OPTIONAL),
1752                                  'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1753                                  'theme' => new external_value(PARAM_THEME,
1754                                          'the new category theme. This option must be enabled on moodle',
1755                                          VALUE_OPTIONAL),
1756                          )
1757                      )
1758                  )
1759              )
1760          );
1761      }
1762  
1763      /**
1764       * Create categories
1765       *
1766       * @param array $categories - see create_categories_parameters() for the array structure
1767       * @return array - see create_categories_returns() for the array structure
1768       * @since Moodle 2.3
1769       */
1770      public static function create_categories($categories) {
1771          global $CFG, $DB;
1772          require_once($CFG->libdir . "/coursecatlib.php");
1773  
1774          $params = self::validate_parameters(self::create_categories_parameters(),
1775                          array('categories' => $categories));
1776  
1777          $transaction = $DB->start_delegated_transaction();
1778  
1779          $createdcategories = array();
1780          foreach ($params['categories'] as $category) {
1781              if ($category['parent']) {
1782                  if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1783                      throw new moodle_exception('unknowcategory');
1784                  }
1785                  $context = context_coursecat::instance($category['parent']);
1786              } else {
1787                  $context = context_system::instance();
1788              }
1789              self::validate_context($context);
1790              require_capability('moodle/category:manage', $context);
1791  
1792              // this will validate format and throw an exception if there are errors
1793              external_validate_format($category['descriptionformat']);
1794  
1795              $newcategory = coursecat::create($category);
1796  
1797              $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1798          }
1799  
1800          $transaction->allow_commit();
1801  
1802          return $createdcategories;
1803      }
1804  
1805      /**
1806       * Returns description of method parameters
1807       *
1808       * @return external_function_parameters
1809       * @since Moodle 2.3
1810       */
1811      public static function create_categories_returns() {
1812          return new external_multiple_structure(
1813              new external_single_structure(
1814                  array(
1815                      'id' => new external_value(PARAM_INT, 'new category id'),
1816                      'name' => new external_value(PARAM_TEXT, 'new category name'),
1817                  )
1818              )
1819          );
1820      }
1821  
1822      /**
1823       * Returns description of method parameters
1824       *
1825       * @return external_function_parameters
1826       * @since Moodle 2.3
1827       */
1828      public static function update_categories_parameters() {
1829          return new external_function_parameters(
1830              array(
1831                  'categories' => new external_multiple_structure(
1832                      new external_single_structure(
1833                          array(
1834                              'id'       => new external_value(PARAM_INT, 'course id'),
1835                              'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1836                              'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1837                              'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1838                              'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1839                              'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1840                              'theme' => new external_value(PARAM_THEME,
1841                                      'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1842                          )
1843                      )
1844                  )
1845              )
1846          );
1847      }
1848  
1849      /**
1850       * Update categories
1851       *
1852       * @param array $categories The list of categories to update
1853       * @return null
1854       * @since Moodle 2.3
1855       */
1856      public static function update_categories($categories) {
1857          global $CFG, $DB;
1858          require_once($CFG->libdir . "/coursecatlib.php");
1859  
1860          // Validate parameters.
1861          $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1862  
1863          $transaction = $DB->start_delegated_transaction();
1864  
1865          foreach ($params['categories'] as $cat) {
1866              $category = coursecat::get($cat['id']);
1867  
1868              $categorycontext = context_coursecat::instance($cat['id']);
1869              self::validate_context($categorycontext);
1870              require_capability('moodle/category:manage', $categorycontext);
1871  
1872              // this will throw an exception if descriptionformat is not valid
1873              external_validate_format($cat['descriptionformat']);
1874  
1875              $category->update($cat);
1876          }
1877  
1878          $transaction->allow_commit();
1879      }
1880  
1881      /**
1882       * Returns description of method result value
1883       *
1884       * @return external_description
1885       * @since Moodle 2.3
1886       */
1887      public static function update_categories_returns() {
1888          return null;
1889      }
1890  
1891      /**
1892       * Returns description of method parameters
1893       *
1894       * @return external_function_parameters
1895       * @since Moodle 2.3
1896       */
1897      public static function delete_categories_parameters() {
1898          return new external_function_parameters(
1899              array(
1900                  'categories' => new external_multiple_structure(
1901                      new external_single_structure(
1902                          array(
1903                              'id' => new external_value(PARAM_INT, 'category id to delete'),
1904                              'newparent' => new external_value(PARAM_INT,
1905                                  'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1906                              'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1907                                  category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1908                          )
1909                      )
1910                  )
1911              )
1912          );
1913      }
1914  
1915      /**
1916       * Delete categories
1917       *
1918       * @param array $categories A list of category ids
1919       * @return array
1920       * @since Moodle 2.3
1921       */
1922      public static function delete_categories($categories) {
1923          global $CFG, $DB;
1924          require_once($CFG->dirroot . "/course/lib.php");
1925          require_once($CFG->libdir . "/coursecatlib.php");
1926  
1927          // Validate parameters.
1928          $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1929  
1930          $transaction = $DB->start_delegated_transaction();
1931  
1932          foreach ($params['categories'] as $category) {
1933              $deletecat = coursecat::get($category['id'], MUST_EXIST);
1934              $context = context_coursecat::instance($deletecat->id);
1935              require_capability('moodle/category:manage', $context);
1936              self::validate_context($context);
1937              self::validate_context(get_category_or_system_context($deletecat->parent));
1938  
1939              if ($category['recursive']) {
1940                  // If recursive was specified, then we recursively delete the category's contents.
1941                  if ($deletecat->can_delete_full()) {
1942                      $deletecat->delete_full(false);
1943                  } else {
1944                      throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1945                  }
1946              } else {
1947                  // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1948                  // If the parent is the root, moving is not supported (because a course must always be inside a category).
1949                  // We must move to an existing category.
1950                  if (!empty($category['newparent'])) {
1951                      $newparentcat = coursecat::get($category['newparent']);
1952                  } else {
1953                      $newparentcat = coursecat::get($deletecat->parent);
1954                  }
1955  
1956                  // This operation is not allowed. We must move contents to an existing category.
1957                  if (!$newparentcat->id) {
1958                      throw new moodle_exception('movecatcontentstoroot');
1959                  }
1960  
1961                  self::validate_context(context_coursecat::instance($newparentcat->id));
1962                  if ($deletecat->can_move_content_to($newparentcat->id)) {
1963                      $deletecat->delete_move($newparentcat->id, false);
1964                  } else {
1965                      throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1966                  }
1967              }
1968          }
1969  
1970          $transaction->allow_commit();
1971      }
1972  
1973      /**
1974       * Returns description of method parameters
1975       *
1976       * @return external_function_parameters
1977       * @since Moodle 2.3
1978       */
1979      public static function delete_categories_returns() {
1980          return null;
1981      }
1982  
1983      /**
1984       * Describes the parameters for delete_modules.
1985       *
1986       * @return external_external_function_parameters
1987       * @since Moodle 2.5
1988       */
1989      public static function delete_modules_parameters() {
1990          return new external_function_parameters (
1991              array(
1992                  'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1993                          VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
1994              )
1995          );
1996      }
1997  
1998      /**
1999       * Deletes a list of provided module instances.
2000       *
2001       * @param array $cmids the course module ids
2002       * @since Moodle 2.5
2003       */
2004      public static function delete_modules($cmids) {
2005          global $CFG, $DB;
2006  
2007          // Require course file containing the course delete module function.
2008          require_once($CFG->dirroot . "/course/lib.php");
2009  
2010          // Clean the parameters.
2011          $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2012  
2013          // Keep track of the course ids we have performed a capability check on to avoid repeating.
2014          $arrcourseschecked = array();
2015  
2016          foreach ($params['cmids'] as $cmid) {
2017              // Get the course module.
2018              $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2019  
2020              // Check if we have not yet confirmed they have permission in this course.
2021              if (!in_array($cm->course, $arrcourseschecked)) {
2022                  // Ensure the current user has required permission in this course.
2023                  $context = context_course::instance($cm->course);
2024                  self::validate_context($context);
2025                  // Add to the array.
2026                  $arrcourseschecked[] = $cm->course;
2027              }
2028  
2029              // Ensure they can delete this module.
2030              $modcontext = context_module::instance($cm->id);
2031              require_capability('moodle/course:manageactivities', $modcontext);
2032  
2033              // Delete the module.
2034              course_delete_module($cm->id);
2035          }
2036      }
2037  
2038      /**
2039       * Describes the delete_modules return value.
2040       *
2041       * @return external_single_structure
2042       * @since Moodle 2.5
2043       */
2044      public static function delete_modules_returns() {
2045          return null;
2046      }
2047  
2048      /**
2049       * Returns description of method parameters
2050       *
2051       * @return external_function_parameters
2052       * @since Moodle 2.9
2053       */
2054      public static function view_course_parameters() {
2055          return new external_function_parameters(
2056              array(
2057                  'courseid' => new external_value(PARAM_INT, 'id of the course'),
2058                  'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2059              )
2060          );
2061      }
2062  
2063      /**
2064       * Trigger the course viewed event.
2065       *
2066       * @param int $courseid id of course
2067       * @param int $sectionnumber sectionnumber (0, 1, 2...)
2068       * @return array of warnings and status result
2069       * @since Moodle 2.9
2070       * @throws moodle_exception
2071       */
2072      public static function view_course($courseid, $sectionnumber = 0) {
2073          global $CFG;
2074          require_once($CFG->dirroot . "/course/lib.php");
2075  
2076          $params = self::validate_parameters(self::view_course_parameters(),
2077                                              array(
2078                                                  'courseid' => $courseid,
2079                                                  'sectionnumber' => $sectionnumber
2080                                              ));
2081  
2082          $warnings = array();
2083  
2084          $course = get_course($params['courseid']);
2085          $context = context_course::instance($course->id);
2086          self::validate_context($context);
2087  
2088          if (!empty($params['sectionnumber'])) {
2089  
2090              // Get section details and check it exists.
2091              $modinfo = get_fast_modinfo($course);
2092              $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2093  
2094              // Check user is allowed to see it.
2095              if (!$coursesection->uservisible) {
2096                  require_capability('moodle/course:viewhiddensections', $context);
2097              }
2098          }
2099  
2100          course_view($context, $params['sectionnumber']);
2101  
2102          $result = array();
2103          $result['status'] = true;
2104          $result['warnings'] = $warnings;
2105          return $result;
2106      }
2107  
2108      /**
2109       * Returns description of method result value
2110       *
2111       * @return external_description
2112       * @since Moodle 2.9
2113       */
2114      public static function view_course_returns() {
2115          return new external_single_structure(
2116              array(
2117                  'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2118                  'warnings' => new external_warnings()
2119              )
2120          );
2121      }
2122  
2123      /**
2124       * Returns description of method parameters
2125       *
2126       * @return external_function_parameters
2127       * @since Moodle 3.0
2128       */
2129      public static function search_courses_parameters() {
2130          return new external_function_parameters(
2131              array(
2132                  'criterianame'  => new external_value(PARAM_ALPHA, 'criteria name
2133                                                          (search, modulelist (only admins), blocklist (only admins), tagid)'),
2134                  'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2135                  'page'          => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2136                  'perpage'       => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
2137                  'requiredcapabilities' => new external_multiple_structure(
2138                      new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'),
2139                      'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
2140                  ),
2141                  'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
2142              )
2143          );
2144      }
2145  
2146      /**
2147       * Search courses following the specified criteria.
2148       *
2149       * @param string $criterianame  Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2150       * @param string $criteriavalue Criteria value
2151       * @param int $page             Page number (for pagination)
2152       * @param int $perpage          Items per page
2153       * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
2154       * @param int $limittoenrolled  Limit to only enrolled courses
2155       * @return array of course objects and warnings
2156       * @since Moodle 3.0
2157       * @throws moodle_exception
2158       */
2159      public static function search_courses($criterianame,
2160                                            $criteriavalue,
2161                                            $page=0,
2162                                            $perpage=0,
2163                                            $requiredcapabilities=array(),
2164                                            $limittoenrolled=0) {
2165          global $CFG;
2166          require_once($CFG->libdir . '/coursecatlib.php');
2167  
2168          $warnings = array();
2169  
2170          $parameters = array(
2171              'criterianame'  => $criterianame,
2172              'criteriavalue' => $criteriavalue,
2173              'page'          => $page,
2174              'perpage'       => $perpage,
2175              'requiredcapabilities' => $requiredcapabilities
2176          );
2177          $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2178          self::validate_context(context_system::instance());
2179  
2180          $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2181          if (!in_array($params['criterianame'], $allowedcriterianames)) {
2182              throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2183                  'allowed values are: '.implode(',', $allowedcriterianames));
2184          }
2185  
2186          if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2187              require_capability('moodle/site:config', context_system::instance());
2188          }
2189  
2190          $paramtype = array(
2191              'search' => PARAM_RAW,
2192              'modulelist' => PARAM_PLUGIN,
2193              'blocklist' => PARAM_INT,
2194              'tagid' => PARAM_INT
2195          );
2196          $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2197  
2198          // Prepare the search API options.
2199          $searchcriteria = array();
2200          $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2201  
2202          $options = array();
2203          if ($params['perpage'] != 0) {
2204              $offset = $params['page'] * $params['perpage'];
2205              $options = array('offset' => $offset, 'limit' => $params['perpage']);
2206          }
2207  
2208          // Search the courses.
2209          $courses = coursecat::search_courses($searchcriteria, $options, $params['requiredcapabilities']);
2210          $totalcount = coursecat::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']);
2211  
2212          if (!empty($limittoenrolled)) {
2213              // Get the courses where the current user has access.
2214              $enrolled = enrol_get_my_courses(array('id', 'cacherev'));
2215          }
2216  
2217          $finalcourses = array();
2218          $categoriescache = array();
2219  
2220          foreach ($courses as $course) {
2221              if (!empty($limittoenrolled)) {
2222                  // Filter out not enrolled courses.
2223                  if (!isset($enrolled[$course->id])) {
2224                      $totalcount--;
2225                      continue;
2226                  }
2227              }
2228  
2229              $coursecontext = context_course::instance($course->id);
2230  
2231              // Category information.
2232              if (!array_key_exists($course->category, $categoriescache)) {
2233                  $categoriescache[$course->category] = coursecat::get($course->category, IGNORE_MISSING);
2234              }
2235              $category = $categoriescache[$course->category];
2236  
2237              // Retrieve course overfiew used files.
2238              $files = array();
2239              foreach ($course->get_course_overviewfiles() as $file) {
2240                  $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2241                                                                          $file->get_filearea(), null, $file->get_filepath(),
2242                                                                          $file->get_filename())->out(false);
2243                  $files[] = array(
2244                      'filename' => $file->get_filename(),
2245                      'fileurl' => $fileurl,
2246                      'filesize' => $file->get_filesize()
2247                  );
2248              }
2249  
2250              // Retrieve the course contacts,
2251              // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2252              $coursecontacts = array();
2253              foreach ($course->get_course_contacts() as $contact) {
2254                   $coursecontacts[] = array(
2255                      'id' => $contact['user']->id,
2256                      'fullname' => $contact['username']
2257                  );
2258              }
2259  
2260              // Allowed enrolment methods (maybe we can self-enrol).
2261              $enroltypes = array();
2262              $instances = enrol_get_instances($course->id, true);
2263              foreach ($instances as $instance) {
2264                  $enroltypes[] = $instance->enrol;
2265              }
2266  
2267              // Format summary.
2268              list($summary, $summaryformat) =
2269                  external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2270  
2271              $displayname = get_course_display_name_for_list($course);
2272              $coursereturns = array();
2273              $coursereturns['id']                = $course->id;
2274              $coursereturns['fullname']          = external_format_string($course->fullname, $coursecontext->id);
2275              $coursereturns['displayname']       = external_format_string($displayname, $coursecontext->id);
2276              $coursereturns['shortname']         = external_format_string($course->shortname, $coursecontext->id);
2277              $coursereturns['categoryid']        = $course->category;
2278              $coursereturns['categoryname']      = $category == null ? '' : $category->name;
2279              $coursereturns['summary']           = $summary;
2280              $coursereturns['summaryformat']     = $summaryformat;
2281              $coursereturns['overviewfiles']     = $files;
2282              $coursereturns['contacts']          = $coursecontacts;
2283              $coursereturns['enrollmentmethods'] = $enroltypes;
2284              $finalcourses[] = $coursereturns;
2285          }
2286  
2287          return array(
2288              'total' => $totalcount,
2289              'courses' => $finalcourses,
2290              'warnings' => $warnings
2291          );
2292      }
2293  
2294      /**
2295       * Returns description of method result value
2296       *
2297       * @return external_description
2298       * @since Moodle 3.0
2299       */
2300      public static function search_courses_returns() {
2301  
2302          return new external_single_structure(
2303              array(
2304                  'total' => new external_value(PARAM_INT, 'total course count'),
2305                  'courses' => new external_multiple_structure(
2306                      new external_single_structure(
2307                          array(
2308                              'id' => new external_value(PARAM_INT, 'course id'),
2309                              'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2310                              'displayname' => new external_value(PARAM_TEXT, 'course display name'),
2311                              'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2312                              'categoryid' => new external_value(PARAM_INT, 'category id'),
2313                              'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2314                              'summary' => new external_value(PARAM_RAW, 'summary'),
2315                              'summaryformat' => new external_format_value('summary'),
2316                              'overviewfiles' => new external_multiple_structure(
2317                                  new external_single_structure(
2318                                      array(
2319                                          'filename' => new external_value(PARAM_FILE, 'overview file name'),
2320                                          'fileurl'  => new external_value(PARAM_URL, 'overview file url'),
2321                                          'filesize'  => new external_value(PARAM_INT, 'overview file size'),
2322                                      )
2323                                  ),
2324                                  'additional overview files attached to this course'
2325                              ),
2326                              'contacts' => new external_multiple_structure(
2327                                  new external_single_structure(
2328                                      array(
2329                                          'id' => new external_value(PARAM_INT, 'contact user id'),
2330                                          'fullname'  => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2331                                      )
2332                                  ),
2333                                  'contact users'
2334                              ),
2335                              'enrollmentmethods' => new external_multiple_structure(
2336                                  new external_value(PARAM_PLUGIN, 'enrollment method'),
2337                                  'enrollment methods list'
2338                              ),
2339                          )
2340                      ), 'course'
2341                  ),
2342                  'warnings' => new external_warnings()
2343              )
2344          );
2345      }
2346  
2347      /**
2348       * Returns description of method parameters
2349       *
2350       * @return external_function_parameters
2351       * @since Moodle 3.0
2352       */
2353      public static function get_course_module_parameters() {
2354          return new external_function_parameters(
2355              array(
2356                  'cmid' => new external_value(PARAM_INT, 'The course module id')
2357              )
2358          );
2359      }
2360  
2361      /**
2362       * Return information about a course module.
2363       *
2364       * @param int $cmid the course module id
2365       * @return array of warnings and the course module
2366       * @since Moodle 3.0
2367       * @throws moodle_exception
2368       */
2369      public static function get_course_module($cmid) {
2370  
2371          $params = self::validate_parameters(self::get_course_module_parameters(),
2372                                              array(
2373                                                  'cmid' => $cmid,
2374                                              ));
2375  
2376          $warnings = array();
2377  
2378          $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2379          $context = context_module::instance($cm->id);
2380          self::validate_context($context);
2381  
2382          // If the user has permissions to manage the activity, return all the information.
2383          if (has_capability('moodle/course:manageactivities', $context)) {
2384              $info = $cm;
2385          } else {
2386              // Return information is safe to show to any user.
2387              $info = new stdClass();
2388              $info->id = $cm->id;
2389              $info->course = $cm->course;
2390              $info->module = $cm->module;
2391              $info->modname = $cm->modname;
2392              $info->instance = $cm->instance;
2393              $info->section = $cm->section;
2394              $info->sectionnum = $cm->sectionnum;
2395              $info->groupmode = $cm->groupmode;
2396              $info->groupingid = $cm->groupingid;
2397              $info->completion = $cm->completion;
2398          }
2399          // Format name.
2400          $info->name = external_format_string($cm->name, $context->id);
2401  
2402          $result = array();
2403          $result['cm'] = $info;
2404          $result['warnings'] = $warnings;
2405          return $result;
2406      }
2407  
2408      /**
2409       * Returns description of method result value
2410       *
2411       * @return external_description
2412       * @since Moodle 3.0
2413       */
2414      public static function get_course_module_returns() {
2415          return new external_single_structure(
2416              array(
2417                  'cm' => new external_single_structure(
2418                      array(
2419                          'id' => new external_value(PARAM_INT, 'The course module id'),
2420                          'course' => new external_value(PARAM_INT, 'The course id'),
2421                          'module' => new external_value(PARAM_INT, 'The module type id'),
2422                          'name' => new external_value(PARAM_RAW, 'The activity name'),
2423                          'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2424                          'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2425                          'section' => new external_value(PARAM_INT, 'The module section id'),
2426                          'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2427                          'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2428                          'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2429                          'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2430                          'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2431                          'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2432                          'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2433                          'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
2434                          'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
2435                          'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
2436                          'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
2437                          'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
2438                          'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
2439                          'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
2440                          'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
2441                      )
2442                  ),
2443                  'warnings' => new external_warnings()
2444              )
2445          );
2446      }
2447  
2448      /**
2449       * Returns description of method parameters
2450       *
2451       * @return external_function_parameters
2452       * @since Moodle 3.0
2453       */
2454      public static function get_course_module_by_instance_parameters() {
2455          return new external_function_parameters(
2456              array(
2457                  'module' => new external_value(PARAM_COMPONENT, 'The module name'),
2458                  'instance' => new external_value(PARAM_INT, 'The module instance id')
2459              )
2460          );
2461      }
2462  
2463      /**
2464       * Return information about a course module.
2465       *
2466       * @param string $module the module name
2467       * @param int $instance the activity instance id
2468       * @return array of warnings and the course module
2469       * @since Moodle 3.0
2470       * @throws moodle_exception
2471       */
2472      public static function get_course_module_by_instance($module, $instance) {
2473  
2474          $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
2475                                              array(
2476                                                  'module' => $module,
2477                                                  'instance' => $instance,
2478                                              ));
2479  
2480          $warnings = array();
2481          $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
2482  
2483          return self::get_course_module($cm->id);
2484      }
2485  
2486      /**
2487       * Returns description of method result value
2488       *
2489       * @return external_description
2490       * @since Moodle 3.0
2491       */
2492      public static function get_course_module_by_instance_returns() {
2493          return self::get_course_module_returns();
2494      }
2495  
2496  }

Search This Site: