MDL-37822 Moodle send site information to a hub even though it's unchecked
[moodle.git] / course / externallib.php
blobb6648907f4b256ddd5a403bd47513a85bc4a6879
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['modname'] = $cm->modname;
144 $module['modplural'] = $cm->modplural;
145 $module['modicon'] = $cm->get_icon_url()->out(false);
146 $module['indent'] = $cm->indent;
148 $modcontext = context_module::instance($cm->id);
150 if (!empty($cm->showdescription) or $cm->modname == 'label') {
151 // We want to use the external format. However from reading get_formatted_content(), get_content() format is always FORMAT_HTML.
152 list($module['description'], $descriptionformat) = external_format_text($cm->get_content(),
153 FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
156 //url of the module
157 $url = $cm->get_url();
158 if ($url) { //labels don't have url
159 $module['url'] = $cm->get_url()->out();
162 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
163 context_module::instance($cm->id));
164 //user that can view hidden module should know about the visibility
165 $module['visible'] = $cm->visible;
167 //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
168 if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
169 $module['availablefrom'] = $cm->availablefrom;
170 $module['availableuntil'] = $cm->availableuntil;
173 $baseurl = 'webservice/pluginfile.php';
175 //call $modulename_export_contents
176 //(each module callback take care about checking the capabilities)
177 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
178 $getcontentfunction = $cm->modname.'_export_contents';
179 if (function_exists($getcontentfunction)) {
180 if ($contents = $getcontentfunction($cm, $baseurl)) {
181 $module['contents'] = $contents;
185 //assign result to $sectioncontents
186 $sectioncontents[] = $module;
190 $sectionvalues['modules'] = $sectioncontents;
192 // assign result to $coursecontents
193 $coursecontents[] = $sectionvalues;
196 return $coursecontents;
200 * Returns description of method result value
202 * @return external_description
203 * @since Moodle 2.2
205 public static function get_course_contents_returns() {
206 return new external_multiple_structure(
207 new external_single_structure(
208 array(
209 'id' => new external_value(PARAM_INT, 'Section ID'),
210 'name' => new external_value(PARAM_TEXT, 'Section name'),
211 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
212 'summary' => new external_value(PARAM_RAW, 'Section description'),
213 'summaryformat' => new external_format_value('summary'),
214 'modules' => new external_multiple_structure(
215 new external_single_structure(
216 array(
217 'id' => new external_value(PARAM_INT, 'activity id'),
218 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
219 'name' => new external_value(PARAM_RAW, 'activity module name'),
220 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
221 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
222 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
223 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
224 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
225 'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
226 'availableuntil' => new external_value(PARAM_INT, 'module availability en date', 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['completionstartonenrol'] = $course->completionstartonenrol;
353 $courseinfo['completionnotify'] = $course->completionnotify;
354 $courseinfo['courseformatoptions'] = array();
355 foreach ($courseformatoptions as $key => $value) {
356 $courseinfo['courseformatoptions'][] = array(
357 'name' => $key,
358 'value' => $value
363 if ($courseadmin or $course->visible
364 or has_capability('moodle/course:viewhiddencourses', $context)) {
365 $coursesinfo[] = $courseinfo;
369 return $coursesinfo;
373 * Returns description of method result value
375 * @return external_description
376 * @since Moodle 2.2
378 public static function get_courses_returns() {
379 return new external_multiple_structure(
380 new external_single_structure(
381 array(
382 'id' => new external_value(PARAM_INT, 'course id'),
383 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
384 'categoryid' => new external_value(PARAM_INT, 'category id'),
385 'categorysortorder' => new external_value(PARAM_INT,
386 'sort order into the category', VALUE_OPTIONAL),
387 'fullname' => new external_value(PARAM_TEXT, 'full name'),
388 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
389 'summary' => new external_value(PARAM_RAW, 'summary'),
390 'summaryformat' => new external_format_value('summary'),
391 'format' => new external_value(PARAM_PLUGIN,
392 'course format: weeks, topics, social, site,..'),
393 'showgrades' => new external_value(PARAM_INT,
394 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
395 'newsitems' => new external_value(PARAM_INT,
396 'number of recent items appearing on the course page', VALUE_OPTIONAL),
397 'startdate' => new external_value(PARAM_INT,
398 'timestamp when the course start'),
399 'numsections' => new external_value(PARAM_INT,
400 '(deprecated, use courseformatoptions) number of weeks/topics',
401 VALUE_OPTIONAL),
402 'maxbytes' => new external_value(PARAM_INT,
403 'largest size of file that can be uploaded into the course',
404 VALUE_OPTIONAL),
405 'showreports' => new external_value(PARAM_INT,
406 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
407 'visible' => new external_value(PARAM_INT,
408 '1: available to student, 0:not available', VALUE_OPTIONAL),
409 'hiddensections' => new external_value(PARAM_INT,
410 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
411 VALUE_OPTIONAL),
412 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
413 VALUE_OPTIONAL),
414 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
415 VALUE_OPTIONAL),
416 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
417 VALUE_OPTIONAL),
418 'timecreated' => new external_value(PARAM_INT,
419 'timestamp when the course have been created', VALUE_OPTIONAL),
420 'timemodified' => new external_value(PARAM_INT,
421 'timestamp when the course have been modified', VALUE_OPTIONAL),
422 'enablecompletion' => new external_value(PARAM_INT,
423 'Enabled, control via completion and activity settings. Disbaled,
424 not shown in activity settings.',
425 VALUE_OPTIONAL),
426 'completionstartonenrol' => new external_value(PARAM_INT,
427 '1: begin tracking a student\'s progress in course completion
428 after course enrolment. 0: does not',
429 VALUE_OPTIONAL),
430 'completionnotify' => new external_value(PARAM_INT,
431 '1: yes 0: no', VALUE_OPTIONAL),
432 'lang' => new external_value(PARAM_SAFEDIR,
433 'forced course language', VALUE_OPTIONAL),
434 'forcetheme' => new external_value(PARAM_PLUGIN,
435 'name of the force theme', VALUE_OPTIONAL),
436 'courseformatoptions' => new external_multiple_structure(
437 new external_single_structure(
438 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
439 'value' => new external_value(PARAM_RAW, 'course format option value')
441 'additional options for particular course format', VALUE_OPTIONAL
443 ), 'course'
449 * Returns description of method parameters
451 * @return external_function_parameters
452 * @since Moodle 2.2
454 public static function create_courses_parameters() {
455 $courseconfig = get_config('moodlecourse'); //needed for many default values
456 return new external_function_parameters(
457 array(
458 'courses' => new external_multiple_structure(
459 new external_single_structure(
460 array(
461 'fullname' => new external_value(PARAM_TEXT, 'full name'),
462 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
463 'categoryid' => new external_value(PARAM_INT, 'category id'),
464 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
465 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
466 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
467 'format' => new external_value(PARAM_PLUGIN,
468 'course format: weeks, topics, social, site,..',
469 VALUE_DEFAULT, $courseconfig->format),
470 'showgrades' => new external_value(PARAM_INT,
471 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
472 $courseconfig->showgrades),
473 'newsitems' => new external_value(PARAM_INT,
474 'number of recent items appearing on the course page',
475 VALUE_DEFAULT, $courseconfig->newsitems),
476 'startdate' => new external_value(PARAM_INT,
477 'timestamp when the course start', VALUE_OPTIONAL),
478 'numsections' => new external_value(PARAM_INT,
479 '(deprecated, use courseformatoptions) number of weeks/topics',
480 VALUE_OPTIONAL),
481 'maxbytes' => new external_value(PARAM_INT,
482 'largest size of file that can be uploaded into the course',
483 VALUE_DEFAULT, $courseconfig->maxbytes),
484 'showreports' => new external_value(PARAM_INT,
485 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
486 $courseconfig->showreports),
487 'visible' => new external_value(PARAM_INT,
488 '1: available to student, 0:not available', VALUE_OPTIONAL),
489 'hiddensections' => new external_value(PARAM_INT,
490 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
491 VALUE_OPTIONAL),
492 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
493 VALUE_DEFAULT, $courseconfig->groupmode),
494 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
495 VALUE_DEFAULT, $courseconfig->groupmodeforce),
496 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
497 VALUE_DEFAULT, 0),
498 'enablecompletion' => new external_value(PARAM_INT,
499 'Enabled, control via completion and activity settings. Disabled,
500 not shown in activity settings.',
501 VALUE_OPTIONAL),
502 'completionstartonenrol' => new external_value(PARAM_INT,
503 '1: begin tracking a student\'s progress in course completion after
504 course enrolment. 0: does not',
505 VALUE_OPTIONAL),
506 'completionnotify' => new external_value(PARAM_INT,
507 '1: yes 0: no', VALUE_OPTIONAL),
508 'lang' => new external_value(PARAM_SAFEDIR,
509 'forced course language', VALUE_OPTIONAL),
510 'forcetheme' => new external_value(PARAM_PLUGIN,
511 'name of the force theme', VALUE_OPTIONAL),
512 'courseformatoptions' => new external_multiple_structure(
513 new external_single_structure(
514 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
515 'value' => new external_value(PARAM_RAW, 'course format option value')
517 'additional options for particular course format', VALUE_OPTIONAL),
519 ), 'courses to create'
526 * Create courses
528 * @param array $courses
529 * @return array courses (id and shortname only)
530 * @since Moodle 2.2
532 public static function create_courses($courses) {
533 global $CFG, $DB;
534 require_once($CFG->dirroot . "/course/lib.php");
535 require_once($CFG->libdir . '/completionlib.php');
537 $params = self::validate_parameters(self::create_courses_parameters(),
538 array('courses' => $courses));
540 $availablethemes = get_plugin_list('theme');
541 $availablelangs = get_string_manager()->get_list_of_translations();
543 $transaction = $DB->start_delegated_transaction();
545 foreach ($params['courses'] as $course) {
547 // Ensure the current user is allowed to run this function
548 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
549 try {
550 self::validate_context($context);
551 } catch (Exception $e) {
552 $exceptionparam = new stdClass();
553 $exceptionparam->message = $e->getMessage();
554 $exceptionparam->catid = $course['categoryid'];
555 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
557 require_capability('moodle/course:create', $context);
559 // Make sure lang is valid
560 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
561 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
564 // Make sure theme is valid
565 if (array_key_exists('forcetheme', $course)) {
566 if (!empty($CFG->allowcoursethemes)) {
567 if (empty($availablethemes[$course['forcetheme']])) {
568 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
569 } else {
570 $course['theme'] = $course['forcetheme'];
575 //force visibility if ws user doesn't have the permission to set it
576 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
577 if (!has_capability('moodle/course:visibility', $context)) {
578 $course['visible'] = $category->visible;
581 //set default value for completion
582 $courseconfig = get_config('moodlecourse');
583 if (completion_info::is_enabled_for_site()) {
584 if (!array_key_exists('enablecompletion', $course)) {
585 $course['enablecompletion'] = $courseconfig->enablecompletion;
587 if (!array_key_exists('completionstartonenrol', $course)) {
588 $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
590 } else {
591 $course['enablecompletion'] = 0;
592 $course['completionstartonenrol'] = 0;
595 $course['category'] = $course['categoryid'];
597 // Summary format.
598 $course['summaryformat'] = external_validate_format($course['summaryformat']);
600 if (!empty($course['courseformatoptions'])) {
601 foreach ($course['courseformatoptions'] as $option) {
602 $course[$option['name']] = $option['value'];
606 //Note: create_course() core function check shortname, idnumber, category
607 $course['id'] = create_course((object) $course)->id;
609 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
612 $transaction->allow_commit();
614 return $resultcourses;
618 * Returns description of method result value
620 * @return external_description
621 * @since Moodle 2.2
623 public static function create_courses_returns() {
624 return new external_multiple_structure(
625 new external_single_structure(
626 array(
627 'id' => new external_value(PARAM_INT, 'course id'),
628 'shortname' => new external_value(PARAM_TEXT, 'short name'),
635 * Returns description of method parameters
637 * @return external_function_parameters
638 * @since Moodle 2.2
640 public static function delete_courses_parameters() {
641 return new external_function_parameters(
642 array(
643 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
649 * Delete courses
651 * @param array $courseids A list of course ids
652 * @since Moodle 2.2
654 public static function delete_courses($courseids) {
655 global $CFG, $DB;
656 require_once($CFG->dirroot."/course/lib.php");
658 // Parameter validation.
659 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
661 $transaction = $DB->start_delegated_transaction();
663 foreach ($params['courseids'] as $courseid) {
664 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
666 // Check if the context is valid.
667 $coursecontext = context_course::instance($course->id);
668 self::validate_context($coursecontext);
670 // Check if the current user has enought permissions.
671 if (!can_delete_course($courseid)) {
672 throw new moodle_exception('cannotdeletecategorycourse', 'error',
673 '', format_string($course->fullname)." (id: $courseid)");
676 delete_course($course, false);
679 $transaction->allow_commit();
681 return null;
685 * Returns description of method result value
687 * @return external_description
688 * @since Moodle 2.2
690 public static function delete_courses_returns() {
691 return null;
695 * Returns description of method parameters
697 * @return external_function_parameters
698 * @since Moodle 2.3
700 public static function duplicate_course_parameters() {
701 return new external_function_parameters(
702 array(
703 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
704 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
705 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
706 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
707 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
708 'options' => new external_multiple_structure(
709 new external_single_structure(
710 array(
711 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
712 "activities" (int) Include course activites (default to 1 that is equal to yes),
713 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
714 "filters" (int) Include course filters (default to 1 that is equal to yes),
715 "users" (int) Include users (default to 0 that is equal to no),
716 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
717 "comments" (int) Include user comments (default to 0 that is equal to no),
718 "completion_information" (int) Include user course completion information (default to 0 that is equal to no),
719 "logs" (int) Include course logs (default to 0 that is equal to no),
720 "histories" (int) Include histories (default to 0 that is equal to no)'
722 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
725 ), VALUE_DEFAULT, array()
732 * Duplicate a course
734 * @param int $courseid
735 * @param string $fullname Duplicated course fullname
736 * @param string $shortname Duplicated course shortname
737 * @param int $categoryid Duplicated course parent category id
738 * @param int $visible Duplicated course availability
739 * @param array $options List of backup options
740 * @return array New course info
741 * @since Moodle 2.3
743 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
744 global $CFG, $USER, $DB;
745 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
746 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
748 // Parameter validation.
749 $params = self::validate_parameters(
750 self::duplicate_course_parameters(),
751 array(
752 'courseid' => $courseid,
753 'fullname' => $fullname,
754 'shortname' => $shortname,
755 'categoryid' => $categoryid,
756 'visible' => $visible,
757 'options' => $options
761 // Context validation.
763 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
764 throw new moodle_exception('invalidcourseid', 'error');
767 // Category where duplicated course is going to be created.
768 $categorycontext = context_coursecat::instance($params['categoryid']);
769 self::validate_context($categorycontext);
771 // Course to be duplicated.
772 $coursecontext = context_course::instance($course->id);
773 self::validate_context($coursecontext);
775 $backupdefaults = array(
776 'activities' => 1,
777 'blocks' => 1,
778 'filters' => 1,
779 'users' => 0,
780 'role_assignments' => 0,
781 'comments' => 0,
782 'completion_information' => 0,
783 'logs' => 0,
784 'histories' => 0
787 $backupsettings = array();
788 // Check for backup and restore options.
789 if (!empty($params['options'])) {
790 foreach ($params['options'] as $option) {
792 // Strict check for a correct value (allways 1 or 0, true or false).
793 $value = clean_param($option['value'], PARAM_INT);
795 if ($value !== 0 and $value !== 1) {
796 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
799 if (!isset($backupdefaults[$option['name']])) {
800 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
803 $backupsettings[$option['name']] = $value;
807 // Capability checking.
809 // The backup controller check for this currently, this may be redundant.
810 require_capability('moodle/course:create', $categorycontext);
811 require_capability('moodle/restore:restorecourse', $categorycontext);
812 require_capability('moodle/backup:backupcourse', $coursecontext);
814 if (!empty($backupsettings['users'])) {
815 require_capability('moodle/backup:userinfo', $coursecontext);
816 require_capability('moodle/restore:userinfo', $categorycontext);
819 // Check if the shortname is used.
820 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
821 foreach ($foundcourses as $foundcourse) {
822 $foundcoursenames[] = $foundcourse->fullname;
825 $foundcoursenamestring = implode(',', $foundcoursenames);
826 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
829 // Backup the course.
831 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
832 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
834 foreach ($backupsettings as $name => $value) {
835 $bc->get_plan()->get_setting($name)->set_value($value);
838 $backupid = $bc->get_backupid();
839 $backupbasepath = $bc->get_plan()->get_basepath();
841 $bc->execute_plan();
842 $results = $bc->get_results();
843 $file = $results['backup_destination'];
845 $bc->destroy();
847 // Restore the backup immediately.
849 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
850 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
851 $file->extract_to_pathname(get_file_packer(), $backupbasepath);
854 // Create new course.
855 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
857 $rc = new restore_controller($backupid, $newcourseid,
858 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
860 foreach ($backupsettings as $name => $value) {
861 $setting = $rc->get_plan()->get_setting($name);
862 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
863 $setting->set_value($value);
867 if (!$rc->execute_precheck()) {
868 $precheckresults = $rc->get_precheck_results();
869 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
870 if (empty($CFG->keeptempdirectoriesonbackup)) {
871 fulldelete($backupbasepath);
874 $errorinfo = '';
876 foreach ($precheckresults['errors'] as $error) {
877 $errorinfo .= $error;
880 if (array_key_exists('warnings', $precheckresults)) {
881 foreach ($precheckresults['warnings'] as $warning) {
882 $errorinfo .= $warning;
886 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
890 $rc->execute_plan();
891 $rc->destroy();
893 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
894 $course->fullname = $params['fullname'];
895 $course->shortname = $params['shortname'];
896 $course->visible = $params['visible'];
898 // Set shortname and fullname back.
899 $DB->update_record('course', $course);
901 if (empty($CFG->keeptempdirectoriesonbackup)) {
902 fulldelete($backupbasepath);
905 // Delete the course backup file created by this WebService. Originally located in the course backups area.
906 $file->delete();
908 return array('id' => $course->id, 'shortname' => $course->shortname);
912 * Returns description of method result value
914 * @return external_description
915 * @since Moodle 2.3
917 public static function duplicate_course_returns() {
918 return new external_single_structure(
919 array(
920 'id' => new external_value(PARAM_INT, 'course id'),
921 'shortname' => new external_value(PARAM_TEXT, 'short name'),
927 * Returns description of method parameters for import_course
929 * @return external_function_parameters
930 * @since Moodle 2.4
932 public static function import_course_parameters() {
933 return new external_function_parameters(
934 array(
935 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
936 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
937 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
938 'options' => new external_multiple_structure(
939 new external_single_structure(
940 array(
941 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
942 "activities" (int) Include course activites (default to 1 that is equal to yes),
943 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
944 "filters" (int) Include course filters (default to 1 that is equal to yes)'
946 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
949 ), VALUE_DEFAULT, array()
956 * Imports a course
958 * @param int $importfrom The id of the course we are importing from
959 * @param int $importto The id of the course we are importing to
960 * @param bool $deletecontent Whether to delete the course we are importing to content
961 * @param array $options List of backup options
962 * @return null
963 * @since Moodle 2.4
965 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
966 global $CFG, $USER, $DB;
967 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
968 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
970 // Parameter validation.
971 $params = self::validate_parameters(
972 self::import_course_parameters(),
973 array(
974 'importfrom' => $importfrom,
975 'importto' => $importto,
976 'deletecontent' => $deletecontent,
977 'options' => $options
981 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
982 throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
985 // Context validation.
987 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
988 throw new moodle_exception('invalidcourseid', 'error');
991 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
992 throw new moodle_exception('invalidcourseid', 'error');
995 $importfromcontext = context_course::instance($importfrom->id);
996 self::validate_context($importfromcontext);
998 $importtocontext = context_course::instance($importto->id);
999 self::validate_context($importtocontext);
1001 $backupdefaults = array(
1002 'activities' => 1,
1003 'blocks' => 1,
1004 'filters' => 1
1007 $backupsettings = array();
1009 // Check for backup and restore options.
1010 if (!empty($params['options'])) {
1011 foreach ($params['options'] as $option) {
1013 // Strict check for a correct value (allways 1 or 0, true or false).
1014 $value = clean_param($option['value'], PARAM_INT);
1016 if ($value !== 0 and $value !== 1) {
1017 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1020 if (!isset($backupdefaults[$option['name']])) {
1021 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1024 $backupsettings[$option['name']] = $value;
1028 // Capability checking.
1030 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1031 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1033 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1034 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1036 foreach ($backupsettings as $name => $value) {
1037 $bc->get_plan()->get_setting($name)->set_value($value);
1040 $backupid = $bc->get_backupid();
1041 $backupbasepath = $bc->get_plan()->get_basepath();
1043 $bc->execute_plan();
1044 $bc->destroy();
1046 // Restore the backup immediately.
1048 // Check if we must delete the contents of the destination course.
1049 if ($params['deletecontent']) {
1050 $restoretarget = backup::TARGET_EXISTING_DELETING;
1051 } else {
1052 $restoretarget = backup::TARGET_EXISTING_ADDING;
1055 $rc = new restore_controller($backupid, $importto->id,
1056 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1058 foreach ($backupsettings as $name => $value) {
1059 $rc->get_plan()->get_setting($name)->set_value($value);
1062 if (!$rc->execute_precheck()) {
1063 $precheckresults = $rc->get_precheck_results();
1064 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1065 if (empty($CFG->keeptempdirectoriesonbackup)) {
1066 fulldelete($backupbasepath);
1069 $errorinfo = '';
1071 foreach ($precheckresults['errors'] as $error) {
1072 $errorinfo .= $error;
1075 if (array_key_exists('warnings', $precheckresults)) {
1076 foreach ($precheckresults['warnings'] as $warning) {
1077 $errorinfo .= $warning;
1081 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1083 } else {
1084 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1085 restore_dbops::delete_course_content($importto->id);
1089 $rc->execute_plan();
1090 $rc->destroy();
1092 if (empty($CFG->keeptempdirectoriesonbackup)) {
1093 fulldelete($backupbasepath);
1096 return null;
1100 * Returns description of method result value
1102 * @return external_description
1103 * @since Moodle 2.4
1105 public static function import_course_returns() {
1106 return null;
1110 * Returns description of method parameters
1112 * @return external_function_parameters
1113 * @since Moodle 2.3
1115 public static function get_categories_parameters() {
1116 return new external_function_parameters(
1117 array(
1118 'criteria' => new external_multiple_structure(
1119 new external_single_structure(
1120 array(
1121 'key' => new external_value(PARAM_ALPHA,
1122 'The category column to search, expected keys (value format) are:'.
1123 '"id" (int) the category id,'.
1124 '"name" (string) the category name,'.
1125 '"parent" (int) the parent category id,'.
1126 '"idnumber" (string) category idnumber'.
1127 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1128 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1129 then the function return all categories that the user can see.'.
1130 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1131 '"theme" (string) only return the categories having this theme'.
1132 ' - user must have \'moodle/category:manage\' to search on theme'),
1133 'value' => new external_value(PARAM_RAW, 'the value to match')
1135 ), 'criteria', VALUE_DEFAULT, array()
1137 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1138 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1144 * Get categories
1146 * @param array $criteria Criteria to match the results
1147 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1148 * @return array list of categories
1149 * @since Moodle 2.3
1151 public static function get_categories($criteria = array(), $addsubcategories = true) {
1152 global $CFG, $DB;
1153 require_once($CFG->dirroot . "/course/lib.php");
1155 // Validate parameters.
1156 $params = self::validate_parameters(self::get_categories_parameters(),
1157 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1159 // Retrieve the categories.
1160 $categories = array();
1161 if (!empty($params['criteria'])) {
1163 $conditions = array();
1164 $wheres = array();
1165 foreach ($params['criteria'] as $crit) {
1166 $key = trim($crit['key']);
1168 // Trying to avoid duplicate keys.
1169 if (!isset($conditions[$key])) {
1171 $context = context_system::instance();
1172 $value = null;
1173 switch ($key) {
1174 case 'id':
1175 $value = clean_param($crit['value'], PARAM_INT);
1176 break;
1178 case 'idnumber':
1179 if (has_capability('moodle/category:manage', $context)) {
1180 $value = clean_param($crit['value'], PARAM_RAW);
1181 } else {
1182 // We must throw an exception.
1183 // Otherwise the dev client would think no idnumber exists.
1184 throw new moodle_exception('criteriaerror',
1185 'webservice', '', null,
1186 'You don\'t have the permissions to search on the "idnumber" field.');
1188 break;
1190 case 'name':
1191 $value = clean_param($crit['value'], PARAM_TEXT);
1192 break;
1194 case 'parent':
1195 $value = clean_param($crit['value'], PARAM_INT);
1196 break;
1198 case 'visible':
1199 if (has_capability('moodle/category:manage', $context)
1200 or has_capability('moodle/category:viewhiddencategories',
1201 context_system::instance())) {
1202 $value = clean_param($crit['value'], PARAM_INT);
1203 } else {
1204 throw new moodle_exception('criteriaerror',
1205 'webservice', '', null,
1206 'You don\'t have the permissions to search on the "visible" field.');
1208 break;
1210 case 'theme':
1211 if (has_capability('moodle/category:manage', $context)) {
1212 $value = clean_param($crit['value'], PARAM_THEME);
1213 } else {
1214 throw new moodle_exception('criteriaerror',
1215 'webservice', '', null,
1216 'You don\'t have the permissions to search on the "theme" field.');
1218 break;
1220 default:
1221 throw new moodle_exception('criteriaerror',
1222 'webservice', '', null,
1223 'You can not search on this criteria: ' . $key);
1226 if (isset($value)) {
1227 $conditions[$key] = $crit['value'];
1228 $wheres[] = $key . " = :" . $key;
1233 if (!empty($wheres)) {
1234 $wheres = implode(" AND ", $wheres);
1236 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1238 // Retrieve its sub subcategories (all levels).
1239 if ($categories and !empty($params['addsubcategories'])) {
1240 $newcategories = array();
1242 // Check if we required visible/theme checks.
1243 $additionalselect = '';
1244 $additionalparams = array();
1245 if (isset($conditions['visible'])) {
1246 $additionalselect .= ' AND visible = :visible';
1247 $additionalparams['visible'] = $conditions['visible'];
1249 if (isset($conditions['theme'])) {
1250 $additionalselect .= ' AND theme= :theme';
1251 $additionalparams['theme'] = $conditions['theme'];
1254 foreach ($categories as $category) {
1255 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1256 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1257 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1258 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1260 $categories = $categories + $newcategories;
1264 } else {
1265 // Retrieve all categories in the database.
1266 $categories = $DB->get_records('course_categories');
1269 // The not returned categories. key => category id, value => reason of exclusion.
1270 $excludedcats = array();
1272 // The returned categories.
1273 $categoriesinfo = array();
1275 // We need to sort the categories by path.
1276 // The parent cats need to be checked by the algo first.
1277 usort($categories, "core_course_external::compare_categories_by_path");
1279 foreach ($categories as $category) {
1281 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1282 $parents = explode('/', $category->path);
1283 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1284 foreach ($parents as $parentid) {
1285 // Note: when the parent exclusion was due to the context,
1286 // the sub category could still be returned.
1287 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1288 $excludedcats[$category->id] = 'parent';
1292 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1293 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1294 and !has_capability('moodle/category:manage', $context)) {
1295 $excludedcats[$category->id] = 'depth';
1298 // Check the user can use the category context.
1299 $context = context_coursecat::instance($category->id);
1300 try {
1301 self::validate_context($context);
1302 } catch (Exception $e) {
1303 $excludedcats[$category->id] = 'context';
1305 // If it was the requested category then throw an exception.
1306 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1307 $exceptionparam = new stdClass();
1308 $exceptionparam->message = $e->getMessage();
1309 $exceptionparam->catid = $category->id;
1310 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1314 // Return the category information.
1315 if (!isset($excludedcats[$category->id])) {
1317 // Final check to see if the category is visible to the user.
1318 if ($category->visible
1319 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1320 or has_capability('moodle/category:manage', $context)) {
1322 $categoryinfo = array();
1323 $categoryinfo['id'] = $category->id;
1324 $categoryinfo['name'] = $category->name;
1325 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1326 external_format_text($category->description, $category->descriptionformat,
1327 $context->id, 'coursecat', 'description', null);
1328 $categoryinfo['parent'] = $category->parent;
1329 $categoryinfo['sortorder'] = $category->sortorder;
1330 $categoryinfo['coursecount'] = $category->coursecount;
1331 $categoryinfo['depth'] = $category->depth;
1332 $categoryinfo['path'] = $category->path;
1334 // Some fields only returned for admin.
1335 if (has_capability('moodle/category:manage', $context)) {
1336 $categoryinfo['idnumber'] = $category->idnumber;
1337 $categoryinfo['visible'] = $category->visible;
1338 $categoryinfo['visibleold'] = $category->visibleold;
1339 $categoryinfo['timemodified'] = $category->timemodified;
1340 $categoryinfo['theme'] = $category->theme;
1343 $categoriesinfo[] = $categoryinfo;
1344 } else {
1345 $excludedcats[$category->id] = 'visibility';
1350 // Sorting the resulting array so it looks a bit better for the client developer.
1351 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1353 return $categoriesinfo;
1357 * Sort categories array by path
1358 * private function: only used by get_categories
1360 * @param array $category1
1361 * @param array $category2
1362 * @return int result of strcmp
1363 * @since Moodle 2.3
1365 private static function compare_categories_by_path($category1, $category2) {
1366 return strcmp($category1->path, $category2->path);
1370 * Sort categories array by sortorder
1371 * private function: only used by get_categories
1373 * @param array $category1
1374 * @param array $category2
1375 * @return int result of strcmp
1376 * @since Moodle 2.3
1378 private static function compare_categories_by_sortorder($category1, $category2) {
1379 return strcmp($category1['sortorder'], $category2['sortorder']);
1383 * Returns description of method result value
1385 * @return external_description
1386 * @since Moodle 2.3
1388 public static function get_categories_returns() {
1389 return new external_multiple_structure(
1390 new external_single_structure(
1391 array(
1392 'id' => new external_value(PARAM_INT, 'category id'),
1393 'name' => new external_value(PARAM_TEXT, 'category name'),
1394 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1395 'description' => new external_value(PARAM_RAW, 'category description'),
1396 'descriptionformat' => new external_format_value('description'),
1397 'parent' => new external_value(PARAM_INT, 'parent category id'),
1398 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1399 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1400 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1401 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1402 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1403 'depth' => new external_value(PARAM_INT, 'category depth'),
1404 'path' => new external_value(PARAM_TEXT, 'category path'),
1405 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1406 ), 'List of categories'
1412 * Returns description of method parameters
1414 * @return external_function_parameters
1415 * @since Moodle 2.3
1417 public static function create_categories_parameters() {
1418 return new external_function_parameters(
1419 array(
1420 'categories' => new external_multiple_structure(
1421 new external_single_structure(
1422 array(
1423 'name' => new external_value(PARAM_TEXT, 'new category name'),
1424 'parent' => new external_value(PARAM_INT,
1425 'the parent category id inside which the new category will be created
1426 - set to 0 for a root category',
1427 VALUE_DEFAULT, 0),
1428 'idnumber' => new external_value(PARAM_RAW,
1429 'the new category idnumber', VALUE_OPTIONAL),
1430 'description' => new external_value(PARAM_RAW,
1431 'the new category description', VALUE_OPTIONAL),
1432 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1433 'theme' => new external_value(PARAM_THEME,
1434 'the new category theme. This option must be enabled on moodle',
1435 VALUE_OPTIONAL),
1444 * Create categories
1446 * @param array $categories - see create_categories_parameters() for the array structure
1447 * @return array - see create_categories_returns() for the array structure
1448 * @since Moodle 2.3
1450 public static function create_categories($categories) {
1451 global $CFG, $DB;
1452 require_once($CFG->dirroot . "/course/lib.php");
1454 $params = self::validate_parameters(self::create_categories_parameters(),
1455 array('categories' => $categories));
1457 $transaction = $DB->start_delegated_transaction();
1459 $createdcategories = array();
1460 foreach ($params['categories'] as $category) {
1461 if ($category['parent']) {
1462 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1463 throw new moodle_exception('unknowcategory');
1465 $context = context_coursecat::instance($category['parent']);
1466 } else {
1467 $context = context_system::instance();
1469 self::validate_context($context);
1470 require_capability('moodle/category:manage', $context);
1472 // Check name.
1473 if (textlib::strlen($category['name'])>255) {
1474 throw new moodle_exception('categorytoolong');
1477 $newcategory = new stdClass();
1478 $newcategory->name = $category['name'];
1479 $newcategory->parent = $category['parent'];
1480 // Format the description.
1481 if (!empty($category['description'])) {
1482 $newcategory->description = $category['description'];
1484 $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
1485 if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
1486 $newcategory->theme = $category['theme'];
1488 // Check id number.
1489 if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
1490 if (textlib::strlen($category['idnumber'])>100) {
1491 throw new moodle_exception('idnumbertoolong');
1493 if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
1494 if ($existing->id) {
1495 throw new moodle_exception('idnumbertaken');
1498 $newcategory->idnumber = $category['idnumber'];
1501 $newcategory = create_course_category($newcategory);
1502 // Populate special fields.
1503 fix_course_sortorder();
1505 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1508 $transaction->allow_commit();
1510 return $createdcategories;
1514 * Returns description of method parameters
1516 * @return external_function_parameters
1517 * @since Moodle 2.3
1519 public static function create_categories_returns() {
1520 return new external_multiple_structure(
1521 new external_single_structure(
1522 array(
1523 'id' => new external_value(PARAM_INT, 'new category id'),
1524 'name' => new external_value(PARAM_TEXT, 'new category name'),
1531 * Returns description of method parameters
1533 * @return external_function_parameters
1534 * @since Moodle 2.3
1536 public static function update_categories_parameters() {
1537 return new external_function_parameters(
1538 array(
1539 'categories' => new external_multiple_structure(
1540 new external_single_structure(
1541 array(
1542 'id' => new external_value(PARAM_INT, 'course id'),
1543 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1544 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1545 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1546 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1547 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1548 'theme' => new external_value(PARAM_THEME,
1549 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1558 * Update categories
1560 * @param array $categories The list of categories to update
1561 * @return null
1562 * @since Moodle 2.3
1564 public static function update_categories($categories) {
1565 global $CFG, $DB;
1566 require_once($CFG->dirroot . "/course/lib.php");
1568 // Validate parameters.
1569 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1571 $transaction = $DB->start_delegated_transaction();
1573 foreach ($params['categories'] as $cat) {
1574 if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
1575 throw new moodle_exception('unknowcategory');
1578 $categorycontext = context_coursecat::instance($cat['id']);
1579 self::validate_context($categorycontext);
1580 require_capability('moodle/category:manage', $categorycontext);
1582 if (!empty($cat['name'])) {
1583 if (textlib::strlen($cat['name'])>255) {
1584 throw new moodle_exception('categorytoolong');
1586 $category->name = $cat['name'];
1588 if (!empty($cat['idnumber'])) {
1589 if (textlib::strlen($cat['idnumber'])>100) {
1590 throw new moodle_exception('idnumbertoolong');
1592 $category->idnumber = $cat['idnumber'];
1594 if (!empty($cat['description'])) {
1595 $category->description = $cat['description'];
1596 $category->descriptionformat = external_validate_format($cat['descriptionformat']);
1598 if (!empty($cat['theme'])) {
1599 $category->theme = $cat['theme'];
1601 if (!empty($cat['parent']) && ($category->parent != $cat['parent'])) {
1602 // First check if parent exists.
1603 if (!$parent_cat = $DB->get_record('course_categories', array('id' => $cat['parent']))) {
1604 throw new moodle_exception('unknowcategory');
1606 // Then check if we have capability.
1607 self::validate_context(get_category_or_system_context((int)$cat['parent']));
1608 require_capability('moodle/category:manage', get_category_or_system_context((int)$cat['parent']));
1609 // Finally move the category.
1610 move_category($category, $parent_cat);
1611 $category->parent = $cat['parent'];
1612 // Get updated path by move_category().
1613 $category->path = $DB->get_field('course_categories', 'path',
1614 array('id' => $category->id));
1616 $DB->update_record('course_categories', $category);
1619 $transaction->allow_commit();
1623 * Returns description of method result value
1625 * @return external_description
1626 * @since Moodle 2.3
1628 public static function update_categories_returns() {
1629 return null;
1633 * Returns description of method parameters
1635 * @return external_function_parameters
1636 * @since Moodle 2.3
1638 public static function delete_categories_parameters() {
1639 return new external_function_parameters(
1640 array(
1641 'categories' => new external_multiple_structure(
1642 new external_single_structure(
1643 array(
1644 'id' => new external_value(PARAM_INT, 'category id to delete'),
1645 'newparent' => new external_value(PARAM_INT,
1646 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1647 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1648 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1657 * Delete categories
1659 * @param array $categories A list of category ids
1660 * @return array
1661 * @since Moodle 2.3
1663 public static function delete_categories($categories) {
1664 global $CFG, $DB;
1665 require_once($CFG->dirroot . "/course/lib.php");
1667 // Validate parameters.
1668 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1670 $transaction = $DB->start_delegated_transaction();
1672 foreach ($params['categories'] as $category) {
1673 if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
1674 throw new moodle_exception('unknowcategory');
1676 $context = context_coursecat::instance($deletecat->id);
1677 require_capability('moodle/category:manage', $context);
1678 self::validate_context($context);
1679 self::validate_context(get_category_or_system_context($deletecat->parent));
1681 if ($category['recursive']) {
1682 // If recursive was specified, then we recursively delete the category's contents.
1683 category_delete_full($deletecat, false);
1684 } else {
1685 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1686 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1687 // We must move to an existing category.
1688 if (!empty($category['newparent'])) {
1689 if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
1690 throw new moodle_exception('unknowcategory');
1692 $newparent = $category['newparent'];
1693 } else {
1694 $newparent = $deletecat->parent;
1697 // This operation is not allowed. We must move contents to an existing category.
1698 if ($newparent == 0) {
1699 throw new moodle_exception('movecatcontentstoroot');
1702 $parentcontext = get_category_or_system_context($newparent);
1703 require_capability('moodle/category:manage', $parentcontext);
1704 self::validate_context($parentcontext);
1705 category_delete_move($deletecat, $newparent, false);
1709 $transaction->allow_commit();
1713 * Returns description of method parameters
1715 * @return external_function_parameters
1716 * @since Moodle 2.3
1718 public static function delete_categories_returns() {
1719 return null;
1725 * Deprecated course external functions
1727 * @package core_course
1728 * @copyright 2009 Petr Skodak
1729 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1730 * @since Moodle 2.0
1731 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1732 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1733 * @see core_course_external
1735 class moodle_course_external extends external_api {
1738 * Returns description of method parameters
1740 * @return external_function_parameters
1741 * @since Moodle 2.0
1742 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1743 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1744 * @see core_course_external::get_courses_parameters()
1746 public static function get_courses_parameters() {
1747 return core_course_external::get_courses_parameters();
1751 * Get courses
1753 * @param array $options
1754 * @return array
1755 * @since Moodle 2.0
1756 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1757 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1758 * @see core_course_external::get_courses()
1760 public static function get_courses($options) {
1761 return core_course_external::get_courses($options);
1765 * Returns description of method result value
1767 * @return external_description
1768 * @since Moodle 2.0
1769 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1770 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1771 * @see core_course_external::get_courses_returns()
1773 public static function get_courses_returns() {
1774 return core_course_external::get_courses_returns();
1778 * Returns description of method parameters
1780 * @return external_function_parameters
1781 * @since Moodle 2.0
1782 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1783 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1784 * @see core_course_external::create_courses_parameters()
1786 public static function create_courses_parameters() {
1787 return core_course_external::create_courses_parameters();
1791 * Create courses
1793 * @param array $courses
1794 * @return array courses (id and shortname only)
1795 * @since Moodle 2.0
1796 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1797 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1798 * @see core_course_external::create_courses()
1800 public static function create_courses($courses) {
1801 return core_course_external::create_courses($courses);
1805 * Returns description of method result value
1807 * @return external_description
1808 * @since Moodle 2.0
1809 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1810 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1811 * @see core_course_external::create_courses_returns()
1813 public static function create_courses_returns() {
1814 return core_course_external::create_courses_returns();