Merge branch 'MDL-57112-master' of git://github.com/cameron1729/moodle
[moodle.git] / course / externallib.php
blob32214693cbe7231eb09136e7db16c47bb4e547ae
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.
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/>.
18 /**
19 * External course API
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
27 defined('MOODLE_INTERNAL') || die;
29 require_once("$CFG->libdir/externallib.php");
31 /**
32 * Course external functions
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
40 class core_course_external extends external_api {
42 /**
43 * Returns description of method parameters
45 * @return external_function_parameters
46 * @since Moodle 2.9 Options available
47 * @since Moodle 2.2
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.')
67 ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
72 /**
73 * Get course contents
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
81 public static function get_course_contents($courseid, $options = array()) {
82 global $CFG, $DB;
83 require_once($CFG->dirroot . "/course/lib.php");
85 //validate parameter
86 $params = self::validate_parameters(self::get_course_contents_parameters(),
87 array('courseid' => $courseid, 'options' => $options));
89 $filters = array();
90 if (!empty($params['options'])) {
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);
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);
120 break;
121 default:
122 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
128 //retrieve the course
129 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
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');
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);
152 $canupdatecourse = has_capability('moodle/course:update', $context);
154 //create return value
155 $coursecontents = array();
157 if ($canupdatecourse or $course->visible
158 or has_capability('moodle/course:viewhiddencourses', $context)) {
160 //retrieve sections
161 $modinfo = get_fast_modinfo($course);
162 $sections = $modinfo->get_section_info_all();
163 $coursenumsections = course_get_format($course)->get_course()->numsections;
165 //for each sections (first displayed to last displayed)
166 $modinfosections = $modinfo->get_sections();
167 foreach ($sections as $key => $section) {
169 if (!$section->uservisible) {
170 continue;
173 // This becomes true when we are filtering and we found the value to filter with.
174 $sectionfound = false;
176 // Filter by section id.
177 if (!empty($filters['sectionid'])) {
178 if ($section->id != $filters['sectionid']) {
179 continue;
180 } else {
181 $sectionfound = true;
185 // Filter by section number. Note that 0 is a valid section number.
186 if (isset($filters['sectionnumber'])) {
187 if ($key != $filters['sectionnumber']) {
188 continue;
189 } else {
190 $sectionfound = true;
194 // reset $sectioncontents
195 $sectionvalues = array();
196 $sectionvalues['id'] = $section->id;
197 $sectionvalues['name'] = get_section_name($course, $section);
198 $sectionvalues['visible'] = $section->visible;
200 $options = (object) array('noclean' => true);
201 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
202 external_format_text($section->summary, $section->summaryformat,
203 $context->id, 'course', 'section', $section->id, $options);
204 $sectionvalues['section'] = $section->section;
205 $sectionvalues['hiddenbynumsections'] = $section->section > $coursenumsections ? 1 : 0;
206 $sectioncontents = array();
208 //for each module of the section
209 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
210 foreach ($modinfosections[$section->section] as $cmid) {
211 $cm = $modinfo->cms[$cmid];
213 // stop here if the module is not visible to the user
214 if (!$cm->uservisible) {
215 continue;
218 // This becomes true when we are filtering and we found the value to filter with.
219 $modfound = false;
221 // Filter by cmid.
222 if (!empty($filters['cmid'])) {
223 if ($cmid != $filters['cmid']) {
224 continue;
225 } else {
226 $modfound = true;
230 // Filter by module name and id.
231 if (!empty($filters['modname'])) {
232 if ($cm->modname != $filters['modname']) {
233 continue;
234 } else if (!empty($filters['modid'])) {
235 if ($cm->instance != $filters['modid']) {
236 continue;
237 } else {
238 // Note that if we are only filtering by modname we don't break the loop.
239 $modfound = true;
244 $module = array();
246 $modcontext = context_module::instance($cm->id);
248 //common info (for people being able to see the module or availability dates)
249 $module['id'] = $cm->id;
250 $module['name'] = external_format_string($cm->name, $modcontext->id);
251 $module['instance'] = $cm->instance;
252 $module['modname'] = $cm->modname;
253 $module['modplural'] = $cm->modplural;
254 $module['modicon'] = $cm->get_icon_url()->out(false);
255 $module['indent'] = $cm->indent;
257 if (!empty($cm->showdescription) or $cm->modname == 'label') {
258 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
259 list($module['description'], $descriptionformat) = external_format_text($cm->content,
260 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
263 //url of the module
264 $url = $cm->url;
265 if ($url) { //labels don't have url
266 $module['url'] = $url->out(false);
269 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
270 context_module::instance($cm->id));
271 //user that can view hidden module should know about the visibility
272 $module['visible'] = $cm->visible;
274 // Availability date (also send to user who can see hidden module).
275 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
276 $module['availability'] = $cm->availability;
279 $baseurl = 'webservice/pluginfile.php';
281 //call $modulename_export_contents
282 //(each module callback take care about checking the capabilities)
284 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
285 $getcontentfunction = $cm->modname.'_export_contents';
286 if (function_exists($getcontentfunction)) {
287 if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
288 $module['contents'] = $contents;
289 } else {
290 $module['contents'] = array();
294 //assign result to $sectioncontents
295 $sectioncontents[] = $module;
297 // If we just did a filtering, break the loop.
298 if ($modfound) {
299 break;
304 $sectionvalues['modules'] = $sectioncontents;
306 // assign result to $coursecontents
307 $coursecontents[] = $sectionvalues;
309 // Break the loop if we are filtering.
310 if ($sectionfound) {
311 break;
315 return $coursecontents;
319 * Returns description of method result value
321 * @return external_description
322 * @since Moodle 2.2
324 public static function get_course_contents_returns() {
325 return new external_multiple_structure(
326 new external_single_structure(
327 array(
328 'id' => new external_value(PARAM_INT, 'Section ID'),
329 'name' => new external_value(PARAM_TEXT, 'Section name'),
330 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
331 'summary' => new external_value(PARAM_RAW, 'Section description'),
332 'summaryformat' => new external_format_value('summary'),
333 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL),
334 'hiddenbynumsections' => new external_value(PARAM_INT, 'Whether is a section hidden in the course format',
335 VALUE_OPTIONAL),
336 'modules' => new external_multiple_structure(
337 new external_single_structure(
338 array(
339 'id' => new external_value(PARAM_INT, 'activity id'),
340 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
341 'name' => new external_value(PARAM_RAW, 'activity module name'),
342 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
343 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
344 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
345 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
346 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
347 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
348 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
349 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
350 'contents' => new external_multiple_structure(
351 new external_single_structure(
352 array(
353 // content info
354 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
355 'filename'=> new external_value(PARAM_FILE, 'filename'),
356 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
357 'filesize'=> new external_value(PARAM_INT, 'filesize'),
358 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
359 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
360 'timecreated' => new external_value(PARAM_INT, 'Time created'),
361 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
362 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
364 // copyright related info
365 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
366 'author' => new external_value(PARAM_TEXT, 'Content owner'),
367 'license' => new external_value(PARAM_TEXT, 'Content license'),
369 ), VALUE_DEFAULT, array()
372 ), 'list of module'
380 * Returns description of method parameters
382 * @return external_function_parameters
383 * @since Moodle 2.3
385 public static function get_courses_parameters() {
386 return new external_function_parameters(
387 array('options' => new external_single_structure(
388 array('ids' => new external_multiple_structure(
389 new external_value(PARAM_INT, 'Course id')
390 , 'List of course id. If empty return all courses
391 except front page course.',
392 VALUE_OPTIONAL)
393 ), 'options - operator OR is used', VALUE_DEFAULT, array())
399 * Get courses
401 * @param array $options It contains an array (list of ids)
402 * @return array
403 * @since Moodle 2.2
405 public static function get_courses($options = array()) {
406 global $CFG, $DB;
407 require_once($CFG->dirroot . "/course/lib.php");
409 //validate parameter
410 $params = self::validate_parameters(self::get_courses_parameters(),
411 array('options' => $options));
413 //retrieve courses
414 if (!array_key_exists('ids', $params['options'])
415 or empty($params['options']['ids'])) {
416 $courses = $DB->get_records('course');
417 } else {
418 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
421 //create return value
422 $coursesinfo = array();
423 foreach ($courses as $course) {
425 // now security checks
426 $context = context_course::instance($course->id, IGNORE_MISSING);
427 $courseformatoptions = course_get_format($course)->get_format_options();
428 try {
429 self::validate_context($context);
430 } catch (Exception $e) {
431 $exceptionparam = new stdClass();
432 $exceptionparam->message = $e->getMessage();
433 $exceptionparam->courseid = $course->id;
434 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
436 require_capability('moodle/course:view', $context);
438 $courseinfo = array();
439 $courseinfo['id'] = $course->id;
440 $courseinfo['fullname'] = external_format_string($course->fullname, $context->id);
441 $courseinfo['shortname'] = external_format_string($course->shortname, $context->id);
442 $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id);
443 $courseinfo['categoryid'] = $course->category;
444 list($courseinfo['summary'], $courseinfo['summaryformat']) =
445 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
446 $courseinfo['format'] = $course->format;
447 $courseinfo['startdate'] = $course->startdate;
448 $courseinfo['enddate'] = $course->enddate;
449 if (array_key_exists('numsections', $courseformatoptions)) {
450 // For backward-compartibility
451 $courseinfo['numsections'] = $courseformatoptions['numsections'];
454 //some field should be returned only if the user has update permission
455 $courseadmin = has_capability('moodle/course:update', $context);
456 if ($courseadmin) {
457 $courseinfo['categorysortorder'] = $course->sortorder;
458 $courseinfo['idnumber'] = $course->idnumber;
459 $courseinfo['showgrades'] = $course->showgrades;
460 $courseinfo['showreports'] = $course->showreports;
461 $courseinfo['newsitems'] = $course->newsitems;
462 $courseinfo['visible'] = $course->visible;
463 $courseinfo['maxbytes'] = $course->maxbytes;
464 if (array_key_exists('hiddensections', $courseformatoptions)) {
465 // For backward-compartibility
466 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
468 $courseinfo['groupmode'] = $course->groupmode;
469 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
470 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
471 $courseinfo['lang'] = $course->lang;
472 $courseinfo['timecreated'] = $course->timecreated;
473 $courseinfo['timemodified'] = $course->timemodified;
474 $courseinfo['forcetheme'] = $course->theme;
475 $courseinfo['enablecompletion'] = $course->enablecompletion;
476 $courseinfo['completionnotify'] = $course->completionnotify;
477 $courseinfo['courseformatoptions'] = array();
478 foreach ($courseformatoptions as $key => $value) {
479 $courseinfo['courseformatoptions'][] = array(
480 'name' => $key,
481 'value' => $value
486 if ($courseadmin or $course->visible
487 or has_capability('moodle/course:viewhiddencourses', $context)) {
488 $coursesinfo[] = $courseinfo;
492 return $coursesinfo;
496 * Returns description of method result value
498 * @return external_description
499 * @since Moodle 2.2
501 public static function get_courses_returns() {
502 return new external_multiple_structure(
503 new external_single_structure(
504 array(
505 'id' => new external_value(PARAM_INT, 'course id'),
506 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
507 'categoryid' => new external_value(PARAM_INT, 'category id'),
508 'categorysortorder' => new external_value(PARAM_INT,
509 'sort order into the category', VALUE_OPTIONAL),
510 'fullname' => new external_value(PARAM_TEXT, 'full name'),
511 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
512 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
513 'summary' => new external_value(PARAM_RAW, 'summary'),
514 'summaryformat' => new external_format_value('summary'),
515 'format' => new external_value(PARAM_PLUGIN,
516 'course format: weeks, topics, social, site,..'),
517 'showgrades' => new external_value(PARAM_INT,
518 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
519 'newsitems' => new external_value(PARAM_INT,
520 'number of recent items appearing on the course page', VALUE_OPTIONAL),
521 'startdate' => new external_value(PARAM_INT,
522 'timestamp when the course start'),
523 'enddate' => new external_value(PARAM_INT,
524 'timestamp when the course end'),
525 'numsections' => new external_value(PARAM_INT,
526 '(deprecated, use courseformatoptions) number of weeks/topics',
527 VALUE_OPTIONAL),
528 'maxbytes' => new external_value(PARAM_INT,
529 'largest size of file that can be uploaded into the course',
530 VALUE_OPTIONAL),
531 'showreports' => new external_value(PARAM_INT,
532 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
533 'visible' => new external_value(PARAM_INT,
534 '1: available to student, 0:not available', VALUE_OPTIONAL),
535 'hiddensections' => new external_value(PARAM_INT,
536 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
537 VALUE_OPTIONAL),
538 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
539 VALUE_OPTIONAL),
540 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
541 VALUE_OPTIONAL),
542 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
543 VALUE_OPTIONAL),
544 'timecreated' => new external_value(PARAM_INT,
545 'timestamp when the course have been created', VALUE_OPTIONAL),
546 'timemodified' => new external_value(PARAM_INT,
547 'timestamp when the course have been modified', VALUE_OPTIONAL),
548 'enablecompletion' => new external_value(PARAM_INT,
549 'Enabled, control via completion and activity settings. Disbaled,
550 not shown in activity settings.',
551 VALUE_OPTIONAL),
552 'completionnotify' => new external_value(PARAM_INT,
553 '1: yes 0: no', VALUE_OPTIONAL),
554 'lang' => new external_value(PARAM_SAFEDIR,
555 'forced course language', VALUE_OPTIONAL),
556 'forcetheme' => new external_value(PARAM_PLUGIN,
557 'name of the force theme', VALUE_OPTIONAL),
558 'courseformatoptions' => new external_multiple_structure(
559 new external_single_structure(
560 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
561 'value' => new external_value(PARAM_RAW, 'course format option value')
563 'additional options for particular course format', VALUE_OPTIONAL
565 ), 'course'
571 * Returns description of method parameters
573 * @return external_function_parameters
574 * @since Moodle 2.2
576 public static function create_courses_parameters() {
577 $courseconfig = get_config('moodlecourse'); //needed for many default values
578 return new external_function_parameters(
579 array(
580 'courses' => new external_multiple_structure(
581 new external_single_structure(
582 array(
583 'fullname' => new external_value(PARAM_TEXT, 'full name'),
584 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
585 'categoryid' => new external_value(PARAM_INT, 'category id'),
586 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
587 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
588 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
589 'format' => new external_value(PARAM_PLUGIN,
590 'course format: weeks, topics, social, site,..',
591 VALUE_DEFAULT, $courseconfig->format),
592 'showgrades' => new external_value(PARAM_INT,
593 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
594 $courseconfig->showgrades),
595 'newsitems' => new external_value(PARAM_INT,
596 'number of recent items appearing on the course page',
597 VALUE_DEFAULT, $courseconfig->newsitems),
598 'startdate' => new external_value(PARAM_INT,
599 'timestamp when the course start', VALUE_OPTIONAL),
600 'enddate' => new external_value(PARAM_INT,
601 'timestamp when the course end', VALUE_OPTIONAL),
602 'numsections' => new external_value(PARAM_INT,
603 '(deprecated, use courseformatoptions) number of weeks/topics',
604 VALUE_OPTIONAL),
605 'maxbytes' => new external_value(PARAM_INT,
606 'largest size of file that can be uploaded into the course',
607 VALUE_DEFAULT, $courseconfig->maxbytes),
608 'showreports' => new external_value(PARAM_INT,
609 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
610 $courseconfig->showreports),
611 'visible' => new external_value(PARAM_INT,
612 '1: available to student, 0:not available', VALUE_OPTIONAL),
613 'hiddensections' => new external_value(PARAM_INT,
614 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
615 VALUE_OPTIONAL),
616 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
617 VALUE_DEFAULT, $courseconfig->groupmode),
618 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
619 VALUE_DEFAULT, $courseconfig->groupmodeforce),
620 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
621 VALUE_DEFAULT, 0),
622 'enablecompletion' => new external_value(PARAM_INT,
623 'Enabled, control via completion and activity settings. Disabled,
624 not shown in activity settings.',
625 VALUE_OPTIONAL),
626 'completionnotify' => new external_value(PARAM_INT,
627 '1: yes 0: no', VALUE_OPTIONAL),
628 'lang' => new external_value(PARAM_SAFEDIR,
629 'forced course language', VALUE_OPTIONAL),
630 'forcetheme' => new external_value(PARAM_PLUGIN,
631 'name of the force theme', VALUE_OPTIONAL),
632 'courseformatoptions' => new external_multiple_structure(
633 new external_single_structure(
634 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
635 'value' => new external_value(PARAM_RAW, 'course format option value')
637 'additional options for particular course format', VALUE_OPTIONAL),
639 ), 'courses to create'
646 * Create courses
648 * @param array $courses
649 * @return array courses (id and shortname only)
650 * @since Moodle 2.2
652 public static function create_courses($courses) {
653 global $CFG, $DB;
654 require_once($CFG->dirroot . "/course/lib.php");
655 require_once($CFG->libdir . '/completionlib.php');
657 $params = self::validate_parameters(self::create_courses_parameters(),
658 array('courses' => $courses));
660 $availablethemes = core_component::get_plugin_list('theme');
661 $availablelangs = get_string_manager()->get_list_of_translations();
663 $transaction = $DB->start_delegated_transaction();
665 foreach ($params['courses'] as $course) {
667 // Ensure the current user is allowed to run this function
668 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
669 try {
670 self::validate_context($context);
671 } catch (Exception $e) {
672 $exceptionparam = new stdClass();
673 $exceptionparam->message = $e->getMessage();
674 $exceptionparam->catid = $course['categoryid'];
675 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
677 require_capability('moodle/course:create', $context);
679 // Make sure lang is valid
680 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
681 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
684 // Make sure theme is valid
685 if (array_key_exists('forcetheme', $course)) {
686 if (!empty($CFG->allowcoursethemes)) {
687 if (empty($availablethemes[$course['forcetheme']])) {
688 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
689 } else {
690 $course['theme'] = $course['forcetheme'];
695 //force visibility if ws user doesn't have the permission to set it
696 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
697 if (!has_capability('moodle/course:visibility', $context)) {
698 $course['visible'] = $category->visible;
701 //set default value for completion
702 $courseconfig = get_config('moodlecourse');
703 if (completion_info::is_enabled_for_site()) {
704 if (!array_key_exists('enablecompletion', $course)) {
705 $course['enablecompletion'] = $courseconfig->enablecompletion;
707 } else {
708 $course['enablecompletion'] = 0;
711 $course['category'] = $course['categoryid'];
713 // Summary format.
714 $course['summaryformat'] = external_validate_format($course['summaryformat']);
716 if (!empty($course['courseformatoptions'])) {
717 foreach ($course['courseformatoptions'] as $option) {
718 $course[$option['name']] = $option['value'];
722 //Note: create_course() core function check shortname, idnumber, category
723 $course['id'] = create_course((object) $course)->id;
725 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
728 $transaction->allow_commit();
730 return $resultcourses;
734 * Returns description of method result value
736 * @return external_description
737 * @since Moodle 2.2
739 public static function create_courses_returns() {
740 return new external_multiple_structure(
741 new external_single_structure(
742 array(
743 'id' => new external_value(PARAM_INT, 'course id'),
744 'shortname' => new external_value(PARAM_TEXT, 'short name'),
751 * Update courses
753 * @return external_function_parameters
754 * @since Moodle 2.5
756 public static function update_courses_parameters() {
757 return new external_function_parameters(
758 array(
759 'courses' => new external_multiple_structure(
760 new external_single_structure(
761 array(
762 'id' => new external_value(PARAM_INT, 'ID of the course'),
763 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
764 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
765 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
766 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
767 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
768 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
769 'format' => new external_value(PARAM_PLUGIN,
770 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
771 'showgrades' => new external_value(PARAM_INT,
772 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
773 'newsitems' => new external_value(PARAM_INT,
774 'number of recent items appearing on the course page', VALUE_OPTIONAL),
775 'startdate' => new external_value(PARAM_INT,
776 'timestamp when the course start', VALUE_OPTIONAL),
777 'enddate' => new external_value(PARAM_INT,
778 'timestamp when the course end', VALUE_OPTIONAL),
779 'numsections' => new external_value(PARAM_INT,
780 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
781 'maxbytes' => new external_value(PARAM_INT,
782 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
783 'showreports' => new external_value(PARAM_INT,
784 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
785 'visible' => new external_value(PARAM_INT,
786 '1: available to student, 0:not available', VALUE_OPTIONAL),
787 'hiddensections' => new external_value(PARAM_INT,
788 '(deprecated, use courseformatoptions) How the hidden sections in the course are
789 displayed to students', VALUE_OPTIONAL),
790 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
791 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
792 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
793 'enablecompletion' => new external_value(PARAM_INT,
794 'Enabled, control via completion and activity settings. Disabled,
795 not shown in activity settings.', VALUE_OPTIONAL),
796 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
797 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
798 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
799 'courseformatoptions' => new external_multiple_structure(
800 new external_single_structure(
801 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
802 'value' => new external_value(PARAM_RAW, 'course format option value')
804 'additional options for particular course format', VALUE_OPTIONAL),
806 ), 'courses to update'
813 * Update courses
815 * @param array $courses
816 * @since Moodle 2.5
818 public static function update_courses($courses) {
819 global $CFG, $DB;
820 require_once($CFG->dirroot . "/course/lib.php");
821 $warnings = array();
823 $params = self::validate_parameters(self::update_courses_parameters(),
824 array('courses' => $courses));
826 $availablethemes = core_component::get_plugin_list('theme');
827 $availablelangs = get_string_manager()->get_list_of_translations();
829 foreach ($params['courses'] as $course) {
830 // Catch any exception while updating course and return as warning to user.
831 try {
832 // Ensure the current user is allowed to run this function.
833 $context = context_course::instance($course['id'], MUST_EXIST);
834 self::validate_context($context);
836 $oldcourse = course_get_format($course['id'])->get_course();
838 require_capability('moodle/course:update', $context);
840 // Check if user can change category.
841 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
842 require_capability('moodle/course:changecategory', $context);
843 $course['category'] = $course['categoryid'];
846 // Check if the user can change fullname.
847 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
848 require_capability('moodle/course:changefullname', $context);
851 // Check if the user can change shortname.
852 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
853 require_capability('moodle/course:changeshortname', $context);
856 // Check if the user can change the idnumber.
857 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
858 require_capability('moodle/course:changeidnumber', $context);
861 // Check if user can change summary.
862 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
863 require_capability('moodle/course:changesummary', $context);
866 // Summary format.
867 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
868 require_capability('moodle/course:changesummary', $context);
869 $course['summaryformat'] = external_validate_format($course['summaryformat']);
872 // Check if user can change visibility.
873 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
874 require_capability('moodle/course:visibility', $context);
877 // Make sure lang is valid.
878 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
879 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
882 // Make sure theme is valid.
883 if (array_key_exists('forcetheme', $course)) {
884 if (!empty($CFG->allowcoursethemes)) {
885 if (empty($availablethemes[$course['forcetheme']])) {
886 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
887 } else {
888 $course['theme'] = $course['forcetheme'];
893 // Make sure completion is enabled before setting it.
894 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
895 $course['enabledcompletion'] = 0;
898 // Make sure maxbytes are less then CFG->maxbytes.
899 if (array_key_exists('maxbytes', $course)) {
900 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
903 if (!empty($course['courseformatoptions'])) {
904 foreach ($course['courseformatoptions'] as $option) {
905 if (isset($option['name']) && isset($option['value'])) {
906 $course[$option['name']] = $option['value'];
911 // Update course if user has all required capabilities.
912 update_course((object) $course);
913 } catch (Exception $e) {
914 $warning = array();
915 $warning['item'] = 'course';
916 $warning['itemid'] = $course['id'];
917 if ($e instanceof moodle_exception) {
918 $warning['warningcode'] = $e->errorcode;
919 } else {
920 $warning['warningcode'] = $e->getCode();
922 $warning['message'] = $e->getMessage();
923 $warnings[] = $warning;
927 $result = array();
928 $result['warnings'] = $warnings;
929 return $result;
933 * Returns description of method result value
935 * @return external_description
936 * @since Moodle 2.5
938 public static function update_courses_returns() {
939 return new external_single_structure(
940 array(
941 'warnings' => new external_warnings()
947 * Returns description of method parameters
949 * @return external_function_parameters
950 * @since Moodle 2.2
952 public static function delete_courses_parameters() {
953 return new external_function_parameters(
954 array(
955 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
961 * Delete courses
963 * @param array $courseids A list of course ids
964 * @since Moodle 2.2
966 public static function delete_courses($courseids) {
967 global $CFG, $DB;
968 require_once($CFG->dirroot."/course/lib.php");
970 // Parameter validation.
971 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
973 $warnings = array();
975 foreach ($params['courseids'] as $courseid) {
976 $course = $DB->get_record('course', array('id' => $courseid));
978 if ($course === false) {
979 $warnings[] = array(
980 'item' => 'course',
981 'itemid' => $courseid,
982 'warningcode' => 'unknowncourseidnumber',
983 'message' => 'Unknown course ID ' . $courseid
985 continue;
988 // Check if the context is valid.
989 $coursecontext = context_course::instance($course->id);
990 self::validate_context($coursecontext);
992 // Check if the current user has permission.
993 if (!can_delete_course($courseid)) {
994 $warnings[] = array(
995 'item' => 'course',
996 'itemid' => $courseid,
997 'warningcode' => 'cannotdeletecourse',
998 'message' => 'You do not have the permission to delete this course' . $courseid
1000 continue;
1003 if (delete_course($course, false) === false) {
1004 $warnings[] = array(
1005 'item' => 'course',
1006 'itemid' => $courseid,
1007 'warningcode' => 'cannotdeletecategorycourse',
1008 'message' => 'Course ' . $courseid . ' failed to be deleted'
1010 continue;
1014 fix_course_sortorder();
1016 return array('warnings' => $warnings);
1020 * Returns description of method result value
1022 * @return external_description
1023 * @since Moodle 2.2
1025 public static function delete_courses_returns() {
1026 return new external_single_structure(
1027 array(
1028 'warnings' => new external_warnings()
1034 * Returns description of method parameters
1036 * @return external_function_parameters
1037 * @since Moodle 2.3
1039 public static function duplicate_course_parameters() {
1040 return new external_function_parameters(
1041 array(
1042 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1043 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1044 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1045 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1046 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1047 'options' => new external_multiple_structure(
1048 new external_single_structure(
1049 array(
1050 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
1051 "activities" (int) Include course activites (default to 1 that is equal to yes),
1052 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1053 "filters" (int) Include course filters (default to 1 that is equal to yes),
1054 "users" (int) Include users (default to 0 that is equal to no),
1055 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
1056 "comments" (int) Include user comments (default to 0 that is equal to no),
1057 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
1058 "logs" (int) Include course logs (default to 0 that is equal to no),
1059 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
1061 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1064 ), VALUE_DEFAULT, array()
1071 * Duplicate a course
1073 * @param int $courseid
1074 * @param string $fullname Duplicated course fullname
1075 * @param string $shortname Duplicated course shortname
1076 * @param int $categoryid Duplicated course parent category id
1077 * @param int $visible Duplicated course availability
1078 * @param array $options List of backup options
1079 * @return array New course info
1080 * @since Moodle 2.3
1082 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
1083 global $CFG, $USER, $DB;
1084 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1085 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1087 // Parameter validation.
1088 $params = self::validate_parameters(
1089 self::duplicate_course_parameters(),
1090 array(
1091 'courseid' => $courseid,
1092 'fullname' => $fullname,
1093 'shortname' => $shortname,
1094 'categoryid' => $categoryid,
1095 'visible' => $visible,
1096 'options' => $options
1100 // Context validation.
1102 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
1103 throw new moodle_exception('invalidcourseid', 'error');
1106 // Category where duplicated course is going to be created.
1107 $categorycontext = context_coursecat::instance($params['categoryid']);
1108 self::validate_context($categorycontext);
1110 // Course to be duplicated.
1111 $coursecontext = context_course::instance($course->id);
1112 self::validate_context($coursecontext);
1114 $backupdefaults = array(
1115 'activities' => 1,
1116 'blocks' => 1,
1117 'filters' => 1,
1118 'users' => 0,
1119 'role_assignments' => 0,
1120 'comments' => 0,
1121 'userscompletion' => 0,
1122 'logs' => 0,
1123 'grade_histories' => 0
1126 $backupsettings = array();
1127 // Check for backup and restore options.
1128 if (!empty($params['options'])) {
1129 foreach ($params['options'] as $option) {
1131 // Strict check for a correct value (allways 1 or 0, true or false).
1132 $value = clean_param($option['value'], PARAM_INT);
1134 if ($value !== 0 and $value !== 1) {
1135 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1138 if (!isset($backupdefaults[$option['name']])) {
1139 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1142 $backupsettings[$option['name']] = $value;
1146 // Capability checking.
1148 // The backup controller check for this currently, this may be redundant.
1149 require_capability('moodle/course:create', $categorycontext);
1150 require_capability('moodle/restore:restorecourse', $categorycontext);
1151 require_capability('moodle/backup:backupcourse', $coursecontext);
1153 if (!empty($backupsettings['users'])) {
1154 require_capability('moodle/backup:userinfo', $coursecontext);
1155 require_capability('moodle/restore:userinfo', $categorycontext);
1158 // Check if the shortname is used.
1159 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1160 foreach ($foundcourses as $foundcourse) {
1161 $foundcoursenames[] = $foundcourse->fullname;
1164 $foundcoursenamestring = implode(',', $foundcoursenames);
1165 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1168 // Backup the course.
1170 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1171 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1173 foreach ($backupsettings as $name => $value) {
1174 $bc->get_plan()->get_setting($name)->set_value($value);
1177 $backupid = $bc->get_backupid();
1178 $backupbasepath = $bc->get_plan()->get_basepath();
1180 $bc->execute_plan();
1181 $results = $bc->get_results();
1182 $file = $results['backup_destination'];
1184 $bc->destroy();
1186 // Restore the backup immediately.
1188 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1189 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1190 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1193 // Create new course.
1194 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1196 $rc = new restore_controller($backupid, $newcourseid,
1197 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1199 foreach ($backupsettings as $name => $value) {
1200 $setting = $rc->get_plan()->get_setting($name);
1201 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1202 $setting->set_value($value);
1206 if (!$rc->execute_precheck()) {
1207 $precheckresults = $rc->get_precheck_results();
1208 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1209 if (empty($CFG->keeptempdirectoriesonbackup)) {
1210 fulldelete($backupbasepath);
1213 $errorinfo = '';
1215 foreach ($precheckresults['errors'] as $error) {
1216 $errorinfo .= $error;
1219 if (array_key_exists('warnings', $precheckresults)) {
1220 foreach ($precheckresults['warnings'] as $warning) {
1221 $errorinfo .= $warning;
1225 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1229 $rc->execute_plan();
1230 $rc->destroy();
1232 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1233 $course->fullname = $params['fullname'];
1234 $course->shortname = $params['shortname'];
1235 $course->visible = $params['visible'];
1237 // Set shortname and fullname back.
1238 $DB->update_record('course', $course);
1240 if (empty($CFG->keeptempdirectoriesonbackup)) {
1241 fulldelete($backupbasepath);
1244 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1245 $file->delete();
1247 return array('id' => $course->id, 'shortname' => $course->shortname);
1251 * Returns description of method result value
1253 * @return external_description
1254 * @since Moodle 2.3
1256 public static function duplicate_course_returns() {
1257 return new external_single_structure(
1258 array(
1259 'id' => new external_value(PARAM_INT, 'course id'),
1260 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1266 * Returns description of method parameters for import_course
1268 * @return external_function_parameters
1269 * @since Moodle 2.4
1271 public static function import_course_parameters() {
1272 return new external_function_parameters(
1273 array(
1274 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1275 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1276 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1277 'options' => new external_multiple_structure(
1278 new external_single_structure(
1279 array(
1280 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1281 "activities" (int) Include course activites (default to 1 that is equal to yes),
1282 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1283 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1285 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1288 ), VALUE_DEFAULT, array()
1295 * Imports a course
1297 * @param int $importfrom The id of the course we are importing from
1298 * @param int $importto The id of the course we are importing to
1299 * @param bool $deletecontent Whether to delete the course we are importing to content
1300 * @param array $options List of backup options
1301 * @return null
1302 * @since Moodle 2.4
1304 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1305 global $CFG, $USER, $DB;
1306 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1307 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1309 // Parameter validation.
1310 $params = self::validate_parameters(
1311 self::import_course_parameters(),
1312 array(
1313 'importfrom' => $importfrom,
1314 'importto' => $importto,
1315 'deletecontent' => $deletecontent,
1316 'options' => $options
1320 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1321 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1324 // Context validation.
1326 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1327 throw new moodle_exception('invalidcourseid', 'error');
1330 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1331 throw new moodle_exception('invalidcourseid', 'error');
1334 $importfromcontext = context_course::instance($importfrom->id);
1335 self::validate_context($importfromcontext);
1337 $importtocontext = context_course::instance($importto->id);
1338 self::validate_context($importtocontext);
1340 $backupdefaults = array(
1341 'activities' => 1,
1342 'blocks' => 1,
1343 'filters' => 1
1346 $backupsettings = array();
1348 // Check for backup and restore options.
1349 if (!empty($params['options'])) {
1350 foreach ($params['options'] as $option) {
1352 // Strict check for a correct value (allways 1 or 0, true or false).
1353 $value = clean_param($option['value'], PARAM_INT);
1355 if ($value !== 0 and $value !== 1) {
1356 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1359 if (!isset($backupdefaults[$option['name']])) {
1360 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1363 $backupsettings[$option['name']] = $value;
1367 // Capability checking.
1369 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1370 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1372 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1373 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1375 foreach ($backupsettings as $name => $value) {
1376 $bc->get_plan()->get_setting($name)->set_value($value);
1379 $backupid = $bc->get_backupid();
1380 $backupbasepath = $bc->get_plan()->get_basepath();
1382 $bc->execute_plan();
1383 $bc->destroy();
1385 // Restore the backup immediately.
1387 // Check if we must delete the contents of the destination course.
1388 if ($params['deletecontent']) {
1389 $restoretarget = backup::TARGET_EXISTING_DELETING;
1390 } else {
1391 $restoretarget = backup::TARGET_EXISTING_ADDING;
1394 $rc = new restore_controller($backupid, $importto->id,
1395 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1397 foreach ($backupsettings as $name => $value) {
1398 $rc->get_plan()->get_setting($name)->set_value($value);
1401 if (!$rc->execute_precheck()) {
1402 $precheckresults = $rc->get_precheck_results();
1403 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1404 if (empty($CFG->keeptempdirectoriesonbackup)) {
1405 fulldelete($backupbasepath);
1408 $errorinfo = '';
1410 foreach ($precheckresults['errors'] as $error) {
1411 $errorinfo .= $error;
1414 if (array_key_exists('warnings', $precheckresults)) {
1415 foreach ($precheckresults['warnings'] as $warning) {
1416 $errorinfo .= $warning;
1420 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1422 } else {
1423 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1424 restore_dbops::delete_course_content($importto->id);
1428 $rc->execute_plan();
1429 $rc->destroy();
1431 if (empty($CFG->keeptempdirectoriesonbackup)) {
1432 fulldelete($backupbasepath);
1435 return null;
1439 * Returns description of method result value
1441 * @return external_description
1442 * @since Moodle 2.4
1444 public static function import_course_returns() {
1445 return null;
1449 * Returns description of method parameters
1451 * @return external_function_parameters
1452 * @since Moodle 2.3
1454 public static function get_categories_parameters() {
1455 return new external_function_parameters(
1456 array(
1457 'criteria' => new external_multiple_structure(
1458 new external_single_structure(
1459 array(
1460 'key' => new external_value(PARAM_ALPHA,
1461 'The category column to search, expected keys (value format) are:'.
1462 '"id" (int) the category id,'.
1463 '"ids" (string) category ids separated by commas,'.
1464 '"name" (string) the category name,'.
1465 '"parent" (int) the parent category id,'.
1466 '"idnumber" (string) category idnumber'.
1467 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1468 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1469 then the function return all categories that the user can see.'.
1470 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1471 '"theme" (string) only return the categories having this theme'.
1472 ' - user must have \'moodle/category:manage\' to search on theme'),
1473 'value' => new external_value(PARAM_RAW, 'the value to match')
1475 ), 'criteria', VALUE_DEFAULT, array()
1477 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1478 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1484 * Get categories
1486 * @param array $criteria Criteria to match the results
1487 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1488 * @return array list of categories
1489 * @since Moodle 2.3
1491 public static function get_categories($criteria = array(), $addsubcategories = true) {
1492 global $CFG, $DB;
1493 require_once($CFG->dirroot . "/course/lib.php");
1495 // Validate parameters.
1496 $params = self::validate_parameters(self::get_categories_parameters(),
1497 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1499 // Retrieve the categories.
1500 $categories = array();
1501 if (!empty($params['criteria'])) {
1503 $conditions = array();
1504 $wheres = array();
1505 foreach ($params['criteria'] as $crit) {
1506 $key = trim($crit['key']);
1508 // Trying to avoid duplicate keys.
1509 if (!isset($conditions[$key])) {
1511 $context = context_system::instance();
1512 $value = null;
1513 switch ($key) {
1514 case 'id':
1515 $value = clean_param($crit['value'], PARAM_INT);
1516 $conditions[$key] = $value;
1517 $wheres[] = $key . " = :" . $key;
1518 break;
1520 case 'ids':
1521 $value = clean_param($crit['value'], PARAM_SEQUENCE);
1522 $ids = explode(',', $value);
1523 list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
1524 $conditions = array_merge($conditions, $paramids);
1525 $wheres[] = 'id ' . $sqlids;
1526 break;
1528 case 'idnumber':
1529 if (has_capability('moodle/category:manage', $context)) {
1530 $value = clean_param($crit['value'], PARAM_RAW);
1531 $conditions[$key] = $value;
1532 $wheres[] = $key . " = :" . $key;
1533 } else {
1534 // We must throw an exception.
1535 // Otherwise the dev client would think no idnumber exists.
1536 throw new moodle_exception('criteriaerror',
1537 'webservice', '', null,
1538 'You don\'t have the permissions to search on the "idnumber" field.');
1540 break;
1542 case 'name':
1543 $value = clean_param($crit['value'], PARAM_TEXT);
1544 $conditions[$key] = $value;
1545 $wheres[] = $key . " = :" . $key;
1546 break;
1548 case 'parent':
1549 $value = clean_param($crit['value'], PARAM_INT);
1550 $conditions[$key] = $value;
1551 $wheres[] = $key . " = :" . $key;
1552 break;
1554 case 'visible':
1555 if (has_capability('moodle/category:manage', $context)
1556 or has_capability('moodle/category:viewhiddencategories',
1557 context_system::instance())) {
1558 $value = clean_param($crit['value'], PARAM_INT);
1559 $conditions[$key] = $value;
1560 $wheres[] = $key . " = :" . $key;
1561 } else {
1562 throw new moodle_exception('criteriaerror',
1563 'webservice', '', null,
1564 'You don\'t have the permissions to search on the "visible" field.');
1566 break;
1568 case 'theme':
1569 if (has_capability('moodle/category:manage', $context)) {
1570 $value = clean_param($crit['value'], PARAM_THEME);
1571 $conditions[$key] = $value;
1572 $wheres[] = $key . " = :" . $key;
1573 } else {
1574 throw new moodle_exception('criteriaerror',
1575 'webservice', '', null,
1576 'You don\'t have the permissions to search on the "theme" field.');
1578 break;
1580 default:
1581 throw new moodle_exception('criteriaerror',
1582 'webservice', '', null,
1583 'You can not search on this criteria: ' . $key);
1588 if (!empty($wheres)) {
1589 $wheres = implode(" AND ", $wheres);
1591 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1593 // Retrieve its sub subcategories (all levels).
1594 if ($categories and !empty($params['addsubcategories'])) {
1595 $newcategories = array();
1597 // Check if we required visible/theme checks.
1598 $additionalselect = '';
1599 $additionalparams = array();
1600 if (isset($conditions['visible'])) {
1601 $additionalselect .= ' AND visible = :visible';
1602 $additionalparams['visible'] = $conditions['visible'];
1604 if (isset($conditions['theme'])) {
1605 $additionalselect .= ' AND theme= :theme';
1606 $additionalparams['theme'] = $conditions['theme'];
1609 foreach ($categories as $category) {
1610 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1611 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1612 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1613 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1615 $categories = $categories + $newcategories;
1619 } else {
1620 // Retrieve all categories in the database.
1621 $categories = $DB->get_records('course_categories');
1624 // The not returned categories. key => category id, value => reason of exclusion.
1625 $excludedcats = array();
1627 // The returned categories.
1628 $categoriesinfo = array();
1630 // We need to sort the categories by path.
1631 // The parent cats need to be checked by the algo first.
1632 usort($categories, "core_course_external::compare_categories_by_path");
1634 foreach ($categories as $category) {
1636 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1637 $parents = explode('/', $category->path);
1638 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1639 foreach ($parents as $parentid) {
1640 // Note: when the parent exclusion was due to the context,
1641 // the sub category could still be returned.
1642 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1643 $excludedcats[$category->id] = 'parent';
1647 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1648 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1649 and !has_capability('moodle/category:manage', $context)) {
1650 $excludedcats[$category->id] = 'depth';
1653 // Check the user can use the category context.
1654 $context = context_coursecat::instance($category->id);
1655 try {
1656 self::validate_context($context);
1657 } catch (Exception $e) {
1658 $excludedcats[$category->id] = 'context';
1660 // If it was the requested category then throw an exception.
1661 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1662 $exceptionparam = new stdClass();
1663 $exceptionparam->message = $e->getMessage();
1664 $exceptionparam->catid = $category->id;
1665 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1669 // Return the category information.
1670 if (!isset($excludedcats[$category->id])) {
1672 // Final check to see if the category is visible to the user.
1673 if ($category->visible
1674 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1675 or has_capability('moodle/category:manage', $context)) {
1677 $categoryinfo = array();
1678 $categoryinfo['id'] = $category->id;
1679 $categoryinfo['name'] = $category->name;
1680 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1681 external_format_text($category->description, $category->descriptionformat,
1682 $context->id, 'coursecat', 'description', null);
1683 $categoryinfo['parent'] = $category->parent;
1684 $categoryinfo['sortorder'] = $category->sortorder;
1685 $categoryinfo['coursecount'] = $category->coursecount;
1686 $categoryinfo['depth'] = $category->depth;
1687 $categoryinfo['path'] = $category->path;
1689 // Some fields only returned for admin.
1690 if (has_capability('moodle/category:manage', $context)) {
1691 $categoryinfo['idnumber'] = $category->idnumber;
1692 $categoryinfo['visible'] = $category->visible;
1693 $categoryinfo['visibleold'] = $category->visibleold;
1694 $categoryinfo['timemodified'] = $category->timemodified;
1695 $categoryinfo['theme'] = $category->theme;
1698 $categoriesinfo[] = $categoryinfo;
1699 } else {
1700 $excludedcats[$category->id] = 'visibility';
1705 // Sorting the resulting array so it looks a bit better for the client developer.
1706 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1708 return $categoriesinfo;
1712 * Sort categories array by path
1713 * private function: only used by get_categories
1715 * @param array $category1
1716 * @param array $category2
1717 * @return int result of strcmp
1718 * @since Moodle 2.3
1720 private static function compare_categories_by_path($category1, $category2) {
1721 return strcmp($category1->path, $category2->path);
1725 * Sort categories array by sortorder
1726 * private function: only used by get_categories
1728 * @param array $category1
1729 * @param array $category2
1730 * @return int result of strcmp
1731 * @since Moodle 2.3
1733 private static function compare_categories_by_sortorder($category1, $category2) {
1734 return strcmp($category1['sortorder'], $category2['sortorder']);
1738 * Returns description of method result value
1740 * @return external_description
1741 * @since Moodle 2.3
1743 public static function get_categories_returns() {
1744 return new external_multiple_structure(
1745 new external_single_structure(
1746 array(
1747 'id' => new external_value(PARAM_INT, 'category id'),
1748 'name' => new external_value(PARAM_TEXT, 'category name'),
1749 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1750 'description' => new external_value(PARAM_RAW, 'category description'),
1751 'descriptionformat' => new external_format_value('description'),
1752 'parent' => new external_value(PARAM_INT, 'parent category id'),
1753 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1754 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1755 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1756 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1757 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1758 'depth' => new external_value(PARAM_INT, 'category depth'),
1759 'path' => new external_value(PARAM_TEXT, 'category path'),
1760 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1761 ), 'List of categories'
1767 * Returns description of method parameters
1769 * @return external_function_parameters
1770 * @since Moodle 2.3
1772 public static function create_categories_parameters() {
1773 return new external_function_parameters(
1774 array(
1775 'categories' => new external_multiple_structure(
1776 new external_single_structure(
1777 array(
1778 'name' => new external_value(PARAM_TEXT, 'new category name'),
1779 'parent' => new external_value(PARAM_INT,
1780 'the parent category id inside which the new category will be created
1781 - set to 0 for a root category',
1782 VALUE_DEFAULT, 0),
1783 'idnumber' => new external_value(PARAM_RAW,
1784 'the new category idnumber', VALUE_OPTIONAL),
1785 'description' => new external_value(PARAM_RAW,
1786 'the new category description', VALUE_OPTIONAL),
1787 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1788 'theme' => new external_value(PARAM_THEME,
1789 'the new category theme. This option must be enabled on moodle',
1790 VALUE_OPTIONAL),
1799 * Create categories
1801 * @param array $categories - see create_categories_parameters() for the array structure
1802 * @return array - see create_categories_returns() for the array structure
1803 * @since Moodle 2.3
1805 public static function create_categories($categories) {
1806 global $CFG, $DB;
1807 require_once($CFG->libdir . "/coursecatlib.php");
1809 $params = self::validate_parameters(self::create_categories_parameters(),
1810 array('categories' => $categories));
1812 $transaction = $DB->start_delegated_transaction();
1814 $createdcategories = array();
1815 foreach ($params['categories'] as $category) {
1816 if ($category['parent']) {
1817 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1818 throw new moodle_exception('unknowcategory');
1820 $context = context_coursecat::instance($category['parent']);
1821 } else {
1822 $context = context_system::instance();
1824 self::validate_context($context);
1825 require_capability('moodle/category:manage', $context);
1827 // this will validate format and throw an exception if there are errors
1828 external_validate_format($category['descriptionformat']);
1830 $newcategory = coursecat::create($category);
1832 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1835 $transaction->allow_commit();
1837 return $createdcategories;
1841 * Returns description of method parameters
1843 * @return external_function_parameters
1844 * @since Moodle 2.3
1846 public static function create_categories_returns() {
1847 return new external_multiple_structure(
1848 new external_single_structure(
1849 array(
1850 'id' => new external_value(PARAM_INT, 'new category id'),
1851 'name' => new external_value(PARAM_TEXT, 'new category name'),
1858 * Returns description of method parameters
1860 * @return external_function_parameters
1861 * @since Moodle 2.3
1863 public static function update_categories_parameters() {
1864 return new external_function_parameters(
1865 array(
1866 'categories' => new external_multiple_structure(
1867 new external_single_structure(
1868 array(
1869 'id' => new external_value(PARAM_INT, 'course id'),
1870 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1871 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1872 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1873 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1874 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1875 'theme' => new external_value(PARAM_THEME,
1876 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1885 * Update categories
1887 * @param array $categories The list of categories to update
1888 * @return null
1889 * @since Moodle 2.3
1891 public static function update_categories($categories) {
1892 global $CFG, $DB;
1893 require_once($CFG->libdir . "/coursecatlib.php");
1895 // Validate parameters.
1896 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1898 $transaction = $DB->start_delegated_transaction();
1900 foreach ($params['categories'] as $cat) {
1901 $category = coursecat::get($cat['id']);
1903 $categorycontext = context_coursecat::instance($cat['id']);
1904 self::validate_context($categorycontext);
1905 require_capability('moodle/category:manage', $categorycontext);
1907 // this will throw an exception if descriptionformat is not valid
1908 external_validate_format($cat['descriptionformat']);
1910 $category->update($cat);
1913 $transaction->allow_commit();
1917 * Returns description of method result value
1919 * @return external_description
1920 * @since Moodle 2.3
1922 public static function update_categories_returns() {
1923 return null;
1927 * Returns description of method parameters
1929 * @return external_function_parameters
1930 * @since Moodle 2.3
1932 public static function delete_categories_parameters() {
1933 return new external_function_parameters(
1934 array(
1935 'categories' => new external_multiple_structure(
1936 new external_single_structure(
1937 array(
1938 'id' => new external_value(PARAM_INT, 'category id to delete'),
1939 'newparent' => new external_value(PARAM_INT,
1940 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1941 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1942 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1951 * Delete categories
1953 * @param array $categories A list of category ids
1954 * @return array
1955 * @since Moodle 2.3
1957 public static function delete_categories($categories) {
1958 global $CFG, $DB;
1959 require_once($CFG->dirroot . "/course/lib.php");
1960 require_once($CFG->libdir . "/coursecatlib.php");
1962 // Validate parameters.
1963 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1965 $transaction = $DB->start_delegated_transaction();
1967 foreach ($params['categories'] as $category) {
1968 $deletecat = coursecat::get($category['id'], MUST_EXIST);
1969 $context = context_coursecat::instance($deletecat->id);
1970 require_capability('moodle/category:manage', $context);
1971 self::validate_context($context);
1972 self::validate_context(get_category_or_system_context($deletecat->parent));
1974 if ($category['recursive']) {
1975 // If recursive was specified, then we recursively delete the category's contents.
1976 if ($deletecat->can_delete_full()) {
1977 $deletecat->delete_full(false);
1978 } else {
1979 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1981 } else {
1982 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1983 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1984 // We must move to an existing category.
1985 if (!empty($category['newparent'])) {
1986 $newparentcat = coursecat::get($category['newparent']);
1987 } else {
1988 $newparentcat = coursecat::get($deletecat->parent);
1991 // This operation is not allowed. We must move contents to an existing category.
1992 if (!$newparentcat->id) {
1993 throw new moodle_exception('movecatcontentstoroot');
1996 self::validate_context(context_coursecat::instance($newparentcat->id));
1997 if ($deletecat->can_move_content_to($newparentcat->id)) {
1998 $deletecat->delete_move($newparentcat->id, false);
1999 } else {
2000 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
2005 $transaction->allow_commit();
2009 * Returns description of method parameters
2011 * @return external_function_parameters
2012 * @since Moodle 2.3
2014 public static function delete_categories_returns() {
2015 return null;
2019 * Describes the parameters for delete_modules.
2021 * @return external_external_function_parameters
2022 * @since Moodle 2.5
2024 public static function delete_modules_parameters() {
2025 return new external_function_parameters (
2026 array(
2027 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
2028 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
2034 * Deletes a list of provided module instances.
2036 * @param array $cmids the course module ids
2037 * @since Moodle 2.5
2039 public static function delete_modules($cmids) {
2040 global $CFG, $DB;
2042 // Require course file containing the course delete module function.
2043 require_once($CFG->dirroot . "/course/lib.php");
2045 // Clean the parameters.
2046 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2048 // Keep track of the course ids we have performed a capability check on to avoid repeating.
2049 $arrcourseschecked = array();
2051 foreach ($params['cmids'] as $cmid) {
2052 // Get the course module.
2053 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2055 // Check if we have not yet confirmed they have permission in this course.
2056 if (!in_array($cm->course, $arrcourseschecked)) {
2057 // Ensure the current user has required permission in this course.
2058 $context = context_course::instance($cm->course);
2059 self::validate_context($context);
2060 // Add to the array.
2061 $arrcourseschecked[] = $cm->course;
2064 // Ensure they can delete this module.
2065 $modcontext = context_module::instance($cm->id);
2066 require_capability('moodle/course:manageactivities', $modcontext);
2068 // Delete the module.
2069 course_delete_module($cm->id);
2074 * Describes the delete_modules return value.
2076 * @return external_single_structure
2077 * @since Moodle 2.5
2079 public static function delete_modules_returns() {
2080 return null;
2084 * Returns description of method parameters
2086 * @return external_function_parameters
2087 * @since Moodle 2.9
2089 public static function view_course_parameters() {
2090 return new external_function_parameters(
2091 array(
2092 'courseid' => new external_value(PARAM_INT, 'id of the course'),
2093 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2099 * Trigger the course viewed event.
2101 * @param int $courseid id of course
2102 * @param int $sectionnumber sectionnumber (0, 1, 2...)
2103 * @return array of warnings and status result
2104 * @since Moodle 2.9
2105 * @throws moodle_exception
2107 public static function view_course($courseid, $sectionnumber = 0) {
2108 global $CFG;
2109 require_once($CFG->dirroot . "/course/lib.php");
2111 $params = self::validate_parameters(self::view_course_parameters(),
2112 array(
2113 'courseid' => $courseid,
2114 'sectionnumber' => $sectionnumber
2117 $warnings = array();
2119 $course = get_course($params['courseid']);
2120 $context = context_course::instance($course->id);
2121 self::validate_context($context);
2123 if (!empty($params['sectionnumber'])) {
2125 // Get section details and check it exists.
2126 $modinfo = get_fast_modinfo($course);
2127 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2129 // Check user is allowed to see it.
2130 if (!$coursesection->uservisible) {
2131 require_capability('moodle/course:viewhiddensections', $context);
2135 course_view($context, $params['sectionnumber']);
2137 $result = array();
2138 $result['status'] = true;
2139 $result['warnings'] = $warnings;
2140 return $result;
2144 * Returns description of method result value
2146 * @return external_description
2147 * @since Moodle 2.9
2149 public static function view_course_returns() {
2150 return new external_single_structure(
2151 array(
2152 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2153 'warnings' => new external_warnings()
2159 * Returns description of method parameters
2161 * @return external_function_parameters
2162 * @since Moodle 3.0
2164 public static function search_courses_parameters() {
2165 return new external_function_parameters(
2166 array(
2167 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name
2168 (search, modulelist (only admins), blocklist (only admins), tagid)'),
2169 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2170 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2171 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
2172 'requiredcapabilities' => new external_multiple_structure(
2173 new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'),
2174 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
2176 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
2182 * Return the course information that is public (visible by every one)
2184 * @param course_in_list $course course in list object
2185 * @param stdClass $coursecontext course context object
2186 * @return array the course information
2187 * @since Moodle 3.2
2189 protected static function get_course_public_information(course_in_list $course, $coursecontext) {
2191 static $categoriescache = array();
2193 // Category information.
2194 if (!array_key_exists($course->category, $categoriescache)) {
2195 $categoriescache[$course->category] = coursecat::get($course->category, IGNORE_MISSING);
2197 $category = $categoriescache[$course->category];
2199 // Retrieve course overview used files.
2200 $files = array();
2201 foreach ($course->get_course_overviewfiles() as $file) {
2202 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2203 $file->get_filearea(), null, $file->get_filepath(),
2204 $file->get_filename())->out(false);
2205 $files[] = array(
2206 'filename' => $file->get_filename(),
2207 'fileurl' => $fileurl,
2208 'filesize' => $file->get_filesize(),
2209 'filepath' => $file->get_filepath(),
2210 'mimetype' => $file->get_mimetype(),
2211 'timemodified' => $file->get_timemodified(),
2215 // Retrieve the course contacts,
2216 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2217 $coursecontacts = array();
2218 foreach ($course->get_course_contacts() as $contact) {
2219 $coursecontacts[] = array(
2220 'id' => $contact['user']->id,
2221 'fullname' => $contact['username']
2225 // Allowed enrolment methods (maybe we can self-enrol).
2226 $enroltypes = array();
2227 $instances = enrol_get_instances($course->id, true);
2228 foreach ($instances as $instance) {
2229 $enroltypes[] = $instance->enrol;
2232 // Format summary.
2233 list($summary, $summaryformat) =
2234 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2236 $displayname = get_course_display_name_for_list($course);
2237 $coursereturns = array();
2238 $coursereturns['id'] = $course->id;
2239 $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id);
2240 $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id);
2241 $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id);
2242 $coursereturns['categoryid'] = $course->category;
2243 $coursereturns['categoryname'] = $category == null ? '' : $category->name;
2244 $coursereturns['summary'] = $summary;
2245 $coursereturns['summaryformat'] = $summaryformat;
2246 $coursereturns['summaryfiles'] = external_util::get_area_files($coursecontext->id, 'course', 'summary', false, false);
2247 $coursereturns['overviewfiles'] = $files;
2248 $coursereturns['contacts'] = $coursecontacts;
2249 $coursereturns['enrollmentmethods'] = $enroltypes;
2250 return $coursereturns;
2254 * Search courses following the specified criteria.
2256 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2257 * @param string $criteriavalue Criteria value
2258 * @param int $page Page number (for pagination)
2259 * @param int $perpage Items per page
2260 * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
2261 * @param int $limittoenrolled Limit to only enrolled courses
2262 * @return array of course objects and warnings
2263 * @since Moodle 3.0
2264 * @throws moodle_exception
2266 public static function search_courses($criterianame,
2267 $criteriavalue,
2268 $page=0,
2269 $perpage=0,
2270 $requiredcapabilities=array(),
2271 $limittoenrolled=0) {
2272 global $CFG;
2273 require_once($CFG->libdir . '/coursecatlib.php');
2275 $warnings = array();
2277 $parameters = array(
2278 'criterianame' => $criterianame,
2279 'criteriavalue' => $criteriavalue,
2280 'page' => $page,
2281 'perpage' => $perpage,
2282 'requiredcapabilities' => $requiredcapabilities
2284 $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2285 self::validate_context(context_system::instance());
2287 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2288 if (!in_array($params['criterianame'], $allowedcriterianames)) {
2289 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2290 'allowed values are: '.implode(',', $allowedcriterianames));
2293 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2294 require_capability('moodle/site:config', context_system::instance());
2297 $paramtype = array(
2298 'search' => PARAM_RAW,
2299 'modulelist' => PARAM_PLUGIN,
2300 'blocklist' => PARAM_INT,
2301 'tagid' => PARAM_INT
2303 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2305 // Prepare the search API options.
2306 $searchcriteria = array();
2307 $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2309 $options = array();
2310 if ($params['perpage'] != 0) {
2311 $offset = $params['page'] * $params['perpage'];
2312 $options = array('offset' => $offset, 'limit' => $params['perpage']);
2315 // Search the courses.
2316 $courses = coursecat::search_courses($searchcriteria, $options, $params['requiredcapabilities']);
2317 $totalcount = coursecat::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']);
2319 if (!empty($limittoenrolled)) {
2320 // Get the courses where the current user has access.
2321 $enrolled = enrol_get_my_courses(array('id', 'cacherev'));
2324 $finalcourses = array();
2325 $categoriescache = array();
2327 foreach ($courses as $course) {
2328 if (!empty($limittoenrolled)) {
2329 // Filter out not enrolled courses.
2330 if (!isset($enrolled[$course->id])) {
2331 $totalcount--;
2332 continue;
2336 $coursecontext = context_course::instance($course->id);
2338 $finalcourses[] = self::get_course_public_information($course, $coursecontext);
2341 return array(
2342 'total' => $totalcount,
2343 'courses' => $finalcourses,
2344 'warnings' => $warnings
2349 * Returns a course structure definition
2351 * @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible
2352 * @return array the course structure
2353 * @since Moodle 3.2
2355 protected static function get_course_structure($onlypublicdata = true) {
2356 $coursestructure = array(
2357 'id' => new external_value(PARAM_INT, 'course id'),
2358 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2359 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
2360 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2361 'categoryid' => new external_value(PARAM_INT, 'category id'),
2362 'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2363 'summary' => new external_value(PARAM_RAW, 'summary'),
2364 'summaryformat' => new external_format_value('summary'),
2365 'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL),
2366 'overviewfiles' => new external_files('additional overview files attached to this course'),
2367 'contacts' => new external_multiple_structure(
2368 new external_single_structure(
2369 array(
2370 'id' => new external_value(PARAM_INT, 'contact user id'),
2371 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2374 'contact users'
2376 'enrollmentmethods' => new external_multiple_structure(
2377 new external_value(PARAM_PLUGIN, 'enrollment method'),
2378 'enrollment methods list'
2382 if (!$onlypublicdata) {
2383 $extra = array(
2384 'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL),
2385 'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
2386 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
2387 'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL),
2388 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
2389 'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL),
2390 'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
2391 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL),
2392 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
2393 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
2394 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
2395 'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL),
2396 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
2397 'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL),
2398 'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL),
2399 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL),
2400 'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL),
2401 'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL),
2402 'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL),
2403 'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL),
2404 'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL),
2405 'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL),
2406 'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL),
2408 $coursestructure = array_merge($coursestructure, $extra);
2410 return new external_single_structure($coursestructure);
2414 * Returns description of method result value
2416 * @return external_description
2417 * @since Moodle 3.0
2419 public static function search_courses_returns() {
2420 return new external_single_structure(
2421 array(
2422 'total' => new external_value(PARAM_INT, 'total course count'),
2423 'courses' => new external_multiple_structure(self::get_course_structure(), 'course'),
2424 'warnings' => new external_warnings()
2430 * Returns description of method parameters
2432 * @return external_function_parameters
2433 * @since Moodle 3.0
2435 public static function get_course_module_parameters() {
2436 return new external_function_parameters(
2437 array(
2438 'cmid' => new external_value(PARAM_INT, 'The course module id')
2444 * Return information about a course module.
2446 * @param int $cmid the course module id
2447 * @return array of warnings and the course module
2448 * @since Moodle 3.0
2449 * @throws moodle_exception
2451 public static function get_course_module($cmid) {
2452 global $CFG, $DB;
2454 $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid));
2455 $warnings = array();
2457 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2458 $context = context_module::instance($cm->id);
2459 self::validate_context($context);
2461 // If the user has permissions to manage the activity, return all the information.
2462 if (has_capability('moodle/course:manageactivities', $context)) {
2463 require_once($CFG->dirroot . '/course/modlib.php');
2464 require_once($CFG->libdir . '/gradelib.php');
2466 $info = $cm;
2467 // Get the extra information: grade, advanced grading and outcomes data.
2468 $course = get_course($cm->course);
2469 list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course);
2470 // Grades.
2471 $gradeinfo = array('grade', 'gradepass', 'gradecat');
2472 foreach ($gradeinfo as $gfield) {
2473 if (isset($extrainfo->{$gfield})) {
2474 $info->{$gfield} = $extrainfo->{$gfield};
2477 if (isset($extrainfo->grade) and $extrainfo->grade < 0) {
2478 $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade)));
2480 // Advanced grading.
2481 if (isset($extrainfo->_advancedgradingdata)) {
2482 $info->advancedgrading = array();
2483 foreach ($extrainfo as $key => $val) {
2484 if (strpos($key, 'advancedgradingmethod_') === 0) {
2485 $info->advancedgrading[] = array(
2486 'area' => str_replace('advancedgradingmethod_', '', $key),
2487 'method' => $val
2492 // Outcomes.
2493 foreach ($extrainfo as $key => $val) {
2494 if (strpos($key, 'outcome_') === 0) {
2495 if (!isset($info->outcomes)) {
2496 $info->outcomes = array();
2498 $id = str_replace('outcome_', '', $key);
2499 $outcome = grade_outcome::fetch(array('id' => $id));
2500 $scaleitems = $outcome->load_scale();
2501 $info->outcomes[] = array(
2502 'id' => $id,
2503 'name' => external_format_string($outcome->get_name(), $context->id),
2504 'scale' => $scaleitems->scale
2508 } else {
2509 // Return information is safe to show to any user.
2510 $info = new stdClass();
2511 $info->id = $cm->id;
2512 $info->course = $cm->course;
2513 $info->module = $cm->module;
2514 $info->modname = $cm->modname;
2515 $info->instance = $cm->instance;
2516 $info->section = $cm->section;
2517 $info->sectionnum = $cm->sectionnum;
2518 $info->groupmode = $cm->groupmode;
2519 $info->groupingid = $cm->groupingid;
2520 $info->completion = $cm->completion;
2522 // Format name.
2523 $info->name = external_format_string($cm->name, $context->id);
2524 $result = array();
2525 $result['cm'] = $info;
2526 $result['warnings'] = $warnings;
2527 return $result;
2531 * Returns description of method result value
2533 * @return external_description
2534 * @since Moodle 3.0
2536 public static function get_course_module_returns() {
2537 return new external_single_structure(
2538 array(
2539 'cm' => new external_single_structure(
2540 array(
2541 'id' => new external_value(PARAM_INT, 'The course module id'),
2542 'course' => new external_value(PARAM_INT, 'The course id'),
2543 'module' => new external_value(PARAM_INT, 'The module type id'),
2544 'name' => new external_value(PARAM_RAW, 'The activity name'),
2545 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2546 'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2547 'section' => new external_value(PARAM_INT, 'The module section id'),
2548 'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2549 'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2550 'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2551 'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2552 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2553 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2554 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2555 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
2556 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
2557 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
2558 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
2559 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
2560 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
2561 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
2562 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
2563 'grade' => new external_value(PARAM_INT, 'Grade (max value or scale id)', VALUE_OPTIONAL),
2564 'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL),
2565 'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL),
2566 'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL),
2567 'advancedgrading' => new external_multiple_structure(
2568 new external_single_structure(
2569 array(
2570 'area' => new external_value(PARAM_AREA, 'Gradable area name'),
2571 'method' => new external_value(PARAM_COMPONENT, 'Grading method'),
2574 'Advanced grading settings', VALUE_OPTIONAL
2576 'outcomes' => new external_multiple_structure(
2577 new external_single_structure(
2578 array(
2579 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'),
2580 'name' => new external_value(PARAM_TEXT, 'Outcome full name'),
2581 'scale' => new external_value(PARAM_TEXT, 'Scale items')
2584 'Outcomes information', VALUE_OPTIONAL
2588 'warnings' => new external_warnings()
2594 * Returns description of method parameters
2596 * @return external_function_parameters
2597 * @since Moodle 3.0
2599 public static function get_course_module_by_instance_parameters() {
2600 return new external_function_parameters(
2601 array(
2602 'module' => new external_value(PARAM_COMPONENT, 'The module name'),
2603 'instance' => new external_value(PARAM_INT, 'The module instance id')
2609 * Return information about a course module.
2611 * @param string $module the module name
2612 * @param int $instance the activity instance id
2613 * @return array of warnings and the course module
2614 * @since Moodle 3.0
2615 * @throws moodle_exception
2617 public static function get_course_module_by_instance($module, $instance) {
2619 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
2620 array(
2621 'module' => $module,
2622 'instance' => $instance,
2625 $warnings = array();
2626 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
2628 return self::get_course_module($cm->id);
2632 * Returns description of method result value
2634 * @return external_description
2635 * @since Moodle 3.0
2637 public static function get_course_module_by_instance_returns() {
2638 return self::get_course_module_returns();
2642 * Returns description of method parameters
2644 * @return external_function_parameters
2645 * @since Moodle 3.2
2647 public static function get_activities_overview_parameters() {
2648 return new external_function_parameters(
2649 array(
2650 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2656 * Return activities overview for the given courses.
2658 * @param array $courseids a list of course ids
2659 * @return array of warnings and the activities overview
2660 * @since Moodle 3.2
2661 * @throws moodle_exception
2663 public static function get_activities_overview($courseids) {
2664 global $USER;
2666 // Parameter validation.
2667 $params = self::validate_parameters(self::get_activities_overview_parameters(), array('courseids' => $courseids));
2668 $courseoverviews = array();
2670 list($courses, $warnings) = external_util::validate_courses($params['courseids']);
2672 if (!empty($courses)) {
2673 // Add lastaccess to each course (required by print_overview function).
2674 // We need the complete user data, the ws server does not load a complete one.
2675 $user = get_complete_user_data('id', $USER->id);
2676 foreach ($courses as $course) {
2677 if (isset($user->lastcourseaccess[$course->id])) {
2678 $course->lastaccess = $user->lastcourseaccess[$course->id];
2679 } else {
2680 $course->lastaccess = 0;
2684 $overviews = array();
2685 if ($modules = get_plugin_list_with_function('mod', 'print_overview')) {
2686 foreach ($modules as $fname) {
2687 $fname($courses, $overviews);
2691 // Format output.
2692 foreach ($overviews as $courseid => $modules) {
2693 $courseoverviews[$courseid]['id'] = $courseid;
2694 $courseoverviews[$courseid]['overviews'] = array();
2696 foreach ($modules as $modname => $overviewtext) {
2697 $courseoverviews[$courseid]['overviews'][] = array(
2698 'module' => $modname,
2699 'overviewtext' => $overviewtext // This text doesn't need formatting.
2705 $result = array(
2706 'courses' => $courseoverviews,
2707 'warnings' => $warnings
2709 return $result;
2713 * Returns description of method result value
2715 * @return external_description
2716 * @since Moodle 3.2
2718 public static function get_activities_overview_returns() {
2719 return new external_single_structure(
2720 array(
2721 'courses' => new external_multiple_structure(
2722 new external_single_structure(
2723 array(
2724 'id' => new external_value(PARAM_INT, 'Course id'),
2725 'overviews' => new external_multiple_structure(
2726 new external_single_structure(
2727 array(
2728 'module' => new external_value(PARAM_PLUGIN, 'Module name'),
2729 'overviewtext' => new external_value(PARAM_RAW, 'Overview text'),
2734 ), 'List of courses'
2736 'warnings' => new external_warnings()
2742 * Returns description of method parameters
2744 * @return external_function_parameters
2745 * @since Moodle 3.2
2747 public static function get_user_navigation_options_parameters() {
2748 return new external_function_parameters(
2749 array(
2750 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2756 * Return a list of navigation options in a set of courses that are avaialable or not for the current user.
2758 * @param array $courseids a list of course ids
2759 * @return array of warnings and the options availability
2760 * @since Moodle 3.2
2761 * @throws moodle_exception
2763 public static function get_user_navigation_options($courseids) {
2764 global $CFG;
2765 require_once($CFG->dirroot . '/course/lib.php');
2767 // Parameter validation.
2768 $params = self::validate_parameters(self::get_user_navigation_options_parameters(), array('courseids' => $courseids));
2769 $courseoptions = array();
2771 list($courses, $warnings) = external_util::validate_courses($params['courseids'], array(), true);
2773 if (!empty($courses)) {
2774 foreach ($courses as $course) {
2775 // Fix the context for the frontpage.
2776 if ($course->id == SITEID) {
2777 $course->context = context_system::instance();
2779 $navoptions = course_get_user_navigation_options($course->context, $course);
2780 $options = array();
2781 foreach ($navoptions as $name => $available) {
2782 $options[] = array(
2783 'name' => $name,
2784 'available' => $available,
2788 $courseoptions[] = array(
2789 'id' => $course->id,
2790 'options' => $options
2795 $result = array(
2796 'courses' => $courseoptions,
2797 'warnings' => $warnings
2799 return $result;
2803 * Returns description of method result value
2805 * @return external_description
2806 * @since Moodle 3.2
2808 public static function get_user_navigation_options_returns() {
2809 return new external_single_structure(
2810 array(
2811 'courses' => new external_multiple_structure(
2812 new external_single_structure(
2813 array(
2814 'id' => new external_value(PARAM_INT, 'Course id'),
2815 'options' => new external_multiple_structure(
2816 new external_single_structure(
2817 array(
2818 'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name'),
2819 'available' => new external_value(PARAM_BOOL, 'Whether the option is available or not'),
2824 ), 'List of courses'
2826 'warnings' => new external_warnings()
2832 * Returns description of method parameters
2834 * @return external_function_parameters
2835 * @since Moodle 3.2
2837 public static function get_user_administration_options_parameters() {
2838 return new external_function_parameters(
2839 array(
2840 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2846 * Return a list of administration options in a set of courses that are available or not for the current user.
2848 * @param array $courseids a list of course ids
2849 * @return array of warnings and the options availability
2850 * @since Moodle 3.2
2851 * @throws moodle_exception
2853 public static function get_user_administration_options($courseids) {
2854 global $CFG;
2855 require_once($CFG->dirroot . '/course/lib.php');
2857 // Parameter validation.
2858 $params = self::validate_parameters(self::get_user_administration_options_parameters(), array('courseids' => $courseids));
2859 $courseoptions = array();
2861 list($courses, $warnings) = external_util::validate_courses($params['courseids'], array(), true);
2863 if (!empty($courses)) {
2864 foreach ($courses as $course) {
2865 $adminoptions = course_get_user_administration_options($course, $course->context);
2866 $options = array();
2867 foreach ($adminoptions as $name => $available) {
2868 $options[] = array(
2869 'name' => $name,
2870 'available' => $available,
2874 $courseoptions[] = array(
2875 'id' => $course->id,
2876 'options' => $options
2881 $result = array(
2882 'courses' => $courseoptions,
2883 'warnings' => $warnings
2885 return $result;
2889 * Returns description of method result value
2891 * @return external_description
2892 * @since Moodle 3.2
2894 public static function get_user_administration_options_returns() {
2895 return self::get_user_navigation_options_returns();
2899 * Returns description of method parameters
2901 * @return external_function_parameters
2902 * @since Moodle 3.2
2904 public static function get_courses_by_field_parameters() {
2905 return new external_function_parameters(
2906 array(
2907 'field' => new external_value(PARAM_ALPHA, 'The field to search can be left empty for all courses or:
2908 id: course id
2909 ids: comma separated course ids
2910 shortname: course short name
2911 idnumber: course id number
2912 category: category id the course belongs to
2913 ', VALUE_DEFAULT, ''),
2914 'value' => new external_value(PARAM_RAW, 'The value to match', VALUE_DEFAULT, '')
2921 * Get courses matching a specific field (id/s, shortname, idnumber, category)
2923 * @param string $field field name to search, or empty for all courses
2924 * @param string $value value to search
2925 * @return array list of courses and warnings
2926 * @throws invalid_parameter_exception
2927 * @since Moodle 3.2
2929 public static function get_courses_by_field($field = '', $value = '') {
2930 global $DB, $CFG;
2931 require_once($CFG->libdir . '/coursecatlib.php');
2933 $params = self::validate_parameters(self::get_courses_by_field_parameters(),
2934 array(
2935 'field' => $field,
2936 'value' => $value,
2939 $warnings = array();
2941 if (empty($params['field'])) {
2942 $courses = $DB->get_records('course', null, 'id ASC');
2943 } else {
2944 switch ($params['field']) {
2945 case 'id':
2946 case 'category':
2947 $value = clean_param($params['value'], PARAM_INT);
2948 break;
2949 case 'ids':
2950 $value = clean_param($params['value'], PARAM_SEQUENCE);
2951 break;
2952 case 'shortname':
2953 $value = clean_param($params['value'], PARAM_TEXT);
2954 break;
2955 case 'idnumber':
2956 $value = clean_param($params['value'], PARAM_RAW);
2957 break;
2958 default:
2959 throw new invalid_parameter_exception('Invalid field name');
2962 if ($params['field'] === 'ids') {
2963 $courses = $DB->get_records_list('course', 'id', explode(',', $value), 'id ASC');
2964 } else {
2965 $courses = $DB->get_records('course', array($params['field'] => $value), 'id ASC');
2969 $coursesdata = array();
2970 foreach ($courses as $course) {
2971 $context = context_course::instance($course->id);
2972 $canupdatecourse = has_capability('moodle/course:update', $context);
2973 $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context);
2975 // Check if the course is visible in the site for the user.
2976 if (!$course->visible and !$canviewhiddencourses and !$canupdatecourse) {
2977 continue;
2979 // Get the public course information, even if we are not enrolled.
2980 $courseinlist = new course_in_list($course);
2981 $coursesdata[$course->id] = self::get_course_public_information($courseinlist, $context);
2983 // Now, check if we have access to the course.
2984 try {
2985 self::validate_context($context);
2986 } catch (Exception $e) {
2987 continue;
2989 // Return information for any user that can access the course.
2990 $coursefields = array('format', 'showgrades', 'newsitems', 'startdate', 'maxbytes', 'showreports', 'visible',
2991 'groupmode', 'groupmodeforce', 'defaultgroupingid', 'enablecompletion', 'completionnotify', 'lang', 'theme',
2992 'sortorder', 'marker');
2994 // Information for managers only.
2995 if ($canupdatecourse) {
2996 $managerfields = array('idnumber', 'legacyfiles', 'calendartype', 'timecreated', 'timemodified', 'requested',
2997 'cacherev');
2998 $coursefields = array_merge($coursefields, $managerfields);
3001 // Populate fields.
3002 foreach ($coursefields as $field) {
3003 $coursesdata[$course->id][$field] = $course->{$field};
3007 return array(
3008 'courses' => $coursesdata,
3009 'warnings' => $warnings
3014 * Returns description of method result value
3016 * @return external_description
3017 * @since Moodle 3.2
3019 public static function get_courses_by_field_returns() {
3020 // Course structure, including not only public viewable fields.
3021 return new external_single_structure(
3022 array(
3023 'courses' => new external_multiple_structure(self::get_course_structure(false), 'Course'),
3024 'warnings' => new external_warnings()
3030 * Returns description of method parameters
3032 * @return external_function_parameters
3033 * @since Moodle 3.2
3035 public static function check_updates_parameters() {
3036 return new external_function_parameters(
3037 array(
3038 'courseid' => new external_value(PARAM_INT, 'Course id to check'),
3039 'tocheck' => new external_multiple_structure(
3040 new external_single_structure(
3041 array(
3042 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level for the file location.
3043 Only module supported right now.'),
3044 'id' => new external_value(PARAM_INT, 'Context instance id'),
3045 'since' => new external_value(PARAM_INT, 'Check updates since this time stamp'),
3048 'Instances to check'
3050 'filter' => new external_multiple_structure(
3051 new external_value(PARAM_ALPHANUM, 'Area name: configuration, fileareas, completion, ratings, comments,
3052 gradeitems, outcomes'),
3053 'Check only for updates in these areas', VALUE_DEFAULT, array()
3060 * Check if there is updates affecting the user for the given course and contexts.
3061 * Right now only modules are supported.
3062 * This WS calls mod_check_updates_since for each module to check if there is any update the user should we aware of.
3064 * @param int $courseid the list of modules to check
3065 * @param array $tocheck the list of modules to check
3066 * @param array $filter check only for updates in these areas
3067 * @return array list of updates and warnings
3068 * @throws moodle_exception
3069 * @since Moodle 3.2
3071 public static function check_updates($courseid, $tocheck, $filter = array()) {
3072 global $CFG, $DB;
3074 $params = self::validate_parameters(
3075 self::check_updates_parameters(),
3076 array(
3077 'courseid' => $courseid,
3078 'tocheck' => $tocheck,
3079 'filter' => $filter,
3083 $course = get_course($params['courseid']);
3084 $context = context_course::instance($course->id);
3085 self::validate_context($context);
3087 list($instances, $warnings) = course_check_updates($course, $params['tocheck'], $filter);
3089 $instancesformatted = array();
3090 foreach ($instances as $instance) {
3091 $updates = array();
3092 foreach ($instance['updates'] as $name => $data) {
3093 if (empty($data->updated)) {
3094 continue;
3096 $updatedata = array(
3097 'name' => $name,
3099 if (!empty($data->timeupdated)) {
3100 $updatedata['timeupdated'] = $data->timeupdated;
3102 if (!empty($data->itemids)) {
3103 $updatedata['itemids'] = $data->itemids;
3105 $updates[] = $updatedata;
3107 if (!empty($updates)) {
3108 $instancesformatted[] = array(
3109 'contextlevel' => $instance['contextlevel'],
3110 'id' => $instance['id'],
3111 'updates' => $updates
3116 return array(
3117 'instances' => $instancesformatted,
3118 'warnings' => $warnings
3123 * Returns description of method result value
3125 * @return external_description
3126 * @since Moodle 3.2
3128 public static function check_updates_returns() {
3129 return new external_single_structure(
3130 array(
3131 'instances' => new external_multiple_structure(
3132 new external_single_structure(
3133 array(
3134 'contextlevel' => new external_value(PARAM_ALPHA, 'The context level'),
3135 'id' => new external_value(PARAM_INT, 'Instance id'),
3136 'updates' => new external_multiple_structure(
3137 new external_single_structure(
3138 array(
3139 'name' => new external_value(PARAM_ALPHANUMEXT, 'Name of the area updated.'),
3140 'timeupdated' => new external_value(PARAM_INT, 'Last time was updated', VALUE_OPTIONAL),
3141 'itemids' => new external_multiple_structure(
3142 new external_value(PARAM_INT, 'Instance id'),
3143 'The ids of the items updated',
3144 VALUE_OPTIONAL
3152 'warnings' => new external_warnings()