Merge branch 'MDL-55951-30' of git://github.com/crazyserver/moodle into MOODLE_30_STABLE
[moodle.git] / course / externallib.php
blob46d4e2ce53a23051a12b1f3c816dac8cfd20b653
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();
164 //for each sections (first displayed to last displayed)
165 $modinfosections = $modinfo->get_sections();
166 foreach ($sections as $key => $section) {
168 if (!$section->uservisible) {
169 continue;
172 // This becomes true when we are filtering and we found the value to filter with.
173 $sectionfound = false;
175 // Filter by section id.
176 if (!empty($filters['sectionid'])) {
177 if ($section->id != $filters['sectionid']) {
178 continue;
179 } else {
180 $sectionfound = true;
184 // Filter by section number. Note that 0 is a valid section number.
185 if (isset($filters['sectionnumber'])) {
186 if ($key != $filters['sectionnumber']) {
187 continue;
188 } else {
189 $sectionfound = true;
193 // reset $sectioncontents
194 $sectionvalues = array();
195 $sectionvalues['id'] = $section->id;
196 $sectionvalues['name'] = get_section_name($course, $section);
197 $sectionvalues['visible'] = $section->visible;
199 $options = (object) array('noclean' => true);
200 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
201 external_format_text($section->summary, $section->summaryformat,
202 $context->id, 'course', 'section', $section->id, $options);
203 $sectioncontents = array();
205 //for each module of the section
206 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
207 foreach ($modinfosections[$section->section] as $cmid) {
208 $cm = $modinfo->cms[$cmid];
210 // stop here if the module is not visible to the user
211 if (!$cm->uservisible) {
212 continue;
215 // This becomes true when we are filtering and we found the value to filter with.
216 $modfound = false;
218 // Filter by cmid.
219 if (!empty($filters['cmid'])) {
220 if ($cmid != $filters['cmid']) {
221 continue;
222 } else {
223 $modfound = true;
227 // Filter by module name and id.
228 if (!empty($filters['modname'])) {
229 if ($cm->modname != $filters['modname']) {
230 continue;
231 } else if (!empty($filters['modid'])) {
232 if ($cm->instance != $filters['modid']) {
233 continue;
234 } else {
235 // Note that if we are only filtering by modname we don't break the loop.
236 $modfound = true;
241 $module = array();
243 $modcontext = context_module::instance($cm->id);
245 //common info (for people being able to see the module or availability dates)
246 $module['id'] = $cm->id;
247 $module['name'] = external_format_string($cm->name, $modcontext->id);
248 $module['instance'] = $cm->instance;
249 $module['modname'] = $cm->modname;
250 $module['modplural'] = $cm->modplural;
251 $module['modicon'] = $cm->get_icon_url()->out(false);
252 $module['indent'] = $cm->indent;
254 if (!empty($cm->showdescription) or $cm->modname == 'label') {
255 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
256 list($module['description'], $descriptionformat) = external_format_text($cm->content,
257 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
260 //url of the module
261 $url = $cm->url;
262 if ($url) { //labels don't have url
263 $module['url'] = $url->out(false);
266 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
267 context_module::instance($cm->id));
268 //user that can view hidden module should know about the visibility
269 $module['visible'] = $cm->visible;
271 // Availability date (also send to user who can see hidden module).
272 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
273 $module['availability'] = $cm->availability;
276 $baseurl = 'webservice/pluginfile.php';
278 //call $modulename_export_contents
279 //(each module callback take care about checking the capabilities)
281 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
282 $getcontentfunction = $cm->modname.'_export_contents';
283 if (function_exists($getcontentfunction)) {
284 if (empty($filters['excludecontents']) and $contents = $getcontentfunction($cm, $baseurl)) {
285 $module['contents'] = $contents;
286 } else {
287 $module['contents'] = array();
291 //assign result to $sectioncontents
292 $sectioncontents[] = $module;
294 // If we just did a filtering, break the loop.
295 if ($modfound) {
296 break;
301 $sectionvalues['modules'] = $sectioncontents;
303 // assign result to $coursecontents
304 $coursecontents[] = $sectionvalues;
306 // Break the loop if we are filtering.
307 if ($sectionfound) {
308 break;
312 return $coursecontents;
316 * Returns description of method result value
318 * @return external_description
319 * @since Moodle 2.2
321 public static function get_course_contents_returns() {
322 return new external_multiple_structure(
323 new external_single_structure(
324 array(
325 'id' => new external_value(PARAM_INT, 'Section ID'),
326 'name' => new external_value(PARAM_TEXT, 'Section name'),
327 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
328 'summary' => new external_value(PARAM_RAW, 'Section description'),
329 'summaryformat' => new external_format_value('summary'),
330 'modules' => new external_multiple_structure(
331 new external_single_structure(
332 array(
333 'id' => new external_value(PARAM_INT, 'activity id'),
334 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
335 'name' => new external_value(PARAM_RAW, 'activity module name'),
336 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
337 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
338 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
339 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
340 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
341 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
342 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
343 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
344 'contents' => new external_multiple_structure(
345 new external_single_structure(
346 array(
347 // content info
348 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
349 'filename'=> new external_value(PARAM_FILE, 'filename'),
350 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
351 'filesize'=> new external_value(PARAM_INT, 'filesize'),
352 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
353 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
354 'timecreated' => new external_value(PARAM_INT, 'Time created'),
355 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
356 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
358 // copyright related info
359 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
360 'author' => new external_value(PARAM_TEXT, 'Content owner'),
361 'license' => new external_value(PARAM_TEXT, 'Content license'),
363 ), VALUE_DEFAULT, array()
366 ), 'list of module'
374 * Returns description of method parameters
376 * @return external_function_parameters
377 * @since Moodle 2.3
379 public static function get_courses_parameters() {
380 return new external_function_parameters(
381 array('options' => new external_single_structure(
382 array('ids' => new external_multiple_structure(
383 new external_value(PARAM_INT, 'Course id')
384 , 'List of course id. If empty return all courses
385 except front page course.',
386 VALUE_OPTIONAL)
387 ), 'options - operator OR is used', VALUE_DEFAULT, array())
393 * Get courses
395 * @param array $options It contains an array (list of ids)
396 * @return array
397 * @since Moodle 2.2
399 public static function get_courses($options = array()) {
400 global $CFG, $DB;
401 require_once($CFG->dirroot . "/course/lib.php");
403 //validate parameter
404 $params = self::validate_parameters(self::get_courses_parameters(),
405 array('options' => $options));
407 //retrieve courses
408 if (!array_key_exists('ids', $params['options'])
409 or empty($params['options']['ids'])) {
410 $courses = $DB->get_records('course');
411 } else {
412 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
415 //create return value
416 $coursesinfo = array();
417 foreach ($courses as $course) {
419 // now security checks
420 $context = context_course::instance($course->id, IGNORE_MISSING);
421 $courseformatoptions = course_get_format($course)->get_format_options();
422 try {
423 self::validate_context($context);
424 } catch (Exception $e) {
425 $exceptionparam = new stdClass();
426 $exceptionparam->message = $e->getMessage();
427 $exceptionparam->courseid = $course->id;
428 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
430 require_capability('moodle/course:view', $context);
432 $courseinfo = array();
433 $courseinfo['id'] = $course->id;
434 $courseinfo['fullname'] = $course->fullname;
435 $courseinfo['shortname'] = $course->shortname;
436 $courseinfo['categoryid'] = $course->category;
437 list($courseinfo['summary'], $courseinfo['summaryformat']) =
438 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
439 $courseinfo['format'] = $course->format;
440 $courseinfo['startdate'] = $course->startdate;
441 if (array_key_exists('numsections', $courseformatoptions)) {
442 // For backward-compartibility
443 $courseinfo['numsections'] = $courseformatoptions['numsections'];
446 //some field should be returned only if the user has update permission
447 $courseadmin = has_capability('moodle/course:update', $context);
448 if ($courseadmin) {
449 $courseinfo['categorysortorder'] = $course->sortorder;
450 $courseinfo['idnumber'] = $course->idnumber;
451 $courseinfo['showgrades'] = $course->showgrades;
452 $courseinfo['showreports'] = $course->showreports;
453 $courseinfo['newsitems'] = $course->newsitems;
454 $courseinfo['visible'] = $course->visible;
455 $courseinfo['maxbytes'] = $course->maxbytes;
456 if (array_key_exists('hiddensections', $courseformatoptions)) {
457 // For backward-compartibility
458 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
460 $courseinfo['groupmode'] = $course->groupmode;
461 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
462 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
463 $courseinfo['lang'] = $course->lang;
464 $courseinfo['timecreated'] = $course->timecreated;
465 $courseinfo['timemodified'] = $course->timemodified;
466 $courseinfo['forcetheme'] = $course->theme;
467 $courseinfo['enablecompletion'] = $course->enablecompletion;
468 $courseinfo['completionnotify'] = $course->completionnotify;
469 $courseinfo['courseformatoptions'] = array();
470 foreach ($courseformatoptions as $key => $value) {
471 $courseinfo['courseformatoptions'][] = array(
472 'name' => $key,
473 'value' => $value
478 if ($courseadmin or $course->visible
479 or has_capability('moodle/course:viewhiddencourses', $context)) {
480 $coursesinfo[] = $courseinfo;
484 return $coursesinfo;
488 * Returns description of method result value
490 * @return external_description
491 * @since Moodle 2.2
493 public static function get_courses_returns() {
494 return new external_multiple_structure(
495 new external_single_structure(
496 array(
497 'id' => new external_value(PARAM_INT, 'course id'),
498 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
499 'categoryid' => new external_value(PARAM_INT, 'category id'),
500 'categorysortorder' => new external_value(PARAM_INT,
501 'sort order into the category', VALUE_OPTIONAL),
502 'fullname' => new external_value(PARAM_TEXT, 'full name'),
503 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
504 'summary' => new external_value(PARAM_RAW, 'summary'),
505 'summaryformat' => new external_format_value('summary'),
506 'format' => new external_value(PARAM_PLUGIN,
507 'course format: weeks, topics, social, site,..'),
508 'showgrades' => new external_value(PARAM_INT,
509 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
510 'newsitems' => new external_value(PARAM_INT,
511 'number of recent items appearing on the course page', VALUE_OPTIONAL),
512 'startdate' => new external_value(PARAM_INT,
513 'timestamp when the course start'),
514 'numsections' => new external_value(PARAM_INT,
515 '(deprecated, use courseformatoptions) number of weeks/topics',
516 VALUE_OPTIONAL),
517 'maxbytes' => new external_value(PARAM_INT,
518 'largest size of file that can be uploaded into the course',
519 VALUE_OPTIONAL),
520 'showreports' => new external_value(PARAM_INT,
521 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
522 'visible' => new external_value(PARAM_INT,
523 '1: available to student, 0:not available', VALUE_OPTIONAL),
524 'hiddensections' => new external_value(PARAM_INT,
525 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
526 VALUE_OPTIONAL),
527 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
528 VALUE_OPTIONAL),
529 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
530 VALUE_OPTIONAL),
531 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
532 VALUE_OPTIONAL),
533 'timecreated' => new external_value(PARAM_INT,
534 'timestamp when the course have been created', VALUE_OPTIONAL),
535 'timemodified' => new external_value(PARAM_INT,
536 'timestamp when the course have been modified', VALUE_OPTIONAL),
537 'enablecompletion' => new external_value(PARAM_INT,
538 'Enabled, control via completion and activity settings. Disbaled,
539 not shown in activity settings.',
540 VALUE_OPTIONAL),
541 'completionnotify' => new external_value(PARAM_INT,
542 '1: yes 0: no', VALUE_OPTIONAL),
543 'lang' => new external_value(PARAM_SAFEDIR,
544 'forced course language', VALUE_OPTIONAL),
545 'forcetheme' => new external_value(PARAM_PLUGIN,
546 'name of the force theme', VALUE_OPTIONAL),
547 'courseformatoptions' => new external_multiple_structure(
548 new external_single_structure(
549 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
550 'value' => new external_value(PARAM_RAW, 'course format option value')
552 'additional options for particular course format', VALUE_OPTIONAL
554 ), 'course'
560 * Returns description of method parameters
562 * @return external_function_parameters
563 * @since Moodle 2.2
565 public static function create_courses_parameters() {
566 $courseconfig = get_config('moodlecourse'); //needed for many default values
567 return new external_function_parameters(
568 array(
569 'courses' => new external_multiple_structure(
570 new external_single_structure(
571 array(
572 'fullname' => new external_value(PARAM_TEXT, 'full name'),
573 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
574 'categoryid' => new external_value(PARAM_INT, 'category id'),
575 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
576 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
577 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
578 'format' => new external_value(PARAM_PLUGIN,
579 'course format: weeks, topics, social, site,..',
580 VALUE_DEFAULT, $courseconfig->format),
581 'showgrades' => new external_value(PARAM_INT,
582 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
583 $courseconfig->showgrades),
584 'newsitems' => new external_value(PARAM_INT,
585 'number of recent items appearing on the course page',
586 VALUE_DEFAULT, $courseconfig->newsitems),
587 'startdate' => new external_value(PARAM_INT,
588 'timestamp when the course start', VALUE_OPTIONAL),
589 'numsections' => new external_value(PARAM_INT,
590 '(deprecated, use courseformatoptions) number of weeks/topics',
591 VALUE_OPTIONAL),
592 'maxbytes' => new external_value(PARAM_INT,
593 'largest size of file that can be uploaded into the course',
594 VALUE_DEFAULT, $courseconfig->maxbytes),
595 'showreports' => new external_value(PARAM_INT,
596 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
597 $courseconfig->showreports),
598 'visible' => new external_value(PARAM_INT,
599 '1: available to student, 0:not available', VALUE_OPTIONAL),
600 'hiddensections' => new external_value(PARAM_INT,
601 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
602 VALUE_OPTIONAL),
603 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
604 VALUE_DEFAULT, $courseconfig->groupmode),
605 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
606 VALUE_DEFAULT, $courseconfig->groupmodeforce),
607 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
608 VALUE_DEFAULT, 0),
609 'enablecompletion' => new external_value(PARAM_INT,
610 'Enabled, control via completion and activity settings. Disabled,
611 not shown in activity settings.',
612 VALUE_OPTIONAL),
613 'completionnotify' => new external_value(PARAM_INT,
614 '1: yes 0: no', VALUE_OPTIONAL),
615 'lang' => new external_value(PARAM_SAFEDIR,
616 'forced course language', VALUE_OPTIONAL),
617 'forcetheme' => new external_value(PARAM_PLUGIN,
618 'name of the force theme', VALUE_OPTIONAL),
619 'courseformatoptions' => new external_multiple_structure(
620 new external_single_structure(
621 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
622 'value' => new external_value(PARAM_RAW, 'course format option value')
624 'additional options for particular course format', VALUE_OPTIONAL),
626 ), 'courses to create'
633 * Create courses
635 * @param array $courses
636 * @return array courses (id and shortname only)
637 * @since Moodle 2.2
639 public static function create_courses($courses) {
640 global $CFG, $DB;
641 require_once($CFG->dirroot . "/course/lib.php");
642 require_once($CFG->libdir . '/completionlib.php');
644 $params = self::validate_parameters(self::create_courses_parameters(),
645 array('courses' => $courses));
647 $availablethemes = core_component::get_plugin_list('theme');
648 $availablelangs = get_string_manager()->get_list_of_translations();
650 $transaction = $DB->start_delegated_transaction();
652 foreach ($params['courses'] as $course) {
654 // Ensure the current user is allowed to run this function
655 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
656 try {
657 self::validate_context($context);
658 } catch (Exception $e) {
659 $exceptionparam = new stdClass();
660 $exceptionparam->message = $e->getMessage();
661 $exceptionparam->catid = $course['categoryid'];
662 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
664 require_capability('moodle/course:create', $context);
666 // Make sure lang is valid
667 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
668 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
671 // Make sure theme is valid
672 if (array_key_exists('forcetheme', $course)) {
673 if (!empty($CFG->allowcoursethemes)) {
674 if (empty($availablethemes[$course['forcetheme']])) {
675 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
676 } else {
677 $course['theme'] = $course['forcetheme'];
682 //force visibility if ws user doesn't have the permission to set it
683 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
684 if (!has_capability('moodle/course:visibility', $context)) {
685 $course['visible'] = $category->visible;
688 //set default value for completion
689 $courseconfig = get_config('moodlecourse');
690 if (completion_info::is_enabled_for_site()) {
691 if (!array_key_exists('enablecompletion', $course)) {
692 $course['enablecompletion'] = $courseconfig->enablecompletion;
694 } else {
695 $course['enablecompletion'] = 0;
698 $course['category'] = $course['categoryid'];
700 // Summary format.
701 $course['summaryformat'] = external_validate_format($course['summaryformat']);
703 if (!empty($course['courseformatoptions'])) {
704 foreach ($course['courseformatoptions'] as $option) {
705 $course[$option['name']] = $option['value'];
709 //Note: create_course() core function check shortname, idnumber, category
710 $course['id'] = create_course((object) $course)->id;
712 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
715 $transaction->allow_commit();
717 return $resultcourses;
721 * Returns description of method result value
723 * @return external_description
724 * @since Moodle 2.2
726 public static function create_courses_returns() {
727 return new external_multiple_structure(
728 new external_single_structure(
729 array(
730 'id' => new external_value(PARAM_INT, 'course id'),
731 'shortname' => new external_value(PARAM_TEXT, 'short name'),
738 * Update courses
740 * @return external_function_parameters
741 * @since Moodle 2.5
743 public static function update_courses_parameters() {
744 return new external_function_parameters(
745 array(
746 'courses' => new external_multiple_structure(
747 new external_single_structure(
748 array(
749 'id' => new external_value(PARAM_INT, 'ID of the course'),
750 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
751 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
752 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
753 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
754 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
755 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
756 'format' => new external_value(PARAM_PLUGIN,
757 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
758 'showgrades' => new external_value(PARAM_INT,
759 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
760 'newsitems' => new external_value(PARAM_INT,
761 'number of recent items appearing on the course page', VALUE_OPTIONAL),
762 'startdate' => new external_value(PARAM_INT,
763 'timestamp when the course start', VALUE_OPTIONAL),
764 'numsections' => new external_value(PARAM_INT,
765 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
766 'maxbytes' => new external_value(PARAM_INT,
767 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
768 'showreports' => new external_value(PARAM_INT,
769 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
770 'visible' => new external_value(PARAM_INT,
771 '1: available to student, 0:not available', VALUE_OPTIONAL),
772 'hiddensections' => new external_value(PARAM_INT,
773 '(deprecated, use courseformatoptions) How the hidden sections in the course are
774 displayed to students', VALUE_OPTIONAL),
775 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
776 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
777 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
778 'enablecompletion' => new external_value(PARAM_INT,
779 'Enabled, control via completion and activity settings. Disabled,
780 not shown in activity settings.', VALUE_OPTIONAL),
781 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
782 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
783 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
784 'courseformatoptions' => new external_multiple_structure(
785 new external_single_structure(
786 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
787 'value' => new external_value(PARAM_RAW, 'course format option value')
789 'additional options for particular course format', VALUE_OPTIONAL),
791 ), 'courses to update'
798 * Update courses
800 * @param array $courses
801 * @since Moodle 2.5
803 public static function update_courses($courses) {
804 global $CFG, $DB;
805 require_once($CFG->dirroot . "/course/lib.php");
806 $warnings = array();
808 $params = self::validate_parameters(self::update_courses_parameters(),
809 array('courses' => $courses));
811 $availablethemes = core_component::get_plugin_list('theme');
812 $availablelangs = get_string_manager()->get_list_of_translations();
814 foreach ($params['courses'] as $course) {
815 // Catch any exception while updating course and return as warning to user.
816 try {
817 // Ensure the current user is allowed to run this function.
818 $context = context_course::instance($course['id'], MUST_EXIST);
819 self::validate_context($context);
821 $oldcourse = course_get_format($course['id'])->get_course();
823 require_capability('moodle/course:update', $context);
825 // Check if user can change category.
826 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
827 require_capability('moodle/course:changecategory', $context);
828 $course['category'] = $course['categoryid'];
831 // Check if the user can change fullname.
832 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
833 require_capability('moodle/course:changefullname', $context);
836 // Check if the user can change shortname.
837 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
838 require_capability('moodle/course:changeshortname', $context);
841 // Check if the user can change the idnumber.
842 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
843 require_capability('moodle/course:changeidnumber', $context);
846 // Check if user can change summary.
847 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
848 require_capability('moodle/course:changesummary', $context);
851 // Summary format.
852 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
853 require_capability('moodle/course:changesummary', $context);
854 $course['summaryformat'] = external_validate_format($course['summaryformat']);
857 // Check if user can change visibility.
858 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
859 require_capability('moodle/course:visibility', $context);
862 // Make sure lang is valid.
863 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
864 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
867 // Make sure theme is valid.
868 if (array_key_exists('forcetheme', $course)) {
869 if (!empty($CFG->allowcoursethemes)) {
870 if (empty($availablethemes[$course['forcetheme']])) {
871 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
872 } else {
873 $course['theme'] = $course['forcetheme'];
878 // Make sure completion is enabled before setting it.
879 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
880 $course['enabledcompletion'] = 0;
883 // Make sure maxbytes are less then CFG->maxbytes.
884 if (array_key_exists('maxbytes', $course)) {
885 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
888 if (!empty($course['courseformatoptions'])) {
889 foreach ($course['courseformatoptions'] as $option) {
890 if (isset($option['name']) && isset($option['value'])) {
891 $course[$option['name']] = $option['value'];
896 // Update course if user has all required capabilities.
897 update_course((object) $course);
898 } catch (Exception $e) {
899 $warning = array();
900 $warning['item'] = 'course';
901 $warning['itemid'] = $course['id'];
902 if ($e instanceof moodle_exception) {
903 $warning['warningcode'] = $e->errorcode;
904 } else {
905 $warning['warningcode'] = $e->getCode();
907 $warning['message'] = $e->getMessage();
908 $warnings[] = $warning;
912 $result = array();
913 $result['warnings'] = $warnings;
914 return $result;
918 * Returns description of method result value
920 * @return external_description
921 * @since Moodle 2.5
923 public static function update_courses_returns() {
924 return new external_single_structure(
925 array(
926 'warnings' => new external_warnings()
932 * Returns description of method parameters
934 * @return external_function_parameters
935 * @since Moodle 2.2
937 public static function delete_courses_parameters() {
938 return new external_function_parameters(
939 array(
940 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
946 * Delete courses
948 * @param array $courseids A list of course ids
949 * @since Moodle 2.2
951 public static function delete_courses($courseids) {
952 global $CFG, $DB;
953 require_once($CFG->dirroot."/course/lib.php");
955 // Parameter validation.
956 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
958 $warnings = array();
960 foreach ($params['courseids'] as $courseid) {
961 $course = $DB->get_record('course', array('id' => $courseid));
963 if ($course === false) {
964 $warnings[] = array(
965 'item' => 'course',
966 'itemid' => $courseid,
967 'warningcode' => 'unknowncourseidnumber',
968 'message' => 'Unknown course ID ' . $courseid
970 continue;
973 // Check if the context is valid.
974 $coursecontext = context_course::instance($course->id);
975 self::validate_context($coursecontext);
977 // Check if the current user has permission.
978 if (!can_delete_course($courseid)) {
979 $warnings[] = array(
980 'item' => 'course',
981 'itemid' => $courseid,
982 'warningcode' => 'cannotdeletecourse',
983 'message' => 'You do not have the permission to delete this course' . $courseid
985 continue;
988 if (delete_course($course, false) === false) {
989 $warnings[] = array(
990 'item' => 'course',
991 'itemid' => $courseid,
992 'warningcode' => 'cannotdeletecategorycourse',
993 'message' => 'Course ' . $courseid . ' failed to be deleted'
995 continue;
999 fix_course_sortorder();
1001 return array('warnings' => $warnings);
1005 * Returns description of method result value
1007 * @return external_description
1008 * @since Moodle 2.2
1010 public static function delete_courses_returns() {
1011 return new external_single_structure(
1012 array(
1013 'warnings' => new external_warnings()
1019 * Returns description of method parameters
1021 * @return external_function_parameters
1022 * @since Moodle 2.3
1024 public static function duplicate_course_parameters() {
1025 return new external_function_parameters(
1026 array(
1027 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1028 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1029 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1030 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1031 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1032 'options' => new external_multiple_structure(
1033 new external_single_structure(
1034 array(
1035 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
1036 "activities" (int) Include course activites (default to 1 that is equal to yes),
1037 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1038 "filters" (int) Include course filters (default to 1 that is equal to yes),
1039 "users" (int) Include users (default to 0 that is equal to no),
1040 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
1041 "comments" (int) Include user comments (default to 0 that is equal to no),
1042 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
1043 "logs" (int) Include course logs (default to 0 that is equal to no),
1044 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
1046 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1049 ), VALUE_DEFAULT, array()
1056 * Duplicate a course
1058 * @param int $courseid
1059 * @param string $fullname Duplicated course fullname
1060 * @param string $shortname Duplicated course shortname
1061 * @param int $categoryid Duplicated course parent category id
1062 * @param int $visible Duplicated course availability
1063 * @param array $options List of backup options
1064 * @return array New course info
1065 * @since Moodle 2.3
1067 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
1068 global $CFG, $USER, $DB;
1069 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1070 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1072 // Parameter validation.
1073 $params = self::validate_parameters(
1074 self::duplicate_course_parameters(),
1075 array(
1076 'courseid' => $courseid,
1077 'fullname' => $fullname,
1078 'shortname' => $shortname,
1079 'categoryid' => $categoryid,
1080 'visible' => $visible,
1081 'options' => $options
1085 // Context validation.
1087 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
1088 throw new moodle_exception('invalidcourseid', 'error');
1091 // Category where duplicated course is going to be created.
1092 $categorycontext = context_coursecat::instance($params['categoryid']);
1093 self::validate_context($categorycontext);
1095 // Course to be duplicated.
1096 $coursecontext = context_course::instance($course->id);
1097 self::validate_context($coursecontext);
1099 $backupdefaults = array(
1100 'activities' => 1,
1101 'blocks' => 1,
1102 'filters' => 1,
1103 'users' => 0,
1104 'role_assignments' => 0,
1105 'comments' => 0,
1106 'userscompletion' => 0,
1107 'logs' => 0,
1108 'grade_histories' => 0
1111 $backupsettings = array();
1112 // Check for backup and restore options.
1113 if (!empty($params['options'])) {
1114 foreach ($params['options'] as $option) {
1116 // Strict check for a correct value (allways 1 or 0, true or false).
1117 $value = clean_param($option['value'], PARAM_INT);
1119 if ($value !== 0 and $value !== 1) {
1120 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1123 if (!isset($backupdefaults[$option['name']])) {
1124 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1127 $backupsettings[$option['name']] = $value;
1131 // Capability checking.
1133 // The backup controller check for this currently, this may be redundant.
1134 require_capability('moodle/course:create', $categorycontext);
1135 require_capability('moodle/restore:restorecourse', $categorycontext);
1136 require_capability('moodle/backup:backupcourse', $coursecontext);
1138 if (!empty($backupsettings['users'])) {
1139 require_capability('moodle/backup:userinfo', $coursecontext);
1140 require_capability('moodle/restore:userinfo', $categorycontext);
1143 // Check if the shortname is used.
1144 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1145 foreach ($foundcourses as $foundcourse) {
1146 $foundcoursenames[] = $foundcourse->fullname;
1149 $foundcoursenamestring = implode(',', $foundcoursenames);
1150 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1153 // Backup the course.
1155 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1156 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1158 foreach ($backupsettings as $name => $value) {
1159 $bc->get_plan()->get_setting($name)->set_value($value);
1162 $backupid = $bc->get_backupid();
1163 $backupbasepath = $bc->get_plan()->get_basepath();
1165 $bc->execute_plan();
1166 $results = $bc->get_results();
1167 $file = $results['backup_destination'];
1169 $bc->destroy();
1171 // Restore the backup immediately.
1173 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1174 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1175 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1178 // Create new course.
1179 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1181 $rc = new restore_controller($backupid, $newcourseid,
1182 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1184 foreach ($backupsettings as $name => $value) {
1185 $setting = $rc->get_plan()->get_setting($name);
1186 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1187 $setting->set_value($value);
1191 if (!$rc->execute_precheck()) {
1192 $precheckresults = $rc->get_precheck_results();
1193 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1194 if (empty($CFG->keeptempdirectoriesonbackup)) {
1195 fulldelete($backupbasepath);
1198 $errorinfo = '';
1200 foreach ($precheckresults['errors'] as $error) {
1201 $errorinfo .= $error;
1204 if (array_key_exists('warnings', $precheckresults)) {
1205 foreach ($precheckresults['warnings'] as $warning) {
1206 $errorinfo .= $warning;
1210 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1214 $rc->execute_plan();
1215 $rc->destroy();
1217 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1218 $course->fullname = $params['fullname'];
1219 $course->shortname = $params['shortname'];
1220 $course->visible = $params['visible'];
1222 // Set shortname and fullname back.
1223 $DB->update_record('course', $course);
1225 if (empty($CFG->keeptempdirectoriesonbackup)) {
1226 fulldelete($backupbasepath);
1229 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1230 $file->delete();
1232 return array('id' => $course->id, 'shortname' => $course->shortname);
1236 * Returns description of method result value
1238 * @return external_description
1239 * @since Moodle 2.3
1241 public static function duplicate_course_returns() {
1242 return new external_single_structure(
1243 array(
1244 'id' => new external_value(PARAM_INT, 'course id'),
1245 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1251 * Returns description of method parameters for import_course
1253 * @return external_function_parameters
1254 * @since Moodle 2.4
1256 public static function import_course_parameters() {
1257 return new external_function_parameters(
1258 array(
1259 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1260 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1261 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1262 'options' => new external_multiple_structure(
1263 new external_single_structure(
1264 array(
1265 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1266 "activities" (int) Include course activites (default to 1 that is equal to yes),
1267 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1268 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1270 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1273 ), VALUE_DEFAULT, array()
1280 * Imports a course
1282 * @param int $importfrom The id of the course we are importing from
1283 * @param int $importto The id of the course we are importing to
1284 * @param bool $deletecontent Whether to delete the course we are importing to content
1285 * @param array $options List of backup options
1286 * @return null
1287 * @since Moodle 2.4
1289 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1290 global $CFG, $USER, $DB;
1291 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1292 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1294 // Parameter validation.
1295 $params = self::validate_parameters(
1296 self::import_course_parameters(),
1297 array(
1298 'importfrom' => $importfrom,
1299 'importto' => $importto,
1300 'deletecontent' => $deletecontent,
1301 'options' => $options
1305 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1306 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1309 // Context validation.
1311 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1312 throw new moodle_exception('invalidcourseid', 'error');
1315 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1316 throw new moodle_exception('invalidcourseid', 'error');
1319 $importfromcontext = context_course::instance($importfrom->id);
1320 self::validate_context($importfromcontext);
1322 $importtocontext = context_course::instance($importto->id);
1323 self::validate_context($importtocontext);
1325 $backupdefaults = array(
1326 'activities' => 1,
1327 'blocks' => 1,
1328 'filters' => 1
1331 $backupsettings = array();
1333 // Check for backup and restore options.
1334 if (!empty($params['options'])) {
1335 foreach ($params['options'] as $option) {
1337 // Strict check for a correct value (allways 1 or 0, true or false).
1338 $value = clean_param($option['value'], PARAM_INT);
1340 if ($value !== 0 and $value !== 1) {
1341 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1344 if (!isset($backupdefaults[$option['name']])) {
1345 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1348 $backupsettings[$option['name']] = $value;
1352 // Capability checking.
1354 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1355 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1357 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1358 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1360 foreach ($backupsettings as $name => $value) {
1361 $bc->get_plan()->get_setting($name)->set_value($value);
1364 $backupid = $bc->get_backupid();
1365 $backupbasepath = $bc->get_plan()->get_basepath();
1367 $bc->execute_plan();
1368 $bc->destroy();
1370 // Restore the backup immediately.
1372 // Check if we must delete the contents of the destination course.
1373 if ($params['deletecontent']) {
1374 $restoretarget = backup::TARGET_EXISTING_DELETING;
1375 } else {
1376 $restoretarget = backup::TARGET_EXISTING_ADDING;
1379 $rc = new restore_controller($backupid, $importto->id,
1380 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1382 foreach ($backupsettings as $name => $value) {
1383 $rc->get_plan()->get_setting($name)->set_value($value);
1386 if (!$rc->execute_precheck()) {
1387 $precheckresults = $rc->get_precheck_results();
1388 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1389 if (empty($CFG->keeptempdirectoriesonbackup)) {
1390 fulldelete($backupbasepath);
1393 $errorinfo = '';
1395 foreach ($precheckresults['errors'] as $error) {
1396 $errorinfo .= $error;
1399 if (array_key_exists('warnings', $precheckresults)) {
1400 foreach ($precheckresults['warnings'] as $warning) {
1401 $errorinfo .= $warning;
1405 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1407 } else {
1408 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1409 restore_dbops::delete_course_content($importto->id);
1413 $rc->execute_plan();
1414 $rc->destroy();
1416 if (empty($CFG->keeptempdirectoriesonbackup)) {
1417 fulldelete($backupbasepath);
1420 return null;
1424 * Returns description of method result value
1426 * @return external_description
1427 * @since Moodle 2.4
1429 public static function import_course_returns() {
1430 return null;
1434 * Returns description of method parameters
1436 * @return external_function_parameters
1437 * @since Moodle 2.3
1439 public static function get_categories_parameters() {
1440 return new external_function_parameters(
1441 array(
1442 'criteria' => new external_multiple_structure(
1443 new external_single_structure(
1444 array(
1445 'key' => new external_value(PARAM_ALPHA,
1446 'The category column to search, expected keys (value format) are:'.
1447 '"id" (int) the category id,'.
1448 '"name" (string) the category name,'.
1449 '"parent" (int) the parent category id,'.
1450 '"idnumber" (string) category idnumber'.
1451 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1452 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1453 then the function return all categories that the user can see.'.
1454 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1455 '"theme" (string) only return the categories having this theme'.
1456 ' - user must have \'moodle/category:manage\' to search on theme'),
1457 'value' => new external_value(PARAM_RAW, 'the value to match')
1459 ), 'criteria', VALUE_DEFAULT, array()
1461 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1462 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1468 * Get categories
1470 * @param array $criteria Criteria to match the results
1471 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1472 * @return array list of categories
1473 * @since Moodle 2.3
1475 public static function get_categories($criteria = array(), $addsubcategories = true) {
1476 global $CFG, $DB;
1477 require_once($CFG->dirroot . "/course/lib.php");
1479 // Validate parameters.
1480 $params = self::validate_parameters(self::get_categories_parameters(),
1481 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1483 // Retrieve the categories.
1484 $categories = array();
1485 if (!empty($params['criteria'])) {
1487 $conditions = array();
1488 $wheres = array();
1489 foreach ($params['criteria'] as $crit) {
1490 $key = trim($crit['key']);
1492 // Trying to avoid duplicate keys.
1493 if (!isset($conditions[$key])) {
1495 $context = context_system::instance();
1496 $value = null;
1497 switch ($key) {
1498 case 'id':
1499 $value = clean_param($crit['value'], PARAM_INT);
1500 break;
1502 case 'idnumber':
1503 if (has_capability('moodle/category:manage', $context)) {
1504 $value = clean_param($crit['value'], PARAM_RAW);
1505 } else {
1506 // We must throw an exception.
1507 // Otherwise the dev client would think no idnumber exists.
1508 throw new moodle_exception('criteriaerror',
1509 'webservice', '', null,
1510 'You don\'t have the permissions to search on the "idnumber" field.');
1512 break;
1514 case 'name':
1515 $value = clean_param($crit['value'], PARAM_TEXT);
1516 break;
1518 case 'parent':
1519 $value = clean_param($crit['value'], PARAM_INT);
1520 break;
1522 case 'visible':
1523 if (has_capability('moodle/category:manage', $context)
1524 or has_capability('moodle/category:viewhiddencategories',
1525 context_system::instance())) {
1526 $value = clean_param($crit['value'], PARAM_INT);
1527 } else {
1528 throw new moodle_exception('criteriaerror',
1529 'webservice', '', null,
1530 'You don\'t have the permissions to search on the "visible" field.');
1532 break;
1534 case 'theme':
1535 if (has_capability('moodle/category:manage', $context)) {
1536 $value = clean_param($crit['value'], PARAM_THEME);
1537 } else {
1538 throw new moodle_exception('criteriaerror',
1539 'webservice', '', null,
1540 'You don\'t have the permissions to search on the "theme" field.');
1542 break;
1544 default:
1545 throw new moodle_exception('criteriaerror',
1546 'webservice', '', null,
1547 'You can not search on this criteria: ' . $key);
1550 if (isset($value)) {
1551 $conditions[$key] = $value;
1552 $wheres[] = $key . " = :" . $key;
1557 if (!empty($wheres)) {
1558 $wheres = implode(" AND ", $wheres);
1560 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1562 // Retrieve its sub subcategories (all levels).
1563 if ($categories and !empty($params['addsubcategories'])) {
1564 $newcategories = array();
1566 // Check if we required visible/theme checks.
1567 $additionalselect = '';
1568 $additionalparams = array();
1569 if (isset($conditions['visible'])) {
1570 $additionalselect .= ' AND visible = :visible';
1571 $additionalparams['visible'] = $conditions['visible'];
1573 if (isset($conditions['theme'])) {
1574 $additionalselect .= ' AND theme= :theme';
1575 $additionalparams['theme'] = $conditions['theme'];
1578 foreach ($categories as $category) {
1579 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1580 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1581 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1582 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1584 $categories = $categories + $newcategories;
1588 } else {
1589 // Retrieve all categories in the database.
1590 $categories = $DB->get_records('course_categories');
1593 // The not returned categories. key => category id, value => reason of exclusion.
1594 $excludedcats = array();
1596 // The returned categories.
1597 $categoriesinfo = array();
1599 // We need to sort the categories by path.
1600 // The parent cats need to be checked by the algo first.
1601 usort($categories, "core_course_external::compare_categories_by_path");
1603 foreach ($categories as $category) {
1605 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1606 $parents = explode('/', $category->path);
1607 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1608 foreach ($parents as $parentid) {
1609 // Note: when the parent exclusion was due to the context,
1610 // the sub category could still be returned.
1611 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1612 $excludedcats[$category->id] = 'parent';
1616 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1617 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1618 and !has_capability('moodle/category:manage', $context)) {
1619 $excludedcats[$category->id] = 'depth';
1622 // Check the user can use the category context.
1623 $context = context_coursecat::instance($category->id);
1624 try {
1625 self::validate_context($context);
1626 } catch (Exception $e) {
1627 $excludedcats[$category->id] = 'context';
1629 // If it was the requested category then throw an exception.
1630 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1631 $exceptionparam = new stdClass();
1632 $exceptionparam->message = $e->getMessage();
1633 $exceptionparam->catid = $category->id;
1634 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1638 // Return the category information.
1639 if (!isset($excludedcats[$category->id])) {
1641 // Final check to see if the category is visible to the user.
1642 if ($category->visible
1643 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1644 or has_capability('moodle/category:manage', $context)) {
1646 $categoryinfo = array();
1647 $categoryinfo['id'] = $category->id;
1648 $categoryinfo['name'] = $category->name;
1649 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1650 external_format_text($category->description, $category->descriptionformat,
1651 $context->id, 'coursecat', 'description', null);
1652 $categoryinfo['parent'] = $category->parent;
1653 $categoryinfo['sortorder'] = $category->sortorder;
1654 $categoryinfo['coursecount'] = $category->coursecount;
1655 $categoryinfo['depth'] = $category->depth;
1656 $categoryinfo['path'] = $category->path;
1658 // Some fields only returned for admin.
1659 if (has_capability('moodle/category:manage', $context)) {
1660 $categoryinfo['idnumber'] = $category->idnumber;
1661 $categoryinfo['visible'] = $category->visible;
1662 $categoryinfo['visibleold'] = $category->visibleold;
1663 $categoryinfo['timemodified'] = $category->timemodified;
1664 $categoryinfo['theme'] = $category->theme;
1667 $categoriesinfo[] = $categoryinfo;
1668 } else {
1669 $excludedcats[$category->id] = 'visibility';
1674 // Sorting the resulting array so it looks a bit better for the client developer.
1675 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1677 return $categoriesinfo;
1681 * Sort categories array by path
1682 * private function: only used by get_categories
1684 * @param array $category1
1685 * @param array $category2
1686 * @return int result of strcmp
1687 * @since Moodle 2.3
1689 private static function compare_categories_by_path($category1, $category2) {
1690 return strcmp($category1->path, $category2->path);
1694 * Sort categories array by sortorder
1695 * private function: only used by get_categories
1697 * @param array $category1
1698 * @param array $category2
1699 * @return int result of strcmp
1700 * @since Moodle 2.3
1702 private static function compare_categories_by_sortorder($category1, $category2) {
1703 return strcmp($category1['sortorder'], $category2['sortorder']);
1707 * Returns description of method result value
1709 * @return external_description
1710 * @since Moodle 2.3
1712 public static function get_categories_returns() {
1713 return new external_multiple_structure(
1714 new external_single_structure(
1715 array(
1716 'id' => new external_value(PARAM_INT, 'category id'),
1717 'name' => new external_value(PARAM_TEXT, 'category name'),
1718 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1719 'description' => new external_value(PARAM_RAW, 'category description'),
1720 'descriptionformat' => new external_format_value('description'),
1721 'parent' => new external_value(PARAM_INT, 'parent category id'),
1722 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1723 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1724 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1725 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1726 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1727 'depth' => new external_value(PARAM_INT, 'category depth'),
1728 'path' => new external_value(PARAM_TEXT, 'category path'),
1729 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1730 ), 'List of categories'
1736 * Returns description of method parameters
1738 * @return external_function_parameters
1739 * @since Moodle 2.3
1741 public static function create_categories_parameters() {
1742 return new external_function_parameters(
1743 array(
1744 'categories' => new external_multiple_structure(
1745 new external_single_structure(
1746 array(
1747 'name' => new external_value(PARAM_TEXT, 'new category name'),
1748 'parent' => new external_value(PARAM_INT,
1749 'the parent category id inside which the new category will be created
1750 - set to 0 for a root category',
1751 VALUE_DEFAULT, 0),
1752 'idnumber' => new external_value(PARAM_RAW,
1753 'the new category idnumber', VALUE_OPTIONAL),
1754 'description' => new external_value(PARAM_RAW,
1755 'the new category description', VALUE_OPTIONAL),
1756 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1757 'theme' => new external_value(PARAM_THEME,
1758 'the new category theme. This option must be enabled on moodle',
1759 VALUE_OPTIONAL),
1768 * Create categories
1770 * @param array $categories - see create_categories_parameters() for the array structure
1771 * @return array - see create_categories_returns() for the array structure
1772 * @since Moodle 2.3
1774 public static function create_categories($categories) {
1775 global $CFG, $DB;
1776 require_once($CFG->libdir . "/coursecatlib.php");
1778 $params = self::validate_parameters(self::create_categories_parameters(),
1779 array('categories' => $categories));
1781 $transaction = $DB->start_delegated_transaction();
1783 $createdcategories = array();
1784 foreach ($params['categories'] as $category) {
1785 if ($category['parent']) {
1786 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1787 throw new moodle_exception('unknowcategory');
1789 $context = context_coursecat::instance($category['parent']);
1790 } else {
1791 $context = context_system::instance();
1793 self::validate_context($context);
1794 require_capability('moodle/category:manage', $context);
1796 // this will validate format and throw an exception if there are errors
1797 external_validate_format($category['descriptionformat']);
1799 $newcategory = coursecat::create($category);
1801 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1804 $transaction->allow_commit();
1806 return $createdcategories;
1810 * Returns description of method parameters
1812 * @return external_function_parameters
1813 * @since Moodle 2.3
1815 public static function create_categories_returns() {
1816 return new external_multiple_structure(
1817 new external_single_structure(
1818 array(
1819 'id' => new external_value(PARAM_INT, 'new category id'),
1820 'name' => new external_value(PARAM_TEXT, 'new category name'),
1827 * Returns description of method parameters
1829 * @return external_function_parameters
1830 * @since Moodle 2.3
1832 public static function update_categories_parameters() {
1833 return new external_function_parameters(
1834 array(
1835 'categories' => new external_multiple_structure(
1836 new external_single_structure(
1837 array(
1838 'id' => new external_value(PARAM_INT, 'course id'),
1839 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1840 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1841 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1842 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1843 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1844 'theme' => new external_value(PARAM_THEME,
1845 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1854 * Update categories
1856 * @param array $categories The list of categories to update
1857 * @return null
1858 * @since Moodle 2.3
1860 public static function update_categories($categories) {
1861 global $CFG, $DB;
1862 require_once($CFG->libdir . "/coursecatlib.php");
1864 // Validate parameters.
1865 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1867 $transaction = $DB->start_delegated_transaction();
1869 foreach ($params['categories'] as $cat) {
1870 $category = coursecat::get($cat['id']);
1872 $categorycontext = context_coursecat::instance($cat['id']);
1873 self::validate_context($categorycontext);
1874 require_capability('moodle/category:manage', $categorycontext);
1876 // this will throw an exception if descriptionformat is not valid
1877 external_validate_format($cat['descriptionformat']);
1879 $category->update($cat);
1882 $transaction->allow_commit();
1886 * Returns description of method result value
1888 * @return external_description
1889 * @since Moodle 2.3
1891 public static function update_categories_returns() {
1892 return null;
1896 * Returns description of method parameters
1898 * @return external_function_parameters
1899 * @since Moodle 2.3
1901 public static function delete_categories_parameters() {
1902 return new external_function_parameters(
1903 array(
1904 'categories' => new external_multiple_structure(
1905 new external_single_structure(
1906 array(
1907 'id' => new external_value(PARAM_INT, 'category id to delete'),
1908 'newparent' => new external_value(PARAM_INT,
1909 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1910 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1911 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1920 * Delete categories
1922 * @param array $categories A list of category ids
1923 * @return array
1924 * @since Moodle 2.3
1926 public static function delete_categories($categories) {
1927 global $CFG, $DB;
1928 require_once($CFG->dirroot . "/course/lib.php");
1929 require_once($CFG->libdir . "/coursecatlib.php");
1931 // Validate parameters.
1932 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1934 $transaction = $DB->start_delegated_transaction();
1936 foreach ($params['categories'] as $category) {
1937 $deletecat = coursecat::get($category['id'], MUST_EXIST);
1938 $context = context_coursecat::instance($deletecat->id);
1939 require_capability('moodle/category:manage', $context);
1940 self::validate_context($context);
1941 self::validate_context(get_category_or_system_context($deletecat->parent));
1943 if ($category['recursive']) {
1944 // If recursive was specified, then we recursively delete the category's contents.
1945 if ($deletecat->can_delete_full()) {
1946 $deletecat->delete_full(false);
1947 } else {
1948 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1950 } else {
1951 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1952 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1953 // We must move to an existing category.
1954 if (!empty($category['newparent'])) {
1955 $newparentcat = coursecat::get($category['newparent']);
1956 } else {
1957 $newparentcat = coursecat::get($deletecat->parent);
1960 // This operation is not allowed. We must move contents to an existing category.
1961 if (!$newparentcat->id) {
1962 throw new moodle_exception('movecatcontentstoroot');
1965 self::validate_context(context_coursecat::instance($newparentcat->id));
1966 if ($deletecat->can_move_content_to($newparentcat->id)) {
1967 $deletecat->delete_move($newparentcat->id, false);
1968 } else {
1969 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1974 $transaction->allow_commit();
1978 * Returns description of method parameters
1980 * @return external_function_parameters
1981 * @since Moodle 2.3
1983 public static function delete_categories_returns() {
1984 return null;
1988 * Describes the parameters for delete_modules.
1990 * @return external_external_function_parameters
1991 * @since Moodle 2.5
1993 public static function delete_modules_parameters() {
1994 return new external_function_parameters (
1995 array(
1996 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1997 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
2003 * Deletes a list of provided module instances.
2005 * @param array $cmids the course module ids
2006 * @since Moodle 2.5
2008 public static function delete_modules($cmids) {
2009 global $CFG, $DB;
2011 // Require course file containing the course delete module function.
2012 require_once($CFG->dirroot . "/course/lib.php");
2014 // Clean the parameters.
2015 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2017 // Keep track of the course ids we have performed a capability check on to avoid repeating.
2018 $arrcourseschecked = array();
2020 foreach ($params['cmids'] as $cmid) {
2021 // Get the course module.
2022 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2024 // Check if we have not yet confirmed they have permission in this course.
2025 if (!in_array($cm->course, $arrcourseschecked)) {
2026 // Ensure the current user has required permission in this course.
2027 $context = context_course::instance($cm->course);
2028 self::validate_context($context);
2029 // Add to the array.
2030 $arrcourseschecked[] = $cm->course;
2033 // Ensure they can delete this module.
2034 $modcontext = context_module::instance($cm->id);
2035 require_capability('moodle/course:manageactivities', $modcontext);
2037 // Delete the module.
2038 course_delete_module($cm->id);
2043 * Describes the delete_modules return value.
2045 * @return external_single_structure
2046 * @since Moodle 2.5
2048 public static function delete_modules_returns() {
2049 return null;
2053 * Returns description of method parameters
2055 * @return external_function_parameters
2056 * @since Moodle 2.9
2058 public static function view_course_parameters() {
2059 return new external_function_parameters(
2060 array(
2061 'courseid' => new external_value(PARAM_INT, 'id of the course'),
2062 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2068 * Trigger the course viewed event.
2070 * @param int $courseid id of course
2071 * @param int $sectionnumber sectionnumber (0, 1, 2...)
2072 * @return array of warnings and status result
2073 * @since Moodle 2.9
2074 * @throws moodle_exception
2076 public static function view_course($courseid, $sectionnumber = 0) {
2077 global $CFG;
2078 require_once($CFG->dirroot . "/course/lib.php");
2080 $params = self::validate_parameters(self::view_course_parameters(),
2081 array(
2082 'courseid' => $courseid,
2083 'sectionnumber' => $sectionnumber
2086 $warnings = array();
2088 $course = get_course($params['courseid']);
2089 $context = context_course::instance($course->id);
2090 self::validate_context($context);
2092 if (!empty($params['sectionnumber'])) {
2094 // Get section details and check it exists.
2095 $modinfo = get_fast_modinfo($course);
2096 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2098 // Check user is allowed to see it.
2099 if (!$coursesection->uservisible) {
2100 require_capability('moodle/course:viewhiddensections', $context);
2104 course_view($context, $params['sectionnumber']);
2106 $result = array();
2107 $result['status'] = true;
2108 $result['warnings'] = $warnings;
2109 return $result;
2113 * Returns description of method result value
2115 * @return external_description
2116 * @since Moodle 2.9
2118 public static function view_course_returns() {
2119 return new external_single_structure(
2120 array(
2121 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2122 'warnings' => new external_warnings()
2128 * Returns description of method parameters
2130 * @return external_function_parameters
2131 * @since Moodle 3.0
2133 public static function search_courses_parameters() {
2134 return new external_function_parameters(
2135 array(
2136 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name
2137 (search, modulelist (only admins), blocklist (only admins), tagid)'),
2138 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2139 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2140 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0)
2146 * Search courses following the specified criteria.
2148 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2149 * @param string $criteriavalue Criteria value
2150 * @param int $page Page number (for pagination)
2151 * @param int $perpage Items per page
2152 * @return array of course objects and warnings
2153 * @since Moodle 3.0
2154 * @throws moodle_exception
2156 public static function search_courses($criterianame, $criteriavalue, $page=0, $perpage=0) {
2157 global $CFG;
2158 require_once($CFG->libdir . '/coursecatlib.php');
2160 $warnings = array();
2162 $parameters = array(
2163 'criterianame' => $criterianame,
2164 'criteriavalue' => $criteriavalue,
2165 'page' => $page,
2166 'perpage' => $perpage
2168 $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2169 self::validate_context(context_system::instance());
2171 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2172 if (!in_array($params['criterianame'], $allowedcriterianames)) {
2173 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2174 'allowed values are: '.implode(',', $allowedcriterianames));
2177 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2178 require_capability('moodle/site:config', context_system::instance());
2181 $paramtype = array(
2182 'search' => PARAM_RAW,
2183 'modulelist' => PARAM_PLUGIN,
2184 'blocklist' => PARAM_INT,
2185 'tagid' => PARAM_INT
2187 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2189 // Prepare the search API options.
2190 $searchcriteria = array();
2191 $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2193 $options = array();
2194 if ($params['perpage'] != 0) {
2195 $offset = $params['page'] * $params['perpage'];
2196 $options = array('offset' => $offset, 'limit' => $params['perpage']);
2199 // Search the courses.
2200 $courses = coursecat::search_courses($searchcriteria, $options);
2201 $totalcount = coursecat::search_courses_count($searchcriteria);
2203 $finalcourses = array();
2204 $categoriescache = array();
2206 foreach ($courses as $course) {
2208 $coursecontext = context_course::instance($course->id);
2210 // Category information.
2211 if (!isset($categoriescache[$course->category])) {
2212 $categoriescache[$course->category] = coursecat::get($course->category);
2214 $category = $categoriescache[$course->category];
2216 // Retrieve course overfiew used files.
2217 $files = array();
2218 foreach ($course->get_course_overviewfiles() as $file) {
2219 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2220 $file->get_filearea(), null, $file->get_filepath(),
2221 $file->get_filename())->out(false);
2222 $files[] = array(
2223 'filename' => $file->get_filename(),
2224 'fileurl' => $fileurl,
2225 'filesize' => $file->get_filesize()
2229 // Retrieve the course contacts,
2230 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2231 $coursecontacts = array();
2232 foreach ($course->get_course_contacts() as $contact) {
2233 $coursecontacts[] = array(
2234 'id' => $contact['user']->id,
2235 'fullname' => $contact['username']
2239 // Allowed enrolment methods (maybe we can self-enrol).
2240 $enroltypes = array();
2241 $instances = enrol_get_instances($course->id, true);
2242 foreach ($instances as $instance) {
2243 $enroltypes[] = $instance->enrol;
2246 // Format summary.
2247 list($summary, $summaryformat) =
2248 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2250 $coursereturns = array();
2251 $coursereturns['id'] = $course->id;
2252 $coursereturns['fullname'] = $course->get_formatted_fullname();
2253 $coursereturns['shortname'] = $course->get_formatted_shortname();
2254 $coursereturns['categoryid'] = $course->category;
2255 $coursereturns['categoryname'] = $category->name;
2256 $coursereturns['summary'] = $summary;
2257 $coursereturns['summaryformat'] = $summaryformat;
2258 $coursereturns['overviewfiles'] = $files;
2259 $coursereturns['contacts'] = $coursecontacts;
2260 $coursereturns['enrollmentmethods'] = $enroltypes;
2261 $finalcourses[] = $coursereturns;
2264 return array(
2265 'total' => $totalcount,
2266 'courses' => $finalcourses,
2267 'warnings' => $warnings
2272 * Returns description of method result value
2274 * @return external_description
2275 * @since Moodle 3.0
2277 public static function search_courses_returns() {
2279 return new external_single_structure(
2280 array(
2281 'total' => new external_value(PARAM_INT, 'total course count'),
2282 'courses' => new external_multiple_structure(
2283 new external_single_structure(
2284 array(
2285 'id' => new external_value(PARAM_INT, 'course id'),
2286 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2287 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2288 'categoryid' => new external_value(PARAM_INT, 'category id'),
2289 'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2290 'summary' => new external_value(PARAM_RAW, 'summary'),
2291 'summaryformat' => new external_format_value('summary'),
2292 'overviewfiles' => new external_multiple_structure(
2293 new external_single_structure(
2294 array(
2295 'filename' => new external_value(PARAM_FILE, 'overview file name'),
2296 'fileurl' => new external_value(PARAM_URL, 'overview file url'),
2297 'filesize' => new external_value(PARAM_INT, 'overview file size'),
2300 'additional overview files attached to this course'
2302 'contacts' => new external_multiple_structure(
2303 new external_single_structure(
2304 array(
2305 'id' => new external_value(PARAM_INT, 'contact user id'),
2306 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2309 'contact users'
2311 'enrollmentmethods' => new external_multiple_structure(
2312 new external_value(PARAM_PLUGIN, 'enrollment method'),
2313 'enrollment methods list'
2316 ), 'course'
2318 'warnings' => new external_warnings()
2324 * Returns description of method parameters
2326 * @return external_function_parameters
2327 * @since Moodle 3.0
2329 public static function get_course_module_parameters() {
2330 return new external_function_parameters(
2331 array(
2332 'cmid' => new external_value(PARAM_INT, 'The course module id')
2338 * Return information about a course module.
2340 * @param int $cmid the course module id
2341 * @return array of warnings and the course module
2342 * @since Moodle 3.0
2343 * @throws moodle_exception
2345 public static function get_course_module($cmid) {
2347 $params = self::validate_parameters(self::get_course_module_parameters(),
2348 array(
2349 'cmid' => $cmid,
2352 $warnings = array();
2354 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2355 $context = context_module::instance($cm->id);
2356 self::validate_context($context);
2358 // If the user has permissions to manage the activity, return all the information.
2359 if (has_capability('moodle/course:manageactivities', $context)) {
2360 $info = $cm;
2361 } else {
2362 // Return information is safe to show to any user.
2363 $info = new stdClass();
2364 $info->id = $cm->id;
2365 $info->course = $cm->course;
2366 $info->module = $cm->module;
2367 $info->modname = $cm->modname;
2368 $info->instance = $cm->instance;
2369 $info->section = $cm->section;
2370 $info->sectionnum = $cm->sectionnum;
2371 $info->groupmode = $cm->groupmode;
2372 $info->groupingid = $cm->groupingid;
2373 $info->completion = $cm->completion;
2375 // Format name.
2376 $info->name = external_format_string($cm->name, $context->id);
2378 $result = array();
2379 $result['cm'] = $info;
2380 $result['warnings'] = $warnings;
2381 return $result;
2385 * Returns description of method result value
2387 * @return external_description
2388 * @since Moodle 3.0
2390 public static function get_course_module_returns() {
2391 return new external_single_structure(
2392 array(
2393 'cm' => new external_single_structure(
2394 array(
2395 'id' => new external_value(PARAM_INT, 'The course module id'),
2396 'course' => new external_value(PARAM_INT, 'The course id'),
2397 'module' => new external_value(PARAM_INT, 'The module type id'),
2398 'name' => new external_value(PARAM_RAW, 'The activity name'),
2399 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2400 'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2401 'section' => new external_value(PARAM_INT, 'The module section id'),
2402 'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2403 'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2404 'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2405 'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2406 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2407 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2408 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2409 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
2410 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
2411 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
2412 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
2413 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
2414 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
2415 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
2416 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
2419 'warnings' => new external_warnings()
2425 * Returns description of method parameters
2427 * @return external_function_parameters
2428 * @since Moodle 3.0
2430 public static function get_course_module_by_instance_parameters() {
2431 return new external_function_parameters(
2432 array(
2433 'module' => new external_value(PARAM_COMPONENT, 'The module name'),
2434 'instance' => new external_value(PARAM_INT, 'The module instance id')
2440 * Return information about a course module.
2442 * @param string $module the module name
2443 * @param int $instance the activity instance id
2444 * @return array of warnings and the course module
2445 * @since Moodle 3.0
2446 * @throws moodle_exception
2448 public static function get_course_module_by_instance($module, $instance) {
2450 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
2451 array(
2452 'module' => $module,
2453 'instance' => $instance,
2456 $warnings = array();
2457 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
2459 return self::get_course_module($cm->id);
2463 * Returns description of method result value
2465 * @return external_description
2466 * @since Moodle 3.0
2468 public static function get_course_module_by_instance_returns() {
2469 return self::get_course_module_returns();
2475 * Deprecated course external functions
2477 * @package core_course
2478 * @copyright 2009 Petr Skodak
2479 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2480 * @since Moodle 2.0
2481 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
2482 * @see core_course_external
2484 class moodle_course_external extends external_api {
2487 * Returns description of method parameters
2489 * @return external_function_parameters
2490 * @since Moodle 2.0
2491 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2492 * @see core_course_external::get_courses_parameters()
2494 public static function get_courses_parameters() {
2495 return core_course_external::get_courses_parameters();
2499 * Get courses
2501 * @param array $options
2502 * @return array
2503 * @since Moodle 2.0
2504 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2505 * @see core_course_external::get_courses()
2507 public static function get_courses($options) {
2508 return core_course_external::get_courses($options);
2512 * Returns description of method result value
2514 * @return external_description
2515 * @since Moodle 2.0
2516 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2517 * @see core_course_external::get_courses_returns()
2519 public static function get_courses_returns() {
2520 return core_course_external::get_courses_returns();
2524 * Marking the method as deprecated.
2526 * @return bool
2528 public static function get_courses_is_deprecated() {
2529 return true;
2533 * Returns description of method parameters
2535 * @return external_function_parameters
2536 * @since Moodle 2.0
2537 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2538 * @see core_course_external::create_courses_parameters()
2540 public static function create_courses_parameters() {
2541 return core_course_external::create_courses_parameters();
2545 * Create courses
2547 * @param array $courses
2548 * @return array courses (id and shortname only)
2549 * @since Moodle 2.0
2550 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2551 * @see core_course_external::create_courses()
2553 public static function create_courses($courses) {
2554 return core_course_external::create_courses($courses);
2558 * Returns description of method result value
2560 * @return external_description
2561 * @since Moodle 2.0
2562 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2563 * @see core_course_external::create_courses_returns()
2565 public static function create_courses_returns() {
2566 return core_course_external::create_courses_returns();
2570 * Marking the method as deprecated.
2572 * @return bool
2574 public static function create_courses_is_deprecated() {
2575 return true;