Merge branch 'MDL-44620_master' of git://github.com/dmonllao/moodle into MOODLE_27_STABLE
[moodle.git] / course / externallib.php
blob71c24a0d5955acbbf2e6a2b4941466d1557ea5d0
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 /**
19 * External course API
21 * @package core_course
22 * @category external
23 * @copyright 2009 Petr Skodak
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die;
29 require_once("$CFG->libdir/externallib.php");
31 /**
32 * Course external functions
34 * @package core_course
35 * @category external
36 * @copyright 2011 Jerome Mouneyrac
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 * @since Moodle 2.2
40 class core_course_external extends external_api {
42 /**
43 * Returns description of method parameters
45 * @return external_function_parameters
46 * @since Moodle 2.2
48 public static function get_course_contents_parameters() {
49 return new external_function_parameters(
50 array('courseid' => new external_value(PARAM_INT, 'course id'),
51 'options' => new external_multiple_structure (
52 new external_single_structure(
53 array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
54 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
56 ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
61 /**
62 * Get course contents
64 * @param int $courseid course id
65 * @param array $options These options are not used yet, might be used in later version
66 * @return array
67 * @since Moodle 2.2
69 public static function get_course_contents($courseid, $options = array()) {
70 global $CFG, $DB;
71 require_once($CFG->dirroot . "/course/lib.php");
73 //validate parameter
74 $params = self::validate_parameters(self::get_course_contents_parameters(),
75 array('courseid' => $courseid, 'options' => $options));
77 //retrieve the course
78 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
80 //check course format exist
81 if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
82 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, get_string('courseformatnotfound', 'error', '', $course->format));
83 } else {
84 require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
87 // now security checks
88 $context = context_course::instance($course->id, IGNORE_MISSING);
89 try {
90 self::validate_context($context);
91 } catch (Exception $e) {
92 $exceptionparam = new stdClass();
93 $exceptionparam->message = $e->getMessage();
94 $exceptionparam->courseid = $course->id;
95 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
98 $canupdatecourse = has_capability('moodle/course:update', $context);
100 //create return value
101 $coursecontents = array();
103 if ($canupdatecourse or $course->visible
104 or has_capability('moodle/course:viewhiddencourses', $context)) {
106 //retrieve sections
107 $modinfo = get_fast_modinfo($course);
108 $sections = $modinfo->get_section_info_all();
110 //for each sections (first displayed to last displayed)
111 $modinfosections = $modinfo->get_sections();
112 foreach ($sections as $key => $section) {
114 if (!$section->uservisible) {
115 continue;
118 // reset $sectioncontents
119 $sectionvalues = array();
120 $sectionvalues['id'] = $section->id;
121 $sectionvalues['name'] = get_section_name($course, $section);
122 $sectionvalues['visible'] = $section->visible;
123 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
124 external_format_text($section->summary, $section->summaryformat,
125 $context->id, 'course', 'section', $section->id);
126 $sectioncontents = array();
128 //for each module of the section
129 if (!empty($modinfosections[$section->section])) {
130 foreach ($modinfosections[$section->section] as $cmid) {
131 $cm = $modinfo->cms[$cmid];
133 // stop here if the module is not visible to the user
134 if (!$cm->uservisible) {
135 continue;
138 $module = array();
140 //common info (for people being able to see the module or availability dates)
141 $module['id'] = $cm->id;
142 $module['name'] = format_string($cm->name, true);
143 $module['instance'] = $cm->instance;
144 $module['modname'] = $cm->modname;
145 $module['modplural'] = $cm->modplural;
146 $module['modicon'] = $cm->get_icon_url()->out(false);
147 $module['indent'] = $cm->indent;
149 $modcontext = context_module::instance($cm->id);
151 if (!empty($cm->showdescription) or $cm->modname == 'label') {
152 // We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
153 list($module['description'], $descriptionformat) = external_format_text($cm->content,
154 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
157 //url of the module
158 $url = $cm->url;
159 if ($url) { //labels don't have url
160 $module['url'] = $url->out(false);
163 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
164 context_module::instance($cm->id));
165 //user that can view hidden module should know about the visibility
166 $module['visible'] = $cm->visible;
168 // Availability date (also send to user who can see hidden module).
169 if ($CFG->enableavailability && ($canviewhidden || $canupdatecourse)) {
170 $module['availability'] = $cm->availability;
173 $baseurl = 'webservice/pluginfile.php';
175 //call $modulename_export_contents
176 //(each module callback take care about checking the capabilities)
177 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
178 $getcontentfunction = $cm->modname.'_export_contents';
179 if (function_exists($getcontentfunction)) {
180 if ($contents = $getcontentfunction($cm, $baseurl)) {
181 $module['contents'] = $contents;
185 //assign result to $sectioncontents
186 $sectioncontents[] = $module;
190 $sectionvalues['modules'] = $sectioncontents;
192 // assign result to $coursecontents
193 $coursecontents[] = $sectionvalues;
196 return $coursecontents;
200 * Returns description of method result value
202 * @return external_description
203 * @since Moodle 2.2
205 public static function get_course_contents_returns() {
206 return new external_multiple_structure(
207 new external_single_structure(
208 array(
209 'id' => new external_value(PARAM_INT, 'Section ID'),
210 'name' => new external_value(PARAM_TEXT, 'Section name'),
211 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
212 'summary' => new external_value(PARAM_RAW, 'Section description'),
213 'summaryformat' => new external_format_value('summary'),
214 'modules' => new external_multiple_structure(
215 new external_single_structure(
216 array(
217 'id' => new external_value(PARAM_INT, 'activity id'),
218 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
219 'name' => new external_value(PARAM_RAW, 'activity module name'),
220 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
221 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
222 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
223 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
224 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
225 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
226 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
227 'contents' => new external_multiple_structure(
228 new external_single_structure(
229 array(
230 // content info
231 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
232 'filename'=> new external_value(PARAM_FILE, 'filename'),
233 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
234 'filesize'=> new external_value(PARAM_INT, 'filesize'),
235 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
236 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
237 'timecreated' => new external_value(PARAM_INT, 'Time created'),
238 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
239 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
241 // copyright related info
242 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
243 'author' => new external_value(PARAM_TEXT, 'Content owner'),
244 'license' => new external_value(PARAM_TEXT, 'Content license'),
246 ), VALUE_DEFAULT, array()
249 ), 'list of module'
257 * Returns description of method parameters
259 * @return external_function_parameters
260 * @since Moodle 2.3
262 public static function get_courses_parameters() {
263 return new external_function_parameters(
264 array('options' => new external_single_structure(
265 array('ids' => new external_multiple_structure(
266 new external_value(PARAM_INT, 'Course id')
267 , 'List of course id. If empty return all courses
268 except front page course.',
269 VALUE_OPTIONAL)
270 ), 'options - operator OR is used', VALUE_DEFAULT, array())
276 * Get courses
278 * @param array $options It contains an array (list of ids)
279 * @return array
280 * @since Moodle 2.2
282 public static function get_courses($options = array()) {
283 global $CFG, $DB;
284 require_once($CFG->dirroot . "/course/lib.php");
286 //validate parameter
287 $params = self::validate_parameters(self::get_courses_parameters(),
288 array('options' => $options));
290 //retrieve courses
291 if (!array_key_exists('ids', $params['options'])
292 or empty($params['options']['ids'])) {
293 $courses = $DB->get_records('course');
294 } else {
295 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
298 //create return value
299 $coursesinfo = array();
300 foreach ($courses as $course) {
302 // now security checks
303 $context = context_course::instance($course->id, IGNORE_MISSING);
304 $courseformatoptions = course_get_format($course)->get_format_options();
305 try {
306 self::validate_context($context);
307 } catch (Exception $e) {
308 $exceptionparam = new stdClass();
309 $exceptionparam->message = $e->getMessage();
310 $exceptionparam->courseid = $course->id;
311 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
313 require_capability('moodle/course:view', $context);
315 $courseinfo = array();
316 $courseinfo['id'] = $course->id;
317 $courseinfo['fullname'] = $course->fullname;
318 $courseinfo['shortname'] = $course->shortname;
319 $courseinfo['categoryid'] = $course->category;
320 list($courseinfo['summary'], $courseinfo['summaryformat']) =
321 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
322 $courseinfo['format'] = $course->format;
323 $courseinfo['startdate'] = $course->startdate;
324 if (array_key_exists('numsections', $courseformatoptions)) {
325 // For backward-compartibility
326 $courseinfo['numsections'] = $courseformatoptions['numsections'];
329 //some field should be returned only if the user has update permission
330 $courseadmin = has_capability('moodle/course:update', $context);
331 if ($courseadmin) {
332 $courseinfo['categorysortorder'] = $course->sortorder;
333 $courseinfo['idnumber'] = $course->idnumber;
334 $courseinfo['showgrades'] = $course->showgrades;
335 $courseinfo['showreports'] = $course->showreports;
336 $courseinfo['newsitems'] = $course->newsitems;
337 $courseinfo['visible'] = $course->visible;
338 $courseinfo['maxbytes'] = $course->maxbytes;
339 if (array_key_exists('hiddensections', $courseformatoptions)) {
340 // For backward-compartibility
341 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
343 $courseinfo['groupmode'] = $course->groupmode;
344 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
345 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
346 $courseinfo['lang'] = $course->lang;
347 $courseinfo['timecreated'] = $course->timecreated;
348 $courseinfo['timemodified'] = $course->timemodified;
349 $courseinfo['forcetheme'] = $course->theme;
350 $courseinfo['enablecompletion'] = $course->enablecompletion;
351 $courseinfo['completionnotify'] = $course->completionnotify;
352 $courseinfo['courseformatoptions'] = array();
353 foreach ($courseformatoptions as $key => $value) {
354 $courseinfo['courseformatoptions'][] = array(
355 'name' => $key,
356 'value' => $value
361 if ($courseadmin or $course->visible
362 or has_capability('moodle/course:viewhiddencourses', $context)) {
363 $coursesinfo[] = $courseinfo;
367 return $coursesinfo;
371 * Returns description of method result value
373 * @return external_description
374 * @since Moodle 2.2
376 public static function get_courses_returns() {
377 return new external_multiple_structure(
378 new external_single_structure(
379 array(
380 'id' => new external_value(PARAM_INT, 'course id'),
381 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
382 'categoryid' => new external_value(PARAM_INT, 'category id'),
383 'categorysortorder' => new external_value(PARAM_INT,
384 'sort order into the category', VALUE_OPTIONAL),
385 'fullname' => new external_value(PARAM_TEXT, 'full name'),
386 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
387 'summary' => new external_value(PARAM_RAW, 'summary'),
388 'summaryformat' => new external_format_value('summary'),
389 'format' => new external_value(PARAM_PLUGIN,
390 'course format: weeks, topics, social, site,..'),
391 'showgrades' => new external_value(PARAM_INT,
392 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
393 'newsitems' => new external_value(PARAM_INT,
394 'number of recent items appearing on the course page', VALUE_OPTIONAL),
395 'startdate' => new external_value(PARAM_INT,
396 'timestamp when the course start'),
397 'numsections' => new external_value(PARAM_INT,
398 '(deprecated, use courseformatoptions) number of weeks/topics',
399 VALUE_OPTIONAL),
400 'maxbytes' => new external_value(PARAM_INT,
401 'largest size of file that can be uploaded into the course',
402 VALUE_OPTIONAL),
403 'showreports' => new external_value(PARAM_INT,
404 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
405 'visible' => new external_value(PARAM_INT,
406 '1: available to student, 0:not available', VALUE_OPTIONAL),
407 'hiddensections' => new external_value(PARAM_INT,
408 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
409 VALUE_OPTIONAL),
410 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
411 VALUE_OPTIONAL),
412 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
413 VALUE_OPTIONAL),
414 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
415 VALUE_OPTIONAL),
416 'timecreated' => new external_value(PARAM_INT,
417 'timestamp when the course have been created', VALUE_OPTIONAL),
418 'timemodified' => new external_value(PARAM_INT,
419 'timestamp when the course have been modified', VALUE_OPTIONAL),
420 'enablecompletion' => new external_value(PARAM_INT,
421 'Enabled, control via completion and activity settings. Disbaled,
422 not shown in activity settings.',
423 VALUE_OPTIONAL),
424 'completionnotify' => new external_value(PARAM_INT,
425 '1: yes 0: no', VALUE_OPTIONAL),
426 'lang' => new external_value(PARAM_SAFEDIR,
427 'forced course language', VALUE_OPTIONAL),
428 'forcetheme' => new external_value(PARAM_PLUGIN,
429 'name of the force theme', VALUE_OPTIONAL),
430 'courseformatoptions' => new external_multiple_structure(
431 new external_single_structure(
432 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
433 'value' => new external_value(PARAM_RAW, 'course format option value')
435 'additional options for particular course format', VALUE_OPTIONAL
437 ), 'course'
443 * Returns description of method parameters
445 * @return external_function_parameters
446 * @since Moodle 2.2
448 public static function create_courses_parameters() {
449 $courseconfig = get_config('moodlecourse'); //needed for many default values
450 return new external_function_parameters(
451 array(
452 'courses' => new external_multiple_structure(
453 new external_single_structure(
454 array(
455 'fullname' => new external_value(PARAM_TEXT, 'full name'),
456 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
457 'categoryid' => new external_value(PARAM_INT, 'category id'),
458 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
459 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
460 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
461 'format' => new external_value(PARAM_PLUGIN,
462 'course format: weeks, topics, social, site,..',
463 VALUE_DEFAULT, $courseconfig->format),
464 'showgrades' => new external_value(PARAM_INT,
465 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
466 $courseconfig->showgrades),
467 'newsitems' => new external_value(PARAM_INT,
468 'number of recent items appearing on the course page',
469 VALUE_DEFAULT, $courseconfig->newsitems),
470 'startdate' => new external_value(PARAM_INT,
471 'timestamp when the course start', VALUE_OPTIONAL),
472 'numsections' => new external_value(PARAM_INT,
473 '(deprecated, use courseformatoptions) number of weeks/topics',
474 VALUE_OPTIONAL),
475 'maxbytes' => new external_value(PARAM_INT,
476 'largest size of file that can be uploaded into the course',
477 VALUE_DEFAULT, $courseconfig->maxbytes),
478 'showreports' => new external_value(PARAM_INT,
479 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
480 $courseconfig->showreports),
481 'visible' => new external_value(PARAM_INT,
482 '1: available to student, 0:not available', VALUE_OPTIONAL),
483 'hiddensections' => new external_value(PARAM_INT,
484 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
485 VALUE_OPTIONAL),
486 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
487 VALUE_DEFAULT, $courseconfig->groupmode),
488 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
489 VALUE_DEFAULT, $courseconfig->groupmodeforce),
490 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
491 VALUE_DEFAULT, 0),
492 'enablecompletion' => new external_value(PARAM_INT,
493 'Enabled, control via completion and activity settings. Disabled,
494 not shown in activity settings.',
495 VALUE_OPTIONAL),
496 'completionnotify' => new external_value(PARAM_INT,
497 '1: yes 0: no', VALUE_OPTIONAL),
498 'lang' => new external_value(PARAM_SAFEDIR,
499 'forced course language', VALUE_OPTIONAL),
500 'forcetheme' => new external_value(PARAM_PLUGIN,
501 'name of the force theme', VALUE_OPTIONAL),
502 'courseformatoptions' => new external_multiple_structure(
503 new external_single_structure(
504 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
505 'value' => new external_value(PARAM_RAW, 'course format option value')
507 'additional options for particular course format', VALUE_OPTIONAL),
509 ), 'courses to create'
516 * Create courses
518 * @param array $courses
519 * @return array courses (id and shortname only)
520 * @since Moodle 2.2
522 public static function create_courses($courses) {
523 global $CFG, $DB;
524 require_once($CFG->dirroot . "/course/lib.php");
525 require_once($CFG->libdir . '/completionlib.php');
527 $params = self::validate_parameters(self::create_courses_parameters(),
528 array('courses' => $courses));
530 $availablethemes = core_component::get_plugin_list('theme');
531 $availablelangs = get_string_manager()->get_list_of_translations();
533 $transaction = $DB->start_delegated_transaction();
535 foreach ($params['courses'] as $course) {
537 // Ensure the current user is allowed to run this function
538 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
539 try {
540 self::validate_context($context);
541 } catch (Exception $e) {
542 $exceptionparam = new stdClass();
543 $exceptionparam->message = $e->getMessage();
544 $exceptionparam->catid = $course['categoryid'];
545 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
547 require_capability('moodle/course:create', $context);
549 // Make sure lang is valid
550 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
551 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
554 // Make sure theme is valid
555 if (array_key_exists('forcetheme', $course)) {
556 if (!empty($CFG->allowcoursethemes)) {
557 if (empty($availablethemes[$course['forcetheme']])) {
558 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
559 } else {
560 $course['theme'] = $course['forcetheme'];
565 //force visibility if ws user doesn't have the permission to set it
566 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
567 if (!has_capability('moodle/course:visibility', $context)) {
568 $course['visible'] = $category->visible;
571 //set default value for completion
572 $courseconfig = get_config('moodlecourse');
573 if (completion_info::is_enabled_for_site()) {
574 if (!array_key_exists('enablecompletion', $course)) {
575 $course['enablecompletion'] = $courseconfig->enablecompletion;
577 } else {
578 $course['enablecompletion'] = 0;
581 $course['category'] = $course['categoryid'];
583 // Summary format.
584 $course['summaryformat'] = external_validate_format($course['summaryformat']);
586 if (!empty($course['courseformatoptions'])) {
587 foreach ($course['courseformatoptions'] as $option) {
588 $course[$option['name']] = $option['value'];
592 //Note: create_course() core function check shortname, idnumber, category
593 $course['id'] = create_course((object) $course)->id;
595 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
598 $transaction->allow_commit();
600 return $resultcourses;
604 * Returns description of method result value
606 * @return external_description
607 * @since Moodle 2.2
609 public static function create_courses_returns() {
610 return new external_multiple_structure(
611 new external_single_structure(
612 array(
613 'id' => new external_value(PARAM_INT, 'course id'),
614 'shortname' => new external_value(PARAM_TEXT, 'short name'),
621 * Update courses
623 * @return external_function_parameters
624 * @since Moodle 2.5
626 public static function update_courses_parameters() {
627 return new external_function_parameters(
628 array(
629 'courses' => new external_multiple_structure(
630 new external_single_structure(
631 array(
632 'id' => new external_value(PARAM_INT, 'ID of the course'),
633 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
634 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
635 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
636 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
637 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
638 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
639 'format' => new external_value(PARAM_PLUGIN,
640 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
641 'showgrades' => new external_value(PARAM_INT,
642 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
643 'newsitems' => new external_value(PARAM_INT,
644 'number of recent items appearing on the course page', VALUE_OPTIONAL),
645 'startdate' => new external_value(PARAM_INT,
646 'timestamp when the course start', VALUE_OPTIONAL),
647 'numsections' => new external_value(PARAM_INT,
648 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
649 'maxbytes' => new external_value(PARAM_INT,
650 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
651 'showreports' => new external_value(PARAM_INT,
652 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
653 'visible' => new external_value(PARAM_INT,
654 '1: available to student, 0:not available', VALUE_OPTIONAL),
655 'hiddensections' => new external_value(PARAM_INT,
656 '(deprecated, use courseformatoptions) How the hidden sections in the course are
657 displayed to students', VALUE_OPTIONAL),
658 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
659 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
660 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
661 'enablecompletion' => new external_value(PARAM_INT,
662 'Enabled, control via completion and activity settings. Disabled,
663 not shown in activity settings.', VALUE_OPTIONAL),
664 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
665 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
666 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
667 'courseformatoptions' => new external_multiple_structure(
668 new external_single_structure(
669 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
670 'value' => new external_value(PARAM_RAW, 'course format option value')
672 'additional options for particular course format', VALUE_OPTIONAL),
674 ), 'courses to update'
681 * Update courses
683 * @param array $courses
684 * @since Moodle 2.5
686 public static function update_courses($courses) {
687 global $CFG, $DB;
688 require_once($CFG->dirroot . "/course/lib.php");
689 $warnings = array();
691 $params = self::validate_parameters(self::update_courses_parameters(),
692 array('courses' => $courses));
694 $availablethemes = core_component::get_plugin_list('theme');
695 $availablelangs = get_string_manager()->get_list_of_translations();
697 foreach ($params['courses'] as $course) {
698 // Catch any exception while updating course and return as warning to user.
699 try {
700 // Ensure the current user is allowed to run this function.
701 $context = context_course::instance($course['id'], MUST_EXIST);
702 self::validate_context($context);
704 $oldcourse = course_get_format($course['id'])->get_course();
706 require_capability('moodle/course:update', $context);
708 // Check if user can change category.
709 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
710 require_capability('moodle/course:changecategory', $context);
711 $course['category'] = $course['categoryid'];
714 // Check if the user can change fullname.
715 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
716 require_capability('moodle/course:changefullname', $context);
719 // Check if the user can change shortname.
720 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
721 require_capability('moodle/course:changeshortname', $context);
724 // Check if the user can change the idnumber.
725 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
726 require_capability('moodle/course:changeidnumber', $context);
729 // Check if user can change summary.
730 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
731 require_capability('moodle/course:changesummary', $context);
734 // Summary format.
735 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
736 require_capability('moodle/course:changesummary', $context);
737 $course['summaryformat'] = external_validate_format($course['summaryformat']);
740 // Check if user can change visibility.
741 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
742 require_capability('moodle/course:visibility', $context);
745 // Make sure lang is valid.
746 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
747 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
750 // Make sure theme is valid.
751 if (array_key_exists('forcetheme', $course)) {
752 if (!empty($CFG->allowcoursethemes)) {
753 if (empty($availablethemes[$course['forcetheme']])) {
754 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
755 } else {
756 $course['theme'] = $course['forcetheme'];
761 // Make sure completion is enabled before setting it.
762 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
763 $course['enabledcompletion'] = 0;
766 // Make sure maxbytes are less then CFG->maxbytes.
767 if (array_key_exists('maxbytes', $course)) {
768 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
771 if (!empty($course['courseformatoptions'])) {
772 foreach ($course['courseformatoptions'] as $option) {
773 if (isset($option['name']) && isset($option['value'])) {
774 $course[$option['name']] = $option['value'];
779 // Update course if user has all required capabilities.
780 update_course((object) $course);
781 } catch (Exception $e) {
782 $warning = array();
783 $warning['item'] = 'course';
784 $warning['itemid'] = $course['id'];
785 if ($e instanceof moodle_exception) {
786 $warning['warningcode'] = $e->errorcode;
787 } else {
788 $warning['warningcode'] = $e->getCode();
790 $warning['message'] = $e->getMessage();
791 $warnings[] = $warning;
795 $result = array();
796 $result['warnings'] = $warnings;
797 return $result;
801 * Returns description of method result value
803 * @return external_description
804 * @since Moodle 2.5
806 public static function update_courses_returns() {
807 return new external_single_structure(
808 array(
809 'warnings' => new external_warnings()
815 * Returns description of method parameters
817 * @return external_function_parameters
818 * @since Moodle 2.2
820 public static function delete_courses_parameters() {
821 return new external_function_parameters(
822 array(
823 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
829 * Delete courses
831 * @param array $courseids A list of course ids
832 * @since Moodle 2.2
834 public static function delete_courses($courseids) {
835 global $CFG, $DB;
836 require_once($CFG->dirroot."/course/lib.php");
838 // Parameter validation.
839 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
841 $transaction = $DB->start_delegated_transaction();
843 foreach ($params['courseids'] as $courseid) {
844 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
846 // Check if the context is valid.
847 $coursecontext = context_course::instance($course->id);
848 self::validate_context($coursecontext);
850 // Check if the current user has enought permissions.
851 if (!can_delete_course($courseid)) {
852 throw new moodle_exception('cannotdeletecategorycourse', 'error',
853 '', format_string($course->fullname)." (id: $courseid)");
856 delete_course($course, false);
859 $transaction->allow_commit();
861 return null;
865 * Returns description of method result value
867 * @return external_description
868 * @since Moodle 2.2
870 public static function delete_courses_returns() {
871 return null;
875 * Returns description of method parameters
877 * @return external_function_parameters
878 * @since Moodle 2.3
880 public static function duplicate_course_parameters() {
881 return new external_function_parameters(
882 array(
883 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
884 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
885 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
886 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
887 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
888 'options' => new external_multiple_structure(
889 new external_single_structure(
890 array(
891 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
892 "activities" (int) Include course activites (default to 1 that is equal to yes),
893 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
894 "filters" (int) Include course filters (default to 1 that is equal to yes),
895 "users" (int) Include users (default to 0 that is equal to no),
896 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
897 "comments" (int) Include user comments (default to 0 that is equal to no),
898 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
899 "logs" (int) Include course logs (default to 0 that is equal to no),
900 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
902 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
905 ), VALUE_DEFAULT, array()
912 * Duplicate a course
914 * @param int $courseid
915 * @param string $fullname Duplicated course fullname
916 * @param string $shortname Duplicated course shortname
917 * @param int $categoryid Duplicated course parent category id
918 * @param int $visible Duplicated course availability
919 * @param array $options List of backup options
920 * @return array New course info
921 * @since Moodle 2.3
923 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
924 global $CFG, $USER, $DB;
925 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
926 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
928 // Parameter validation.
929 $params = self::validate_parameters(
930 self::duplicate_course_parameters(),
931 array(
932 'courseid' => $courseid,
933 'fullname' => $fullname,
934 'shortname' => $shortname,
935 'categoryid' => $categoryid,
936 'visible' => $visible,
937 'options' => $options
941 // Context validation.
943 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
944 throw new moodle_exception('invalidcourseid', 'error');
947 // Category where duplicated course is going to be created.
948 $categorycontext = context_coursecat::instance($params['categoryid']);
949 self::validate_context($categorycontext);
951 // Course to be duplicated.
952 $coursecontext = context_course::instance($course->id);
953 self::validate_context($coursecontext);
955 $backupdefaults = array(
956 'activities' => 1,
957 'blocks' => 1,
958 'filters' => 1,
959 'users' => 0,
960 'role_assignments' => 0,
961 'comments' => 0,
962 'userscompletion' => 0,
963 'logs' => 0,
964 'grade_histories' => 0
967 $backupsettings = array();
968 // Check for backup and restore options.
969 if (!empty($params['options'])) {
970 foreach ($params['options'] as $option) {
972 // Strict check for a correct value (allways 1 or 0, true or false).
973 $value = clean_param($option['value'], PARAM_INT);
975 if ($value !== 0 and $value !== 1) {
976 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
979 if (!isset($backupdefaults[$option['name']])) {
980 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
983 $backupsettings[$option['name']] = $value;
987 // Capability checking.
989 // The backup controller check for this currently, this may be redundant.
990 require_capability('moodle/course:create', $categorycontext);
991 require_capability('moodle/restore:restorecourse', $categorycontext);
992 require_capability('moodle/backup:backupcourse', $coursecontext);
994 if (!empty($backupsettings['users'])) {
995 require_capability('moodle/backup:userinfo', $coursecontext);
996 require_capability('moodle/restore:userinfo', $categorycontext);
999 // Check if the shortname is used.
1000 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1001 foreach ($foundcourses as $foundcourse) {
1002 $foundcoursenames[] = $foundcourse->fullname;
1005 $foundcoursenamestring = implode(',', $foundcoursenames);
1006 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1009 // Backup the course.
1011 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1012 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1014 foreach ($backupsettings as $name => $value) {
1015 $bc->get_plan()->get_setting($name)->set_value($value);
1018 $backupid = $bc->get_backupid();
1019 $backupbasepath = $bc->get_plan()->get_basepath();
1021 $bc->execute_plan();
1022 $results = $bc->get_results();
1023 $file = $results['backup_destination'];
1025 $bc->destroy();
1027 // Restore the backup immediately.
1029 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1030 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1031 $file->extract_to_pathname(get_file_packer(), $backupbasepath);
1034 // Create new course.
1035 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1037 $rc = new restore_controller($backupid, $newcourseid,
1038 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1040 foreach ($backupsettings as $name => $value) {
1041 $setting = $rc->get_plan()->get_setting($name);
1042 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1043 $setting->set_value($value);
1047 if (!$rc->execute_precheck()) {
1048 $precheckresults = $rc->get_precheck_results();
1049 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1050 if (empty($CFG->keeptempdirectoriesonbackup)) {
1051 fulldelete($backupbasepath);
1054 $errorinfo = '';
1056 foreach ($precheckresults['errors'] as $error) {
1057 $errorinfo .= $error;
1060 if (array_key_exists('warnings', $precheckresults)) {
1061 foreach ($precheckresults['warnings'] as $warning) {
1062 $errorinfo .= $warning;
1066 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1070 $rc->execute_plan();
1071 $rc->destroy();
1073 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1074 $course->fullname = $params['fullname'];
1075 $course->shortname = $params['shortname'];
1076 $course->visible = $params['visible'];
1078 // Set shortname and fullname back.
1079 $DB->update_record('course', $course);
1081 if (empty($CFG->keeptempdirectoriesonbackup)) {
1082 fulldelete($backupbasepath);
1085 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1086 $file->delete();
1088 return array('id' => $course->id, 'shortname' => $course->shortname);
1092 * Returns description of method result value
1094 * @return external_description
1095 * @since Moodle 2.3
1097 public static function duplicate_course_returns() {
1098 return new external_single_structure(
1099 array(
1100 'id' => new external_value(PARAM_INT, 'course id'),
1101 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1107 * Returns description of method parameters for import_course
1109 * @return external_function_parameters
1110 * @since Moodle 2.4
1112 public static function import_course_parameters() {
1113 return new external_function_parameters(
1114 array(
1115 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1116 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1117 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1118 'options' => new external_multiple_structure(
1119 new external_single_structure(
1120 array(
1121 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1122 "activities" (int) Include course activites (default to 1 that is equal to yes),
1123 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1124 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1126 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1129 ), VALUE_DEFAULT, array()
1136 * Imports a course
1138 * @param int $importfrom The id of the course we are importing from
1139 * @param int $importto The id of the course we are importing to
1140 * @param bool $deletecontent Whether to delete the course we are importing to content
1141 * @param array $options List of backup options
1142 * @return null
1143 * @since Moodle 2.4
1145 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1146 global $CFG, $USER, $DB;
1147 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1148 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1150 // Parameter validation.
1151 $params = self::validate_parameters(
1152 self::import_course_parameters(),
1153 array(
1154 'importfrom' => $importfrom,
1155 'importto' => $importto,
1156 'deletecontent' => $deletecontent,
1157 'options' => $options
1161 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1162 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1165 // Context validation.
1167 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1168 throw new moodle_exception('invalidcourseid', 'error');
1171 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1172 throw new moodle_exception('invalidcourseid', 'error');
1175 $importfromcontext = context_course::instance($importfrom->id);
1176 self::validate_context($importfromcontext);
1178 $importtocontext = context_course::instance($importto->id);
1179 self::validate_context($importtocontext);
1181 $backupdefaults = array(
1182 'activities' => 1,
1183 'blocks' => 1,
1184 'filters' => 1
1187 $backupsettings = array();
1189 // Check for backup and restore options.
1190 if (!empty($params['options'])) {
1191 foreach ($params['options'] as $option) {
1193 // Strict check for a correct value (allways 1 or 0, true or false).
1194 $value = clean_param($option['value'], PARAM_INT);
1196 if ($value !== 0 and $value !== 1) {
1197 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1200 if (!isset($backupdefaults[$option['name']])) {
1201 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1204 $backupsettings[$option['name']] = $value;
1208 // Capability checking.
1210 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1211 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1213 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1214 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1216 foreach ($backupsettings as $name => $value) {
1217 $bc->get_plan()->get_setting($name)->set_value($value);
1220 $backupid = $bc->get_backupid();
1221 $backupbasepath = $bc->get_plan()->get_basepath();
1223 $bc->execute_plan();
1224 $bc->destroy();
1226 // Restore the backup immediately.
1228 // Check if we must delete the contents of the destination course.
1229 if ($params['deletecontent']) {
1230 $restoretarget = backup::TARGET_EXISTING_DELETING;
1231 } else {
1232 $restoretarget = backup::TARGET_EXISTING_ADDING;
1235 $rc = new restore_controller($backupid, $importto->id,
1236 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1238 foreach ($backupsettings as $name => $value) {
1239 $rc->get_plan()->get_setting($name)->set_value($value);
1242 if (!$rc->execute_precheck()) {
1243 $precheckresults = $rc->get_precheck_results();
1244 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1245 if (empty($CFG->keeptempdirectoriesonbackup)) {
1246 fulldelete($backupbasepath);
1249 $errorinfo = '';
1251 foreach ($precheckresults['errors'] as $error) {
1252 $errorinfo .= $error;
1255 if (array_key_exists('warnings', $precheckresults)) {
1256 foreach ($precheckresults['warnings'] as $warning) {
1257 $errorinfo .= $warning;
1261 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1263 } else {
1264 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1265 restore_dbops::delete_course_content($importto->id);
1269 $rc->execute_plan();
1270 $rc->destroy();
1272 if (empty($CFG->keeptempdirectoriesonbackup)) {
1273 fulldelete($backupbasepath);
1276 return null;
1280 * Returns description of method result value
1282 * @return external_description
1283 * @since Moodle 2.4
1285 public static function import_course_returns() {
1286 return null;
1290 * Returns description of method parameters
1292 * @return external_function_parameters
1293 * @since Moodle 2.3
1295 public static function get_categories_parameters() {
1296 return new external_function_parameters(
1297 array(
1298 'criteria' => new external_multiple_structure(
1299 new external_single_structure(
1300 array(
1301 'key' => new external_value(PARAM_ALPHA,
1302 'The category column to search, expected keys (value format) are:'.
1303 '"id" (int) the category id,'.
1304 '"name" (string) the category name,'.
1305 '"parent" (int) the parent category id,'.
1306 '"idnumber" (string) category idnumber'.
1307 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1308 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1309 then the function return all categories that the user can see.'.
1310 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1311 '"theme" (string) only return the categories having this theme'.
1312 ' - user must have \'moodle/category:manage\' to search on theme'),
1313 'value' => new external_value(PARAM_RAW, 'the value to match')
1315 ), 'criteria', VALUE_DEFAULT, array()
1317 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1318 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1324 * Get categories
1326 * @param array $criteria Criteria to match the results
1327 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1328 * @return array list of categories
1329 * @since Moodle 2.3
1331 public static function get_categories($criteria = array(), $addsubcategories = true) {
1332 global $CFG, $DB;
1333 require_once($CFG->dirroot . "/course/lib.php");
1335 // Validate parameters.
1336 $params = self::validate_parameters(self::get_categories_parameters(),
1337 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1339 // Retrieve the categories.
1340 $categories = array();
1341 if (!empty($params['criteria'])) {
1343 $conditions = array();
1344 $wheres = array();
1345 foreach ($params['criteria'] as $crit) {
1346 $key = trim($crit['key']);
1348 // Trying to avoid duplicate keys.
1349 if (!isset($conditions[$key])) {
1351 $context = context_system::instance();
1352 $value = null;
1353 switch ($key) {
1354 case 'id':
1355 $value = clean_param($crit['value'], PARAM_INT);
1356 break;
1358 case 'idnumber':
1359 if (has_capability('moodle/category:manage', $context)) {
1360 $value = clean_param($crit['value'], PARAM_RAW);
1361 } else {
1362 // We must throw an exception.
1363 // Otherwise the dev client would think no idnumber exists.
1364 throw new moodle_exception('criteriaerror',
1365 'webservice', '', null,
1366 'You don\'t have the permissions to search on the "idnumber" field.');
1368 break;
1370 case 'name':
1371 $value = clean_param($crit['value'], PARAM_TEXT);
1372 break;
1374 case 'parent':
1375 $value = clean_param($crit['value'], PARAM_INT);
1376 break;
1378 case 'visible':
1379 if (has_capability('moodle/category:manage', $context)
1380 or has_capability('moodle/category:viewhiddencategories',
1381 context_system::instance())) {
1382 $value = clean_param($crit['value'], PARAM_INT);
1383 } else {
1384 throw new moodle_exception('criteriaerror',
1385 'webservice', '', null,
1386 'You don\'t have the permissions to search on the "visible" field.');
1388 break;
1390 case 'theme':
1391 if (has_capability('moodle/category:manage', $context)) {
1392 $value = clean_param($crit['value'], PARAM_THEME);
1393 } else {
1394 throw new moodle_exception('criteriaerror',
1395 'webservice', '', null,
1396 'You don\'t have the permissions to search on the "theme" field.');
1398 break;
1400 default:
1401 throw new moodle_exception('criteriaerror',
1402 'webservice', '', null,
1403 'You can not search on this criteria: ' . $key);
1406 if (isset($value)) {
1407 $conditions[$key] = $crit['value'];
1408 $wheres[] = $key . " = :" . $key;
1413 if (!empty($wheres)) {
1414 $wheres = implode(" AND ", $wheres);
1416 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1418 // Retrieve its sub subcategories (all levels).
1419 if ($categories and !empty($params['addsubcategories'])) {
1420 $newcategories = array();
1422 // Check if we required visible/theme checks.
1423 $additionalselect = '';
1424 $additionalparams = array();
1425 if (isset($conditions['visible'])) {
1426 $additionalselect .= ' AND visible = :visible';
1427 $additionalparams['visible'] = $conditions['visible'];
1429 if (isset($conditions['theme'])) {
1430 $additionalselect .= ' AND theme= :theme';
1431 $additionalparams['theme'] = $conditions['theme'];
1434 foreach ($categories as $category) {
1435 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1436 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1437 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1438 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1440 $categories = $categories + $newcategories;
1444 } else {
1445 // Retrieve all categories in the database.
1446 $categories = $DB->get_records('course_categories');
1449 // The not returned categories. key => category id, value => reason of exclusion.
1450 $excludedcats = array();
1452 // The returned categories.
1453 $categoriesinfo = array();
1455 // We need to sort the categories by path.
1456 // The parent cats need to be checked by the algo first.
1457 usort($categories, "core_course_external::compare_categories_by_path");
1459 foreach ($categories as $category) {
1461 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1462 $parents = explode('/', $category->path);
1463 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1464 foreach ($parents as $parentid) {
1465 // Note: when the parent exclusion was due to the context,
1466 // the sub category could still be returned.
1467 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1468 $excludedcats[$category->id] = 'parent';
1472 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1473 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1474 and !has_capability('moodle/category:manage', $context)) {
1475 $excludedcats[$category->id] = 'depth';
1478 // Check the user can use the category context.
1479 $context = context_coursecat::instance($category->id);
1480 try {
1481 self::validate_context($context);
1482 } catch (Exception $e) {
1483 $excludedcats[$category->id] = 'context';
1485 // If it was the requested category then throw an exception.
1486 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1487 $exceptionparam = new stdClass();
1488 $exceptionparam->message = $e->getMessage();
1489 $exceptionparam->catid = $category->id;
1490 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1494 // Return the category information.
1495 if (!isset($excludedcats[$category->id])) {
1497 // Final check to see if the category is visible to the user.
1498 if ($category->visible
1499 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1500 or has_capability('moodle/category:manage', $context)) {
1502 $categoryinfo = array();
1503 $categoryinfo['id'] = $category->id;
1504 $categoryinfo['name'] = $category->name;
1505 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1506 external_format_text($category->description, $category->descriptionformat,
1507 $context->id, 'coursecat', 'description', null);
1508 $categoryinfo['parent'] = $category->parent;
1509 $categoryinfo['sortorder'] = $category->sortorder;
1510 $categoryinfo['coursecount'] = $category->coursecount;
1511 $categoryinfo['depth'] = $category->depth;
1512 $categoryinfo['path'] = $category->path;
1514 // Some fields only returned for admin.
1515 if (has_capability('moodle/category:manage', $context)) {
1516 $categoryinfo['idnumber'] = $category->idnumber;
1517 $categoryinfo['visible'] = $category->visible;
1518 $categoryinfo['visibleold'] = $category->visibleold;
1519 $categoryinfo['timemodified'] = $category->timemodified;
1520 $categoryinfo['theme'] = $category->theme;
1523 $categoriesinfo[] = $categoryinfo;
1524 } else {
1525 $excludedcats[$category->id] = 'visibility';
1530 // Sorting the resulting array so it looks a bit better for the client developer.
1531 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1533 return $categoriesinfo;
1537 * Sort categories array by path
1538 * private function: only used by get_categories
1540 * @param array $category1
1541 * @param array $category2
1542 * @return int result of strcmp
1543 * @since Moodle 2.3
1545 private static function compare_categories_by_path($category1, $category2) {
1546 return strcmp($category1->path, $category2->path);
1550 * Sort categories array by sortorder
1551 * private function: only used by get_categories
1553 * @param array $category1
1554 * @param array $category2
1555 * @return int result of strcmp
1556 * @since Moodle 2.3
1558 private static function compare_categories_by_sortorder($category1, $category2) {
1559 return strcmp($category1['sortorder'], $category2['sortorder']);
1563 * Returns description of method result value
1565 * @return external_description
1566 * @since Moodle 2.3
1568 public static function get_categories_returns() {
1569 return new external_multiple_structure(
1570 new external_single_structure(
1571 array(
1572 'id' => new external_value(PARAM_INT, 'category id'),
1573 'name' => new external_value(PARAM_TEXT, 'category name'),
1574 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1575 'description' => new external_value(PARAM_RAW, 'category description'),
1576 'descriptionformat' => new external_format_value('description'),
1577 'parent' => new external_value(PARAM_INT, 'parent category id'),
1578 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1579 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1580 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1581 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1582 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1583 'depth' => new external_value(PARAM_INT, 'category depth'),
1584 'path' => new external_value(PARAM_TEXT, 'category path'),
1585 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1586 ), 'List of categories'
1592 * Returns description of method parameters
1594 * @return external_function_parameters
1595 * @since Moodle 2.3
1597 public static function create_categories_parameters() {
1598 return new external_function_parameters(
1599 array(
1600 'categories' => new external_multiple_structure(
1601 new external_single_structure(
1602 array(
1603 'name' => new external_value(PARAM_TEXT, 'new category name'),
1604 'parent' => new external_value(PARAM_INT,
1605 'the parent category id inside which the new category will be created
1606 - set to 0 for a root category',
1607 VALUE_DEFAULT, 0),
1608 'idnumber' => new external_value(PARAM_RAW,
1609 'the new category idnumber', VALUE_OPTIONAL),
1610 'description' => new external_value(PARAM_RAW,
1611 'the new category description', VALUE_OPTIONAL),
1612 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1613 'theme' => new external_value(PARAM_THEME,
1614 'the new category theme. This option must be enabled on moodle',
1615 VALUE_OPTIONAL),
1624 * Create categories
1626 * @param array $categories - see create_categories_parameters() for the array structure
1627 * @return array - see create_categories_returns() for the array structure
1628 * @since Moodle 2.3
1630 public static function create_categories($categories) {
1631 global $CFG, $DB;
1632 require_once($CFG->libdir . "/coursecatlib.php");
1634 $params = self::validate_parameters(self::create_categories_parameters(),
1635 array('categories' => $categories));
1637 $transaction = $DB->start_delegated_transaction();
1639 $createdcategories = array();
1640 foreach ($params['categories'] as $category) {
1641 if ($category['parent']) {
1642 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1643 throw new moodle_exception('unknowcategory');
1645 $context = context_coursecat::instance($category['parent']);
1646 } else {
1647 $context = context_system::instance();
1649 self::validate_context($context);
1650 require_capability('moodle/category:manage', $context);
1652 // this will validate format and throw an exception if there are errors
1653 external_validate_format($category['descriptionformat']);
1655 $newcategory = coursecat::create($category);
1657 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1660 $transaction->allow_commit();
1662 return $createdcategories;
1666 * Returns description of method parameters
1668 * @return external_function_parameters
1669 * @since Moodle 2.3
1671 public static function create_categories_returns() {
1672 return new external_multiple_structure(
1673 new external_single_structure(
1674 array(
1675 'id' => new external_value(PARAM_INT, 'new category id'),
1676 'name' => new external_value(PARAM_TEXT, 'new category name'),
1683 * Returns description of method parameters
1685 * @return external_function_parameters
1686 * @since Moodle 2.3
1688 public static function update_categories_parameters() {
1689 return new external_function_parameters(
1690 array(
1691 'categories' => new external_multiple_structure(
1692 new external_single_structure(
1693 array(
1694 'id' => new external_value(PARAM_INT, 'course id'),
1695 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1696 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1697 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1698 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1699 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1700 'theme' => new external_value(PARAM_THEME,
1701 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1710 * Update categories
1712 * @param array $categories The list of categories to update
1713 * @return null
1714 * @since Moodle 2.3
1716 public static function update_categories($categories) {
1717 global $CFG, $DB;
1718 require_once($CFG->libdir . "/coursecatlib.php");
1720 // Validate parameters.
1721 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1723 $transaction = $DB->start_delegated_transaction();
1725 foreach ($params['categories'] as $cat) {
1726 $category = coursecat::get($cat['id']);
1728 $categorycontext = context_coursecat::instance($cat['id']);
1729 self::validate_context($categorycontext);
1730 require_capability('moodle/category:manage', $categorycontext);
1732 // this will throw an exception if descriptionformat is not valid
1733 external_validate_format($cat['descriptionformat']);
1735 $category->update($cat);
1738 $transaction->allow_commit();
1742 * Returns description of method result value
1744 * @return external_description
1745 * @since Moodle 2.3
1747 public static function update_categories_returns() {
1748 return null;
1752 * Returns description of method parameters
1754 * @return external_function_parameters
1755 * @since Moodle 2.3
1757 public static function delete_categories_parameters() {
1758 return new external_function_parameters(
1759 array(
1760 'categories' => new external_multiple_structure(
1761 new external_single_structure(
1762 array(
1763 'id' => new external_value(PARAM_INT, 'category id to delete'),
1764 'newparent' => new external_value(PARAM_INT,
1765 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1766 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1767 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1776 * Delete categories
1778 * @param array $categories A list of category ids
1779 * @return array
1780 * @since Moodle 2.3
1782 public static function delete_categories($categories) {
1783 global $CFG, $DB;
1784 require_once($CFG->dirroot . "/course/lib.php");
1785 require_once($CFG->libdir . "/coursecatlib.php");
1787 // Validate parameters.
1788 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1790 $transaction = $DB->start_delegated_transaction();
1792 foreach ($params['categories'] as $category) {
1793 $deletecat = coursecat::get($category['id'], MUST_EXIST);
1794 $context = context_coursecat::instance($deletecat->id);
1795 require_capability('moodle/category:manage', $context);
1796 self::validate_context($context);
1797 self::validate_context(get_category_or_system_context($deletecat->parent));
1799 if ($category['recursive']) {
1800 // If recursive was specified, then we recursively delete the category's contents.
1801 if ($deletecat->can_delete_full()) {
1802 $deletecat->delete_full(false);
1803 } else {
1804 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1806 } else {
1807 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1808 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1809 // We must move to an existing category.
1810 if (!empty($category['newparent'])) {
1811 $newparentcat = coursecat::get($category['newparent']);
1812 } else {
1813 $newparentcat = coursecat::get($deletecat->parent);
1816 // This operation is not allowed. We must move contents to an existing category.
1817 if (!$newparentcat->id) {
1818 throw new moodle_exception('movecatcontentstoroot');
1821 self::validate_context(context_coursecat::instance($newparentcat->id));
1822 if ($deletecat->can_move_content_to($newparentcat->id)) {
1823 $deletecat->delete_move($newparentcat->id, false);
1824 } else {
1825 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1830 $transaction->allow_commit();
1834 * Returns description of method parameters
1836 * @return external_function_parameters
1837 * @since Moodle 2.3
1839 public static function delete_categories_returns() {
1840 return null;
1844 * Describes the parameters for delete_modules.
1846 * @return external_external_function_parameters
1847 * @since Moodle 2.5
1849 public static function delete_modules_parameters() {
1850 return new external_function_parameters (
1851 array(
1852 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1853 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
1859 * Deletes a list of provided module instances.
1861 * @param array $cmids the course module ids
1862 * @since Moodle 2.5
1864 public static function delete_modules($cmids) {
1865 global $CFG, $DB;
1867 // Require course file containing the course delete module function.
1868 require_once($CFG->dirroot . "/course/lib.php");
1870 // Clean the parameters.
1871 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
1873 // Keep track of the course ids we have performed a capability check on to avoid repeating.
1874 $arrcourseschecked = array();
1876 foreach ($params['cmids'] as $cmid) {
1877 // Get the course module.
1878 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
1880 // Check if we have not yet confirmed they have permission in this course.
1881 if (!in_array($cm->course, $arrcourseschecked)) {
1882 // Ensure the current user has required permission in this course.
1883 $context = context_course::instance($cm->course);
1884 self::validate_context($context);
1885 // Add to the array.
1886 $arrcourseschecked[] = $cm->course;
1889 // Ensure they can delete this module.
1890 $modcontext = context_module::instance($cm->id);
1891 require_capability('moodle/course:manageactivities', $modcontext);
1893 // Delete the module.
1894 course_delete_module($cm->id);
1899 * Describes the delete_modules return value.
1901 * @return external_single_structure
1902 * @since Moodle 2.5
1904 public static function delete_modules_returns() {
1905 return null;
1910 * Deprecated course external functions
1912 * @package core_course
1913 * @copyright 2009 Petr Skodak
1914 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1915 * @since Moodle 2.0
1916 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1917 * @see core_course_external
1919 class moodle_course_external extends external_api {
1922 * Returns description of method parameters
1924 * @return external_function_parameters
1925 * @since Moodle 2.0
1926 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1927 * @see core_course_external::get_courses_parameters()
1929 public static function get_courses_parameters() {
1930 return core_course_external::get_courses_parameters();
1934 * Get courses
1936 * @param array $options
1937 * @return array
1938 * @since Moodle 2.0
1939 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1940 * @see core_course_external::get_courses()
1942 public static function get_courses($options) {
1943 return core_course_external::get_courses($options);
1947 * Returns description of method result value
1949 * @return external_description
1950 * @since Moodle 2.0
1951 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1952 * @see core_course_external::get_courses_returns()
1954 public static function get_courses_returns() {
1955 return core_course_external::get_courses_returns();
1959 * Returns description of method parameters
1961 * @return external_function_parameters
1962 * @since Moodle 2.0
1963 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1964 * @see core_course_external::create_courses_parameters()
1966 public static function create_courses_parameters() {
1967 return core_course_external::create_courses_parameters();
1971 * Create courses
1973 * @param array $courses
1974 * @return array courses (id and shortname only)
1975 * @since Moodle 2.0
1976 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1977 * @see core_course_external::create_courses()
1979 public static function create_courses($courses) {
1980 return core_course_external::create_courses($courses);
1984 * Returns description of method result value
1986 * @return external_description
1987 * @since Moodle 2.0
1988 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1989 * @see core_course_external::create_courses_returns()
1991 public static function create_courses_returns() {
1992 return core_course_external::create_courses_returns();