2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
21 * @package core_course
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 use core_course\external\course_summary_exporter;
31 require_once("$CFG->libdir/externallib.php");
32 require_once("lib.php");
35 * Course external functions
37 * @package core_course
39 * @copyright 2011 Jerome Mouneyrac
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 class core_course_external extends external_api {
46 * Returns description of method parameters
48 * @return external_function_parameters
49 * @since Moodle 2.9 Options available
52 public static function get_course_contents_parameters() {
53 return new external_function_parameters(
54 array('courseid' => new external_value(PARAM_INT, 'course id'),
55 'options' => new external_multiple_structure (
56 new external_single_structure(
58 'name' => new external_value(PARAM_ALPHANUM,
59 'The expected keys (value format) are:
60 excludemodules (bool) Do not return modules, return only the sections structure
61 excludecontents (bool) Do not return module contents (i.e: files inside a resource)
62 includestealthmodules (bool) Return stealth modules for students in a special
64 sectionid (int) Return only this section
65 sectionnumber (int) Return only this section with number (order)
66 cmid (int) Return only this module information (among the whole sections structure)
67 modname (string) Return only modules with this name "label, forum, etc..."
68 modid (int) Return only the module with this id (to be used with modname'),
69 'value' => new external_value(PARAM_RAW, 'the value of the option,
70 this param is personaly validated in the external function.')
72 ), 'Options, used since Moodle 2.9', VALUE_DEFAULT, array())
80 * @param int $courseid course id
81 * @param array $options Options for filtering the results, used since Moodle 2.9
83 * @since Moodle 2.9 Options available
86 public static function get_course_contents($courseid, $options = array()) {
88 require_once($CFG->dirroot . "/course/lib.php");
89 require_once($CFG->libdir . '/completionlib.php');
92 $params = self::validate_parameters(self::get_course_contents_parameters(),
93 array('courseid' => $courseid, 'options' => $options));
96 if (!empty($params['options'])) {
98 foreach ($params['options'] as $option) {
99 $name = trim($option['name']);
100 // Avoid duplicated options.
101 if (!isset($filters[$name])) {
103 case 'excludemodules':
104 case 'excludecontents':
105 case 'includestealthmodules':
106 $value = clean_param($option['value'], PARAM_BOOL);
107 $filters[$name] = $value;
110 case 'sectionnumber':
113 $value = clean_param($option['value'], PARAM_INT);
114 if (is_numeric($value)) {
115 $filters[$name] = $value;
117 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
121 $value = clean_param($option['value'], PARAM_PLUGIN);
123 $filters[$name] = $value;
125 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
129 throw new moodle_exception('errorinvalidparam', 'webservice', '', $name);
135 //retrieve the course
136 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
138 if ($course->id != SITEID) {
139 // Check course format exist.
140 if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
141 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null,
142 get_string('courseformatnotfound', 'error', $course->format));
144 require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
148 // now security checks
149 $context = context_course::instance($course->id, IGNORE_MISSING);
151 self::validate_context($context);
152 } catch (Exception $e) {
153 $exceptionparam = new stdClass();
154 $exceptionparam->message = $e->getMessage();
155 $exceptionparam->courseid = $course->id;
156 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
159 $canupdatecourse = has_capability('moodle/course:update', $context);
161 //create return value
162 $coursecontents = array();
164 if ($canupdatecourse or $course->visible
165 or has_capability('moodle/course:viewhiddencourses', $context)) {
168 $modinfo = get_fast_modinfo($course);
169 $sections = $modinfo->get_section_info_all();
170 $coursenumsections = course_get_format($course)->get_last_section_number();
171 $stealthmodules = array(); // Array to keep all the modules available but not visible in a course section/topic.
173 $completioninfo = new completion_info($course);
175 //for each sections (first displayed to last displayed)
176 $modinfosections = $modinfo->get_sections();
177 foreach ($sections as $key => $section) {
179 // This becomes true when we are filtering and we found the value to filter with.
180 $sectionfound = false;
182 // Filter by section id.
183 if (!empty($filters['sectionid'])) {
184 if ($section->id != $filters['sectionid']) {
187 $sectionfound = true;
191 // Filter by section number. Note that 0 is a valid section number.
192 if (isset($filters['sectionnumber'])) {
193 if ($key != $filters['sectionnumber']) {
196 $sectionfound = true;
200 // reset $sectioncontents
201 $sectionvalues = array();
202 $sectionvalues['id'] = $section->id;
203 $sectionvalues['name'] = get_section_name($course, $section);
204 $sectionvalues['visible'] = $section->visible;
206 $options = (object) array('noclean' => true);
207 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
208 external_format_text($section->summary, $section->summaryformat,
209 $context->id, 'course', 'section', $section->id, $options);
210 $sectionvalues['section'] = $section->section;
211 $sectionvalues['hiddenbynumsections'] = $section->section > $coursenumsections ? 1 : 0;
212 $sectionvalues['uservisible'] = $section->uservisible;
213 if (!empty($section->availableinfo)) {
214 $sectionvalues['availabilityinfo'] = \core_availability\info::format_info($section->availableinfo, $course);
217 $sectioncontents = array();
219 // For each module of the section.
220 if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
221 foreach ($modinfosections[$section->section] as $cmid) {
222 $cm = $modinfo->cms[$cmid];
224 // Stop here if the module is not visible to the user on the course main page:
225 // The user can't access the module and the user can't view the module on the course page.
226 if (!$cm->uservisible && !$cm->is_visible_on_course_page()) {
230 // This becomes true when we are filtering and we found the value to filter with.
234 if (!empty($filters['cmid'])) {
235 if ($cmid != $filters['cmid']) {
242 // Filter by module name and id.
243 if (!empty($filters['modname'])) {
244 if ($cm->modname != $filters['modname']) {
246 } else if (!empty($filters['modid'])) {
247 if ($cm->instance != $filters['modid']) {
250 // Note that if we are only filtering by modname we don't break the loop.
258 $modcontext = context_module::instance($cm->id);
260 //common info (for people being able to see the module or availability dates)
261 $module['id'] = $cm->id;
262 $module['name'] = external_format_string($cm->name, $modcontext->id);
263 $module['instance'] = $cm->instance;
264 $module['modname'] = $cm->modname;
265 $module['modplural'] = $cm->modplural;
266 $module['modicon'] = $cm->get_icon_url()->out(false);
267 $module['indent'] = $cm->indent;
268 $module['onclick'] = $cm->onclick;
269 $module['afterlink'] = $cm->afterlink;
270 $module['customdata'] = json_encode($cm->customdata);
271 $module['completion'] = $cm->completion;
273 // Check module completion.
274 $completion = $completioninfo->is_enabled($cm);
275 if ($completion != COMPLETION_DISABLED) {
276 $completiondata = $completioninfo->get_data($cm, true);
277 $module['completiondata'] = array(
278 'state' => $completiondata->completionstate,
279 'timecompleted' => $completiondata->timemodified,
280 'overrideby' => $completiondata->overrideby,
281 'valueused' => core_availability\info::completion_value_used($course, $cm->id)
285 if (!empty($cm->showdescription) or $cm->modname == 'label') {
286 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
287 $options = array('noclean' => true);
288 list($module['description'], $descriptionformat) = external_format_text($cm->content,
289 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id, $options);
294 if ($url) { //labels don't have url
295 $module['url'] = $url->out(false);
298 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
299 context_module::instance($cm->id));
300 //user that can view hidden module should know about the visibility
301 $module['visible'] = $cm->visible;
302 $module['visibleoncoursepage'] = $cm->visibleoncoursepage;
303 $module['uservisible'] = $cm->uservisible;
304 if (!empty($cm->availableinfo)) {
305 $module['availabilityinfo'] = \core_availability\info::format_info($cm->availableinfo, $course);
308 // Availability date (also send to user who can see hidden module).
309 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
310 $module['availability'] = $cm->availability;
313 // Return contents only if the user can access to the module.
314 if ($cm->uservisible) {
315 $baseurl = 'webservice/pluginfile.php';
317 // Call $modulename_export_contents (each module callback take care about checking the capabilities).
318 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
319 $getcontentfunction = $cm->modname.'_export_contents';
320 if (function_exists($getcontentfunction)) {
321 $contents = $getcontentfunction($cm, $baseurl);
322 $module['contentsinfo'] = array(
323 'filescount' => count($contents),
326 'mimetypes' => array(),
328 foreach ($contents as $content) {
329 if (isset($content['filesize'])) {
330 $module['contentsinfo']['filessize'] += $content['filesize'];
332 if (isset($content['timemodified']) &&
333 ($content['timemodified'] > $module['contentsinfo']['lastmodified'])) {
335 $module['contentsinfo']['lastmodified'] = $content['timemodified'];
337 if (isset($content['mimetype'])) {
338 $module['contentsinfo']['mimetypes'][$content['mimetype']] = $content['mimetype'];
342 if (empty($filters['excludecontents']) and !empty($contents)) {
343 $module['contents'] = $contents;
345 $module['contents'] = array();
350 // Assign result to $sectioncontents, there is an exception,
351 // stealth activities in non-visible sections for students go to a special section.
352 if (!empty($filters['includestealthmodules']) && !$section->uservisible && $cm->is_stealth()) {
353 $stealthmodules[] = $module;
355 $sectioncontents[] = $module;
358 // If we just did a filtering, break the loop.
365 $sectionvalues['modules'] = $sectioncontents;
367 // assign result to $coursecontents
368 $coursecontents[$key] = $sectionvalues;
370 // Break the loop if we are filtering.
376 // Now that we have iterated over all the sections and activities, check the visibility.
377 // We didn't this before to be able to retrieve stealth activities.
378 foreach ($coursecontents as $sectionnumber => $sectioncontents) {
379 $section = $sections[$sectionnumber];
380 // Show the section if the user is permitted to access it, OR if it's not available
381 // but there is some available info text which explains the reason & should display.
382 $showsection = $section->uservisible ||
383 ($section->visible && !$section->available &&
384 !empty($section->availableinfo));
387 unset($coursecontents[$sectionnumber]);
391 // Remove modules information if the section is not visible for the user.
392 if (!$section->uservisible) {
393 $coursecontents[$sectionnumber]['modules'] = array();
397 // Include stealth modules in special section (without any info).
398 if (!empty($stealthmodules)) {
399 $coursecontents[] = array(
403 'summaryformat' => FORMAT_MOODLE,
404 'modules' => $stealthmodules
409 return $coursecontents;
413 * Returns description of method result value
415 * @return external_description
418 public static function get_course_contents_returns() {
419 return new external_multiple_structure(
420 new external_single_structure(
422 'id' => new external_value(PARAM_INT, 'Section ID'),
423 'name' => new external_value(PARAM_TEXT, 'Section name'),
424 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
425 'summary' => new external_value(PARAM_RAW, 'Section description'),
426 'summaryformat' => new external_format_value('summary'),
427 'section' => new external_value(PARAM_INT, 'Section number inside the course', VALUE_OPTIONAL),
428 'hiddenbynumsections' => new external_value(PARAM_INT, 'Whether is a section hidden in the course format',
430 'uservisible' => new external_value(PARAM_BOOL, 'Is the section visible for the user?', VALUE_OPTIONAL),
431 'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.', VALUE_OPTIONAL),
432 'modules' => new external_multiple_structure(
433 new external_single_structure(
435 'id' => new external_value(PARAM_INT, 'activity id'),
436 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
437 'name' => new external_value(PARAM_RAW, 'activity module name'),
438 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
439 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
440 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
441 'uservisible' => new external_value(PARAM_BOOL, 'Is the module visible for the user?',
443 'availabilityinfo' => new external_value(PARAM_RAW, 'Availability information.',
445 'visibleoncoursepage' => new external_value(PARAM_INT, 'is the module visible on course page',
447 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
448 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
449 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
450 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
451 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
452 'onclick' => new external_value(PARAM_RAW, 'Onclick action.', VALUE_OPTIONAL),
453 'afterlink' => new external_value(PARAM_RAW, 'After link info to be displayed.',
455 'customdata' => new external_value(PARAM_RAW, 'Custom data (JSON encoded).', VALUE_OPTIONAL),
456 'completion' => new external_value(PARAM_INT, 'Type of completion tracking:
457 0 means none, 1 manual, 2 automatic.', VALUE_OPTIONAL),
458 'completiondata' => new external_single_structure(
460 'state' => new external_value(PARAM_INT, 'Completion state value:
461 0 means incomplete, 1 complete, 2 complete pass, 3 complete fail'),
462 'timecompleted' => new external_value(PARAM_INT, 'Timestamp for completion status.'),
463 'overrideby' => new external_value(PARAM_INT, 'The user id who has overriden the
465 'valueused' => new external_value(PARAM_BOOL, 'Whether the completion status affects
466 the availability of another activity.', VALUE_OPTIONAL),
467 ), 'Module completion data.', VALUE_OPTIONAL
469 'contents' => new external_multiple_structure(
470 new external_single_structure(
473 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
474 'filename'=> new external_value(PARAM_FILE, 'filename'),
475 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
476 'filesize'=> new external_value(PARAM_INT, 'filesize'),
477 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
478 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
479 'timecreated' => new external_value(PARAM_INT, 'Time created'),
480 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
481 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
482 'mimetype' => new external_value(PARAM_RAW, 'File mime type.', VALUE_OPTIONAL),
483 'isexternalfile' => new external_value(PARAM_BOOL, 'Whether is an external file.',
485 'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for external files.',
488 // copyright related info
489 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
490 'author' => new external_value(PARAM_TEXT, 'Content owner'),
491 'license' => new external_value(PARAM_TEXT, 'Content license'),
493 ), VALUE_DEFAULT, array()
495 'contentsinfo' => new external_single_structure(
497 'filescount' => new external_value(PARAM_INT, 'Total number of files.'),
498 'filessize' => new external_value(PARAM_INT, 'Total files size.'),
499 'lastmodified' => new external_value(PARAM_INT, 'Last time files were modified.'),
500 'mimetypes' => new external_multiple_structure(
501 new external_value(PARAM_RAW, 'File mime type.'),
504 ), 'Contents summary information.', VALUE_OPTIONAL
515 * Returns description of method parameters
517 * @return external_function_parameters
520 public static function get_courses_parameters() {
521 return new external_function_parameters(
522 array('options' => new external_single_structure(
523 array('ids' => new external_multiple_structure(
524 new external_value(PARAM_INT, 'Course id')
525 , 'List of course id. If empty return all courses
526 except front page course.',
528 ), 'options - operator OR is used', VALUE_DEFAULT, array())
536 * @param array $options It contains an array (list of ids)
540 public static function get_courses($options = array()) {
542 require_once($CFG->dirroot . "/course/lib.php");
545 $params = self::validate_parameters(self::get_courses_parameters(),
546 array('options' => $options));
549 if (!array_key_exists('ids', $params['options'])
550 or empty($params['options']['ids'])) {
551 $courses = $DB->get_records('course');
553 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
556 //create return value
557 $coursesinfo = array();
558 foreach ($courses as $course) {
560 // now security checks
561 $context = context_course::instance($course->id, IGNORE_MISSING);
562 $courseformatoptions = course_get_format($course)->get_format_options();
564 self::validate_context($context);
565 } catch (Exception $e) {
566 $exceptionparam = new stdClass();
567 $exceptionparam->message = $e->getMessage();
568 $exceptionparam->courseid = $course->id;
569 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
571 if ($course->id != SITEID) {
572 require_capability('moodle/course:view', $context);
575 $courseinfo = array();
576 $courseinfo['id'] = $course->id;
577 $courseinfo['fullname'] = external_format_string($course->fullname, $context->id);
578 $courseinfo['shortname'] = external_format_string($course->shortname, $context->id);
579 $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id);
580 $courseinfo['categoryid'] = $course->category;
581 list($courseinfo['summary'], $courseinfo['summaryformat']) =
582 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
583 $courseinfo['format'] = $course->format;
584 $courseinfo['startdate'] = $course->startdate;
585 $courseinfo['enddate'] = $course->enddate;
586 if (array_key_exists('numsections', $courseformatoptions)) {
587 // For backward-compartibility
588 $courseinfo['numsections'] = $courseformatoptions['numsections'];
591 $handler = core_course\customfield\course_handler::create();
592 if ($customfields = $handler->export_instance_data($course->id)) {
593 $courseinfo['customfields'] = [];
594 foreach ($customfields as $data) {
595 $courseinfo['customfields'][] = [
596 'type' => $data->get_type(),
597 'value' => $data->get_value(),
598 'name' => $data->get_name(),
599 'shortname' => $data->get_shortname()
604 //some field should be returned only if the user has update permission
605 $courseadmin = has_capability('moodle/course:update', $context);
607 $courseinfo['categorysortorder'] = $course->sortorder;
608 $courseinfo['idnumber'] = $course->idnumber;
609 $courseinfo['showgrades'] = $course->showgrades;
610 $courseinfo['showreports'] = $course->showreports;
611 $courseinfo['newsitems'] = $course->newsitems;
612 $courseinfo['visible'] = $course->visible;
613 $courseinfo['maxbytes'] = $course->maxbytes;
614 if (array_key_exists('hiddensections', $courseformatoptions)) {
615 // For backward-compartibility
616 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
618 // Return numsections for backward-compatibility with clients who expect it.
619 $courseinfo['numsections'] = course_get_format($course)->get_last_section_number();
620 $courseinfo['groupmode'] = $course->groupmode;
621 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
622 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
623 $courseinfo['lang'] = clean_param($course->lang, PARAM_LANG);
624 $courseinfo['timecreated'] = $course->timecreated;
625 $courseinfo['timemodified'] = $course->timemodified;
626 $courseinfo['forcetheme'] = clean_param($course->theme, PARAM_THEME);
627 $courseinfo['enablecompletion'] = $course->enablecompletion;
628 $courseinfo['completionnotify'] = $course->completionnotify;
629 $courseinfo['courseformatoptions'] = array();
630 foreach ($courseformatoptions as $key => $value) {
631 $courseinfo['courseformatoptions'][] = array(
638 if ($courseadmin or $course->visible
639 or has_capability('moodle/course:viewhiddencourses', $context)) {
640 $coursesinfo[] = $courseinfo;
648 * Returns description of method result value
650 * @return external_description
653 public static function get_courses_returns() {
654 return new external_multiple_structure(
655 new external_single_structure(
657 'id' => new external_value(PARAM_INT, 'course id'),
658 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
659 'categoryid' => new external_value(PARAM_INT, 'category id'),
660 'categorysortorder' => new external_value(PARAM_INT,
661 'sort order into the category', VALUE_OPTIONAL),
662 'fullname' => new external_value(PARAM_TEXT, 'full name'),
663 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
664 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
665 'summary' => new external_value(PARAM_RAW, 'summary'),
666 'summaryformat' => new external_format_value('summary'),
667 'format' => new external_value(PARAM_PLUGIN,
668 'course format: weeks, topics, social, site,..'),
669 'showgrades' => new external_value(PARAM_INT,
670 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
671 'newsitems' => new external_value(PARAM_INT,
672 'number of recent items appearing on the course page', VALUE_OPTIONAL),
673 'startdate' => new external_value(PARAM_INT,
674 'timestamp when the course start'),
675 'enddate' => new external_value(PARAM_INT,
676 'timestamp when the course end'),
677 'numsections' => new external_value(PARAM_INT,
678 '(deprecated, use courseformatoptions) number of weeks/topics',
680 'maxbytes' => new external_value(PARAM_INT,
681 'largest size of file that can be uploaded into the course',
683 'showreports' => new external_value(PARAM_INT,
684 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
685 'visible' => new external_value(PARAM_INT,
686 '1: available to student, 0:not available', VALUE_OPTIONAL),
687 'hiddensections' => new external_value(PARAM_INT,
688 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
690 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
692 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
694 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
696 'timecreated' => new external_value(PARAM_INT,
697 'timestamp when the course have been created', VALUE_OPTIONAL),
698 'timemodified' => new external_value(PARAM_INT,
699 'timestamp when the course have been modified', VALUE_OPTIONAL),
700 'enablecompletion' => new external_value(PARAM_INT,
701 'Enabled, control via completion and activity settings. Disbaled,
702 not shown in activity settings.',
704 'completionnotify' => new external_value(PARAM_INT,
705 '1: yes 0: no', VALUE_OPTIONAL),
706 'lang' => new external_value(PARAM_SAFEDIR,
707 'forced course language', VALUE_OPTIONAL),
708 'forcetheme' => new external_value(PARAM_PLUGIN,
709 'name of the force theme', VALUE_OPTIONAL),
710 'courseformatoptions' => new external_multiple_structure(
711 new external_single_structure(
712 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
713 'value' => new external_value(PARAM_RAW, 'course format option value')
714 )), 'additional options for particular course format', VALUE_OPTIONAL
716 'customfields' => new external_multiple_structure(
717 new external_single_structure(
718 ['name' => new external_value(PARAM_TEXT, 'The name of the custom field'),
719 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
720 'type' => new external_value(PARAM_COMPONENT,
721 'The type of the custom field - text, checkbox...'),
722 'value' => new external_value(PARAM_RAW, 'The value of the custom field')]
723 ), 'Custom fields and associated values', VALUE_OPTIONAL),
730 * Returns description of method parameters
732 * @return external_function_parameters
735 public static function create_courses_parameters() {
736 $courseconfig = get_config('moodlecourse'); //needed for many default values
737 return new external_function_parameters(
739 'courses' => new external_multiple_structure(
740 new external_single_structure(
742 'fullname' => new external_value(PARAM_TEXT, 'full name'),
743 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
744 'categoryid' => new external_value(PARAM_INT, 'category id'),
745 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
746 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
747 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
748 'format' => new external_value(PARAM_PLUGIN,
749 'course format: weeks, topics, social, site,..',
750 VALUE_DEFAULT, $courseconfig->format),
751 'showgrades' => new external_value(PARAM_INT,
752 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
753 $courseconfig->showgrades),
754 'newsitems' => new external_value(PARAM_INT,
755 'number of recent items appearing on the course page',
756 VALUE_DEFAULT, $courseconfig->newsitems),
757 'startdate' => new external_value(PARAM_INT,
758 'timestamp when the course start', VALUE_OPTIONAL),
759 'enddate' => new external_value(PARAM_INT,
760 'timestamp when the course end', VALUE_OPTIONAL),
761 'numsections' => new external_value(PARAM_INT,
762 '(deprecated, use courseformatoptions) number of weeks/topics',
764 'maxbytes' => new external_value(PARAM_INT,
765 'largest size of file that can be uploaded into the course',
766 VALUE_DEFAULT, $courseconfig->maxbytes),
767 'showreports' => new external_value(PARAM_INT,
768 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
769 $courseconfig->showreports),
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 displayed to students',
775 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
776 VALUE_DEFAULT, $courseconfig->groupmode),
777 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
778 VALUE_DEFAULT, $courseconfig->groupmodeforce),
779 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
781 'enablecompletion' => new external_value(PARAM_INT,
782 'Enabled, control via completion and activity settings. Disabled,
783 not shown in activity settings.',
785 'completionnotify' => new external_value(PARAM_INT,
786 '1: yes 0: no', VALUE_OPTIONAL),
787 'lang' => new external_value(PARAM_SAFEDIR,
788 'forced course language', VALUE_OPTIONAL),
789 'forcetheme' => new external_value(PARAM_PLUGIN,
790 'name of the force theme', VALUE_OPTIONAL),
791 'courseformatoptions' => new external_multiple_structure(
792 new external_single_structure(
793 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
794 'value' => new external_value(PARAM_RAW, 'course format option value')
796 'additional options for particular course format', VALUE_OPTIONAL),
797 'customfields' => new external_multiple_structure(
798 new external_single_structure(
800 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
801 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
802 )), 'custom fields for the course', VALUE_OPTIONAL
804 )), 'courses to create'
813 * @param array $courses
814 * @return array courses (id and shortname only)
817 public static function create_courses($courses) {
819 require_once($CFG->dirroot . "/course/lib.php");
820 require_once($CFG->libdir . '/completionlib.php');
822 $params = self::validate_parameters(self::create_courses_parameters(),
823 array('courses' => $courses));
825 $availablethemes = core_component::get_plugin_list('theme');
826 $availablelangs = get_string_manager()->get_list_of_translations();
828 $transaction = $DB->start_delegated_transaction();
830 foreach ($params['courses'] as $course) {
832 // Ensure the current user is allowed to run this function
833 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
835 self::validate_context($context);
836 } catch (Exception $e) {
837 $exceptionparam = new stdClass();
838 $exceptionparam->message = $e->getMessage();
839 $exceptionparam->catid = $course['categoryid'];
840 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
842 require_capability('moodle/course:create', $context);
844 // Make sure lang is valid
845 if (array_key_exists('lang', $course)) {
846 if (empty($availablelangs[$course['lang']])) {
847 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
849 if (!has_capability('moodle/course:setforcedlanguage', $context)) {
850 unset($course['lang']);
854 // Make sure theme is valid
855 if (array_key_exists('forcetheme', $course)) {
856 if (!empty($CFG->allowcoursethemes)) {
857 if (empty($availablethemes[$course['forcetheme']])) {
858 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
860 $course['theme'] = $course['forcetheme'];
865 //force visibility if ws user doesn't have the permission to set it
866 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
867 if (!has_capability('moodle/course:visibility', $context)) {
868 $course['visible'] = $category->visible;
871 //set default value for completion
872 $courseconfig = get_config('moodlecourse');
873 if (completion_info::is_enabled_for_site()) {
874 if (!array_key_exists('enablecompletion', $course)) {
875 $course['enablecompletion'] = $courseconfig->enablecompletion;
878 $course['enablecompletion'] = 0;
881 $course['category'] = $course['categoryid'];
884 $course['summaryformat'] = external_validate_format($course['summaryformat']);
886 if (!empty($course['courseformatoptions'])) {
887 foreach ($course['courseformatoptions'] as $option) {
888 $course[$option['name']] = $option['value'];
893 if (!empty($course['customfields'])) {
894 foreach ($course['customfields'] as $field) {
895 $course['customfield_'.$field['shortname']] = $field['value'];
899 //Note: create_course() core function check shortname, idnumber, category
900 $course['id'] = create_course((object) $course)->id;
902 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
905 $transaction->allow_commit();
907 return $resultcourses;
911 * Returns description of method result value
913 * @return external_description
916 public static function create_courses_returns() {
917 return new external_multiple_structure(
918 new external_single_structure(
920 'id' => new external_value(PARAM_INT, 'course id'),
921 'shortname' => new external_value(PARAM_TEXT, 'short name'),
930 * @return external_function_parameters
933 public static function update_courses_parameters() {
934 return new external_function_parameters(
936 'courses' => new external_multiple_structure(
937 new external_single_structure(
939 'id' => new external_value(PARAM_INT, 'ID of the course'),
940 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
941 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
942 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
943 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
944 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
945 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
946 'format' => new external_value(PARAM_PLUGIN,
947 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
948 'showgrades' => new external_value(PARAM_INT,
949 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
950 'newsitems' => new external_value(PARAM_INT,
951 'number of recent items appearing on the course page', VALUE_OPTIONAL),
952 'startdate' => new external_value(PARAM_INT,
953 'timestamp when the course start', VALUE_OPTIONAL),
954 'enddate' => new external_value(PARAM_INT,
955 'timestamp when the course end', VALUE_OPTIONAL),
956 'numsections' => new external_value(PARAM_INT,
957 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
958 'maxbytes' => new external_value(PARAM_INT,
959 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
960 'showreports' => new external_value(PARAM_INT,
961 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
962 'visible' => new external_value(PARAM_INT,
963 '1: available to student, 0:not available', VALUE_OPTIONAL),
964 'hiddensections' => new external_value(PARAM_INT,
965 '(deprecated, use courseformatoptions) How the hidden sections in the course are
966 displayed to students', VALUE_OPTIONAL),
967 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
968 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
969 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
970 'enablecompletion' => new external_value(PARAM_INT,
971 'Enabled, control via completion and activity settings. Disabled,
972 not shown in activity settings.', VALUE_OPTIONAL),
973 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
974 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
975 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
976 'courseformatoptions' => new external_multiple_structure(
977 new external_single_structure(
978 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
979 'value' => new external_value(PARAM_RAW, 'course format option value')
980 )), 'additional options for particular course format', VALUE_OPTIONAL),
981 'customfields' => new external_multiple_structure(
982 new external_single_structure(
984 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'The shortname of the custom field'),
985 'value' => new external_value(PARAM_RAW, 'The value of the custom field')
987 ), 'Custom fields', VALUE_OPTIONAL),
989 ), 'courses to update'
998 * @param array $courses
1001 public static function update_courses($courses) {
1003 require_once($CFG->dirroot . "/course/lib.php");
1004 $warnings = array();
1006 $params = self::validate_parameters(self::update_courses_parameters(),
1007 array('courses' => $courses));
1009 $availablethemes = core_component::get_plugin_list('theme');
1010 $availablelangs = get_string_manager()->get_list_of_translations();
1012 foreach ($params['courses'] as $course) {
1013 // Catch any exception while updating course and return as warning to user.
1015 // Ensure the current user is allowed to run this function.
1016 $context = context_course::instance($course['id'], MUST_EXIST);
1017 self::validate_context($context);
1019 $oldcourse = course_get_format($course['id'])->get_course();
1021 require_capability('moodle/course:update', $context);
1023 // Check if user can change category.
1024 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
1025 require_capability('moodle/course:changecategory', $context);
1026 $course['category'] = $course['categoryid'];
1029 // Check if the user can change fullname.
1030 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
1031 require_capability('moodle/course:changefullname', $context);
1034 // Check if the user can change shortname.
1035 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
1036 require_capability('moodle/course:changeshortname', $context);
1039 // Check if the user can change the idnumber.
1040 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
1041 require_capability('moodle/course:changeidnumber', $context);
1044 // Check if user can change summary.
1045 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
1046 require_capability('moodle/course:changesummary', $context);
1050 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
1051 require_capability('moodle/course:changesummary', $context);
1052 $course['summaryformat'] = external_validate_format($course['summaryformat']);
1055 // Check if user can change visibility.
1056 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
1057 require_capability('moodle/course:visibility', $context);
1060 // Make sure lang is valid.
1061 if (array_key_exists('lang', $course) && ($oldcourse->lang != $course['lang'])) {
1062 require_capability('moodle/course:setforcedlanguage', $context);
1063 if (empty($availablelangs[$course['lang']])) {
1064 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
1068 // Make sure theme is valid.
1069 if (array_key_exists('forcetheme', $course)) {
1070 if (!empty($CFG->allowcoursethemes)) {
1071 if (empty($availablethemes[$course['forcetheme']])) {
1072 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
1074 $course['theme'] = $course['forcetheme'];
1079 // Make sure completion is enabled before setting it.
1080 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
1081 $course['enabledcompletion'] = 0;
1084 // Make sure maxbytes are less then CFG->maxbytes.
1085 if (array_key_exists('maxbytes', $course)) {
1086 // We allow updates back to 0 max bytes, a special value denoting the course uses the site limit.
1087 // Otherwise, either use the size specified, or cap at the max size for the course.
1088 if ($course['maxbytes'] != 0) {
1089 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
1093 if (!empty($course['courseformatoptions'])) {
1094 foreach ($course['courseformatoptions'] as $option) {
1095 if (isset($option['name']) && isset($option['value'])) {
1096 $course[$option['name']] = $option['value'];
1101 // Prepare list of custom fields.
1102 if (isset($course['customfields'])) {
1103 foreach ($course['customfields'] as $field) {
1104 $course['customfield_' . $field['shortname']] = $field['value'];
1108 // Update course if user has all required capabilities.
1109 update_course((object) $course);
1110 } catch (Exception $e) {
1112 $warning['item'] = 'course';
1113 $warning['itemid'] = $course['id'];
1114 if ($e instanceof moodle_exception) {
1115 $warning['warningcode'] = $e->errorcode;
1117 $warning['warningcode'] = $e->getCode();
1119 $warning['message'] = $e->getMessage();
1120 $warnings[] = $warning;
1125 $result['warnings'] = $warnings;
1130 * Returns description of method result value
1132 * @return external_description
1135 public static function update_courses_returns() {
1136 return new external_single_structure(
1138 'warnings' => new external_warnings()
1144 * Returns description of method parameters
1146 * @return external_function_parameters
1149 public static function delete_courses_parameters() {
1150 return new external_function_parameters(
1152 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
1160 * @param array $courseids A list of course ids
1163 public static function delete_courses($courseids) {
1165 require_once($CFG->dirroot."/course/lib.php");
1167 // Parameter validation.
1168 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
1170 $warnings = array();
1172 foreach ($params['courseids'] as $courseid) {
1173 $course = $DB->get_record('course', array('id' => $courseid));
1175 if ($course === false) {
1176 $warnings[] = array(
1178 'itemid' => $courseid,
1179 'warningcode' => 'unknowncourseidnumber',
1180 'message' => 'Unknown course ID ' . $courseid
1185 // Check if the context is valid.
1186 $coursecontext = context_course::instance($course->id);
1187 self::validate_context($coursecontext);
1189 // Check if the current user has permission.
1190 if (!can_delete_course($courseid)) {
1191 $warnings[] = array(
1193 'itemid' => $courseid,
1194 'warningcode' => 'cannotdeletecourse',
1195 'message' => 'You do not have the permission to delete this course' . $courseid
1200 if (delete_course($course, false) === false) {
1201 $warnings[] = array(
1203 'itemid' => $courseid,
1204 'warningcode' => 'cannotdeletecategorycourse',
1205 'message' => 'Course ' . $courseid . ' failed to be deleted'
1211 fix_course_sortorder();
1213 return array('warnings' => $warnings);
1217 * Returns description of method result value
1219 * @return external_description
1222 public static function delete_courses_returns() {
1223 return new external_single_structure(
1225 'warnings' => new external_warnings()
1231 * Returns description of method parameters
1233 * @return external_function_parameters
1236 public static function duplicate_course_parameters() {
1237 return new external_function_parameters(
1239 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
1240 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
1241 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
1242 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
1243 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
1244 'options' => new external_multiple_structure(
1245 new external_single_structure(
1247 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
1248 "activities" (int) Include course activites (default to 1 that is equal to yes),
1249 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1250 "filters" (int) Include course filters (default to 1 that is equal to yes),
1251 "users" (int) Include users (default to 0 that is equal to no),
1252 "enrolments" (int) Include enrolment methods (default to 1 - restore only with users),
1253 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
1254 "comments" (int) Include user comments (default to 0 that is equal to no),
1255 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
1256 "logs" (int) Include course logs (default to 0 that is equal to no),
1257 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
1259 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1262 ), VALUE_DEFAULT, array()
1269 * Duplicate a course
1271 * @param int $courseid
1272 * @param string $fullname Duplicated course fullname
1273 * @param string $shortname Duplicated course shortname
1274 * @param int $categoryid Duplicated course parent category id
1275 * @param int $visible Duplicated course availability
1276 * @param array $options List of backup options
1277 * @return array New course info
1280 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
1281 global $CFG, $USER, $DB;
1282 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1283 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1285 // Parameter validation.
1286 $params = self::validate_parameters(
1287 self::duplicate_course_parameters(),
1289 'courseid' => $courseid,
1290 'fullname' => $fullname,
1291 'shortname' => $shortname,
1292 'categoryid' => $categoryid,
1293 'visible' => $visible,
1294 'options' => $options
1298 // Context validation.
1300 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
1301 throw new moodle_exception('invalidcourseid', 'error');
1304 // Category where duplicated course is going to be created.
1305 $categorycontext = context_coursecat::instance($params['categoryid']);
1306 self::validate_context($categorycontext);
1308 // Course to be duplicated.
1309 $coursecontext = context_course::instance($course->id);
1310 self::validate_context($coursecontext);
1312 $backupdefaults = array(
1317 'enrolments' => backup::ENROL_WITHUSERS,
1318 'role_assignments' => 0,
1320 'userscompletion' => 0,
1322 'grade_histories' => 0
1325 $backupsettings = array();
1326 // Check for backup and restore options.
1327 if (!empty($params['options'])) {
1328 foreach ($params['options'] as $option) {
1330 // Strict check for a correct value (allways 1 or 0, true or false).
1331 $value = clean_param($option['value'], PARAM_INT);
1333 if ($value !== 0 and $value !== 1) {
1334 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1337 if (!isset($backupdefaults[$option['name']])) {
1338 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1341 $backupsettings[$option['name']] = $value;
1345 // Capability checking.
1347 // The backup controller check for this currently, this may be redundant.
1348 require_capability('moodle/course:create', $categorycontext);
1349 require_capability('moodle/restore:restorecourse', $categorycontext);
1350 require_capability('moodle/backup:backupcourse', $coursecontext);
1352 if (!empty($backupsettings['users'])) {
1353 require_capability('moodle/backup:userinfo', $coursecontext);
1354 require_capability('moodle/restore:userinfo', $categorycontext);
1357 // Check if the shortname is used.
1358 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1359 foreach ($foundcourses as $foundcourse) {
1360 $foundcoursenames[] = $foundcourse->fullname;
1363 $foundcoursenamestring = implode(',', $foundcoursenames);
1364 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1367 // Backup the course.
1369 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1370 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1372 foreach ($backupsettings as $name => $value) {
1373 if ($setting = $bc->get_plan()->get_setting($name)) {
1374 $bc->get_plan()->get_setting($name)->set_value($value);
1378 $backupid = $bc->get_backupid();
1379 $backupbasepath = $bc->get_plan()->get_basepath();
1381 $bc->execute_plan();
1382 $results = $bc->get_results();
1383 $file = $results['backup_destination'];
1387 // Restore the backup immediately.
1389 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1390 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1391 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1394 // Create new course.
1395 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1397 $rc = new restore_controller($backupid, $newcourseid,
1398 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1400 foreach ($backupsettings as $name => $value) {
1401 $setting = $rc->get_plan()->get_setting($name);
1402 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1403 $setting->set_value($value);
1407 if (!$rc->execute_precheck()) {
1408 $precheckresults = $rc->get_precheck_results();
1409 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1410 if (empty($CFG->keeptempdirectoriesonbackup)) {
1411 fulldelete($backupbasepath);
1416 foreach ($precheckresults['errors'] as $error) {
1417 $errorinfo .= $error;
1420 if (array_key_exists('warnings', $precheckresults)) {
1421 foreach ($precheckresults['warnings'] as $warning) {
1422 $errorinfo .= $warning;
1426 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1430 $rc->execute_plan();
1433 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1434 $course->fullname = $params['fullname'];
1435 $course->shortname = $params['shortname'];
1436 $course->visible = $params['visible'];
1438 // Set shortname and fullname back.
1439 $DB->update_record('course', $course);
1441 if (empty($CFG->keeptempdirectoriesonbackup)) {
1442 fulldelete($backupbasepath);
1445 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1448 return array('id' => $course->id, 'shortname' => $course->shortname);
1452 * Returns description of method result value
1454 * @return external_description
1457 public static function duplicate_course_returns() {
1458 return new external_single_structure(
1460 'id' => new external_value(PARAM_INT, 'course id'),
1461 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1467 * Returns description of method parameters for import_course
1469 * @return external_function_parameters
1472 public static function import_course_parameters() {
1473 return new external_function_parameters(
1475 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1476 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1477 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1478 'options' => new external_multiple_structure(
1479 new external_single_structure(
1481 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1482 "activities" (int) Include course activites (default to 1 that is equal to yes),
1483 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1484 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1486 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1489 ), VALUE_DEFAULT, array()
1498 * @param int $importfrom The id of the course we are importing from
1499 * @param int $importto The id of the course we are importing to
1500 * @param bool $deletecontent Whether to delete the course we are importing to content
1501 * @param array $options List of backup options
1505 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1506 global $CFG, $USER, $DB;
1507 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1508 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1510 // Parameter validation.
1511 $params = self::validate_parameters(
1512 self::import_course_parameters(),
1514 'importfrom' => $importfrom,
1515 'importto' => $importto,
1516 'deletecontent' => $deletecontent,
1517 'options' => $options
1521 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1522 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1525 // Context validation.
1527 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1528 throw new moodle_exception('invalidcourseid', 'error');
1531 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1532 throw new moodle_exception('invalidcourseid', 'error');
1535 $importfromcontext = context_course::instance($importfrom->id);
1536 self::validate_context($importfromcontext);
1538 $importtocontext = context_course::instance($importto->id);
1539 self::validate_context($importtocontext);
1541 $backupdefaults = array(
1547 $backupsettings = array();
1549 // Check for backup and restore options.
1550 if (!empty($params['options'])) {
1551 foreach ($params['options'] as $option) {
1553 // Strict check for a correct value (allways 1 or 0, true or false).
1554 $value = clean_param($option['value'], PARAM_INT);
1556 if ($value !== 0 and $value !== 1) {
1557 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1560 if (!isset($backupdefaults[$option['name']])) {
1561 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1564 $backupsettings[$option['name']] = $value;
1568 // Capability checking.
1570 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1571 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1573 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1574 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1576 foreach ($backupsettings as $name => $value) {
1577 $bc->get_plan()->get_setting($name)->set_value($value);
1580 $backupid = $bc->get_backupid();
1581 $backupbasepath = $bc->get_plan()->get_basepath();
1583 $bc->execute_plan();
1586 // Restore the backup immediately.
1588 // Check if we must delete the contents of the destination course.
1589 if ($params['deletecontent']) {
1590 $restoretarget = backup::TARGET_EXISTING_DELETING;
1592 $restoretarget = backup::TARGET_EXISTING_ADDING;
1595 $rc = new restore_controller($backupid, $importto->id,
1596 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1598 foreach ($backupsettings as $name => $value) {
1599 $rc->get_plan()->get_setting($name)->set_value($value);
1602 if (!$rc->execute_precheck()) {
1603 $precheckresults = $rc->get_precheck_results();
1604 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1605 if (empty($CFG->keeptempdirectoriesonbackup)) {
1606 fulldelete($backupbasepath);
1611 foreach ($precheckresults['errors'] as $error) {
1612 $errorinfo .= $error;
1615 if (array_key_exists('warnings', $precheckresults)) {
1616 foreach ($precheckresults['warnings'] as $warning) {
1617 $errorinfo .= $warning;
1621 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1624 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1625 restore_dbops::delete_course_content($importto->id);
1629 $rc->execute_plan();
1632 if (empty($CFG->keeptempdirectoriesonbackup)) {
1633 fulldelete($backupbasepath);
1640 * Returns description of method result value
1642 * @return external_description
1645 public static function import_course_returns() {
1650 * Returns description of method parameters
1652 * @return external_function_parameters
1655 public static function get_categories_parameters() {
1656 return new external_function_parameters(
1658 'criteria' => new external_multiple_structure(
1659 new external_single_structure(
1661 'key' => new external_value(PARAM_ALPHA,
1662 'The category column to search, expected keys (value format) are:'.
1663 '"id" (int) the category id,'.
1664 '"ids" (string) category ids separated by commas,'.
1665 '"name" (string) the category name,'.
1666 '"parent" (int) the parent category id,'.
1667 '"idnumber" (string) category idnumber'.
1668 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1669 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1670 then the function return all categories that the user can see.'.
1671 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1672 '"theme" (string) only return the categories having this theme'.
1673 ' - user must have \'moodle/category:manage\' to search on theme'),
1674 'value' => new external_value(PARAM_RAW, 'the value to match')
1676 ), 'criteria', VALUE_DEFAULT, array()
1678 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1679 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1687 * @param array $criteria Criteria to match the results
1688 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1689 * @return array list of categories
1692 public static function get_categories($criteria = array(), $addsubcategories = true) {
1694 require_once($CFG->dirroot . "/course/lib.php");
1696 // Validate parameters.
1697 $params = self::validate_parameters(self::get_categories_parameters(),
1698 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1700 // Retrieve the categories.
1701 $categories = array();
1702 if (!empty($params['criteria'])) {
1704 $conditions = array();
1706 foreach ($params['criteria'] as $crit) {
1707 $key = trim($crit['key']);
1709 // Trying to avoid duplicate keys.
1710 if (!isset($conditions[$key])) {
1712 $context = context_system::instance();
1716 $value = clean_param($crit['value'], PARAM_INT);
1717 $conditions[$key] = $value;
1718 $wheres[] = $key . " = :" . $key;
1722 $value = clean_param($crit['value'], PARAM_SEQUENCE);
1723 $ids = explode(',', $value);
1724 list($sqlids, $paramids) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
1725 $conditions = array_merge($conditions, $paramids);
1726 $wheres[] = 'id ' . $sqlids;
1730 if (has_capability('moodle/category:manage', $context)) {
1731 $value = clean_param($crit['value'], PARAM_RAW);
1732 $conditions[$key] = $value;
1733 $wheres[] = $key . " = :" . $key;
1735 // We must throw an exception.
1736 // Otherwise the dev client would think no idnumber exists.
1737 throw new moodle_exception('criteriaerror',
1738 'webservice', '', null,
1739 'You don\'t have the permissions to search on the "idnumber" field.');
1744 $value = clean_param($crit['value'], PARAM_TEXT);
1745 $conditions[$key] = $value;
1746 $wheres[] = $key . " = :" . $key;
1750 $value = clean_param($crit['value'], PARAM_INT);
1751 $conditions[$key] = $value;
1752 $wheres[] = $key . " = :" . $key;
1756 if (has_capability('moodle/category:viewhiddencategories', $context)) {
1757 $value = clean_param($crit['value'], PARAM_INT);
1758 $conditions[$key] = $value;
1759 $wheres[] = $key . " = :" . $key;
1761 throw new moodle_exception('criteriaerror',
1762 'webservice', '', null,
1763 'You don\'t have the permissions to search on the "visible" field.');
1768 if (has_capability('moodle/category:manage', $context)) {
1769 $value = clean_param($crit['value'], PARAM_THEME);
1770 $conditions[$key] = $value;
1771 $wheres[] = $key . " = :" . $key;
1773 throw new moodle_exception('criteriaerror',
1774 'webservice', '', null,
1775 'You don\'t have the permissions to search on the "theme" field.');
1780 throw new moodle_exception('criteriaerror',
1781 'webservice', '', null,
1782 'You can not search on this criteria: ' . $key);
1787 if (!empty($wheres)) {
1788 $wheres = implode(" AND ", $wheres);
1790 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1792 // Retrieve its sub subcategories (all levels).
1793 if ($categories and !empty($params['addsubcategories'])) {
1794 $newcategories = array();
1796 // Check if we required visible/theme checks.
1797 $additionalselect = '';
1798 $additionalparams = array();
1799 if (isset($conditions['visible'])) {
1800 $additionalselect .= ' AND visible = :visible';
1801 $additionalparams['visible'] = $conditions['visible'];
1803 if (isset($conditions['theme'])) {
1804 $additionalselect .= ' AND theme= :theme';
1805 $additionalparams['theme'] = $conditions['theme'];
1808 foreach ($categories as $category) {
1809 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1810 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1811 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1812 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1814 $categories = $categories + $newcategories;
1819 // Retrieve all categories in the database.
1820 $categories = $DB->get_records('course_categories');
1823 // The not returned categories. key => category id, value => reason of exclusion.
1824 $excludedcats = array();
1826 // The returned categories.
1827 $categoriesinfo = array();
1829 // We need to sort the categories by path.
1830 // The parent cats need to be checked by the algo first.
1831 usort($categories, "core_course_external::compare_categories_by_path");
1833 foreach ($categories as $category) {
1835 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1836 $parents = explode('/', $category->path);
1837 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1838 foreach ($parents as $parentid) {
1839 // Note: when the parent exclusion was due to the context,
1840 // the sub category could still be returned.
1841 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1842 $excludedcats[$category->id] = 'parent';
1846 // Check the user can use the category context.
1847 $context = context_coursecat::instance($category->id);
1849 self::validate_context($context);
1850 } catch (Exception $e) {
1851 $excludedcats[$category->id] = 'context';
1853 // If it was the requested category then throw an exception.
1854 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1855 $exceptionparam = new stdClass();
1856 $exceptionparam->message = $e->getMessage();
1857 $exceptionparam->catid = $category->id;
1858 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1862 // Return the category information.
1863 if (!isset($excludedcats[$category->id])) {
1865 // Final check to see if the category is visible to the user.
1866 if ($category->visible or has_capability('moodle/category:viewhiddencategories', $context)) {
1868 $categoryinfo = array();
1869 $categoryinfo['id'] = $category->id;
1870 $categoryinfo['name'] = external_format_string($category->name, $context);
1871 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1872 external_format_text($category->description, $category->descriptionformat,
1873 $context->id, 'coursecat', 'description', null);
1874 $categoryinfo['parent'] = $category->parent;
1875 $categoryinfo['sortorder'] = $category->sortorder;
1876 $categoryinfo['coursecount'] = $category->coursecount;
1877 $categoryinfo['depth'] = $category->depth;
1878 $categoryinfo['path'] = $category->path;
1880 // Some fields only returned for admin.
1881 if (has_capability('moodle/category:manage', $context)) {
1882 $categoryinfo['idnumber'] = $category->idnumber;
1883 $categoryinfo['visible'] = $category->visible;
1884 $categoryinfo['visibleold'] = $category->visibleold;
1885 $categoryinfo['timemodified'] = $category->timemodified;
1886 $categoryinfo['theme'] = clean_param($category->theme, PARAM_THEME);
1889 $categoriesinfo[] = $categoryinfo;
1891 $excludedcats[$category->id] = 'visibility';
1896 // Sorting the resulting array so it looks a bit better for the client developer.
1897 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1899 return $categoriesinfo;
1903 * Sort categories array by path
1904 * private function: only used by get_categories
1906 * @param array $category1
1907 * @param array $category2
1908 * @return int result of strcmp
1911 private static function compare_categories_by_path($category1, $category2) {
1912 return strcmp($category1->path, $category2->path);
1916 * Sort categories array by sortorder
1917 * private function: only used by get_categories
1919 * @param array $category1
1920 * @param array $category2
1921 * @return int result of strcmp
1924 private static function compare_categories_by_sortorder($category1, $category2) {
1925 return strcmp($category1['sortorder'], $category2['sortorder']);
1929 * Returns description of method result value
1931 * @return external_description
1934 public static function get_categories_returns() {
1935 return new external_multiple_structure(
1936 new external_single_structure(
1938 'id' => new external_value(PARAM_INT, 'category id'),
1939 'name' => new external_value(PARAM_TEXT, 'category name'),
1940 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1941 'description' => new external_value(PARAM_RAW, 'category description'),
1942 'descriptionformat' => new external_format_value('description'),
1943 'parent' => new external_value(PARAM_INT, 'parent category id'),
1944 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1945 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1946 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1947 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1948 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1949 'depth' => new external_value(PARAM_INT, 'category depth'),
1950 'path' => new external_value(PARAM_TEXT, 'category path'),
1951 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1952 ), 'List of categories'
1958 * Returns description of method parameters
1960 * @return external_function_parameters
1963 public static function create_categories_parameters() {
1964 return new external_function_parameters(
1966 'categories' => new external_multiple_structure(
1967 new external_single_structure(
1969 'name' => new external_value(PARAM_TEXT, 'new category name'),
1970 'parent' => new external_value(PARAM_INT,
1971 'the parent category id inside which the new category will be created
1972 - set to 0 for a root category',
1974 'idnumber' => new external_value(PARAM_RAW,
1975 'the new category idnumber', VALUE_OPTIONAL),
1976 'description' => new external_value(PARAM_RAW,
1977 'the new category description', VALUE_OPTIONAL),
1978 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1979 'theme' => new external_value(PARAM_THEME,
1980 'the new category theme. This option must be enabled on moodle',
1992 * @param array $categories - see create_categories_parameters() for the array structure
1993 * @return array - see create_categories_returns() for the array structure
1996 public static function create_categories($categories) {
1999 $params = self::validate_parameters(self::create_categories_parameters(),
2000 array('categories' => $categories));
2002 $transaction = $DB->start_delegated_transaction();
2004 $createdcategories = array();
2005 foreach ($params['categories'] as $category) {
2006 if ($category['parent']) {
2007 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
2008 throw new moodle_exception('unknowcategory');
2010 $context = context_coursecat::instance($category['parent']);
2012 $context = context_system::instance();
2014 self::validate_context($context);
2015 require_capability('moodle/category:manage', $context);
2017 // this will validate format and throw an exception if there are errors
2018 external_validate_format($category['descriptionformat']);
2020 $newcategory = core_course_category::create($category);
2021 $context = context_coursecat::instance($newcategory->id);
2023 $createdcategories[] = array(
2024 'id' => $newcategory->id,
2025 'name' => external_format_string($newcategory->name, $context),
2029 $transaction->allow_commit();
2031 return $createdcategories;
2035 * Returns description of method parameters
2037 * @return external_function_parameters
2040 public static function create_categories_returns() {
2041 return new external_multiple_structure(
2042 new external_single_structure(
2044 'id' => new external_value(PARAM_INT, 'new category id'),
2045 'name' => new external_value(PARAM_TEXT, 'new category name'),
2052 * Returns description of method parameters
2054 * @return external_function_parameters
2057 public static function update_categories_parameters() {
2058 return new external_function_parameters(
2060 'categories' => new external_multiple_structure(
2061 new external_single_structure(
2063 'id' => new external_value(PARAM_INT, 'course id'),
2064 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
2065 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
2066 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
2067 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
2068 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
2069 'theme' => new external_value(PARAM_THEME,
2070 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
2081 * @param array $categories The list of categories to update
2085 public static function update_categories($categories) {
2088 // Validate parameters.
2089 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
2091 $transaction = $DB->start_delegated_transaction();
2093 foreach ($params['categories'] as $cat) {
2094 $category = core_course_category::get($cat['id']);
2096 $categorycontext = context_coursecat::instance($cat['id']);
2097 self::validate_context($categorycontext);
2098 require_capability('moodle/category:manage', $categorycontext);
2100 // this will throw an exception if descriptionformat is not valid
2101 external_validate_format($cat['descriptionformat']);
2103 $category->update($cat);
2106 $transaction->allow_commit();
2110 * Returns description of method result value
2112 * @return external_description
2115 public static function update_categories_returns() {
2120 * Returns description of method parameters
2122 * @return external_function_parameters
2125 public static function delete_categories_parameters() {
2126 return new external_function_parameters(
2128 'categories' => new external_multiple_structure(
2129 new external_single_structure(
2131 'id' => new external_value(PARAM_INT, 'category id to delete'),
2132 'newparent' => new external_value(PARAM_INT,
2133 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
2134 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
2135 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
2146 * @param array $categories A list of category ids
2150 public static function delete_categories($categories) {
2152 require_once($CFG->dirroot . "/course/lib.php");
2154 // Validate parameters.
2155 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
2157 $transaction = $DB->start_delegated_transaction();
2159 foreach ($params['categories'] as $category) {
2160 $deletecat = core_course_category::get($category['id'], MUST_EXIST);
2161 $context = context_coursecat::instance($deletecat->id);
2162 require_capability('moodle/category:manage', $context);
2163 self::validate_context($context);
2164 self::validate_context(get_category_or_system_context($deletecat->parent));
2166 if ($category['recursive']) {
2167 // If recursive was specified, then we recursively delete the category's contents.
2168 if ($deletecat->can_delete_full()) {
2169 $deletecat->delete_full(false);
2171 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
2174 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
2175 // If the parent is the root, moving is not supported (because a course must always be inside a category).
2176 // We must move to an existing category.
2177 if (!empty($category['newparent'])) {
2178 $newparentcat = core_course_category::get($category['newparent']);
2180 $newparentcat = core_course_category::get($deletecat->parent);
2183 // This operation is not allowed. We must move contents to an existing category.
2184 if (!$newparentcat->id) {
2185 throw new moodle_exception('movecatcontentstoroot');
2188 self::validate_context(context_coursecat::instance($newparentcat->id));
2189 if ($deletecat->can_move_content_to($newparentcat->id)) {
2190 $deletecat->delete_move($newparentcat->id, false);
2192 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
2197 $transaction->allow_commit();
2201 * Returns description of method parameters
2203 * @return external_function_parameters
2206 public static function delete_categories_returns() {
2211 * Describes the parameters for delete_modules.
2213 * @return external_function_parameters
2216 public static function delete_modules_parameters() {
2217 return new external_function_parameters (
2219 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
2220 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
2226 * Deletes a list of provided module instances.
2228 * @param array $cmids the course module ids
2231 public static function delete_modules($cmids) {
2234 // Require course file containing the course delete module function.
2235 require_once($CFG->dirroot . "/course/lib.php");
2237 // Clean the parameters.
2238 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
2240 // Keep track of the course ids we have performed a capability check on to avoid repeating.
2241 $arrcourseschecked = array();
2243 foreach ($params['cmids'] as $cmid) {
2244 // Get the course module.
2245 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
2247 // Check if we have not yet confirmed they have permission in this course.
2248 if (!in_array($cm->course, $arrcourseschecked)) {
2249 // Ensure the current user has required permission in this course.
2250 $context = context_course::instance($cm->course);
2251 self::validate_context($context);
2252 // Add to the array.
2253 $arrcourseschecked[] = $cm->course;
2256 // Ensure they can delete this module.
2257 $modcontext = context_module::instance($cm->id);
2258 require_capability('moodle/course:manageactivities', $modcontext);
2260 // Delete the module.
2261 course_delete_module($cm->id);
2266 * Describes the delete_modules return value.
2268 * @return external_single_structure
2271 public static function delete_modules_returns() {
2276 * Returns description of method parameters
2278 * @return external_function_parameters
2281 public static function view_course_parameters() {
2282 return new external_function_parameters(
2284 'courseid' => new external_value(PARAM_INT, 'id of the course'),
2285 'sectionnumber' => new external_value(PARAM_INT, 'section number', VALUE_DEFAULT, 0)
2291 * Trigger the course viewed event.
2293 * @param int $courseid id of course
2294 * @param int $sectionnumber sectionnumber (0, 1, 2...)
2295 * @return array of warnings and status result
2297 * @throws moodle_exception
2299 public static function view_course($courseid, $sectionnumber = 0) {
2301 require_once($CFG->dirroot . "/course/lib.php");
2303 $params = self::validate_parameters(self::view_course_parameters(),
2305 'courseid' => $courseid,
2306 'sectionnumber' => $sectionnumber
2309 $warnings = array();
2311 $course = get_course($params['courseid']);
2312 $context = context_course::instance($course->id);
2313 self::validate_context($context);
2315 if (!empty($params['sectionnumber'])) {
2317 // Get section details and check it exists.
2318 $modinfo = get_fast_modinfo($course);
2319 $coursesection = $modinfo->get_section_info($params['sectionnumber'], MUST_EXIST);
2321 // Check user is allowed to see it.
2322 if (!$coursesection->uservisible) {
2323 require_capability('moodle/course:viewhiddensections', $context);
2327 course_view($context, $params['sectionnumber']);
2330 $result['status'] = true;
2331 $result['warnings'] = $warnings;
2336 * Returns description of method result value
2338 * @return external_description
2341 public static function view_course_returns() {
2342 return new external_single_structure(
2344 'status' => new external_value(PARAM_BOOL, 'status: true if success'),
2345 'warnings' => new external_warnings()
2351 * Returns description of method parameters
2353 * @return external_function_parameters
2356 public static function search_courses_parameters() {
2357 return new external_function_parameters(
2359 'criterianame' => new external_value(PARAM_ALPHA, 'criteria name
2360 (search, modulelist (only admins), blocklist (only admins), tagid)'),
2361 'criteriavalue' => new external_value(PARAM_RAW, 'criteria value'),
2362 'page' => new external_value(PARAM_INT, 'page number (0 based)', VALUE_DEFAULT, 0),
2363 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0),
2364 'requiredcapabilities' => new external_multiple_structure(
2365 new external_value(PARAM_CAPABILITY, 'Capability string used to filter courses by permission'),
2366 'Optional list of required capabilities (used to filter the list)', VALUE_DEFAULT, array()
2368 'limittoenrolled' => new external_value(PARAM_BOOL, 'limit to enrolled courses', VALUE_DEFAULT, 0),
2374 * Return the course information that is public (visible by every one)
2376 * @param core_course_list_element $course course in list object
2377 * @param stdClass $coursecontext course context object
2378 * @return array the course information
2381 protected static function get_course_public_information(core_course_list_element $course, $coursecontext) {
2383 static $categoriescache = array();
2385 // Category information.
2386 if (!array_key_exists($course->category, $categoriescache)) {
2387 $categoriescache[$course->category] = core_course_category::get($course->category, IGNORE_MISSING);
2389 $category = $categoriescache[$course->category];
2391 // Retrieve course overview used files.
2393 foreach ($course->get_course_overviewfiles() as $file) {
2394 $fileurl = moodle_url::make_webservice_pluginfile_url($file->get_contextid(), $file->get_component(),
2395 $file->get_filearea(), null, $file->get_filepath(),
2396 $file->get_filename())->out(false);
2398 'filename' => $file->get_filename(),
2399 'fileurl' => $fileurl,
2400 'filesize' => $file->get_filesize(),
2401 'filepath' => $file->get_filepath(),
2402 'mimetype' => $file->get_mimetype(),
2403 'timemodified' => $file->get_timemodified(),
2407 // Retrieve the course contacts,
2408 // we need here the users fullname since if we are not enrolled can be difficult to obtain them via other Web Services.
2409 $coursecontacts = array();
2410 foreach ($course->get_course_contacts() as $contact) {
2411 $coursecontacts[] = array(
2412 'id' => $contact['user']->id,
2413 'fullname' => $contact['username'],
2414 'roles' => array_map(function($role){
2415 return array('id' => $role->id, 'name' => $role->displayname);
2416 }, $contact['roles']),
2417 'role' => array('id' => $contact['role']->id, 'name' => $contact['role']->displayname),
2418 'rolename' => $contact['rolename']
2422 // Allowed enrolment methods (maybe we can self-enrol).
2423 $enroltypes = array();
2424 $instances = enrol_get_instances($course->id, true);
2425 foreach ($instances as $instance) {
2426 $enroltypes[] = $instance->enrol;
2430 list($summary, $summaryformat) =
2431 external_format_text($course->summary, $course->summaryformat, $coursecontext->id, 'course', 'summary', null);
2434 if (!empty($category)) {
2435 $categoryname = external_format_string($category->name, $category->get_context());
2438 $displayname = get_course_display_name_for_list($course);
2439 $coursereturns = array();
2440 $coursereturns['id'] = $course->id;
2441 $coursereturns['fullname'] = external_format_string($course->fullname, $coursecontext->id);
2442 $coursereturns['displayname'] = external_format_string($displayname, $coursecontext->id);
2443 $coursereturns['shortname'] = external_format_string($course->shortname, $coursecontext->id);
2444 $coursereturns['categoryid'] = $course->category;
2445 $coursereturns['categoryname'] = $categoryname;
2446 $coursereturns['summary'] = $summary;
2447 $coursereturns['summaryformat'] = $summaryformat;
2448 $coursereturns['summaryfiles'] = external_util::get_area_files($coursecontext->id, 'course', 'summary', false, false);
2449 $coursereturns['overviewfiles'] = $files;
2450 $coursereturns['contacts'] = $coursecontacts;
2451 $coursereturns['enrollmentmethods'] = $enroltypes;
2452 $coursereturns['sortorder'] = $course->sortorder;
2453 return $coursereturns;
2457 * Search courses following the specified criteria.
2459 * @param string $criterianame Criteria name (search, modulelist (only admins), blocklist (only admins), tagid)
2460 * @param string $criteriavalue Criteria value
2461 * @param int $page Page number (for pagination)
2462 * @param int $perpage Items per page
2463 * @param array $requiredcapabilities Optional list of required capabilities (used to filter the list).
2464 * @param int $limittoenrolled Limit to only enrolled courses
2465 * @return array of course objects and warnings
2467 * @throws moodle_exception
2469 public static function search_courses($criterianame,
2473 $requiredcapabilities=array(),
2474 $limittoenrolled=0) {
2477 $warnings = array();
2479 $parameters = array(
2480 'criterianame' => $criterianame,
2481 'criteriavalue' => $criteriavalue,
2483 'perpage' => $perpage,
2484 'requiredcapabilities' => $requiredcapabilities
2486 $params = self::validate_parameters(self::search_courses_parameters(), $parameters);
2487 self::validate_context(context_system::instance());
2489 $allowedcriterianames = array('search', 'modulelist', 'blocklist', 'tagid');
2490 if (!in_array($params['criterianame'], $allowedcriterianames)) {
2491 throw new invalid_parameter_exception('Invalid value for criterianame parameter (value: '.$params['criterianame'].'),' .
2492 'allowed values are: '.implode(',', $allowedcriterianames));
2495 if ($params['criterianame'] == 'modulelist' or $params['criterianame'] == 'blocklist') {
2496 require_capability('moodle/site:config', context_system::instance());
2500 'search' => PARAM_RAW,
2501 'modulelist' => PARAM_PLUGIN,
2502 'blocklist' => PARAM_INT,
2503 'tagid' => PARAM_INT
2505 $params['criteriavalue'] = clean_param($params['criteriavalue'], $paramtype[$params['criterianame']]);
2507 // Prepare the search API options.
2508 $searchcriteria = array();
2509 $searchcriteria[$params['criterianame']] = $params['criteriavalue'];
2512 if ($params['perpage'] != 0) {
2513 $offset = $params['page'] * $params['perpage'];
2514 $options = array('offset' => $offset, 'limit' => $params['perpage']);
2517 // Search the courses.
2518 $courses = core_course_category::search_courses($searchcriteria, $options, $params['requiredcapabilities']);
2519 $totalcount = core_course_category::search_courses_count($searchcriteria, $options, $params['requiredcapabilities']);
2521 if (!empty($limittoenrolled)) {
2522 // Get the courses where the current user has access.
2523 $enrolled = enrol_get_my_courses(array('id', 'cacherev'));
2526 $finalcourses = array();
2527 $categoriescache = array();
2529 foreach ($courses as $course) {
2530 if (!empty($limittoenrolled)) {
2531 // Filter out not enrolled courses.
2532 if (!isset($enrolled[$course->id])) {
2538 $coursecontext = context_course::instance($course->id);
2540 $finalcourses[] = self::get_course_public_information($course, $coursecontext);
2544 'total' => $totalcount,
2545 'courses' => $finalcourses,
2546 'warnings' => $warnings
2551 * Returns a course structure definition
2553 * @param boolean $onlypublicdata set to true, to retrieve only fields viewable by anyone when the course is visible
2554 * @return array the course structure
2557 protected static function get_course_structure($onlypublicdata = true) {
2558 $coursestructure = array(
2559 'id' => new external_value(PARAM_INT, 'course id'),
2560 'fullname' => new external_value(PARAM_TEXT, 'course full name'),
2561 'displayname' => new external_value(PARAM_TEXT, 'course display name'),
2562 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
2563 'categoryid' => new external_value(PARAM_INT, 'category id'),
2564 'categoryname' => new external_value(PARAM_TEXT, 'category name'),
2565 'sortorder' => new external_value(PARAM_INT, 'Sort order in the category', VALUE_OPTIONAL),
2566 'summary' => new external_value(PARAM_RAW, 'summary'),
2567 'summaryformat' => new external_format_value('summary'),
2568 'summaryfiles' => new external_files('summary files in the summary field', VALUE_OPTIONAL),
2569 'overviewfiles' => new external_files('additional overview files attached to this course'),
2570 'contacts' => new external_multiple_structure(
2571 new external_single_structure(
2573 'id' => new external_value(PARAM_INT, 'contact user id'),
2574 'fullname' => new external_value(PARAM_NOTAGS, 'contact user fullname'),
2579 'enrollmentmethods' => new external_multiple_structure(
2580 new external_value(PARAM_PLUGIN, 'enrollment method'),
2581 'enrollment methods list'
2583 'customfields' => new external_multiple_structure(
2584 new external_single_structure(
2586 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
2587 'shortname' => new external_value(PARAM_RAW,
2588 'The shortname of the custom field - to be able to build the field class in the code'),
2589 'type' => new external_value(PARAM_ALPHANUMEXT,
2590 'The type of the custom field - text field, checkbox...'),
2591 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
2593 ), 'Custom fields', VALUE_OPTIONAL),
2596 if (!$onlypublicdata) {
2598 'idnumber' => new external_value(PARAM_RAW, 'Id number', VALUE_OPTIONAL),
2599 'format' => new external_value(PARAM_PLUGIN, 'Course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
2600 'showgrades' => new external_value(PARAM_INT, '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
2601 'newsitems' => new external_value(PARAM_INT, 'Number of recent items appearing on the course page', VALUE_OPTIONAL),
2602 'startdate' => new external_value(PARAM_INT, 'Timestamp when the course start', VALUE_OPTIONAL),
2603 'enddate' => new external_value(PARAM_INT, 'Timestamp when the course end', VALUE_OPTIONAL),
2604 'maxbytes' => new external_value(PARAM_INT, 'Largest size of file that can be uploaded into', VALUE_OPTIONAL),
2605 'showreports' => new external_value(PARAM_INT, 'Are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
2606 'visible' => new external_value(PARAM_INT, '1: available to student, 0:not available', VALUE_OPTIONAL),
2607 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
2608 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
2609 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
2610 'enablecompletion' => new external_value(PARAM_INT, 'Completion enabled? 1: yes 0: no', VALUE_OPTIONAL),
2611 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
2612 'lang' => new external_value(PARAM_SAFEDIR, 'Forced course language', VALUE_OPTIONAL),
2613 'theme' => new external_value(PARAM_PLUGIN, 'Fame of the forced theme', VALUE_OPTIONAL),
2614 'marker' => new external_value(PARAM_INT, 'Current course marker', VALUE_OPTIONAL),
2615 'legacyfiles' => new external_value(PARAM_INT, 'If legacy files are enabled', VALUE_OPTIONAL),
2616 'calendartype' => new external_value(PARAM_PLUGIN, 'Calendar type', VALUE_OPTIONAL),
2617 'timecreated' => new external_value(PARAM_INT, 'Time when the course was created', VALUE_OPTIONAL),
2618 'timemodified' => new external_value(PARAM_INT, 'Last time the course was updated', VALUE_OPTIONAL),
2619 'requested' => new external_value(PARAM_INT, 'If is a requested course', VALUE_OPTIONAL),
2620 'cacherev' => new external_value(PARAM_INT, 'Cache revision number', VALUE_OPTIONAL),
2621 'filters' => new external_multiple_structure(
2622 new external_single_structure(
2624 'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name'),
2625 'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit'),
2626 'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit'),
2629 'Course filters', VALUE_OPTIONAL
2631 'courseformatoptions' => new external_multiple_structure(
2632 new external_single_structure(
2634 'name' => new external_value(PARAM_RAW, 'Course format option name.'),
2635 'value' => new external_value(PARAM_RAW, 'Course format option value.'),
2638 'Additional options for particular course format.', VALUE_OPTIONAL
2641 $coursestructure = array_merge($coursestructure, $extra);
2643 return new external_single_structure($coursestructure);
2647 * Returns description of method result value
2649 * @return external_description
2652 public static function search_courses_returns() {
2653 return new external_single_structure(
2655 'total' => new external_value(PARAM_INT, 'total course count'),
2656 'courses' => new external_multiple_structure(self::get_course_structure(), 'course'),
2657 'warnings' => new external_warnings()
2663 * Returns description of method parameters
2665 * @return external_function_parameters
2668 public static function get_course_module_parameters() {
2669 return new external_function_parameters(
2671 'cmid' => new external_value(PARAM_INT, 'The course module id')
2677 * Return information about a course module.
2679 * @param int $cmid the course module id
2680 * @return array of warnings and the course module
2682 * @throws moodle_exception
2684 public static function get_course_module($cmid) {
2687 $params = self::validate_parameters(self::get_course_module_parameters(), array('cmid' => $cmid));
2688 $warnings = array();
2690 $cm = get_coursemodule_from_id(null, $params['cmid'], 0, true, MUST_EXIST);
2691 $context = context_module::instance($cm->id);
2692 self::validate_context($context);
2694 // If the user has permissions to manage the activity, return all the information.
2695 if (has_capability('moodle/course:manageactivities', $context)) {
2696 require_once($CFG->dirroot . '/course/modlib.php');
2697 require_once($CFG->libdir . '/gradelib.php');
2700 // Get the extra information: grade, advanced grading and outcomes data.
2701 $course = get_course($cm->course);
2702 list($newcm, $newcontext, $module, $extrainfo, $cw) = get_moduleinfo_data($cm, $course);
2704 $gradeinfo = array('grade', 'gradepass', 'gradecat');
2705 foreach ($gradeinfo as $gfield) {
2706 if (isset($extrainfo->{$gfield})) {
2707 $info->{$gfield} = $extrainfo->{$gfield};
2710 if (isset($extrainfo->grade) and $extrainfo->grade < 0) {
2711 $info->scale = $DB->get_field('scale', 'scale', array('id' => abs($extrainfo->grade)));
2713 // Advanced grading.
2714 if (isset($extrainfo->_advancedgradingdata)) {
2715 $info->advancedgrading = array();
2716 foreach ($extrainfo as $key => $val) {
2717 if (strpos($key, 'advancedgradingmethod_') === 0) {
2718 $info->advancedgrading[] = array(
2719 'area' => str_replace('advancedgradingmethod_', '', $key),
2726 foreach ($extrainfo as $key => $val) {
2727 if (strpos($key, 'outcome_') === 0) {
2728 if (!isset($info->outcomes)) {
2729 $info->outcomes = array();
2731 $id = str_replace('outcome_', '', $key);
2732 $outcome = grade_outcome::fetch(array('id' => $id));
2733 $scaleitems = $outcome->load_scale();
2734 $info->outcomes[] = array(
2736 'name' => external_format_string($outcome->get_name(), $context->id),
2737 'scale' => $scaleitems->scale
2742 // Return information is safe to show to any user.
2743 $info = new stdClass();
2744 $info->id = $cm->id;
2745 $info->course = $cm->course;
2746 $info->module = $cm->module;
2747 $info->modname = $cm->modname;
2748 $info->instance = $cm->instance;
2749 $info->section = $cm->section;
2750 $info->sectionnum = $cm->sectionnum;
2751 $info->groupmode = $cm->groupmode;
2752 $info->groupingid = $cm->groupingid;
2753 $info->completion = $cm->completion;
2756 $info->name = external_format_string($cm->name, $context->id);
2758 $result['cm'] = $info;
2759 $result['warnings'] = $warnings;
2764 * Returns description of method result value
2766 * @return external_description
2769 public static function get_course_module_returns() {
2770 return new external_single_structure(
2772 'cm' => new external_single_structure(
2774 'id' => new external_value(PARAM_INT, 'The course module id'),
2775 'course' => new external_value(PARAM_INT, 'The course id'),
2776 'module' => new external_value(PARAM_INT, 'The module type id'),
2777 'name' => new external_value(PARAM_RAW, 'The activity name'),
2778 'modname' => new external_value(PARAM_COMPONENT, 'The module component name (forum, assign, etc..)'),
2779 'instance' => new external_value(PARAM_INT, 'The activity instance id'),
2780 'section' => new external_value(PARAM_INT, 'The module section id'),
2781 'sectionnum' => new external_value(PARAM_INT, 'The module section number'),
2782 'groupmode' => new external_value(PARAM_INT, 'Group mode'),
2783 'groupingid' => new external_value(PARAM_INT, 'Grouping id'),
2784 'completion' => new external_value(PARAM_INT, 'If completion is enabled'),
2785 'idnumber' => new external_value(PARAM_RAW, 'Module id number', VALUE_OPTIONAL),
2786 'added' => new external_value(PARAM_INT, 'Time added', VALUE_OPTIONAL),
2787 'score' => new external_value(PARAM_INT, 'Score', VALUE_OPTIONAL),
2788 'indent' => new external_value(PARAM_INT, 'Indentation', VALUE_OPTIONAL),
2789 'visible' => new external_value(PARAM_INT, 'If visible', VALUE_OPTIONAL),
2790 'visibleoncoursepage' => new external_value(PARAM_INT, 'If visible on course page', VALUE_OPTIONAL),
2791 'visibleold' => new external_value(PARAM_INT, 'Visible old', VALUE_OPTIONAL),
2792 'completiongradeitemnumber' => new external_value(PARAM_INT, 'Completion grade item', VALUE_OPTIONAL),
2793 'completionview' => new external_value(PARAM_INT, 'Completion view setting', VALUE_OPTIONAL),
2794 'completionexpected' => new external_value(PARAM_INT, 'Completion time expected', VALUE_OPTIONAL),
2795 'showdescription' => new external_value(PARAM_INT, 'If the description is showed', VALUE_OPTIONAL),
2796 'availability' => new external_value(PARAM_RAW, 'Availability settings', VALUE_OPTIONAL),
2797 'grade' => new external_value(PARAM_FLOAT, 'Grade (max value or scale id)', VALUE_OPTIONAL),
2798 'scale' => new external_value(PARAM_TEXT, 'Scale items (if used)', VALUE_OPTIONAL),
2799 'gradepass' => new external_value(PARAM_RAW, 'Grade to pass (float)', VALUE_OPTIONAL),
2800 'gradecat' => new external_value(PARAM_INT, 'Grade category', VALUE_OPTIONAL),
2801 'advancedgrading' => new external_multiple_structure(
2802 new external_single_structure(
2804 'area' => new external_value(PARAM_AREA, 'Gradable area name'),
2805 'method' => new external_value(PARAM_COMPONENT, 'Grading method'),
2808 'Advanced grading settings', VALUE_OPTIONAL
2810 'outcomes' => new external_multiple_structure(
2811 new external_single_structure(
2813 'id' => new external_value(PARAM_ALPHANUMEXT, 'Outcome id'),
2814 'name' => new external_value(PARAM_TEXT, 'Outcome full name'),
2815 'scale' => new external_value(PARAM_TEXT, 'Scale items')
2818 'Outcomes information', VALUE_OPTIONAL
2822 'warnings' => new external_warnings()
2828 * Returns description of method parameters
2830 * @return external_function_parameters
2833 public static function get_course_module_by_instance_parameters() {
2834 return new external_function_parameters(
2836 'module' => new external_value(PARAM_COMPONENT, 'The module name'),
2837 'instance' => new external_value(PARAM_INT, 'The module instance id')
2843 * Return information about a course module.
2845 * @param string $module the module name
2846 * @param int $instance the activity instance id
2847 * @return array of warnings and the course module
2849 * @throws moodle_exception
2851 public static function get_course_module_by_instance($module, $instance) {
2853 $params = self::validate_parameters(self::get_course_module_by_instance_parameters(),
2855 'module' => $module,
2856 'instance' => $instance,
2859 $warnings = array();
2860 $cm = get_coursemodule_from_instance($params['module'], $params['instance'], 0, false, MUST_EXIST);
2862 return self::get_course_module($cm->id);
2866 * Returns description of method result value
2868 * @return external_description
2871 public static function get_course_module_by_instance_returns() {
2872 return self::get_course_module_returns();
2876 * Returns description of method parameters
2878 * @deprecated since 3.3
2879 * @todo The final deprecation of this function will take place in Moodle 3.7 - see MDL-57487.
2880 * @return external_function_parameters
2883 public static function get_activities_overview_parameters() {
2884 return new external_function_parameters(
2886 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'Course id.')),
2892 * Return activities overview for the given courses.
2894 * @deprecated since 3.3
2895 * @todo The final deprecation of this function will take place in Moodle 3.7 - see MDL-57487.
2896 * @param array $courseids a list of course ids
2897 * @return array of warnings and the activities overview
2899 * @throws moodle_exception
2901 public static function get_activities_overview($courseids) {
2904 // Parameter validation.
2905 $params = self::validate_parameters(self::get_activities_overview_parameters(), array('courseids' => $courseids));
2906 $courseoverviews = array();
2908 list($courses, $warnings) = external_util::validate_courses($params['courseids']);
2910 if (!empty($courses)) {
2911 // Add lastaccess to each course (required by print_overview function).
2912 // We need the complete user data, the ws server does not load a complete one.
2913 $user = get_complete_user_data('id', $USER->id);
2914 foreach ($courses as $course) {
2915 if (isset($user->lastcourseaccess[$course->id])) {
2916 $course->lastaccess = $user->lastcourseaccess[$course->id];
2918 $course->lastaccess = 0;
2922 $overviews = array();
2923 if ($modules = get_plugin_list_with_function('mod', 'print_overview')) {
2924 foreach ($modules as $fname) {
2925 $fname($courses, $overviews);
2930 foreach ($overviews as $courseid => $modules) {
2931 $courseoverviews[$courseid]['id'] = $courseid;
2932 $courseoverviews[$courseid]['overviews'] = array();
2934 foreach ($modules as $modname => $overviewtext) {
2935 $courseoverviews[$courseid]['overviews'][] = array(
2936 'module' => $modname,
2937 'overviewtext' => $overviewtext // This text doesn't need formatting.
2944 'courses' => $courseoverviews,
2945 'warnings' => $warnings
2951 * Returns description of method result value
2953 * @deprecated since 3.3
2954 * @todo The final deprecation of this function will take place in Moodle 3.7 - see MDL-57487.
2955 * @return external_description
2958 public static function get_activities_overview_returns() {
2959 return new external_single_structure(
2961 'courses' => new external_multiple_structure(
2962 new external_single_structure(
2964 'id' => new external_value(PARAM_INT, 'Course id'),
2965 'overviews' => new external_multiple_structure(
2966 new external_single_structure(
2968 'module' => new external_value(PARAM_PLUGIN, 'Module name'),
2969 'overviewtext' => new external_value(PARAM_RAW, 'Overview text'),
2974 ), 'List of courses'
2976 'warnings' => new external_warnings()