Merge branch 'MDL-49036-master' of git://github.com/jleyva/moodle
[moodle.git] / course / externallib.php
blobff3dbfa42f1933228c6e7f3882eadff3b2dc3f73
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 'instance' => new external_value(PARAM_INT, 'instance id', VALUE_OPTIONAL),
221 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
222 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
223 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
224 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
225 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
226 'availability' => new external_value(PARAM_RAW, 'module availability settings', VALUE_OPTIONAL),
227 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
228 'contents' => new external_multiple_structure(
229 new external_single_structure(
230 array(
231 // content info
232 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
233 'filename'=> new external_value(PARAM_FILE, 'filename'),
234 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
235 'filesize'=> new external_value(PARAM_INT, 'filesize'),
236 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
237 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
238 'timecreated' => new external_value(PARAM_INT, 'Time created'),
239 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
240 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
242 // copyright related info
243 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
244 'author' => new external_value(PARAM_TEXT, 'Content owner'),
245 'license' => new external_value(PARAM_TEXT, 'Content license'),
247 ), VALUE_DEFAULT, array()
250 ), 'list of module'
258 * Returns description of method parameters
260 * @return external_function_parameters
261 * @since Moodle 2.3
263 public static function get_courses_parameters() {
264 return new external_function_parameters(
265 array('options' => new external_single_structure(
266 array('ids' => new external_multiple_structure(
267 new external_value(PARAM_INT, 'Course id')
268 , 'List of course id. If empty return all courses
269 except front page course.',
270 VALUE_OPTIONAL)
271 ), 'options - operator OR is used', VALUE_DEFAULT, array())
277 * Get courses
279 * @param array $options It contains an array (list of ids)
280 * @return array
281 * @since Moodle 2.2
283 public static function get_courses($options = array()) {
284 global $CFG, $DB;
285 require_once($CFG->dirroot . "/course/lib.php");
287 //validate parameter
288 $params = self::validate_parameters(self::get_courses_parameters(),
289 array('options' => $options));
291 //retrieve courses
292 if (!array_key_exists('ids', $params['options'])
293 or empty($params['options']['ids'])) {
294 $courses = $DB->get_records('course');
295 } else {
296 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
299 //create return value
300 $coursesinfo = array();
301 foreach ($courses as $course) {
303 // now security checks
304 $context = context_course::instance($course->id, IGNORE_MISSING);
305 $courseformatoptions = course_get_format($course)->get_format_options();
306 try {
307 self::validate_context($context);
308 } catch (Exception $e) {
309 $exceptionparam = new stdClass();
310 $exceptionparam->message = $e->getMessage();
311 $exceptionparam->courseid = $course->id;
312 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
314 require_capability('moodle/course:view', $context);
316 $courseinfo = array();
317 $courseinfo['id'] = $course->id;
318 $courseinfo['fullname'] = $course->fullname;
319 $courseinfo['shortname'] = $course->shortname;
320 $courseinfo['categoryid'] = $course->category;
321 list($courseinfo['summary'], $courseinfo['summaryformat']) =
322 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
323 $courseinfo['format'] = $course->format;
324 $courseinfo['startdate'] = $course->startdate;
325 if (array_key_exists('numsections', $courseformatoptions)) {
326 // For backward-compartibility
327 $courseinfo['numsections'] = $courseformatoptions['numsections'];
330 //some field should be returned only if the user has update permission
331 $courseadmin = has_capability('moodle/course:update', $context);
332 if ($courseadmin) {
333 $courseinfo['categorysortorder'] = $course->sortorder;
334 $courseinfo['idnumber'] = $course->idnumber;
335 $courseinfo['showgrades'] = $course->showgrades;
336 $courseinfo['showreports'] = $course->showreports;
337 $courseinfo['newsitems'] = $course->newsitems;
338 $courseinfo['visible'] = $course->visible;
339 $courseinfo['maxbytes'] = $course->maxbytes;
340 if (array_key_exists('hiddensections', $courseformatoptions)) {
341 // For backward-compartibility
342 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
344 $courseinfo['groupmode'] = $course->groupmode;
345 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
346 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
347 $courseinfo['lang'] = $course->lang;
348 $courseinfo['timecreated'] = $course->timecreated;
349 $courseinfo['timemodified'] = $course->timemodified;
350 $courseinfo['forcetheme'] = $course->theme;
351 $courseinfo['enablecompletion'] = $course->enablecompletion;
352 $courseinfo['completionnotify'] = $course->completionnotify;
353 $courseinfo['courseformatoptions'] = array();
354 foreach ($courseformatoptions as $key => $value) {
355 $courseinfo['courseformatoptions'][] = array(
356 'name' => $key,
357 'value' => $value
362 if ($courseadmin or $course->visible
363 or has_capability('moodle/course:viewhiddencourses', $context)) {
364 $coursesinfo[] = $courseinfo;
368 return $coursesinfo;
372 * Returns description of method result value
374 * @return external_description
375 * @since Moodle 2.2
377 public static function get_courses_returns() {
378 return new external_multiple_structure(
379 new external_single_structure(
380 array(
381 'id' => new external_value(PARAM_INT, 'course id'),
382 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
383 'categoryid' => new external_value(PARAM_INT, 'category id'),
384 'categorysortorder' => new external_value(PARAM_INT,
385 'sort order into the category', VALUE_OPTIONAL),
386 'fullname' => new external_value(PARAM_TEXT, 'full name'),
387 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
388 'summary' => new external_value(PARAM_RAW, 'summary'),
389 'summaryformat' => new external_format_value('summary'),
390 'format' => new external_value(PARAM_PLUGIN,
391 'course format: weeks, topics, social, site,..'),
392 'showgrades' => new external_value(PARAM_INT,
393 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
394 'newsitems' => new external_value(PARAM_INT,
395 'number of recent items appearing on the course page', VALUE_OPTIONAL),
396 'startdate' => new external_value(PARAM_INT,
397 'timestamp when the course start'),
398 'numsections' => new external_value(PARAM_INT,
399 '(deprecated, use courseformatoptions) number of weeks/topics',
400 VALUE_OPTIONAL),
401 'maxbytes' => new external_value(PARAM_INT,
402 'largest size of file that can be uploaded into the course',
403 VALUE_OPTIONAL),
404 'showreports' => new external_value(PARAM_INT,
405 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
406 'visible' => new external_value(PARAM_INT,
407 '1: available to student, 0:not available', VALUE_OPTIONAL),
408 'hiddensections' => new external_value(PARAM_INT,
409 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
410 VALUE_OPTIONAL),
411 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
412 VALUE_OPTIONAL),
413 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
414 VALUE_OPTIONAL),
415 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
416 VALUE_OPTIONAL),
417 'timecreated' => new external_value(PARAM_INT,
418 'timestamp when the course have been created', VALUE_OPTIONAL),
419 'timemodified' => new external_value(PARAM_INT,
420 'timestamp when the course have been modified', VALUE_OPTIONAL),
421 'enablecompletion' => new external_value(PARAM_INT,
422 'Enabled, control via completion and activity settings. Disbaled,
423 not shown in activity settings.',
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 'completionnotify' => new external_value(PARAM_INT,
498 '1: yes 0: no', VALUE_OPTIONAL),
499 'lang' => new external_value(PARAM_SAFEDIR,
500 'forced course language', VALUE_OPTIONAL),
501 'forcetheme' => new external_value(PARAM_PLUGIN,
502 'name of the force theme', VALUE_OPTIONAL),
503 'courseformatoptions' => new external_multiple_structure(
504 new external_single_structure(
505 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
506 'value' => new external_value(PARAM_RAW, 'course format option value')
508 'additional options for particular course format', VALUE_OPTIONAL),
510 ), 'courses to create'
517 * Create courses
519 * @param array $courses
520 * @return array courses (id and shortname only)
521 * @since Moodle 2.2
523 public static function create_courses($courses) {
524 global $CFG, $DB;
525 require_once($CFG->dirroot . "/course/lib.php");
526 require_once($CFG->libdir . '/completionlib.php');
528 $params = self::validate_parameters(self::create_courses_parameters(),
529 array('courses' => $courses));
531 $availablethemes = core_component::get_plugin_list('theme');
532 $availablelangs = get_string_manager()->get_list_of_translations();
534 $transaction = $DB->start_delegated_transaction();
536 foreach ($params['courses'] as $course) {
538 // Ensure the current user is allowed to run this function
539 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
540 try {
541 self::validate_context($context);
542 } catch (Exception $e) {
543 $exceptionparam = new stdClass();
544 $exceptionparam->message = $e->getMessage();
545 $exceptionparam->catid = $course['categoryid'];
546 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
548 require_capability('moodle/course:create', $context);
550 // Make sure lang is valid
551 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
552 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
555 // Make sure theme is valid
556 if (array_key_exists('forcetheme', $course)) {
557 if (!empty($CFG->allowcoursethemes)) {
558 if (empty($availablethemes[$course['forcetheme']])) {
559 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
560 } else {
561 $course['theme'] = $course['forcetheme'];
566 //force visibility if ws user doesn't have the permission to set it
567 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
568 if (!has_capability('moodle/course:visibility', $context)) {
569 $course['visible'] = $category->visible;
572 //set default value for completion
573 $courseconfig = get_config('moodlecourse');
574 if (completion_info::is_enabled_for_site()) {
575 if (!array_key_exists('enablecompletion', $course)) {
576 $course['enablecompletion'] = $courseconfig->enablecompletion;
578 } else {
579 $course['enablecompletion'] = 0;
582 $course['category'] = $course['categoryid'];
584 // Summary format.
585 $course['summaryformat'] = external_validate_format($course['summaryformat']);
587 if (!empty($course['courseformatoptions'])) {
588 foreach ($course['courseformatoptions'] as $option) {
589 $course[$option['name']] = $option['value'];
593 //Note: create_course() core function check shortname, idnumber, category
594 $course['id'] = create_course((object) $course)->id;
596 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
599 $transaction->allow_commit();
601 return $resultcourses;
605 * Returns description of method result value
607 * @return external_description
608 * @since Moodle 2.2
610 public static function create_courses_returns() {
611 return new external_multiple_structure(
612 new external_single_structure(
613 array(
614 'id' => new external_value(PARAM_INT, 'course id'),
615 'shortname' => new external_value(PARAM_TEXT, 'short name'),
622 * Update courses
624 * @return external_function_parameters
625 * @since Moodle 2.5
627 public static function update_courses_parameters() {
628 return new external_function_parameters(
629 array(
630 'courses' => new external_multiple_structure(
631 new external_single_structure(
632 array(
633 'id' => new external_value(PARAM_INT, 'ID of the course'),
634 'fullname' => new external_value(PARAM_TEXT, 'full name', VALUE_OPTIONAL),
635 'shortname' => new external_value(PARAM_TEXT, 'course short name', VALUE_OPTIONAL),
636 'categoryid' => new external_value(PARAM_INT, 'category id', VALUE_OPTIONAL),
637 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
638 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
639 'summaryformat' => new external_format_value('summary', VALUE_OPTIONAL),
640 'format' => new external_value(PARAM_PLUGIN,
641 'course format: weeks, topics, social, site,..', VALUE_OPTIONAL),
642 'showgrades' => new external_value(PARAM_INT,
643 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
644 'newsitems' => new external_value(PARAM_INT,
645 'number of recent items appearing on the course page', VALUE_OPTIONAL),
646 'startdate' => new external_value(PARAM_INT,
647 'timestamp when the course start', VALUE_OPTIONAL),
648 'numsections' => new external_value(PARAM_INT,
649 '(deprecated, use courseformatoptions) number of weeks/topics', VALUE_OPTIONAL),
650 'maxbytes' => new external_value(PARAM_INT,
651 'largest size of file that can be uploaded into the course', VALUE_OPTIONAL),
652 'showreports' => new external_value(PARAM_INT,
653 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
654 'visible' => new external_value(PARAM_INT,
655 '1: available to student, 0:not available', VALUE_OPTIONAL),
656 'hiddensections' => new external_value(PARAM_INT,
657 '(deprecated, use courseformatoptions) How the hidden sections in the course are
658 displayed to students', VALUE_OPTIONAL),
659 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible', VALUE_OPTIONAL),
660 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no', VALUE_OPTIONAL),
661 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id', VALUE_OPTIONAL),
662 'enablecompletion' => new external_value(PARAM_INT,
663 'Enabled, control via completion and activity settings. Disabled,
664 not shown in activity settings.', VALUE_OPTIONAL),
665 'completionnotify' => new external_value(PARAM_INT, '1: yes 0: no', VALUE_OPTIONAL),
666 'lang' => new external_value(PARAM_SAFEDIR, 'forced course language', VALUE_OPTIONAL),
667 'forcetheme' => new external_value(PARAM_PLUGIN, 'name of the force theme', VALUE_OPTIONAL),
668 'courseformatoptions' => new external_multiple_structure(
669 new external_single_structure(
670 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
671 'value' => new external_value(PARAM_RAW, 'course format option value')
673 'additional options for particular course format', VALUE_OPTIONAL),
675 ), 'courses to update'
682 * Update courses
684 * @param array $courses
685 * @since Moodle 2.5
687 public static function update_courses($courses) {
688 global $CFG, $DB;
689 require_once($CFG->dirroot . "/course/lib.php");
690 $warnings = array();
692 $params = self::validate_parameters(self::update_courses_parameters(),
693 array('courses' => $courses));
695 $availablethemes = core_component::get_plugin_list('theme');
696 $availablelangs = get_string_manager()->get_list_of_translations();
698 foreach ($params['courses'] as $course) {
699 // Catch any exception while updating course and return as warning to user.
700 try {
701 // Ensure the current user is allowed to run this function.
702 $context = context_course::instance($course['id'], MUST_EXIST);
703 self::validate_context($context);
705 $oldcourse = course_get_format($course['id'])->get_course();
707 require_capability('moodle/course:update', $context);
709 // Check if user can change category.
710 if (array_key_exists('categoryid', $course) && ($oldcourse->category != $course['categoryid'])) {
711 require_capability('moodle/course:changecategory', $context);
712 $course['category'] = $course['categoryid'];
715 // Check if the user can change fullname.
716 if (array_key_exists('fullname', $course) && ($oldcourse->fullname != $course['fullname'])) {
717 require_capability('moodle/course:changefullname', $context);
720 // Check if the user can change shortname.
721 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
722 require_capability('moodle/course:changeshortname', $context);
725 // Check if the user can change the idnumber.
726 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
727 require_capability('moodle/course:changeidnumber', $context);
730 // Check if user can change summary.
731 if (array_key_exists('summary', $course) && ($oldcourse->summary != $course['summary'])) {
732 require_capability('moodle/course:changesummary', $context);
735 // Summary format.
736 if (array_key_exists('summaryformat', $course) && ($oldcourse->summaryformat != $course['summaryformat'])) {
737 require_capability('moodle/course:changesummary', $context);
738 $course['summaryformat'] = external_validate_format($course['summaryformat']);
741 // Check if user can change visibility.
742 if (array_key_exists('visible', $course) && ($oldcourse->visible != $course['visible'])) {
743 require_capability('moodle/course:visibility', $context);
746 // Make sure lang is valid.
747 if (array_key_exists('lang', $course) && empty($availablelangs[$course['lang']])) {
748 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
751 // Make sure theme is valid.
752 if (array_key_exists('forcetheme', $course)) {
753 if (!empty($CFG->allowcoursethemes)) {
754 if (empty($availablethemes[$course['forcetheme']])) {
755 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
756 } else {
757 $course['theme'] = $course['forcetheme'];
762 // Make sure completion is enabled before setting it.
763 if (array_key_exists('enabledcompletion', $course) && !completion_info::is_enabled_for_site()) {
764 $course['enabledcompletion'] = 0;
767 // Make sure maxbytes are less then CFG->maxbytes.
768 if (array_key_exists('maxbytes', $course)) {
769 $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
772 if (!empty($course['courseformatoptions'])) {
773 foreach ($course['courseformatoptions'] as $option) {
774 if (isset($option['name']) && isset($option['value'])) {
775 $course[$option['name']] = $option['value'];
780 // Update course if user has all required capabilities.
781 update_course((object) $course);
782 } catch (Exception $e) {
783 $warning = array();
784 $warning['item'] = 'course';
785 $warning['itemid'] = $course['id'];
786 if ($e instanceof moodle_exception) {
787 $warning['warningcode'] = $e->errorcode;
788 } else {
789 $warning['warningcode'] = $e->getCode();
791 $warning['message'] = $e->getMessage();
792 $warnings[] = $warning;
796 $result = array();
797 $result['warnings'] = $warnings;
798 return $result;
802 * Returns description of method result value
804 * @return external_description
805 * @since Moodle 2.5
807 public static function update_courses_returns() {
808 return new external_single_structure(
809 array(
810 'warnings' => new external_warnings()
816 * Returns description of method parameters
818 * @return external_function_parameters
819 * @since Moodle 2.2
821 public static function delete_courses_parameters() {
822 return new external_function_parameters(
823 array(
824 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
830 * Delete courses
832 * @param array $courseids A list of course ids
833 * @since Moodle 2.2
835 public static function delete_courses($courseids) {
836 global $CFG, $DB;
837 require_once($CFG->dirroot."/course/lib.php");
839 // Parameter validation.
840 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
842 $warnings = array();
844 foreach ($params['courseids'] as $courseid) {
845 $course = $DB->get_record('course', array('id' => $courseid));
847 if ($course === false) {
848 $warnings[] = array(
849 'item' => 'course',
850 'itemid' => $courseid,
851 'warningcode' => 'unknowncourseidnumber',
852 'message' => 'Unknown course ID ' . $courseid
854 continue;
857 // Check if the context is valid.
858 $coursecontext = context_course::instance($course->id);
859 self::validate_context($coursecontext);
861 // Check if the current user has permission.
862 if (!can_delete_course($courseid)) {
863 $warnings[] = array(
864 'item' => 'course',
865 'itemid' => $courseid,
866 'warningcode' => 'cannotdeletecourse',
867 'message' => 'You do not have the permission to delete this course' . $courseid
869 continue;
872 if (delete_course($course, false) === false) {
873 $warnings[] = array(
874 'item' => 'course',
875 'itemid' => $courseid,
876 'warningcode' => 'cannotdeletecategorycourse',
877 'message' => 'Course ' . $courseid . ' failed to be deleted'
879 continue;
883 fix_course_sortorder();
885 return array('warnings' => $warnings);
889 * Returns description of method result value
891 * @return external_description
892 * @since Moodle 2.2
894 public static function delete_courses_returns() {
895 return new external_single_structure(
896 array(
897 'warnings' => new external_warnings()
903 * Returns description of method parameters
905 * @return external_function_parameters
906 * @since Moodle 2.3
908 public static function duplicate_course_parameters() {
909 return new external_function_parameters(
910 array(
911 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
912 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
913 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
914 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
915 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
916 'options' => new external_multiple_structure(
917 new external_single_structure(
918 array(
919 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
920 "activities" (int) Include course activites (default to 1 that is equal to yes),
921 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
922 "filters" (int) Include course filters (default to 1 that is equal to yes),
923 "users" (int) Include users (default to 0 that is equal to no),
924 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
925 "comments" (int) Include user comments (default to 0 that is equal to no),
926 "userscompletion" (int) Include user course completion information (default to 0 that is equal to no),
927 "logs" (int) Include course logs (default to 0 that is equal to no),
928 "grade_histories" (int) Include histories (default to 0 that is equal to no)'
930 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
933 ), VALUE_DEFAULT, array()
940 * Duplicate a course
942 * @param int $courseid
943 * @param string $fullname Duplicated course fullname
944 * @param string $shortname Duplicated course shortname
945 * @param int $categoryid Duplicated course parent category id
946 * @param int $visible Duplicated course availability
947 * @param array $options List of backup options
948 * @return array New course info
949 * @since Moodle 2.3
951 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
952 global $CFG, $USER, $DB;
953 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
954 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
956 // Parameter validation.
957 $params = self::validate_parameters(
958 self::duplicate_course_parameters(),
959 array(
960 'courseid' => $courseid,
961 'fullname' => $fullname,
962 'shortname' => $shortname,
963 'categoryid' => $categoryid,
964 'visible' => $visible,
965 'options' => $options
969 // Context validation.
971 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
972 throw new moodle_exception('invalidcourseid', 'error');
975 // Category where duplicated course is going to be created.
976 $categorycontext = context_coursecat::instance($params['categoryid']);
977 self::validate_context($categorycontext);
979 // Course to be duplicated.
980 $coursecontext = context_course::instance($course->id);
981 self::validate_context($coursecontext);
983 $backupdefaults = array(
984 'activities' => 1,
985 'blocks' => 1,
986 'filters' => 1,
987 'users' => 0,
988 'role_assignments' => 0,
989 'comments' => 0,
990 'userscompletion' => 0,
991 'logs' => 0,
992 'grade_histories' => 0
995 $backupsettings = array();
996 // Check for backup and restore options.
997 if (!empty($params['options'])) {
998 foreach ($params['options'] as $option) {
1000 // Strict check for a correct value (allways 1 or 0, true or false).
1001 $value = clean_param($option['value'], PARAM_INT);
1003 if ($value !== 0 and $value !== 1) {
1004 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1007 if (!isset($backupdefaults[$option['name']])) {
1008 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1011 $backupsettings[$option['name']] = $value;
1015 // Capability checking.
1017 // The backup controller check for this currently, this may be redundant.
1018 require_capability('moodle/course:create', $categorycontext);
1019 require_capability('moodle/restore:restorecourse', $categorycontext);
1020 require_capability('moodle/backup:backupcourse', $coursecontext);
1022 if (!empty($backupsettings['users'])) {
1023 require_capability('moodle/backup:userinfo', $coursecontext);
1024 require_capability('moodle/restore:userinfo', $categorycontext);
1027 // Check if the shortname is used.
1028 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
1029 foreach ($foundcourses as $foundcourse) {
1030 $foundcoursenames[] = $foundcourse->fullname;
1033 $foundcoursenamestring = implode(',', $foundcoursenames);
1034 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
1037 // Backup the course.
1039 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
1040 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
1042 foreach ($backupsettings as $name => $value) {
1043 $bc->get_plan()->get_setting($name)->set_value($value);
1046 $backupid = $bc->get_backupid();
1047 $backupbasepath = $bc->get_plan()->get_basepath();
1049 $bc->execute_plan();
1050 $results = $bc->get_results();
1051 $file = $results['backup_destination'];
1053 $bc->destroy();
1055 // Restore the backup immediately.
1057 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
1058 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
1059 $file->extract_to_pathname(get_file_packer('application/vnd.moodle.backup'), $backupbasepath);
1062 // Create new course.
1063 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
1065 $rc = new restore_controller($backupid, $newcourseid,
1066 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
1068 foreach ($backupsettings as $name => $value) {
1069 $setting = $rc->get_plan()->get_setting($name);
1070 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
1071 $setting->set_value($value);
1075 if (!$rc->execute_precheck()) {
1076 $precheckresults = $rc->get_precheck_results();
1077 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1078 if (empty($CFG->keeptempdirectoriesonbackup)) {
1079 fulldelete($backupbasepath);
1082 $errorinfo = '';
1084 foreach ($precheckresults['errors'] as $error) {
1085 $errorinfo .= $error;
1088 if (array_key_exists('warnings', $precheckresults)) {
1089 foreach ($precheckresults['warnings'] as $warning) {
1090 $errorinfo .= $warning;
1094 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1098 $rc->execute_plan();
1099 $rc->destroy();
1101 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
1102 $course->fullname = $params['fullname'];
1103 $course->shortname = $params['shortname'];
1104 $course->visible = $params['visible'];
1106 // Set shortname and fullname back.
1107 $DB->update_record('course', $course);
1109 if (empty($CFG->keeptempdirectoriesonbackup)) {
1110 fulldelete($backupbasepath);
1113 // Delete the course backup file created by this WebService. Originally located in the course backups area.
1114 $file->delete();
1116 return array('id' => $course->id, 'shortname' => $course->shortname);
1120 * Returns description of method result value
1122 * @return external_description
1123 * @since Moodle 2.3
1125 public static function duplicate_course_returns() {
1126 return new external_single_structure(
1127 array(
1128 'id' => new external_value(PARAM_INT, 'course id'),
1129 'shortname' => new external_value(PARAM_TEXT, 'short name'),
1135 * Returns description of method parameters for import_course
1137 * @return external_function_parameters
1138 * @since Moodle 2.4
1140 public static function import_course_parameters() {
1141 return new external_function_parameters(
1142 array(
1143 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
1144 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
1145 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
1146 'options' => new external_multiple_structure(
1147 new external_single_structure(
1148 array(
1149 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
1150 "activities" (int) Include course activites (default to 1 that is equal to yes),
1151 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
1152 "filters" (int) Include course filters (default to 1 that is equal to yes)'
1154 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
1157 ), VALUE_DEFAULT, array()
1164 * Imports a course
1166 * @param int $importfrom The id of the course we are importing from
1167 * @param int $importto The id of the course we are importing to
1168 * @param bool $deletecontent Whether to delete the course we are importing to content
1169 * @param array $options List of backup options
1170 * @return null
1171 * @since Moodle 2.4
1173 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
1174 global $CFG, $USER, $DB;
1175 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
1176 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
1178 // Parameter validation.
1179 $params = self::validate_parameters(
1180 self::import_course_parameters(),
1181 array(
1182 'importfrom' => $importfrom,
1183 'importto' => $importto,
1184 'deletecontent' => $deletecontent,
1185 'options' => $options
1189 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
1190 throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']);
1193 // Context validation.
1195 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
1196 throw new moodle_exception('invalidcourseid', 'error');
1199 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
1200 throw new moodle_exception('invalidcourseid', 'error');
1203 $importfromcontext = context_course::instance($importfrom->id);
1204 self::validate_context($importfromcontext);
1206 $importtocontext = context_course::instance($importto->id);
1207 self::validate_context($importtocontext);
1209 $backupdefaults = array(
1210 'activities' => 1,
1211 'blocks' => 1,
1212 'filters' => 1
1215 $backupsettings = array();
1217 // Check for backup and restore options.
1218 if (!empty($params['options'])) {
1219 foreach ($params['options'] as $option) {
1221 // Strict check for a correct value (allways 1 or 0, true or false).
1222 $value = clean_param($option['value'], PARAM_INT);
1224 if ($value !== 0 and $value !== 1) {
1225 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1228 if (!isset($backupdefaults[$option['name']])) {
1229 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1232 $backupsettings[$option['name']] = $value;
1236 // Capability checking.
1238 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1239 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1241 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1242 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1244 foreach ($backupsettings as $name => $value) {
1245 $bc->get_plan()->get_setting($name)->set_value($value);
1248 $backupid = $bc->get_backupid();
1249 $backupbasepath = $bc->get_plan()->get_basepath();
1251 $bc->execute_plan();
1252 $bc->destroy();
1254 // Restore the backup immediately.
1256 // Check if we must delete the contents of the destination course.
1257 if ($params['deletecontent']) {
1258 $restoretarget = backup::TARGET_EXISTING_DELETING;
1259 } else {
1260 $restoretarget = backup::TARGET_EXISTING_ADDING;
1263 $rc = new restore_controller($backupid, $importto->id,
1264 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1266 foreach ($backupsettings as $name => $value) {
1267 $rc->get_plan()->get_setting($name)->set_value($value);
1270 if (!$rc->execute_precheck()) {
1271 $precheckresults = $rc->get_precheck_results();
1272 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1273 if (empty($CFG->keeptempdirectoriesonbackup)) {
1274 fulldelete($backupbasepath);
1277 $errorinfo = '';
1279 foreach ($precheckresults['errors'] as $error) {
1280 $errorinfo .= $error;
1283 if (array_key_exists('warnings', $precheckresults)) {
1284 foreach ($precheckresults['warnings'] as $warning) {
1285 $errorinfo .= $warning;
1289 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1291 } else {
1292 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1293 restore_dbops::delete_course_content($importto->id);
1297 $rc->execute_plan();
1298 $rc->destroy();
1300 if (empty($CFG->keeptempdirectoriesonbackup)) {
1301 fulldelete($backupbasepath);
1304 return null;
1308 * Returns description of method result value
1310 * @return external_description
1311 * @since Moodle 2.4
1313 public static function import_course_returns() {
1314 return null;
1318 * Returns description of method parameters
1320 * @return external_function_parameters
1321 * @since Moodle 2.3
1323 public static function get_categories_parameters() {
1324 return new external_function_parameters(
1325 array(
1326 'criteria' => new external_multiple_structure(
1327 new external_single_structure(
1328 array(
1329 'key' => new external_value(PARAM_ALPHA,
1330 'The category column to search, expected keys (value format) are:'.
1331 '"id" (int) the category id,'.
1332 '"name" (string) the category name,'.
1333 '"parent" (int) the parent category id,'.
1334 '"idnumber" (string) category idnumber'.
1335 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1336 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1337 then the function return all categories that the user can see.'.
1338 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1339 '"theme" (string) only return the categories having this theme'.
1340 ' - user must have \'moodle/category:manage\' to search on theme'),
1341 'value' => new external_value(PARAM_RAW, 'the value to match')
1343 ), 'criteria', VALUE_DEFAULT, array()
1345 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1346 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1352 * Get categories
1354 * @param array $criteria Criteria to match the results
1355 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1356 * @return array list of categories
1357 * @since Moodle 2.3
1359 public static function get_categories($criteria = array(), $addsubcategories = true) {
1360 global $CFG, $DB;
1361 require_once($CFG->dirroot . "/course/lib.php");
1363 // Validate parameters.
1364 $params = self::validate_parameters(self::get_categories_parameters(),
1365 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1367 // Retrieve the categories.
1368 $categories = array();
1369 if (!empty($params['criteria'])) {
1371 $conditions = array();
1372 $wheres = array();
1373 foreach ($params['criteria'] as $crit) {
1374 $key = trim($crit['key']);
1376 // Trying to avoid duplicate keys.
1377 if (!isset($conditions[$key])) {
1379 $context = context_system::instance();
1380 $value = null;
1381 switch ($key) {
1382 case 'id':
1383 $value = clean_param($crit['value'], PARAM_INT);
1384 break;
1386 case 'idnumber':
1387 if (has_capability('moodle/category:manage', $context)) {
1388 $value = clean_param($crit['value'], PARAM_RAW);
1389 } else {
1390 // We must throw an exception.
1391 // Otherwise the dev client would think no idnumber exists.
1392 throw new moodle_exception('criteriaerror',
1393 'webservice', '', null,
1394 'You don\'t have the permissions to search on the "idnumber" field.');
1396 break;
1398 case 'name':
1399 $value = clean_param($crit['value'], PARAM_TEXT);
1400 break;
1402 case 'parent':
1403 $value = clean_param($crit['value'], PARAM_INT);
1404 break;
1406 case 'visible':
1407 if (has_capability('moodle/category:manage', $context)
1408 or has_capability('moodle/category:viewhiddencategories',
1409 context_system::instance())) {
1410 $value = clean_param($crit['value'], PARAM_INT);
1411 } else {
1412 throw new moodle_exception('criteriaerror',
1413 'webservice', '', null,
1414 'You don\'t have the permissions to search on the "visible" field.');
1416 break;
1418 case 'theme':
1419 if (has_capability('moodle/category:manage', $context)) {
1420 $value = clean_param($crit['value'], PARAM_THEME);
1421 } else {
1422 throw new moodle_exception('criteriaerror',
1423 'webservice', '', null,
1424 'You don\'t have the permissions to search on the "theme" field.');
1426 break;
1428 default:
1429 throw new moodle_exception('criteriaerror',
1430 'webservice', '', null,
1431 'You can not search on this criteria: ' . $key);
1434 if (isset($value)) {
1435 $conditions[$key] = $crit['value'];
1436 $wheres[] = $key . " = :" . $key;
1441 if (!empty($wheres)) {
1442 $wheres = implode(" AND ", $wheres);
1444 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1446 // Retrieve its sub subcategories (all levels).
1447 if ($categories and !empty($params['addsubcategories'])) {
1448 $newcategories = array();
1450 // Check if we required visible/theme checks.
1451 $additionalselect = '';
1452 $additionalparams = array();
1453 if (isset($conditions['visible'])) {
1454 $additionalselect .= ' AND visible = :visible';
1455 $additionalparams['visible'] = $conditions['visible'];
1457 if (isset($conditions['theme'])) {
1458 $additionalselect .= ' AND theme= :theme';
1459 $additionalparams['theme'] = $conditions['theme'];
1462 foreach ($categories as $category) {
1463 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1464 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1465 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1466 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1468 $categories = $categories + $newcategories;
1472 } else {
1473 // Retrieve all categories in the database.
1474 $categories = $DB->get_records('course_categories');
1477 // The not returned categories. key => category id, value => reason of exclusion.
1478 $excludedcats = array();
1480 // The returned categories.
1481 $categoriesinfo = array();
1483 // We need to sort the categories by path.
1484 // The parent cats need to be checked by the algo first.
1485 usort($categories, "core_course_external::compare_categories_by_path");
1487 foreach ($categories as $category) {
1489 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1490 $parents = explode('/', $category->path);
1491 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1492 foreach ($parents as $parentid) {
1493 // Note: when the parent exclusion was due to the context,
1494 // the sub category could still be returned.
1495 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1496 $excludedcats[$category->id] = 'parent';
1500 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1501 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1502 and !has_capability('moodle/category:manage', $context)) {
1503 $excludedcats[$category->id] = 'depth';
1506 // Check the user can use the category context.
1507 $context = context_coursecat::instance($category->id);
1508 try {
1509 self::validate_context($context);
1510 } catch (Exception $e) {
1511 $excludedcats[$category->id] = 'context';
1513 // If it was the requested category then throw an exception.
1514 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1515 $exceptionparam = new stdClass();
1516 $exceptionparam->message = $e->getMessage();
1517 $exceptionparam->catid = $category->id;
1518 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1522 // Return the category information.
1523 if (!isset($excludedcats[$category->id])) {
1525 // Final check to see if the category is visible to the user.
1526 if ($category->visible
1527 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1528 or has_capability('moodle/category:manage', $context)) {
1530 $categoryinfo = array();
1531 $categoryinfo['id'] = $category->id;
1532 $categoryinfo['name'] = $category->name;
1533 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1534 external_format_text($category->description, $category->descriptionformat,
1535 $context->id, 'coursecat', 'description', null);
1536 $categoryinfo['parent'] = $category->parent;
1537 $categoryinfo['sortorder'] = $category->sortorder;
1538 $categoryinfo['coursecount'] = $category->coursecount;
1539 $categoryinfo['depth'] = $category->depth;
1540 $categoryinfo['path'] = $category->path;
1542 // Some fields only returned for admin.
1543 if (has_capability('moodle/category:manage', $context)) {
1544 $categoryinfo['idnumber'] = $category->idnumber;
1545 $categoryinfo['visible'] = $category->visible;
1546 $categoryinfo['visibleold'] = $category->visibleold;
1547 $categoryinfo['timemodified'] = $category->timemodified;
1548 $categoryinfo['theme'] = $category->theme;
1551 $categoriesinfo[] = $categoryinfo;
1552 } else {
1553 $excludedcats[$category->id] = 'visibility';
1558 // Sorting the resulting array so it looks a bit better for the client developer.
1559 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1561 return $categoriesinfo;
1565 * Sort categories array by path
1566 * private function: only used by get_categories
1568 * @param array $category1
1569 * @param array $category2
1570 * @return int result of strcmp
1571 * @since Moodle 2.3
1573 private static function compare_categories_by_path($category1, $category2) {
1574 return strcmp($category1->path, $category2->path);
1578 * Sort categories array by sortorder
1579 * private function: only used by get_categories
1581 * @param array $category1
1582 * @param array $category2
1583 * @return int result of strcmp
1584 * @since Moodle 2.3
1586 private static function compare_categories_by_sortorder($category1, $category2) {
1587 return strcmp($category1['sortorder'], $category2['sortorder']);
1591 * Returns description of method result value
1593 * @return external_description
1594 * @since Moodle 2.3
1596 public static function get_categories_returns() {
1597 return new external_multiple_structure(
1598 new external_single_structure(
1599 array(
1600 'id' => new external_value(PARAM_INT, 'category id'),
1601 'name' => new external_value(PARAM_TEXT, 'category name'),
1602 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1603 'description' => new external_value(PARAM_RAW, 'category description'),
1604 'descriptionformat' => new external_format_value('description'),
1605 'parent' => new external_value(PARAM_INT, 'parent category id'),
1606 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1607 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1608 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1609 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1610 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1611 'depth' => new external_value(PARAM_INT, 'category depth'),
1612 'path' => new external_value(PARAM_TEXT, 'category path'),
1613 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1614 ), 'List of categories'
1620 * Returns description of method parameters
1622 * @return external_function_parameters
1623 * @since Moodle 2.3
1625 public static function create_categories_parameters() {
1626 return new external_function_parameters(
1627 array(
1628 'categories' => new external_multiple_structure(
1629 new external_single_structure(
1630 array(
1631 'name' => new external_value(PARAM_TEXT, 'new category name'),
1632 'parent' => new external_value(PARAM_INT,
1633 'the parent category id inside which the new category will be created
1634 - set to 0 for a root category',
1635 VALUE_DEFAULT, 0),
1636 'idnumber' => new external_value(PARAM_RAW,
1637 'the new category idnumber', VALUE_OPTIONAL),
1638 'description' => new external_value(PARAM_RAW,
1639 'the new category description', VALUE_OPTIONAL),
1640 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1641 'theme' => new external_value(PARAM_THEME,
1642 'the new category theme. This option must be enabled on moodle',
1643 VALUE_OPTIONAL),
1652 * Create categories
1654 * @param array $categories - see create_categories_parameters() for the array structure
1655 * @return array - see create_categories_returns() for the array structure
1656 * @since Moodle 2.3
1658 public static function create_categories($categories) {
1659 global $CFG, $DB;
1660 require_once($CFG->libdir . "/coursecatlib.php");
1662 $params = self::validate_parameters(self::create_categories_parameters(),
1663 array('categories' => $categories));
1665 $transaction = $DB->start_delegated_transaction();
1667 $createdcategories = array();
1668 foreach ($params['categories'] as $category) {
1669 if ($category['parent']) {
1670 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1671 throw new moodle_exception('unknowcategory');
1673 $context = context_coursecat::instance($category['parent']);
1674 } else {
1675 $context = context_system::instance();
1677 self::validate_context($context);
1678 require_capability('moodle/category:manage', $context);
1680 // this will validate format and throw an exception if there are errors
1681 external_validate_format($category['descriptionformat']);
1683 $newcategory = coursecat::create($category);
1685 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1688 $transaction->allow_commit();
1690 return $createdcategories;
1694 * Returns description of method parameters
1696 * @return external_function_parameters
1697 * @since Moodle 2.3
1699 public static function create_categories_returns() {
1700 return new external_multiple_structure(
1701 new external_single_structure(
1702 array(
1703 'id' => new external_value(PARAM_INT, 'new category id'),
1704 'name' => new external_value(PARAM_TEXT, 'new category name'),
1711 * Returns description of method parameters
1713 * @return external_function_parameters
1714 * @since Moodle 2.3
1716 public static function update_categories_parameters() {
1717 return new external_function_parameters(
1718 array(
1719 'categories' => new external_multiple_structure(
1720 new external_single_structure(
1721 array(
1722 'id' => new external_value(PARAM_INT, 'course id'),
1723 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1724 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1725 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1726 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1727 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1728 'theme' => new external_value(PARAM_THEME,
1729 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1738 * Update categories
1740 * @param array $categories The list of categories to update
1741 * @return null
1742 * @since Moodle 2.3
1744 public static function update_categories($categories) {
1745 global $CFG, $DB;
1746 require_once($CFG->libdir . "/coursecatlib.php");
1748 // Validate parameters.
1749 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1751 $transaction = $DB->start_delegated_transaction();
1753 foreach ($params['categories'] as $cat) {
1754 $category = coursecat::get($cat['id']);
1756 $categorycontext = context_coursecat::instance($cat['id']);
1757 self::validate_context($categorycontext);
1758 require_capability('moodle/category:manage', $categorycontext);
1760 // this will throw an exception if descriptionformat is not valid
1761 external_validate_format($cat['descriptionformat']);
1763 $category->update($cat);
1766 $transaction->allow_commit();
1770 * Returns description of method result value
1772 * @return external_description
1773 * @since Moodle 2.3
1775 public static function update_categories_returns() {
1776 return null;
1780 * Returns description of method parameters
1782 * @return external_function_parameters
1783 * @since Moodle 2.3
1785 public static function delete_categories_parameters() {
1786 return new external_function_parameters(
1787 array(
1788 'categories' => new external_multiple_structure(
1789 new external_single_structure(
1790 array(
1791 'id' => new external_value(PARAM_INT, 'category id to delete'),
1792 'newparent' => new external_value(PARAM_INT,
1793 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1794 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1795 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1804 * Delete categories
1806 * @param array $categories A list of category ids
1807 * @return array
1808 * @since Moodle 2.3
1810 public static function delete_categories($categories) {
1811 global $CFG, $DB;
1812 require_once($CFG->dirroot . "/course/lib.php");
1813 require_once($CFG->libdir . "/coursecatlib.php");
1815 // Validate parameters.
1816 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1818 $transaction = $DB->start_delegated_transaction();
1820 foreach ($params['categories'] as $category) {
1821 $deletecat = coursecat::get($category['id'], MUST_EXIST);
1822 $context = context_coursecat::instance($deletecat->id);
1823 require_capability('moodle/category:manage', $context);
1824 self::validate_context($context);
1825 self::validate_context(get_category_or_system_context($deletecat->parent));
1827 if ($category['recursive']) {
1828 // If recursive was specified, then we recursively delete the category's contents.
1829 if ($deletecat->can_delete_full()) {
1830 $deletecat->delete_full(false);
1831 } else {
1832 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1834 } else {
1835 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1836 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1837 // We must move to an existing category.
1838 if (!empty($category['newparent'])) {
1839 $newparentcat = coursecat::get($category['newparent']);
1840 } else {
1841 $newparentcat = coursecat::get($deletecat->parent);
1844 // This operation is not allowed. We must move contents to an existing category.
1845 if (!$newparentcat->id) {
1846 throw new moodle_exception('movecatcontentstoroot');
1849 self::validate_context(context_coursecat::instance($newparentcat->id));
1850 if ($deletecat->can_move_content_to($newparentcat->id)) {
1851 $deletecat->delete_move($newparentcat->id, false);
1852 } else {
1853 throw new moodle_exception('youcannotdeletecategory', '', '', $deletecat->get_formatted_name());
1858 $transaction->allow_commit();
1862 * Returns description of method parameters
1864 * @return external_function_parameters
1865 * @since Moodle 2.3
1867 public static function delete_categories_returns() {
1868 return null;
1872 * Describes the parameters for delete_modules.
1874 * @return external_external_function_parameters
1875 * @since Moodle 2.5
1877 public static function delete_modules_parameters() {
1878 return new external_function_parameters (
1879 array(
1880 'cmids' => new external_multiple_structure(new external_value(PARAM_INT, 'course module ID',
1881 VALUE_REQUIRED, '', NULL_NOT_ALLOWED), 'Array of course module IDs'),
1887 * Deletes a list of provided module instances.
1889 * @param array $cmids the course module ids
1890 * @since Moodle 2.5
1892 public static function delete_modules($cmids) {
1893 global $CFG, $DB;
1895 // Require course file containing the course delete module function.
1896 require_once($CFG->dirroot . "/course/lib.php");
1898 // Clean the parameters.
1899 $params = self::validate_parameters(self::delete_modules_parameters(), array('cmids' => $cmids));
1901 // Keep track of the course ids we have performed a capability check on to avoid repeating.
1902 $arrcourseschecked = array();
1904 foreach ($params['cmids'] as $cmid) {
1905 // Get the course module.
1906 $cm = $DB->get_record('course_modules', array('id' => $cmid), '*', MUST_EXIST);
1908 // Check if we have not yet confirmed they have permission in this course.
1909 if (!in_array($cm->course, $arrcourseschecked)) {
1910 // Ensure the current user has required permission in this course.
1911 $context = context_course::instance($cm->course);
1912 self::validate_context($context);
1913 // Add to the array.
1914 $arrcourseschecked[] = $cm->course;
1917 // Ensure they can delete this module.
1918 $modcontext = context_module::instance($cm->id);
1919 require_capability('moodle/course:manageactivities', $modcontext);
1921 // Delete the module.
1922 course_delete_module($cm->id);
1927 * Describes the delete_modules return value.
1929 * @return external_single_structure
1930 * @since Moodle 2.5
1932 public static function delete_modules_returns() {
1933 return null;
1938 * Deprecated course external functions
1940 * @package core_course
1941 * @copyright 2009 Petr Skodak
1942 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1943 * @since Moodle 2.0
1944 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1945 * @see core_course_external
1947 class moodle_course_external extends external_api {
1950 * Returns description of method parameters
1952 * @return external_function_parameters
1953 * @since Moodle 2.0
1954 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1955 * @see core_course_external::get_courses_parameters()
1957 public static function get_courses_parameters() {
1958 return core_course_external::get_courses_parameters();
1962 * Get courses
1964 * @param array $options
1965 * @return array
1966 * @since Moodle 2.0
1967 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1968 * @see core_course_external::get_courses()
1970 public static function get_courses($options) {
1971 return core_course_external::get_courses($options);
1975 * Returns description of method result value
1977 * @return external_description
1978 * @since Moodle 2.0
1979 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1980 * @see core_course_external::get_courses_returns()
1982 public static function get_courses_returns() {
1983 return core_course_external::get_courses_returns();
1987 * Marking the method as deprecated.
1989 * @return bool
1991 public static function get_courses_is_deprecated() {
1992 return true;
1996 * Returns description of method parameters
1998 * @return external_function_parameters
1999 * @since Moodle 2.0
2000 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2001 * @see core_course_external::create_courses_parameters()
2003 public static function create_courses_parameters() {
2004 return core_course_external::create_courses_parameters();
2008 * Create courses
2010 * @param array $courses
2011 * @return array courses (id and shortname only)
2012 * @since Moodle 2.0
2013 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2014 * @see core_course_external::create_courses()
2016 public static function create_courses($courses) {
2017 return core_course_external::create_courses($courses);
2021 * Returns description of method result value
2023 * @return external_description
2024 * @since Moodle 2.0
2025 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
2026 * @see core_course_external::create_courses_returns()
2028 public static function create_courses_returns() {
2029 return core_course_external::create_courses_returns();
2033 * Marking the method as deprecated.
2035 * @return bool
2037 public static function create_courses_is_deprecated() {
2038 return true;