Merge branch 'MDL-38821_master' of git://github.com/dmonllao/moodle
[moodle.git] / course / externallib.php
blob362f9287f0c2e3b78d4732e0b15826dad93e79ef
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 foreach ($sections as $key => $section) {
113 if (!$section->uservisible) {
114 continue;
117 // reset $sectioncontents
118 $sectionvalues = array();
119 $sectionvalues['id'] = $section->id;
120 $sectionvalues['name'] = get_section_name($course, $section);
121 $sectionvalues['visible'] = $section->visible;
122 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
123 external_format_text($section->summary, $section->summaryformat,
124 $context->id, 'course', 'section', $section->id);
125 $sectioncontents = array();
127 //for each module of the section
128 foreach ($modinfo->sections[$section->section] as $cmid) {
129 $cm = $modinfo->cms[$cmid];
131 // stop here if the module is not visible to the user
132 if (!$cm->uservisible) {
133 continue;
136 $module = array();
138 //common info (for people being able to see the module or availability dates)
139 $module['id'] = $cm->id;
140 $module['name'] = format_string($cm->name, true);
141 $module['modname'] = $cm->modname;
142 $module['modplural'] = $cm->modplural;
143 $module['modicon'] = $cm->get_icon_url()->out(false);
144 $module['indent'] = $cm->indent;
146 $modcontext = context_module::instance($cm->id);
148 if (!empty($cm->showdescription)) {
149 $module['description'] = $cm->get_content();
152 //url of the module
153 $url = $cm->get_url();
154 if ($url) { //labels don't have url
155 $module['url'] = $cm->get_url()->out();
158 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
159 context_module::instance($cm->id));
160 //user that can view hidden module should know about the visibility
161 $module['visible'] = $cm->visible;
163 //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
164 if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
165 $module['availablefrom'] = $cm->availablefrom;
166 $module['availableuntil'] = $cm->availableuntil;
169 $baseurl = 'webservice/pluginfile.php';
171 //call $modulename_export_contents
172 //(each module callback take care about checking the capabilities)
173 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
174 $getcontentfunction = $cm->modname.'_export_contents';
175 if (function_exists($getcontentfunction)) {
176 if ($contents = $getcontentfunction($cm, $baseurl)) {
177 $module['contents'] = $contents;
181 //assign result to $sectioncontents
182 $sectioncontents[] = $module;
185 $sectionvalues['modules'] = $sectioncontents;
187 // assign result to $coursecontents
188 $coursecontents[] = $sectionvalues;
191 return $coursecontents;
195 * Returns description of method result value
197 * @return external_description
198 * @since Moodle 2.2
200 public static function get_course_contents_returns() {
201 return new external_multiple_structure(
202 new external_single_structure(
203 array(
204 'id' => new external_value(PARAM_INT, 'Section ID'),
205 'name' => new external_value(PARAM_TEXT, 'Section name'),
206 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
207 'summary' => new external_value(PARAM_RAW, 'Section description'),
208 'summaryformat' => new external_format_value('summary'),
209 'modules' => new external_multiple_structure(
210 new external_single_structure(
211 array(
212 'id' => new external_value(PARAM_INT, 'activity id'),
213 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
214 'name' => new external_value(PARAM_RAW, 'activity module name'),
215 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
216 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
217 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
218 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
219 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
220 'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
221 'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
222 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
223 'contents' => new external_multiple_structure(
224 new external_single_structure(
225 array(
226 // content info
227 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
228 'filename'=> new external_value(PARAM_FILE, 'filename'),
229 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
230 'filesize'=> new external_value(PARAM_INT, 'filesize'),
231 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
232 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
233 'timecreated' => new external_value(PARAM_INT, 'Time created'),
234 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
235 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
237 // copyright related info
238 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
239 'author' => new external_value(PARAM_TEXT, 'Content owner'),
240 'license' => new external_value(PARAM_TEXT, 'Content license'),
242 ), VALUE_DEFAULT, array()
245 ), 'list of module'
253 * Returns description of method parameters
255 * @return external_function_parameters
256 * @since Moodle 2.3
258 public static function get_courses_parameters() {
259 return new external_function_parameters(
260 array('options' => new external_single_structure(
261 array('ids' => new external_multiple_structure(
262 new external_value(PARAM_INT, 'Course id')
263 , 'List of course id. If empty return all courses
264 except front page course.',
265 VALUE_OPTIONAL)
266 ), 'options - operator OR is used', VALUE_DEFAULT, array())
272 * Get courses
274 * @param array $options It contains an array (list of ids)
275 * @return array
276 * @since Moodle 2.2
278 public static function get_courses($options = array()) {
279 global $CFG, $DB;
280 require_once($CFG->dirroot . "/course/lib.php");
282 //validate parameter
283 $params = self::validate_parameters(self::get_courses_parameters(),
284 array('options' => $options));
286 //retrieve courses
287 if (!array_key_exists('ids', $params['options'])
288 or empty($params['options']['ids'])) {
289 $courses = $DB->get_records('course');
290 } else {
291 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
294 //create return value
295 $coursesinfo = array();
296 foreach ($courses as $course) {
298 // now security checks
299 $context = context_course::instance($course->id, IGNORE_MISSING);
300 $courseformatoptions = course_get_format($course)->get_format_options();
301 try {
302 self::validate_context($context);
303 } catch (Exception $e) {
304 $exceptionparam = new stdClass();
305 $exceptionparam->message = $e->getMessage();
306 $exceptionparam->courseid = $course->id;
307 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
309 require_capability('moodle/course:view', $context);
311 $courseinfo = array();
312 $courseinfo['id'] = $course->id;
313 $courseinfo['fullname'] = $course->fullname;
314 $courseinfo['shortname'] = $course->shortname;
315 $courseinfo['categoryid'] = $course->category;
316 list($courseinfo['summary'], $courseinfo['summaryformat']) =
317 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
318 $courseinfo['format'] = $course->format;
319 $courseinfo['startdate'] = $course->startdate;
320 if (array_key_exists('numsections', $courseformatoptions)) {
321 // For backward-compartibility
322 $courseinfo['numsections'] = $courseformatoptions['numsections'];
325 //some field should be returned only if the user has update permission
326 $courseadmin = has_capability('moodle/course:update', $context);
327 if ($courseadmin) {
328 $courseinfo['categorysortorder'] = $course->sortorder;
329 $courseinfo['idnumber'] = $course->idnumber;
330 $courseinfo['showgrades'] = $course->showgrades;
331 $courseinfo['showreports'] = $course->showreports;
332 $courseinfo['newsitems'] = $course->newsitems;
333 $courseinfo['visible'] = $course->visible;
334 $courseinfo['maxbytes'] = $course->maxbytes;
335 if (array_key_exists('hiddensections', $courseformatoptions)) {
336 // For backward-compartibility
337 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
339 $courseinfo['groupmode'] = $course->groupmode;
340 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
341 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
342 $courseinfo['lang'] = $course->lang;
343 $courseinfo['timecreated'] = $course->timecreated;
344 $courseinfo['timemodified'] = $course->timemodified;
345 $courseinfo['forcetheme'] = $course->theme;
346 $courseinfo['enablecompletion'] = $course->enablecompletion;
347 $courseinfo['completionstartonenrol'] = $course->completionstartonenrol;
348 $courseinfo['completionnotify'] = $course->completionnotify;
349 $courseinfo['courseformatoptions'] = array();
350 foreach ($courseformatoptions as $key => $value) {
351 $courseinfo['courseformatoptions'][] = array(
352 'name' => $key,
353 'value' => $value
358 if ($courseadmin or $course->visible
359 or has_capability('moodle/course:viewhiddencourses', $context)) {
360 $coursesinfo[] = $courseinfo;
364 return $coursesinfo;
368 * Returns description of method result value
370 * @return external_description
371 * @since Moodle 2.2
373 public static function get_courses_returns() {
374 return new external_multiple_structure(
375 new external_single_structure(
376 array(
377 'id' => new external_value(PARAM_INT, 'course id'),
378 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
379 'categoryid' => new external_value(PARAM_INT, 'category id'),
380 'categorysortorder' => new external_value(PARAM_INT,
381 'sort order into the category', VALUE_OPTIONAL),
382 'fullname' => new external_value(PARAM_TEXT, 'full name'),
383 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
384 'summary' => new external_value(PARAM_RAW, 'summary'),
385 'summaryformat' => new external_format_value('summary'),
386 'format' => new external_value(PARAM_PLUGIN,
387 'course format: weeks, topics, social, site,..'),
388 'showgrades' => new external_value(PARAM_INT,
389 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
390 'newsitems' => new external_value(PARAM_INT,
391 'number of recent items appearing on the course page', VALUE_OPTIONAL),
392 'startdate' => new external_value(PARAM_INT,
393 'timestamp when the course start'),
394 'numsections' => new external_value(PARAM_INT,
395 '(deprecated, use courseformatoptions) number of weeks/topics',
396 VALUE_OPTIONAL),
397 'maxbytes' => new external_value(PARAM_INT,
398 'largest size of file that can be uploaded into the course',
399 VALUE_OPTIONAL),
400 'showreports' => new external_value(PARAM_INT,
401 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
402 'visible' => new external_value(PARAM_INT,
403 '1: available to student, 0:not available', VALUE_OPTIONAL),
404 'hiddensections' => new external_value(PARAM_INT,
405 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
406 VALUE_OPTIONAL),
407 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
408 VALUE_OPTIONAL),
409 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
410 VALUE_OPTIONAL),
411 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
412 VALUE_OPTIONAL),
413 'timecreated' => new external_value(PARAM_INT,
414 'timestamp when the course have been created', VALUE_OPTIONAL),
415 'timemodified' => new external_value(PARAM_INT,
416 'timestamp when the course have been modified', VALUE_OPTIONAL),
417 'enablecompletion' => new external_value(PARAM_INT,
418 'Enabled, control via completion and activity settings. Disbaled,
419 not shown in activity settings.',
420 VALUE_OPTIONAL),
421 'completionstartonenrol' => new external_value(PARAM_INT,
422 '1: begin tracking a student\'s progress in course completion
423 after course enrolment. 0: does not',
424 VALUE_OPTIONAL),
425 'completionnotify' => new external_value(PARAM_INT,
426 '1: yes 0: no', VALUE_OPTIONAL),
427 'lang' => new external_value(PARAM_SAFEDIR,
428 'forced course language', VALUE_OPTIONAL),
429 'forcetheme' => new external_value(PARAM_PLUGIN,
430 'name of the force theme', VALUE_OPTIONAL),
431 'courseformatoptions' => new external_multiple_structure(
432 new external_single_structure(
433 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
434 'value' => new external_value(PARAM_RAW, 'course format option value')
436 'additional options for particular course format', VALUE_OPTIONAL
438 ), 'course'
444 * Returns description of method parameters
446 * @return external_function_parameters
447 * @since Moodle 2.2
449 public static function create_courses_parameters() {
450 $courseconfig = get_config('moodlecourse'); //needed for many default values
451 return new external_function_parameters(
452 array(
453 'courses' => new external_multiple_structure(
454 new external_single_structure(
455 array(
456 'fullname' => new external_value(PARAM_TEXT, 'full name'),
457 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
458 'categoryid' => new external_value(PARAM_INT, 'category id'),
459 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
460 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
461 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
462 'format' => new external_value(PARAM_PLUGIN,
463 'course format: weeks, topics, social, site,..',
464 VALUE_DEFAULT, $courseconfig->format),
465 'showgrades' => new external_value(PARAM_INT,
466 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
467 $courseconfig->showgrades),
468 'newsitems' => new external_value(PARAM_INT,
469 'number of recent items appearing on the course page',
470 VALUE_DEFAULT, $courseconfig->newsitems),
471 'startdate' => new external_value(PARAM_INT,
472 'timestamp when the course start', VALUE_OPTIONAL),
473 'numsections' => new external_value(PARAM_INT,
474 '(deprecated, use courseformatoptions) number of weeks/topics',
475 VALUE_OPTIONAL),
476 'maxbytes' => new external_value(PARAM_INT,
477 'largest size of file that can be uploaded into the course',
478 VALUE_DEFAULT, $courseconfig->maxbytes),
479 'showreports' => new external_value(PARAM_INT,
480 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
481 $courseconfig->showreports),
482 'visible' => new external_value(PARAM_INT,
483 '1: available to student, 0:not available', VALUE_OPTIONAL),
484 'hiddensections' => new external_value(PARAM_INT,
485 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
486 VALUE_OPTIONAL),
487 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
488 VALUE_DEFAULT, $courseconfig->groupmode),
489 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
490 VALUE_DEFAULT, $courseconfig->groupmodeforce),
491 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
492 VALUE_DEFAULT, 0),
493 'enablecompletion' => new external_value(PARAM_INT,
494 'Enabled, control via completion and activity settings. Disabled,
495 not shown in activity settings.',
496 VALUE_OPTIONAL),
497 'completionstartonenrol' => new external_value(PARAM_INT,
498 '1: begin tracking a student\'s progress in course completion after
499 course enrolment. 0: does not',
500 VALUE_OPTIONAL),
501 'completionnotify' => new external_value(PARAM_INT,
502 '1: yes 0: no', VALUE_OPTIONAL),
503 'lang' => new external_value(PARAM_SAFEDIR,
504 'forced course language', VALUE_OPTIONAL),
505 'forcetheme' => new external_value(PARAM_PLUGIN,
506 'name of the force theme', VALUE_OPTIONAL),
507 'courseformatoptions' => new external_multiple_structure(
508 new external_single_structure(
509 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
510 'value' => new external_value(PARAM_RAW, 'course format option value')
512 'additional options for particular course format', VALUE_OPTIONAL),
514 ), 'courses to create'
521 * Create courses
523 * @param array $courses
524 * @return array courses (id and shortname only)
525 * @since Moodle 2.2
527 public static function create_courses($courses) {
528 global $CFG, $DB;
529 require_once($CFG->dirroot . "/course/lib.php");
530 require_once($CFG->libdir . '/completionlib.php');
532 $params = self::validate_parameters(self::create_courses_parameters(),
533 array('courses' => $courses));
535 $availablethemes = get_plugin_list('theme');
536 $availablelangs = get_string_manager()->get_list_of_translations();
538 $transaction = $DB->start_delegated_transaction();
540 foreach ($params['courses'] as $course) {
542 // Ensure the current user is allowed to run this function
543 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
544 try {
545 self::validate_context($context);
546 } catch (Exception $e) {
547 $exceptionparam = new stdClass();
548 $exceptionparam->message = $e->getMessage();
549 $exceptionparam->catid = $course['categoryid'];
550 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
552 require_capability('moodle/course:create', $context);
554 // Make sure lang is valid
555 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
556 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
559 // Make sure theme is valid
560 if (array_key_exists('forcetheme', $course)) {
561 if (!empty($CFG->allowcoursethemes)) {
562 if (empty($availablethemes[$course['forcetheme']])) {
563 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
564 } else {
565 $course['theme'] = $course['forcetheme'];
570 //force visibility if ws user doesn't have the permission to set it
571 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
572 if (!has_capability('moodle/course:visibility', $context)) {
573 $course['visible'] = $category->visible;
576 //set default value for completion
577 $courseconfig = get_config('moodlecourse');
578 if (completion_info::is_enabled_for_site()) {
579 if (!array_key_exists('enablecompletion', $course)) {
580 $course['enablecompletion'] = $courseconfig->enablecompletion;
582 if (!array_key_exists('completionstartonenrol', $course)) {
583 $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
585 } else {
586 $course['enablecompletion'] = 0;
587 $course['completionstartonenrol'] = 0;
590 $course['category'] = $course['categoryid'];
592 // Summary format.
593 $course['summaryformat'] = external_validate_format($course['summaryformat']);
595 if (!empty($course['courseformatoptions'])) {
596 foreach ($course['courseformatoptions'] as $option) {
597 $course[$option['name']] = $option['value'];
601 //Note: create_course() core function check shortname, idnumber, category
602 $course['id'] = create_course((object) $course)->id;
604 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
607 $transaction->allow_commit();
609 return $resultcourses;
613 * Returns description of method result value
615 * @return external_description
616 * @since Moodle 2.2
618 public static function create_courses_returns() {
619 return new external_multiple_structure(
620 new external_single_structure(
621 array(
622 'id' => new external_value(PARAM_INT, 'course id'),
623 'shortname' => new external_value(PARAM_TEXT, 'short name'),
630 * Update courses
632 * @return external_function_parameters
633 * @since Moodle 2.5
635 public static function update_courses_parameters() {
636 return new external_function_parameters(
637 array(
638 'courses' => new external_multiple_structure(
639 new external_single_structure(
640 array(
641 'id' => new external_value(PARAM_INT, 'ID of the course'),
642 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
643 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
644 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
645 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
646 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
647 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
648 'format' => new external_value(PARAM_PLUGIN,
649 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
650 'showgrades' => new external_value(PARAM_INT,
651 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
652 'newsitems' => new external_value(PARAM_INT,
653 'number of recent items appearing on the course page', VALUE_OPTIONAL),
654 'startdate' => new external_value(PARAM_INT,
655 'timestamp when the course start', VALUE_OPTIONAL),
656 'numsections' => new external_value(PARAM_INT,
657 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
658 'maxbytes' => new external_value(PARAM_INT,
659 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
660 'showreports' => new external_value(PARAM_INT,
661 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
662 'visible' => new external_value(PARAM_INT,
663 '1: available to student, 0:not available', VALUE_OPTIONAL),
664 'hiddensections' => new external_value(PARAM_INT,
665 '(deprecated, use courseformatoptions) How the hidden sections in the course are
666 displayed to students', VALUE_OPTIONAL),
667 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
668 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
669 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
670 'enablecompletion' => new external_value(PARAM_INT,
671 'Enabled, control via completion and activity settings. Disabled,
672 not shown in activity settings.', VALUE_OPTIONAL),
673 'completionstartonenrol' => new external_value(PARAM_INT,
674 '1: begin tracking a student\'s progress in course completion after
675 course enrolment. 0: does not', VALUE_OPTIONAL),
676 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
677 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
678 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
679 'courseformatoptions' => new external_multiple_structure(
680 new external_single_structure(
681 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
682 'value' => new external_value(PARAM_RAW, 'course format option value')
684 'additional options for particular course format', VALUE_OPTIONAL),
686 ), 'courses to update'
693 * Update courses
695 * @param array $courses
696 * @since Moodle 2.5
698 public static function update_courses($courses) {
699 global $CFG, $DB;
700 require_once($CFG->dirroot . "/course/lib.php");
701 $warnings = array();
703 $params = self::validate_parameters(self::update_courses_parameters(),
704 array('courses' => $courses));
706 $availablethemes = get_plugin_list('theme');
707 $availablelangs = get_string_manager()->get_list_of_translations();
709 foreach ($params['courses'] as $course) {
710 // Catch any exception while updating course and return as warning to user.
711 try {
712 // Ensure the current user is allowed to run this function.
713 $context = context_course::instance($course['id'], MUST_EXIST);
714 self::validate_context($context);
716 $oldcourse = course_get_format($course['id'])->get_course();
718 require_capability('moodle/course:update', $context);
720 // Check if user can change category.
721 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
722 require_capability('moodle/course:changecategory', $context);
723 $course['category'] = $course['categoryid'];
726 // Check if the user can change fullname.
727 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
728 require_capability('moodle/course:changefullname', $context);
731 // Check if the shortname already exist and user have capability.
732 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
733 require_capability('moodle/course:changeshortname', $context);
734 if ($DB->record_exists('course', array('shortname' => $course['shortname']))) {
735 throw new moodle_exception('shortnametaken');
739 // Check if the id number already exist and user have capability.
740 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
741 require_capability('moodle/course:changeidnumber', $context);
742 if ($DB->record_exists('course', array('idnumber' => $course['idnumber']))) {
743 throw new moodle_exception('idnumbertaken');
747 // Check if user can change summary.
748 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
749 require_capability('moodle/course:changesummary', $context);
752 // Summary format.
753 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
754 require_capability('moodle/course:changesummary', $context);
755 $course['summaryformat'] = external_validate_format($course['summaryformat']);
758 // Check if user can change visibility.
759 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
760 require_capability('moodle/course:visibility', $context);
763 // Make sure lang is valid.
764 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
765 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
768 // Make sure theme is valid.
769 if (array_key_exists('forcetheme', $course)) {
770 if (!empty($CFG->allowcoursethemes)) {
771 if (empty($availablethemes[$course['forcetheme']])) {
772 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
773 } else {
774 $course['theme'] = $course['forcetheme'];
779 // Make sure completion is enabled before setting it.
780 if ((array_key_exists('enabledcompletion', $course) ||
781 array_key_exists('completionstartonenrol', $course)) &&
782 !completion_info::is_enabled_for_site()) {
783 $course['enabledcompletion'] = 0;
784 $course['completionstartonenrol'] = 0;
787 // Make sure maxbytes are less then CFG->maxbytes.
788 if (array_key_exists('maxbytes', $course)) {
789 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
792 if (!empty($course['courseformatoptions'])) {
793 foreach ($course['courseformatoptions'] as $option) {
794 if (isset($option['name']) && isset($option['value'])) {
795 $course[$option['name']] = $option['value'];
800 // Update course if user has all required capabilities.
801 update_course((object) $course);
802 } catch (Exception $e) {
803 $warning = array();
804 $warning['item'] = 'course';
805 $warning['itemid'] = $course['id'];
806 if ($e instanceof moodle_exception) {
807 $warning['warningcode'] = $e->errorcode;
808 } else {
809 $warning['warningcode'] = $e->getCode();
811 $warning['message'] = $e->getMessage();
812 $warnings[] = $warning;
816 $result = array();
817 $result['warnings'] = $warnings;
818 return $result;
822 * Returns description of method result value
824 * @return external_description
825 * @since Moodle 2.5
827 public static function update_courses_returns() {
828 return new external_single_structure(
829 array(
830 'warnings' => new external_warnings()
836 * Returns description of method parameters
838 * @return external_function_parameters
839 * @since Moodle 2.2
841 public static function delete_courses_parameters() {
842 return new external_function_parameters(
843 array(
844 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
850 * Delete courses
852 * @param array $courseids A list of course ids
853 * @since Moodle 2.2
855 public static function delete_courses($courseids) {
856 global $CFG, $DB;
857 require_once($CFG->dirroot."/course/lib.php");
859 // Parameter validation.
860 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
862 $transaction = $DB->start_delegated_transaction();
864 foreach ($params['courseids'] as $courseid) {
865 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
867 // Check if the context is valid.
868 $coursecontext = context_course::instance($course->id);
869 self::validate_context($coursecontext);
871 // Check if the current user has enought permissions.
872 if (!can_delete_course($courseid)) {
873 throw new moodle_exception('cannotdeletecategorycourse', 'error',
874 '', format_string($course->fullname)." (id: $courseid)");
877 delete_course($course, false);
880 $transaction->allow_commit();
882 return null;
886 * Returns description of method result value
888 * @return external_description
889 * @since Moodle 2.2
891 public static function delete_courses_returns() {
892 return null;
896 * Returns description of method parameters
898 * @return external_function_parameters
899 * @since Moodle 2.3
901 public static function duplicate_course_parameters() {
902 return new external_function_parameters(
903 array(
904 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
905 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
906 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
907 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
908 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
909 'options' => new external_multiple_structure(
910 new external_single_structure(
911 array(
912 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
913 "activities" (int) Include course activites (default to 1 that is equal to yes),
914 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
915 "filters" (int) Include course filters (default to 1 that is equal to yes),
916 "users" (int) Include users (default to 0 that is equal to no),
917 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
918 "comments" (int) Include user comments (default to 0 that is equal to no),
919 "completion_information" (int) Include user course completion information (default to 0 that is equal to no),
920 "logs" (int) Include course logs (default to 0 that is equal to no),
921 "histories" (int) Include histories (default to 0 that is equal to no)'
923 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
926 ), VALUE_DEFAULT, array()
933 * Duplicate a course
935 * @param int $courseid
936 * @param string $fullname Duplicated course fullname
937 * @param string $shortname Duplicated course shortname
938 * @param int $categoryid Duplicated course parent category id
939 * @param int $visible Duplicated course availability
940 * @param array $options List of backup options
941 * @return array New course info
942 * @since Moodle 2.3
944 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
945 global $CFG, $USER, $DB;
946 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
947 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
949 // Parameter validation.
950 $params = self::validate_parameters(
951 self::duplicate_course_parameters(),
952 array(
953 'courseid' => $courseid,
954 'fullname' => $fullname,
955 'shortname' => $shortname,
956 'categoryid' => $categoryid,
957 'visible' => $visible,
958 'options' => $options
962 // Context validation.
964 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
965 throw new moodle_exception('invalidcourseid', 'error');
968 // Category where duplicated course is going to be created.
969 $categorycontext = context_coursecat::instance($params['categoryid']);
970 self::validate_context($categorycontext);
972 // Course to be duplicated.
973 $coursecontext = context_course::instance($course->id);
974 self::validate_context($coursecontext);
976 $backupdefaults = array(
977 'activities' => 1,
978 'blocks' => 1,
979 'filters' => 1,
980 'users' => 0,
981 'role_assignments' => 0,
982 'comments' => 0,
983 'completion_information' => 0,
984 'logs' => 0,
985 'histories' => 0
988 $backupsettings = array();
989 // Check for backup and restore options.
990 if (!empty($params['options'])) {
991 foreach ($params['options'] as $option) {
993 // Strict check for a correct value (allways 1 or 0, true or false).
994 $value = clean_param($option['value'], PARAM_INT);
996 if ($value !== 0 and $value !== 1) {
997 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1000 if (!isset($backupdefaults[$option['name']])) {
1001 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1004 $backupsettings[$option['name']] = $value;
1008 // Capability checking.
1010 // The backup controller check for this currently, this may be redundant.
1011 require_capability('moodle/course:create', $categorycontext);
1012 require_capability('moodle/restore:restorecourse', $categorycontext);
1013 require_capability('moodle/backup:backupcourse', $coursecontext);
1015 if (!empty($backupsettings['users'])) {
1016 require_capability('moodle/backup:userinfo', $coursecontext);
1017 require_capability('moodle/restore:userinfo', $categorycontext);
1020 // Check if the shortname is used.
1021 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1022 foreach ($foundcourses as $foundcourse) {
1023 $foundcoursenames[] = $foundcourse->fullname;
1026 $foundcoursenamestring = implode(',', $foundcoursenames);
1027 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1030 // Backup the course.
1032 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1033 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1035 foreach ($backupsettings as $name => $value) {
1036 $bc->get_plan()->get_setting($name)->set_value($value);
1039 $backupid = $bc->get_backupid();
1040 $backupbasepath = $bc->get_plan()->get_basepath();
1042 $bc->execute_plan();
1043 $results = $bc->get_results();
1044 $file = $results['backup_destination'];
1046 $bc->destroy();
1048 // Restore the backup immediately.
1050 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1051 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1052 $file->extract_to_pathname(get_file_packer(), $backupbasepath);
1055 // Create new course.
1056 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1058 $rc = new restore_controller($backupid, $newcourseid,
1059 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1061 foreach ($backupsettings as $name => $value) {
1062 $setting = $rc->get_plan()->get_setting($name);
1063 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1064 $setting->set_value($value);
1068 if (!$rc->execute_precheck()) {
1069 $precheckresults = $rc->get_precheck_results();
1070 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1071 if (empty($CFG->keeptempdirectoriesonbackup)) {
1072 fulldelete($backupbasepath);
1075 $errorinfo = '';
1077 foreach ($precheckresults['errors'] as $error) {
1078 $errorinfo .= $error;
1081 if (array_key_exists('warnings', $precheckresults)) {
1082 foreach ($precheckresults['warnings'] as $warning) {
1083 $errorinfo .= $warning;
1087 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1091 $rc->execute_plan();
1092 $rc->destroy();
1094 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1095 $course->fullname = $params['fullname'];
1096 $course->shortname = $params['shortname'];
1097 $course->visible = $params['visible'];
1099 // Set shortname and fullname back.
1100 $DB->update_record('course', $course);
1102 if (empty($CFG->keeptempdirectoriesonbackup)) {
1103 fulldelete($backupbasepath);
1106 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1107 $file->delete();
1109 return array('id' => $course->id, 'shortname' => $course->shortname);
1113 * Returns description of method result value
1115 * @return external_description
1116 * @since Moodle 2.3
1118 public static function duplicate_course_returns() {
1119 return new external_single_structure(
1120 array(
1121 'id' => new external_value(PARAM_INT, 'course id'),
1122 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1128 * Returns description of method parameters for import_course
1130 * @return external_function_parameters
1131 * @since Moodle 2.4
1133 public static function import_course_parameters() {
1134 return new external_function_parameters(
1135 array(
1136 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1137 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1138 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1139 'options' => new external_multiple_structure(
1140 new external_single_structure(
1141 array(
1142 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1143 "activities" (int) Include course activites (default to 1 that is equal to yes),
1144 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1145 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1147 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1150 ), VALUE_DEFAULT, array()
1157 * Imports a course
1159 * @param int $importfrom The id of the course we are importing from
1160 * @param int $importto The id of the course we are importing to
1161 * @param bool $deletecontent Whether to delete the course we are importing to content
1162 * @param array $options List of backup options
1163 * @return null
1164 * @since Moodle 2.4
1166 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1167 global $CFG, $USER, $DB;
1168 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1169 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1171 // Parameter validation.
1172 $params = self::validate_parameters(
1173 self::import_course_parameters(),
1174 array(
1175 'importfrom' => $importfrom,
1176 'importto' => $importto,
1177 'deletecontent' => $deletecontent,
1178 'options' => $options
1182 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1183 throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
1186 // Context validation.
1188 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1189 throw new moodle_exception('invalidcourseid', 'error');
1192 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1193 throw new moodle_exception('invalidcourseid', 'error');
1196 $importfromcontext = context_course::instance($importfrom->id);
1197 self::validate_context($importfromcontext);
1199 $importtocontext = context_course::instance($importto->id);
1200 self::validate_context($importtocontext);
1202 $backupdefaults = array(
1203 'activities' => 1,
1204 'blocks' => 1,
1205 'filters' => 1
1208 $backupsettings = array();
1210 // Check for backup and restore options.
1211 if (!empty($params['options'])) {
1212 foreach ($params['options'] as $option) {
1214 // Strict check for a correct value (allways 1 or 0, true or false).
1215 $value = clean_param($option['value'], PARAM_INT);
1217 if ($value !== 0 and $value !== 1) {
1218 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1221 if (!isset($backupdefaults[$option['name']])) {
1222 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1225 $backupsettings[$option['name']] = $value;
1229 // Capability checking.
1231 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1232 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1234 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1235 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1237 foreach ($backupsettings as $name => $value) {
1238 $bc->get_plan()->get_setting($name)->set_value($value);
1241 $backupid = $bc->get_backupid();
1242 $backupbasepath = $bc->get_plan()->get_basepath();
1244 $bc->execute_plan();
1245 $bc->destroy();
1247 // Restore the backup immediately.
1249 // Check if we must delete the contents of the destination course.
1250 if ($params['deletecontent']) {
1251 $restoretarget = backup::TARGET_EXISTING_DELETING;
1252 } else {
1253 $restoretarget = backup::TARGET_EXISTING_ADDING;
1256 $rc = new restore_controller($backupid, $importto->id,
1257 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1259 foreach ($backupsettings as $name => $value) {
1260 $rc->get_plan()->get_setting($name)->set_value($value);
1263 if (!$rc->execute_precheck()) {
1264 $precheckresults = $rc->get_precheck_results();
1265 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1266 if (empty($CFG->keeptempdirectoriesonbackup)) {
1267 fulldelete($backupbasepath);
1270 $errorinfo = '';
1272 foreach ($precheckresults['errors'] as $error) {
1273 $errorinfo .= $error;
1276 if (array_key_exists('warnings', $precheckresults)) {
1277 foreach ($precheckresults['warnings'] as $warning) {
1278 $errorinfo .= $warning;
1282 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1284 } else {
1285 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1286 restore_dbops::delete_course_content($importto->id);
1290 $rc->execute_plan();
1291 $rc->destroy();
1293 if (empty($CFG->keeptempdirectoriesonbackup)) {
1294 fulldelete($backupbasepath);
1297 return null;
1301 * Returns description of method result value
1303 * @return external_description
1304 * @since Moodle 2.4
1306 public static function import_course_returns() {
1307 return null;
1311 * Returns description of method parameters
1313 * @return external_function_parameters
1314 * @since Moodle 2.3
1316 public static function get_categories_parameters() {
1317 return new external_function_parameters(
1318 array(
1319 'criteria' => new external_multiple_structure(
1320 new external_single_structure(
1321 array(
1322 'key' => new external_value(PARAM_ALPHA,
1323 'The category column to search, expected keys (value format) are:'.
1324 '"id" (int) the category id,'.
1325 '"name" (string) the category name,'.
1326 '"parent" (int) the parent category id,'.
1327 '"idnumber" (string) category idnumber'.
1328 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1329 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1330 then the function return all categories that the user can see.'.
1331 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1332 '"theme" (string) only return the categories having this theme'.
1333 ' - user must have \'moodle/category:manage\' to search on theme'),
1334 'value' => new external_value(PARAM_RAW, 'the value to match')
1336 ), 'criteria', VALUE_DEFAULT, array()
1338 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1339 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1345 * Get categories
1347 * @param array $criteria Criteria to match the results
1348 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1349 * @return array list of categories
1350 * @since Moodle 2.3
1352 public static function get_categories($criteria = array(), $addsubcategories = true) {
1353 global $CFG, $DB;
1354 require_once($CFG->dirroot . "/course/lib.php");
1356 // Validate parameters.
1357 $params = self::validate_parameters(self::get_categories_parameters(),
1358 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1360 // Retrieve the categories.
1361 $categories = array();
1362 if (!empty($params['criteria'])) {
1364 $conditions = array();
1365 $wheres = array();
1366 foreach ($params['criteria'] as $crit) {
1367 $key = trim($crit['key']);
1369 // Trying to avoid duplicate keys.
1370 if (!isset($conditions[$key])) {
1372 $context = context_system::instance();
1373 $value = null;
1374 switch ($key) {
1375 case 'id':
1376 $value = clean_param($crit['value'], PARAM_INT);
1377 break;
1379 case 'idnumber':
1380 if (has_capability('moodle/category:manage', $context)) {
1381 $value = clean_param($crit['value'], PARAM_RAW);
1382 } else {
1383 // We must throw an exception.
1384 // Otherwise the dev client would think no idnumber exists.
1385 throw new moodle_exception('criteriaerror',
1386 'webservice', '', null,
1387 'You don\'t have the permissions to search on the "idnumber" field.');
1389 break;
1391 case 'name':
1392 $value = clean_param($crit['value'], PARAM_TEXT);
1393 break;
1395 case 'parent':
1396 $value = clean_param($crit['value'], PARAM_INT);
1397 break;
1399 case 'visible':
1400 if (has_capability('moodle/category:manage', $context)
1401 or has_capability('moodle/category:viewhiddencategories',
1402 context_system::instance())) {
1403 $value = clean_param($crit['value'], PARAM_INT);
1404 } else {
1405 throw new moodle_exception('criteriaerror',
1406 'webservice', '', null,
1407 'You don\'t have the permissions to search on the "visible" field.');
1409 break;
1411 case 'theme':
1412 if (has_capability('moodle/category:manage', $context)) {
1413 $value = clean_param($crit['value'], PARAM_THEME);
1414 } else {
1415 throw new moodle_exception('criteriaerror',
1416 'webservice', '', null,
1417 'You don\'t have the permissions to search on the "theme" field.');
1419 break;
1421 default:
1422 throw new moodle_exception('criteriaerror',
1423 'webservice', '', null,
1424 'You can not search on this criteria: ' . $key);
1427 if (isset($value)) {
1428 $conditions[$key] = $crit['value'];
1429 $wheres[] = $key . " = :" . $key;
1434 if (!empty($wheres)) {
1435 $wheres = implode(" AND ", $wheres);
1437 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1439 // Retrieve its sub subcategories (all levels).
1440 if ($categories and !empty($params['addsubcategories'])) {
1441 $newcategories = array();
1443 // Check if we required visible/theme checks.
1444 $additionalselect = '';
1445 $additionalparams = array();
1446 if (isset($conditions['visible'])) {
1447 $additionalselect .= ' AND visible = :visible';
1448 $additionalparams['visible'] = $conditions['visible'];
1450 if (isset($conditions['theme'])) {
1451 $additionalselect .= ' AND theme= :theme';
1452 $additionalparams['theme'] = $conditions['theme'];
1455 foreach ($categories as $category) {
1456 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1457 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1458 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1459 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1461 $categories = $categories + $newcategories;
1465 } else {
1466 // Retrieve all categories in the database.
1467 $categories = $DB->get_records('course_categories');
1470 // The not returned categories. key => category id, value => reason of exclusion.
1471 $excludedcats = array();
1473 // The returned categories.
1474 $categoriesinfo = array();
1476 // We need to sort the categories by path.
1477 // The parent cats need to be checked by the algo first.
1478 usort($categories, "core_course_external::compare_categories_by_path");
1480 foreach ($categories as $category) {
1482 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1483 $parents = explode('/', $category->path);
1484 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1485 foreach ($parents as $parentid) {
1486 // Note: when the parent exclusion was due to the context,
1487 // the sub category could still be returned.
1488 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1489 $excludedcats[$category->id] = 'parent';
1493 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1494 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1495 and !has_capability('moodle/category:manage', $context)) {
1496 $excludedcats[$category->id] = 'depth';
1499 // Check the user can use the category context.
1500 $context = context_coursecat::instance($category->id);
1501 try {
1502 self::validate_context($context);
1503 } catch (Exception $e) {
1504 $excludedcats[$category->id] = 'context';
1506 // If it was the requested category then throw an exception.
1507 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1508 $exceptionparam = new stdClass();
1509 $exceptionparam->message = $e->getMessage();
1510 $exceptionparam->catid = $category->id;
1511 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1515 // Return the category information.
1516 if (!isset($excludedcats[$category->id])) {
1518 // Final check to see if the category is visible to the user.
1519 if ($category->visible
1520 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1521 or has_capability('moodle/category:manage', $context)) {
1523 $categoryinfo = array();
1524 $categoryinfo['id'] = $category->id;
1525 $categoryinfo['name'] = $category->name;
1526 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1527 external_format_text($category->description, $category->descriptionformat,
1528 $context->id, 'coursecat', 'description', null);
1529 $categoryinfo['parent'] = $category->parent;
1530 $categoryinfo['sortorder'] = $category->sortorder;
1531 $categoryinfo['coursecount'] = $category->coursecount;
1532 $categoryinfo['depth'] = $category->depth;
1533 $categoryinfo['path'] = $category->path;
1535 // Some fields only returned for admin.
1536 if (has_capability('moodle/category:manage', $context)) {
1537 $categoryinfo['idnumber'] = $category->idnumber;
1538 $categoryinfo['visible'] = $category->visible;
1539 $categoryinfo['visibleold'] = $category->visibleold;
1540 $categoryinfo['timemodified'] = $category->timemodified;
1541 $categoryinfo['theme'] = $category->theme;
1544 $categoriesinfo[] = $categoryinfo;
1545 } else {
1546 $excludedcats[$category->id] = 'visibility';
1551 // Sorting the resulting array so it looks a bit better for the client developer.
1552 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1554 return $categoriesinfo;
1558 * Sort categories array by path
1559 * private function: only used by get_categories
1561 * @param array $category1
1562 * @param array $category2
1563 * @return int result of strcmp
1564 * @since Moodle 2.3
1566 private static function compare_categories_by_path($category1, $category2) {
1567 return strcmp($category1->path, $category2->path);
1571 * Sort categories array by sortorder
1572 * private function: only used by get_categories
1574 * @param array $category1
1575 * @param array $category2
1576 * @return int result of strcmp
1577 * @since Moodle 2.3
1579 private static function compare_categories_by_sortorder($category1, $category2) {
1580 return strcmp($category1['sortorder'], $category2['sortorder']);
1584 * Returns description of method result value
1586 * @return external_description
1587 * @since Moodle 2.3
1589 public static function get_categories_returns() {
1590 return new external_multiple_structure(
1591 new external_single_structure(
1592 array(
1593 'id' => new external_value(PARAM_INT, 'category id'),
1594 'name' => new external_value(PARAM_TEXT, 'category name'),
1595 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1596 'description' => new external_value(PARAM_RAW, 'category description'),
1597 'descriptionformat' => new external_format_value('description'),
1598 'parent' => new external_value(PARAM_INT, 'parent category id'),
1599 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1600 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1601 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1602 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1603 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1604 'depth' => new external_value(PARAM_INT, 'category depth'),
1605 'path' => new external_value(PARAM_TEXT, 'category path'),
1606 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1607 ), 'List of categories'
1613 * Returns description of method parameters
1615 * @return external_function_parameters
1616 * @since Moodle 2.3
1618 public static function create_categories_parameters() {
1619 return new external_function_parameters(
1620 array(
1621 'categories' => new external_multiple_structure(
1622 new external_single_structure(
1623 array(
1624 'name' => new external_value(PARAM_TEXT, 'new category name'),
1625 'parent' => new external_value(PARAM_INT,
1626 'the parent category id inside which the new category will be created
1627 - set to 0 for a root category',
1628 VALUE_DEFAULT, 0),
1629 'idnumber' => new external_value(PARAM_RAW,
1630 'the new category idnumber', VALUE_OPTIONAL),
1631 'description' => new external_value(PARAM_RAW,
1632 'the new category description', VALUE_OPTIONAL),
1633 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1634 'theme' => new external_value(PARAM_THEME,
1635 'the new category theme. This option must be enabled on moodle',
1636 VALUE_OPTIONAL),
1645 * Create categories
1647 * @param array $categories - see create_categories_parameters() for the array structure
1648 * @return array - see create_categories_returns() for the array structure
1649 * @since Moodle 2.3
1651 public static function create_categories($categories) {
1652 global $CFG, $DB;
1653 require_once($CFG->libdir . "/coursecatlib.php");
1655 $params = self::validate_parameters(self::create_categories_parameters(),
1656 array('categories' => $categories));
1658 $transaction = $DB->start_delegated_transaction();
1660 $createdcategories = array();
1661 foreach ($params['categories'] as $category) {
1662 if ($category['parent']) {
1663 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1664 throw new moodle_exception('unknowcategory');
1666 $context = context_coursecat::instance($category['parent']);
1667 } else {
1668 $context = context_system::instance();
1670 self::validate_context($context);
1671 require_capability('moodle/category:manage', $context);
1673 // this will validate format and throw an exception if there are errors
1674 external_validate_format($category['descriptionformat']);
1676 $newcategory = coursecat::create($category);
1678 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1681 $transaction->allow_commit();
1683 return $createdcategories;
1687 * Returns description of method parameters
1689 * @return external_function_parameters
1690 * @since Moodle 2.3
1692 public static function create_categories_returns() {
1693 return new external_multiple_structure(
1694 new external_single_structure(
1695 array(
1696 'id' => new external_value(PARAM_INT, 'new category id'),
1697 'name' => new external_value(PARAM_TEXT, 'new category name'),
1704 * Returns description of method parameters
1706 * @return external_function_parameters
1707 * @since Moodle 2.3
1709 public static function update_categories_parameters() {
1710 return new external_function_parameters(
1711 array(
1712 'categories' => new external_multiple_structure(
1713 new external_single_structure(
1714 array(
1715 'id' => new external_value(PARAM_INT, 'course id'),
1716 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1717 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1718 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1719 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1720 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1721 'theme' => new external_value(PARAM_THEME,
1722 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1731 * Update categories
1733 * @param array $categories The list of categories to update
1734 * @return null
1735 * @since Moodle 2.3
1737 public static function update_categories($categories) {
1738 global $CFG, $DB;
1739 require_once($CFG->libdir . "/coursecatlib.php");
1741 // Validate parameters.
1742 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1744 $transaction = $DB->start_delegated_transaction();
1746 foreach ($params['categories'] as $cat) {
1747 $category = coursecat::get($cat['id']);
1749 $categorycontext = context_coursecat::instance($cat['id']);
1750 self::validate_context($categorycontext);
1751 require_capability('moodle/category:manage', $categorycontext);
1753 // this will throw an exception if descriptionformat is not valid
1754 external_validate_format($cat['descriptionformat']);
1756 $category->update($cat);
1759 $transaction->allow_commit();
1763 * Returns description of method result value
1765 * @return external_description
1766 * @since Moodle 2.3
1768 public static function update_categories_returns() {
1769 return null;
1773 * Returns description of method parameters
1775 * @return external_function_parameters
1776 * @since Moodle 2.3
1778 public static function delete_categories_parameters() {
1779 return new external_function_parameters(
1780 array(
1781 'categories' => new external_multiple_structure(
1782 new external_single_structure(
1783 array(
1784 'id' => new external_value(PARAM_INT, 'category id to delete'),
1785 'newparent' => new external_value(PARAM_INT,
1786 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1787 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1788 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1797 * Delete categories
1799 * @param array $categories A list of category ids
1800 * @return array
1801 * @since Moodle 2.3
1803 public static function delete_categories($categories) {
1804 global $CFG, $DB;
1805 require_once($CFG->dirroot . "/course/lib.php");
1806 require_once($CFG->libdir . "/coursecatlib.php");
1808 // Validate parameters.
1809 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1811 $transaction = $DB->start_delegated_transaction();
1813 foreach ($params['categories'] as $category) {
1814 $deletecat = coursecat::get($category['id'], MUST_EXIST);
1815 $context = context_coursecat::instance($deletecat->id);
1816 require_capability('moodle/category:manage', $context);
1817 self::validate_context($context);
1818 self::validate_context(get_category_or_system_context($deletecat->parent));
1820 if ($category['recursive']) {
1821 // If recursive was specified, then we recursively delete the category's contents.
1822 if ($deletecat->can_delete_full()) {
1823 $deletecat->delete_full(false);
1824 } else {
1825 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1827 } else {
1828 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1829 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1830 // We must move to an existing category.
1831 if (!empty($category['newparent'])) {
1832 $newparentcat = coursecat::get($category['newparent']);
1833 } else {
1834 $newparentcat = coursecat::get($deletecat->parent);
1837 // This operation is not allowed. We must move contents to an existing category.
1838 if (!$newparentcat->id) {
1839 throw new moodle_exception('movecatcontentstoroot');
1842 self::validate_context(context_coursecat::instance($newparentcat->id));
1843 if ($deletecat->can_move_content_to($newparentcat->id)) {
1844 $deletecat->delete_move($newparentcat->id, false);
1845 } else {
1846 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1851 $transaction->allow_commit();
1855 * Returns description of method parameters
1857 * @return external_function_parameters
1858 * @since Moodle 2.3
1860 public static function delete_categories_returns() {
1861 return null;
1865 * Describes the parameters for delete_modules.
1867 * @return external_external_function_parameters
1868 * @since Moodle 2.5
1870 public static function delete_modules_parameters() {
1871 return new external_function_parameters (
1872 array(
1873 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1874 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
1880 * Deletes a list of provided module instances.
1882 * @param array $cmids the course module ids
1883 * @since Moodle 2.5
1885 public static function delete_modules($cmids) {
1886 global $CFG, $DB;
1888 // Require course file containing the course delete module function.
1889 require_once($CFG->dirroot . "/course/lib.php");
1891 // Clean the parameters.
1892 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
1894 // Keep track of the course ids we have performed a capability check on to avoid repeating.
1895 $arrcourseschecked = array();
1897 foreach ($params['cmids'] as $cmid) {
1898 // Get the course module.
1899 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
1901 // Check if we have not yet confirmed they have permission in this course.
1902 if (!in_array($cm->course, $arrcourseschecked)) {
1903 // Ensure the current user has required permission in this course.
1904 $context = context_course::instance($cm->course);
1905 self::validate_context($context);
1906 // Add to the array.
1907 $arrcourseschecked[] = $cm->course;
1910 // Ensure they can delete this module.
1911 $modcontext = context_module::instance($cm->id);
1912 require_capability('moodle/course:manageactivities', $modcontext);
1914 // Delete the module.
1915 course_delete_module($cm->id);
1920 * Describes the delete_modules return value.
1922 * @return external_single_structure
1923 * @since Moodle 2.5
1925 public static function delete_modules_returns() {
1926 return null;
1931 * Deprecated course external functions
1933 * @package core_course
1934 * @copyright 2009 Petr Skodak
1935 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1936 * @since Moodle 2.0
1937 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1938 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1939 * @see core_course_external
1941 class moodle_course_external extends external_api {
1944 * Returns description of method parameters
1946 * @return external_function_parameters
1947 * @since Moodle 2.0
1948 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1949 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1950 * @see core_course_external::get_courses_parameters()
1952 public static function get_courses_parameters() {
1953 return core_course_external::get_courses_parameters();
1957 * Get courses
1959 * @param array $options
1960 * @return array
1961 * @since Moodle 2.0
1962 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1963 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1964 * @see core_course_external::get_courses()
1966 public static function get_courses($options) {
1967 return core_course_external::get_courses($options);
1971 * Returns description of method result value
1973 * @return external_description
1974 * @since Moodle 2.0
1975 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1976 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1977 * @see core_course_external::get_courses_returns()
1979 public static function get_courses_returns() {
1980 return core_course_external::get_courses_returns();
1984 * Returns description of method parameters
1986 * @return external_function_parameters
1987 * @since Moodle 2.0
1988 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1989 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1990 * @see core_course_external::create_courses_parameters()
1992 public static function create_courses_parameters() {
1993 return core_course_external::create_courses_parameters();
1997 * Create courses
1999 * @param array $courses
2000 * @return array courses (id and shortname only)
2001 * @since Moodle 2.0
2002 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2003 * @todo MDL-31194 This will be deleted in Moodle 2.5.
2004 * @see core_course_external::create_courses()
2006 public static function create_courses($courses) {
2007 return core_course_external::create_courses($courses);
2011 * Returns description of method result value
2013 * @return external_description
2014 * @since Moodle 2.0
2015 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2016 * @todo MDL-31194 This will be deleted in Moodle 2.5.
2017 * @see core_course_external::create_courses_returns()
2019 public static function create_courses_returns() {
2020 return core_course_external::create_courses_returns();