MDL-36983 fix incorrect test method name
[moodle.git] / course / externallib.php
blob659228c53b14bf526aa0ebb9f6966045e64c3231
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 /**
19 * External course API
21 * @package core_course
22 * @category external
23 * @copyright 2009 Petr Skodak
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die;
29 require_once("$CFG->libdir/externallib.php");
31 /**
32 * Course external functions
34 * @package core_course
35 * @category external
36 * @copyright 2011 Jerome Mouneyrac
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 * @since Moodle 2.2
40 class core_course_external extends external_api {
42 /**
43 * Returns description of method parameters
45 * @return external_function_parameters
46 * @since Moodle 2.2
48 public static function get_course_contents_parameters() {
49 return new external_function_parameters(
50 array('courseid' => new external_value(PARAM_INT, 'course id'),
51 'options' => new external_multiple_structure (
52 new external_single_structure(
53 array('name' => new external_value(PARAM_ALPHANUM, 'option name'),
54 'value' => new external_value(PARAM_RAW, 'the value of the option, this param is personaly validated in the external function.')
56 ), 'Options, not used yet, might be used in later version', VALUE_DEFAULT, array())
61 /**
62 * Get course contents
64 * @param int $courseid course id
65 * @param array $options These options are not used yet, might be used in later version
66 * @return array
67 * @since Moodle 2.2
69 public static function get_course_contents($courseid, $options = array()) {
70 global $CFG, $DB;
71 require_once($CFG->dirroot . "/course/lib.php");
73 //validate parameter
74 $params = self::validate_parameters(self::get_course_contents_parameters(),
75 array('courseid' => $courseid, 'options' => $options));
77 //retrieve the course
78 $course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
80 //check course format exist
81 if (!file_exists($CFG->dirroot . '/course/format/' . $course->format . '/lib.php')) {
82 throw new moodle_exception('cannotgetcoursecontents', 'webservice', '', null, get_string('courseformatnotfound', 'error', '', $course->format));
83 } else {
84 require_once($CFG->dirroot . '/course/format/' . $course->format . '/lib.php');
87 // now security checks
88 $context = context_course::instance($course->id, IGNORE_MISSING);
89 try {
90 self::validate_context($context);
91 } catch (Exception $e) {
92 $exceptionparam = new stdClass();
93 $exceptionparam->message = $e->getMessage();
94 $exceptionparam->courseid = $course->id;
95 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
98 $canupdatecourse = has_capability('moodle/course:update', $context);
100 //create return value
101 $coursecontents = array();
103 if ($canupdatecourse or $course->visible
104 or has_capability('moodle/course:viewhiddencourses', $context)) {
106 //retrieve sections
107 $modinfo = get_fast_modinfo($course);
108 $sections = $modinfo->get_section_info_all();
110 //for each sections (first displayed to last displayed)
111 foreach ($sections as $key => $section) {
113 if (!$section->uservisible) {
114 continue;
117 // reset $sectioncontents
118 $sectionvalues = array();
119 $sectionvalues['id'] = $section->id;
120 $sectionvalues['name'] = get_section_name($course, $section);
121 $sectionvalues['visible'] = $section->visible;
122 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
123 external_format_text($section->summary, $section->summaryformat,
124 $context->id, 'course', 'section', $section->id);
125 $sectioncontents = array();
127 //for each module of the section
128 foreach ($modinfo->sections[$section->section] as $cmid) { //matching /course/lib.php:print_section() logic
129 $cm = $modinfo->cms[$cmid];
131 // stop here if the module is not visible to the user
132 if (!$cm->uservisible) {
133 continue;
136 $module = array();
138 //common info (for people being able to see the module or availability dates)
139 $module['id'] = $cm->id;
140 $module['name'] = format_string($cm->name, true);
141 $module['modname'] = $cm->modname;
142 $module['modplural'] = $cm->modplural;
143 $module['modicon'] = $cm->get_icon_url()->out(false);
144 $module['indent'] = $cm->indent;
146 $modcontext = context_module::instance($cm->id);
148 if (!empty($cm->showdescription)) {
149 $module['description'] = $cm->get_content();
152 //url of the module
153 $url = $cm->get_url();
154 if ($url) { //labels don't have url
155 $module['url'] = $cm->get_url()->out();
158 $canviewhidden = has_capability('moodle/course:viewhiddenactivities',
159 context_module::instance($cm->id));
160 //user that can view hidden module should know about the visibility
161 $module['visible'] = $cm->visible;
163 //availability date (also send to user who can see hidden module when the showavailabilyt is ON)
164 if ($canupdatecourse or ($CFG->enableavailability && $canviewhidden && $cm->showavailability)) {
165 $module['availablefrom'] = $cm->availablefrom;
166 $module['availableuntil'] = $cm->availableuntil;
169 $baseurl = 'webservice/pluginfile.php';
171 //call $modulename_export_contents
172 //(each module callback take care about checking the capabilities)
173 require_once($CFG->dirroot . '/mod/' . $cm->modname . '/lib.php');
174 $getcontentfunction = $cm->modname.'_export_contents';
175 if (function_exists($getcontentfunction)) {
176 if ($contents = $getcontentfunction($cm, $baseurl)) {
177 $module['contents'] = $contents;
181 //assign result to $sectioncontents
182 $sectioncontents[] = $module;
185 $sectionvalues['modules'] = $sectioncontents;
187 // assign result to $coursecontents
188 $coursecontents[] = $sectionvalues;
191 return $coursecontents;
195 * Returns description of method result value
197 * @return external_description
198 * @since Moodle 2.2
200 public static function get_course_contents_returns() {
201 return new external_multiple_structure(
202 new external_single_structure(
203 array(
204 'id' => new external_value(PARAM_INT, 'Section ID'),
205 'name' => new external_value(PARAM_TEXT, 'Section name'),
206 'visible' => new external_value(PARAM_INT, 'is the section visible', VALUE_OPTIONAL),
207 'summary' => new external_value(PARAM_RAW, 'Section description'),
208 'summaryformat' => new external_format_value('summary'),
209 'modules' => new external_multiple_structure(
210 new external_single_structure(
211 array(
212 'id' => new external_value(PARAM_INT, 'activity id'),
213 'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
214 'name' => new external_value(PARAM_TEXT, 'activity module name'),
215 'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
216 'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
217 'modicon' => new external_value(PARAM_URL, 'activity icon url'),
218 'modname' => new external_value(PARAM_PLUGIN, 'activity module type'),
219 'modplural' => new external_value(PARAM_TEXT, 'activity module plural name'),
220 'availablefrom' => new external_value(PARAM_INT, 'module availability start date', VALUE_OPTIONAL),
221 'availableuntil' => new external_value(PARAM_INT, 'module availability en date', VALUE_OPTIONAL),
222 'indent' => new external_value(PARAM_INT, 'number of identation in the site'),
223 'contents' => new external_multiple_structure(
224 new external_single_structure(
225 array(
226 // content info
227 'type'=> new external_value(PARAM_TEXT, 'a file or a folder or external link'),
228 'filename'=> new external_value(PARAM_FILE, 'filename'),
229 'filepath'=> new external_value(PARAM_PATH, 'filepath'),
230 'filesize'=> new external_value(PARAM_INT, 'filesize'),
231 'fileurl' => new external_value(PARAM_URL, 'downloadable file url', VALUE_OPTIONAL),
232 'content' => new external_value(PARAM_RAW, 'Raw content, will be used when type is content', VALUE_OPTIONAL),
233 'timecreated' => new external_value(PARAM_INT, 'Time created'),
234 'timemodified' => new external_value(PARAM_INT, 'Time modified'),
235 'sortorder' => new external_value(PARAM_INT, 'Content sort order'),
237 // copyright related info
238 'userid' => new external_value(PARAM_INT, 'User who added this content to moodle'),
239 'author' => new external_value(PARAM_TEXT, 'Content owner'),
240 'license' => new external_value(PARAM_TEXT, 'Content license'),
242 ), VALUE_DEFAULT, array()
245 ), 'list of module'
253 * Returns description of method parameters
255 * @return external_function_parameters
256 * @since Moodle 2.3
258 public static function get_courses_parameters() {
259 return new external_function_parameters(
260 array('options' => new external_single_structure(
261 array('ids' => new external_multiple_structure(
262 new external_value(PARAM_INT, 'Course id')
263 , 'List of course id. If empty return all courses
264 except front page course.',
265 VALUE_OPTIONAL)
266 ), 'options - operator OR is used', VALUE_DEFAULT, array())
272 * Get courses
274 * @param array $options It contains an array (list of ids)
275 * @return array
276 * @since Moodle 2.2
278 public static function get_courses($options = array()) {
279 global $CFG, $DB;
280 require_once($CFG->dirroot . "/course/lib.php");
282 //validate parameter
283 $params = self::validate_parameters(self::get_courses_parameters(),
284 array('options' => $options));
286 //retrieve courses
287 if (!array_key_exists('ids', $params['options'])
288 or empty($params['options']['ids'])) {
289 $courses = $DB->get_records('course');
290 } else {
291 $courses = $DB->get_records_list('course', 'id', $params['options']['ids']);
294 //create return value
295 $coursesinfo = array();
296 foreach ($courses as $course) {
298 // now security checks
299 $context = context_course::instance($course->id, IGNORE_MISSING);
300 $courseformatoptions = course_get_format($course)->get_format_options();
301 try {
302 self::validate_context($context);
303 } catch (Exception $e) {
304 $exceptionparam = new stdClass();
305 $exceptionparam->message = $e->getMessage();
306 $exceptionparam->courseid = $course->id;
307 throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
309 require_capability('moodle/course:view', $context);
311 $courseinfo = array();
312 $courseinfo['id'] = $course->id;
313 $courseinfo['fullname'] = $course->fullname;
314 $courseinfo['shortname'] = $course->shortname;
315 $courseinfo['categoryid'] = $course->category;
316 list($courseinfo['summary'], $courseinfo['summaryformat']) =
317 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', 0);
318 $courseinfo['format'] = $course->format;
319 $courseinfo['startdate'] = $course->startdate;
320 if (array_key_exists('numsections', $courseformatoptions)) {
321 // For backward-compartibility
322 $courseinfo['numsections'] = $courseformatoptions['numsections'];
325 //some field should be returned only if the user has update permission
326 $courseadmin = has_capability('moodle/course:update', $context);
327 if ($courseadmin) {
328 $courseinfo['categorysortorder'] = $course->sortorder;
329 $courseinfo['idnumber'] = $course->idnumber;
330 $courseinfo['showgrades'] = $course->showgrades;
331 $courseinfo['showreports'] = $course->showreports;
332 $courseinfo['newsitems'] = $course->newsitems;
333 $courseinfo['visible'] = $course->visible;
334 $courseinfo['maxbytes'] = $course->maxbytes;
335 if (array_key_exists('hiddensections', $courseformatoptions)) {
336 // For backward-compartibility
337 $courseinfo['hiddensections'] = $courseformatoptions['hiddensections'];
339 $courseinfo['groupmode'] = $course->groupmode;
340 $courseinfo['groupmodeforce'] = $course->groupmodeforce;
341 $courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
342 $courseinfo['lang'] = $course->lang;
343 $courseinfo['timecreated'] = $course->timecreated;
344 $courseinfo['timemodified'] = $course->timemodified;
345 $courseinfo['forcetheme'] = $course->theme;
346 $courseinfo['enablecompletion'] = $course->enablecompletion;
347 $courseinfo['completionstartonenrol'] = $course->completionstartonenrol;
348 $courseinfo['completionnotify'] = $course->completionnotify;
349 $courseinfo['courseformatoptions'] = array();
350 foreach ($courseformatoptions as $key => $value) {
351 $courseinfo['courseformatoptions'][] = array(
352 'name' => $key,
353 'value' => $value
358 if ($courseadmin or $course->visible
359 or has_capability('moodle/course:viewhiddencourses', $context)) {
360 $coursesinfo[] = $courseinfo;
364 return $coursesinfo;
368 * Returns description of method result value
370 * @return external_description
371 * @since Moodle 2.2
373 public static function get_courses_returns() {
374 return new external_multiple_structure(
375 new external_single_structure(
376 array(
377 'id' => new external_value(PARAM_INT, 'course id'),
378 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
379 'categoryid' => new external_value(PARAM_INT, 'category id'),
380 'categorysortorder' => new external_value(PARAM_INT,
381 'sort order into the category', VALUE_OPTIONAL),
382 'fullname' => new external_value(PARAM_TEXT, 'full name'),
383 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
384 'summary' => new external_value(PARAM_RAW, 'summary'),
385 'summaryformat' => new external_format_value('summary'),
386 'format' => new external_value(PARAM_PLUGIN,
387 'course format: weeks, topics, social, site,..'),
388 'showgrades' => new external_value(PARAM_INT,
389 '1 if grades are shown, otherwise 0', VALUE_OPTIONAL),
390 'newsitems' => new external_value(PARAM_INT,
391 'number of recent items appearing on the course page', VALUE_OPTIONAL),
392 'startdate' => new external_value(PARAM_INT,
393 'timestamp when the course start'),
394 'numsections' => new external_value(PARAM_INT,
395 '(deprecated, use courseformatoptions) number of weeks/topics',
396 VALUE_OPTIONAL),
397 'maxbytes' => new external_value(PARAM_INT,
398 'largest size of file that can be uploaded into the course',
399 VALUE_OPTIONAL),
400 'showreports' => new external_value(PARAM_INT,
401 'are activity report shown (yes = 1, no =0)', VALUE_OPTIONAL),
402 'visible' => new external_value(PARAM_INT,
403 '1: available to student, 0:not available', VALUE_OPTIONAL),
404 'hiddensections' => new external_value(PARAM_INT,
405 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
406 VALUE_OPTIONAL),
407 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
408 VALUE_OPTIONAL),
409 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
410 VALUE_OPTIONAL),
411 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
412 VALUE_OPTIONAL),
413 'timecreated' => new external_value(PARAM_INT,
414 'timestamp when the course have been created', VALUE_OPTIONAL),
415 'timemodified' => new external_value(PARAM_INT,
416 'timestamp when the course have been modified', VALUE_OPTIONAL),
417 'enablecompletion' => new external_value(PARAM_INT,
418 'Enabled, control via completion and activity settings. Disbaled,
419 not shown in activity settings.',
420 VALUE_OPTIONAL),
421 'completionstartonenrol' => new external_value(PARAM_INT,
422 '1: begin tracking a student\'s progress in course completion
423 after course enrolment. 0: does not',
424 VALUE_OPTIONAL),
425 'completionnotify' => new external_value(PARAM_INT,
426 '1: yes 0: no', VALUE_OPTIONAL),
427 'lang' => new external_value(PARAM_SAFEDIR,
428 'forced course language', VALUE_OPTIONAL),
429 'forcetheme' => new external_value(PARAM_PLUGIN,
430 'name of the force theme', VALUE_OPTIONAL),
431 'courseformatoptions' => new external_multiple_structure(
432 new external_single_structure(
433 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
434 'value' => new external_value(PARAM_RAW, 'course format option value')
436 'additional options for particular course format', VALUE_OPTIONAL
438 ), 'course'
444 * Returns description of method parameters
446 * @return external_function_parameters
447 * @since Moodle 2.2
449 public static function create_courses_parameters() {
450 $courseconfig = get_config('moodlecourse'); //needed for many default values
451 return new external_function_parameters(
452 array(
453 'courses' => new external_multiple_structure(
454 new external_single_structure(
455 array(
456 'fullname' => new external_value(PARAM_TEXT, 'full name'),
457 'shortname' => new external_value(PARAM_TEXT, 'course short name'),
458 'categoryid' => new external_value(PARAM_INT, 'category id'),
459 'idnumber' => new external_value(PARAM_RAW, 'id number', VALUE_OPTIONAL),
460 'summary' => new external_value(PARAM_RAW, 'summary', VALUE_OPTIONAL),
461 'summaryformat' => new external_format_value('summary', VALUE_DEFAULT),
462 'format' => new external_value(PARAM_PLUGIN,
463 'course format: weeks, topics, social, site,..',
464 VALUE_DEFAULT, $courseconfig->format),
465 'showgrades' => new external_value(PARAM_INT,
466 '1 if grades are shown, otherwise 0', VALUE_DEFAULT,
467 $courseconfig->showgrades),
468 'newsitems' => new external_value(PARAM_INT,
469 'number of recent items appearing on the course page',
470 VALUE_DEFAULT, $courseconfig->newsitems),
471 'startdate' => new external_value(PARAM_INT,
472 'timestamp when the course start', VALUE_OPTIONAL),
473 'numsections' => new external_value(PARAM_INT,
474 '(deprecated, use courseformatoptions) number of weeks/topics',
475 VALUE_OPTIONAL),
476 'maxbytes' => new external_value(PARAM_INT,
477 'largest size of file that can be uploaded into the course',
478 VALUE_DEFAULT, $courseconfig->maxbytes),
479 'showreports' => new external_value(PARAM_INT,
480 'are activity report shown (yes = 1, no =0)', VALUE_DEFAULT,
481 $courseconfig->showreports),
482 'visible' => new external_value(PARAM_INT,
483 '1: available to student, 0:not available', VALUE_OPTIONAL),
484 'hiddensections' => new external_value(PARAM_INT,
485 '(deprecated, use courseformatoptions) How the hidden sections in the course are displayed to students',
486 VALUE_OPTIONAL),
487 'groupmode' => new external_value(PARAM_INT, 'no group, separate, visible',
488 VALUE_DEFAULT, $courseconfig->groupmode),
489 'groupmodeforce' => new external_value(PARAM_INT, '1: yes, 0: no',
490 VALUE_DEFAULT, $courseconfig->groupmodeforce),
491 'defaultgroupingid' => new external_value(PARAM_INT, 'default grouping id',
492 VALUE_DEFAULT, 0),
493 'enablecompletion' => new external_value(PARAM_INT,
494 'Enabled, control via completion and activity settings. Disabled,
495 not shown in activity settings.',
496 VALUE_OPTIONAL),
497 'completionstartonenrol' => new external_value(PARAM_INT,
498 '1: begin tracking a student\'s progress in course completion after
499 course enrolment. 0: does not',
500 VALUE_OPTIONAL),
501 'completionnotify' => new external_value(PARAM_INT,
502 '1: yes 0: no', VALUE_OPTIONAL),
503 'lang' => new external_value(PARAM_SAFEDIR,
504 'forced course language', VALUE_OPTIONAL),
505 'forcetheme' => new external_value(PARAM_PLUGIN,
506 'name of the force theme', VALUE_OPTIONAL),
507 'courseformatoptions' => new external_multiple_structure(
508 new external_single_structure(
509 array('name' => new external_value(PARAM_ALPHANUMEXT, 'course format option name'),
510 'value' => new external_value(PARAM_RAW, 'course format option value')
512 'additional options for particular course format', VALUE_OPTIONAL),
514 ), 'courses to create'
521 * Create courses
523 * @param array $courses
524 * @return array courses (id and shortname only)
525 * @since Moodle 2.2
527 public static function create_courses($courses) {
528 global $CFG, $DB;
529 require_once($CFG->dirroot . "/course/lib.php");
530 require_once($CFG->libdir . '/completionlib.php');
532 $params = self::validate_parameters(self::create_courses_parameters(),
533 array('courses' => $courses));
535 $availablethemes = get_plugin_list('theme');
536 $availablelangs = get_string_manager()->get_list_of_translations();
538 $transaction = $DB->start_delegated_transaction();
540 foreach ($params['courses'] as $course) {
542 // Ensure the current user is allowed to run this function
543 $context = context_coursecat::instance($course['categoryid'], IGNORE_MISSING);
544 try {
545 self::validate_context($context);
546 } catch (Exception $e) {
547 $exceptionparam = new stdClass();
548 $exceptionparam->message = $e->getMessage();
549 $exceptionparam->catid = $course['categoryid'];
550 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
552 require_capability('moodle/course:create', $context);
554 // Make sure lang is valid
555 if (array_key_exists('lang', $course) and empty($availablelangs[$course['lang']])) {
556 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'lang');
559 // Make sure theme is valid
560 if (array_key_exists('forcetheme', $course)) {
561 if (!empty($CFG->allowcoursethemes)) {
562 if (empty($availablethemes[$course['forcetheme']])) {
563 throw new moodle_exception('errorinvalidparam', 'webservice', '', 'forcetheme');
564 } else {
565 $course['theme'] = $course['forcetheme'];
570 //force visibility if ws user doesn't have the permission to set it
571 $category = $DB->get_record('course_categories', array('id' => $course['categoryid']));
572 if (!has_capability('moodle/course:visibility', $context)) {
573 $course['visible'] = $category->visible;
576 //set default value for completion
577 $courseconfig = get_config('moodlecourse');
578 if (completion_info::is_enabled_for_site()) {
579 if (!array_key_exists('enablecompletion', $course)) {
580 $course['enablecompletion'] = $courseconfig->enablecompletion;
582 if (!array_key_exists('completionstartonenrol', $course)) {
583 $course['completionstartonenrol'] = $courseconfig->completionstartonenrol;
585 } else {
586 $course['enablecompletion'] = 0;
587 $course['completionstartonenrol'] = 0;
590 $course['category'] = $course['categoryid'];
592 // Summary format.
593 $course['summaryformat'] = external_validate_format($course['summaryformat']);
595 if (!empty($course['courseformatoptions'])) {
596 foreach ($course['courseformatoptions'] as $option) {
597 $course[$option['name']] = $option['value'];
601 //Note: create_course() core function check shortname, idnumber, category
602 $course['id'] = create_course((object) $course)->id;
604 $resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
607 $transaction->allow_commit();
609 return $resultcourses;
613 * Returns description of method result value
615 * @return external_description
616 * @since Moodle 2.2
618 public static function create_courses_returns() {
619 return new external_multiple_structure(
620 new external_single_structure(
621 array(
622 'id' => new external_value(PARAM_INT, 'course id'),
623 'shortname' => new external_value(PARAM_TEXT, 'short name'),
630 * Returns description of method parameters
632 * @return external_function_parameters
633 * @since Moodle 2.2
635 public static function delete_courses_parameters() {
636 return new external_function_parameters(
637 array(
638 'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
644 * Delete courses
646 * @param array $courseids A list of course ids
647 * @since Moodle 2.2
649 public static function delete_courses($courseids) {
650 global $CFG, $DB;
651 require_once($CFG->dirroot."/course/lib.php");
653 // Parameter validation.
654 $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
656 $transaction = $DB->start_delegated_transaction();
658 foreach ($params['courseids'] as $courseid) {
659 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
661 // Check if the context is valid.
662 $coursecontext = context_course::instance($course->id);
663 self::validate_context($coursecontext);
665 // Check if the current user has enought permissions.
666 if (!can_delete_course($courseid)) {
667 throw new moodle_exception('cannotdeletecategorycourse', 'error',
668 '', format_string($course->fullname)." (id: $courseid)");
671 delete_course($course, false);
674 $transaction->allow_commit();
676 return null;
680 * Returns description of method result value
682 * @return external_description
683 * @since Moodle 2.2
685 public static function delete_courses_returns() {
686 return null;
690 * Returns description of method parameters
692 * @return external_function_parameters
693 * @since Moodle 2.3
695 public static function duplicate_course_parameters() {
696 return new external_function_parameters(
697 array(
698 'courseid' => new external_value(PARAM_INT, 'course to duplicate id'),
699 'fullname' => new external_value(PARAM_TEXT, 'duplicated course full name'),
700 'shortname' => new external_value(PARAM_TEXT, 'duplicated course short name'),
701 'categoryid' => new external_value(PARAM_INT, 'duplicated course category parent'),
702 'visible' => new external_value(PARAM_INT, 'duplicated course visible, default to yes', VALUE_DEFAULT, 1),
703 'options' => new external_multiple_structure(
704 new external_single_structure(
705 array(
706 'name' => new external_value(PARAM_ALPHAEXT, 'The backup option name:
707 "activities" (int) Include course activites (default to 1 that is equal to yes),
708 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
709 "filters" (int) Include course filters (default to 1 that is equal to yes),
710 "users" (int) Include users (default to 0 that is equal to no),
711 "role_assignments" (int) Include role assignments (default to 0 that is equal to no),
712 "comments" (int) Include user comments (default to 0 that is equal to no),
713 "completion_information" (int) Include user course completion information (default to 0 that is equal to no),
714 "logs" (int) Include course logs (default to 0 that is equal to no),
715 "histories" (int) Include histories (default to 0 that is equal to no)'
717 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
720 ), VALUE_DEFAULT, array()
727 * Duplicate a course
729 * @param int $courseid
730 * @param string $fullname Duplicated course fullname
731 * @param string $shortname Duplicated course shortname
732 * @param int $categoryid Duplicated course parent category id
733 * @param int $visible Duplicated course availability
734 * @param array $options List of backup options
735 * @return array New course info
736 * @since Moodle 2.3
738 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible = 1, $options = array()) {
739 global $CFG, $USER, $DB;
740 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
741 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
743 // Parameter validation.
744 $params = self::validate_parameters(
745 self::duplicate_course_parameters(),
746 array(
747 'courseid' => $courseid,
748 'fullname' => $fullname,
749 'shortname' => $shortname,
750 'categoryid' => $categoryid,
751 'visible' => $visible,
752 'options' => $options
756 // Context validation.
758 if (! ($course = $DB->get_record('course', array('id'=>$params['courseid'])))) {
759 throw new moodle_exception('invalidcourseid', 'error');
762 // Category where duplicated course is going to be created.
763 $categorycontext = context_coursecat::instance($params['categoryid']);
764 self::validate_context($categorycontext);
766 // Course to be duplicated.
767 $coursecontext = context_course::instance($course->id);
768 self::validate_context($coursecontext);
770 $backupdefaults = array(
771 'activities' => 1,
772 'blocks' => 1,
773 'filters' => 1,
774 'users' => 0,
775 'role_assignments' => 0,
776 'comments' => 0,
777 'completion_information' => 0,
778 'logs' => 0,
779 'histories' => 0
782 $backupsettings = array();
783 // Check for backup and restore options.
784 if (!empty($params['options'])) {
785 foreach ($params['options'] as $option) {
787 // Strict check for a correct value (allways 1 or 0, true or false).
788 $value = clean_param($option['value'], PARAM_INT);
790 if ($value !== 0 and $value !== 1) {
791 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
794 if (!isset($backupdefaults[$option['name']])) {
795 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
798 $backupsettings[$option['name']] = $value;
802 // Capability checking.
804 // The backup controller check for this currently, this may be redundant.
805 require_capability('moodle/course:create', $categorycontext);
806 require_capability('moodle/restore:restorecourse', $categorycontext);
807 require_capability('moodle/backup:backupcourse', $coursecontext);
809 if (!empty($backupsettings['users'])) {
810 require_capability('moodle/backup:userinfo', $coursecontext);
811 require_capability('moodle/restore:userinfo', $categorycontext);
814 // Check if the shortname is used.
815 if ($foundcourses = $DB->get_records('course', array('shortname'=>$shortname))) {
816 foreach ($foundcourses as $foundcourse) {
817 $foundcoursenames[] = $foundcourse->fullname;
820 $foundcoursenamestring = implode(',', $foundcoursenames);
821 throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
824 // Backup the course.
826 $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
827 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
829 foreach ($backupsettings as $name => $value) {
830 $bc->get_plan()->get_setting($name)->set_value($value);
833 $backupid = $bc->get_backupid();
834 $backupbasepath = $bc->get_plan()->get_basepath();
836 $bc->execute_plan();
837 $results = $bc->get_results();
838 $file = $results['backup_destination'];
840 $bc->destroy();
842 // Restore the backup immediately.
844 // Check if we need to unzip the file because the backup temp dir does not contains backup files.
845 if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
846 $file->extract_to_pathname(get_file_packer(), $backupbasepath);
849 // Create new course.
850 $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
852 $rc = new restore_controller($backupid, $newcourseid,
853 backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
855 foreach ($backupsettings as $name => $value) {
856 $setting = $rc->get_plan()->get_setting($name);
857 if ($setting->get_status() == backup_setting::NOT_LOCKED) {
858 $setting->set_value($value);
862 if (!$rc->execute_precheck()) {
863 $precheckresults = $rc->get_precheck_results();
864 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
865 if (empty($CFG->keeptempdirectoriesonbackup)) {
866 fulldelete($backupbasepath);
869 $errorinfo = '';
871 foreach ($precheckresults['errors'] as $error) {
872 $errorinfo .= $error;
875 if (array_key_exists('warnings', $precheckresults)) {
876 foreach ($precheckresults['warnings'] as $warning) {
877 $errorinfo .= $warning;
881 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
885 $rc->execute_plan();
886 $rc->destroy();
888 $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
889 $course->fullname = $params['fullname'];
890 $course->shortname = $params['shortname'];
891 $course->visible = $params['visible'];
893 // Set shortname and fullname back.
894 $DB->update_record('course', $course);
896 if (empty($CFG->keeptempdirectoriesonbackup)) {
897 fulldelete($backupbasepath);
900 // Delete the course backup file created by this WebService. Originally located in the course backups area.
901 $file->delete();
903 return array('id' => $course->id, 'shortname' => $course->shortname);
907 * Returns description of method result value
909 * @return external_description
910 * @since Moodle 2.3
912 public static function duplicate_course_returns() {
913 return new external_single_structure(
914 array(
915 'id' => new external_value(PARAM_INT, 'course id'),
916 'shortname' => new external_value(PARAM_TEXT, 'short name'),
922 * Returns description of method parameters for import_course
924 * @return external_function_parameters
925 * @since Moodle 2.4
927 public static function import_course_parameters() {
928 return new external_function_parameters(
929 array(
930 'importfrom' => new external_value(PARAM_INT, 'the id of the course we are importing from'),
931 'importto' => new external_value(PARAM_INT, 'the id of the course we are importing to'),
932 'deletecontent' => new external_value(PARAM_INT, 'whether to delete the course content where we are importing to (default to 0 = No)', VALUE_DEFAULT, 0),
933 'options' => new external_multiple_structure(
934 new external_single_structure(
935 array(
936 'name' => new external_value(PARAM_ALPHA, 'The backup option name:
937 "activities" (int) Include course activites (default to 1 that is equal to yes),
938 "blocks" (int) Include course blocks (default to 1 that is equal to yes),
939 "filters" (int) Include course filters (default to 1 that is equal to yes)'
941 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
944 ), VALUE_DEFAULT, array()
951 * Imports a course
953 * @param int $importfrom The id of the course we are importing from
954 * @param int $importto The id of the course we are importing to
955 * @param bool $deletecontent Whether to delete the course we are importing to content
956 * @param array $options List of backup options
957 * @return null
958 * @since Moodle 2.4
960 public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) {
961 global $CFG, $USER, $DB;
962 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
963 require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
965 // Parameter validation.
966 $params = self::validate_parameters(
967 self::import_course_parameters(),
968 array(
969 'importfrom' => $importfrom,
970 'importto' => $importto,
971 'deletecontent' => $deletecontent,
972 'options' => $options
976 if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) {
977 throw new moodle_exception('invalidextparam', 'webservice', '', $option['deletecontent']);
980 // Context validation.
982 if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) {
983 throw new moodle_exception('invalidcourseid', 'error');
986 if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) {
987 throw new moodle_exception('invalidcourseid', 'error');
990 $importfromcontext = context_course::instance($importfrom->id);
991 self::validate_context($importfromcontext);
993 $importtocontext = context_course::instance($importto->id);
994 self::validate_context($importtocontext);
996 $backupdefaults = array(
997 'activities' => 1,
998 'blocks' => 1,
999 'filters' => 1
1002 $backupsettings = array();
1004 // Check for backup and restore options.
1005 if (!empty($params['options'])) {
1006 foreach ($params['options'] as $option) {
1008 // Strict check for a correct value (allways 1 or 0, true or false).
1009 $value = clean_param($option['value'], PARAM_INT);
1011 if ($value !== 0 and $value !== 1) {
1012 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1015 if (!isset($backupdefaults[$option['name']])) {
1016 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
1019 $backupsettings[$option['name']] = $value;
1023 // Capability checking.
1025 require_capability('moodle/backup:backuptargetimport', $importfromcontext);
1026 require_capability('moodle/restore:restoretargetimport', $importtocontext);
1028 $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE,
1029 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
1031 foreach ($backupsettings as $name => $value) {
1032 $bc->get_plan()->get_setting($name)->set_value($value);
1035 $backupid = $bc->get_backupid();
1036 $backupbasepath = $bc->get_plan()->get_basepath();
1038 $bc->execute_plan();
1039 $bc->destroy();
1041 // Restore the backup immediately.
1043 // Check if we must delete the contents of the destination course.
1044 if ($params['deletecontent']) {
1045 $restoretarget = backup::TARGET_EXISTING_DELETING;
1046 } else {
1047 $restoretarget = backup::TARGET_EXISTING_ADDING;
1050 $rc = new restore_controller($backupid, $importto->id,
1051 backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget);
1053 foreach ($backupsettings as $name => $value) {
1054 $rc->get_plan()->get_setting($name)->set_value($value);
1057 if (!$rc->execute_precheck()) {
1058 $precheckresults = $rc->get_precheck_results();
1059 if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
1060 if (empty($CFG->keeptempdirectoriesonbackup)) {
1061 fulldelete($backupbasepath);
1064 $errorinfo = '';
1066 foreach ($precheckresults['errors'] as $error) {
1067 $errorinfo .= $error;
1070 if (array_key_exists('warnings', $precheckresults)) {
1071 foreach ($precheckresults['warnings'] as $warning) {
1072 $errorinfo .= $warning;
1076 throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
1078 } else {
1079 if ($restoretarget == backup::TARGET_EXISTING_DELETING) {
1080 restore_dbops::delete_course_content($importto->id);
1084 $rc->execute_plan();
1085 $rc->destroy();
1087 if (empty($CFG->keeptempdirectoriesonbackup)) {
1088 fulldelete($backupbasepath);
1091 return null;
1095 * Returns description of method result value
1097 * @return external_description
1098 * @since Moodle 2.4
1100 public static function import_course_returns() {
1101 return null;
1105 * Returns description of method parameters
1107 * @return external_function_parameters
1108 * @since Moodle 2.3
1110 public static function get_categories_parameters() {
1111 return new external_function_parameters(
1112 array(
1113 'criteria' => new external_multiple_structure(
1114 new external_single_structure(
1115 array(
1116 'key' => new external_value(PARAM_ALPHA,
1117 'The category column to search, expected keys (value format) are:'.
1118 '"id" (int) the category id,'.
1119 '"name" (string) the category name,'.
1120 '"parent" (int) the parent category id,'.
1121 '"idnumber" (string) category idnumber'.
1122 ' - user must have \'moodle/category:manage\' to search on idnumber,'.
1123 '"visible" (int) whether the returned categories must be visible or hidden. If the key is not passed,
1124 then the function return all categories that the user can see.'.
1125 ' - user must have \'moodle/category:manage\' or \'moodle/category:viewhiddencategories\' to search on visible,'.
1126 '"theme" (string) only return the categories having this theme'.
1127 ' - user must have \'moodle/category:manage\' to search on theme'),
1128 'value' => new external_value(PARAM_RAW, 'the value to match')
1130 ), 'criteria', VALUE_DEFAULT, array()
1132 'addsubcategories' => new external_value(PARAM_BOOL, 'return the sub categories infos
1133 (1 - default) otherwise only the category info (0)', VALUE_DEFAULT, 1)
1139 * Get categories
1141 * @param array $criteria Criteria to match the results
1142 * @param booln $addsubcategories obtain only the category (false) or its subcategories (true - default)
1143 * @return array list of categories
1144 * @since Moodle 2.3
1146 public static function get_categories($criteria = array(), $addsubcategories = true) {
1147 global $CFG, $DB;
1148 require_once($CFG->dirroot . "/course/lib.php");
1150 // Validate parameters.
1151 $params = self::validate_parameters(self::get_categories_parameters(),
1152 array('criteria' => $criteria, 'addsubcategories' => $addsubcategories));
1154 // Retrieve the categories.
1155 $categories = array();
1156 if (!empty($params['criteria'])) {
1158 $conditions = array();
1159 $wheres = array();
1160 foreach ($params['criteria'] as $crit) {
1161 $key = trim($crit['key']);
1163 // Trying to avoid duplicate keys.
1164 if (!isset($conditions[$key])) {
1166 $context = context_system::instance();
1167 $value = null;
1168 switch ($key) {
1169 case 'id':
1170 $value = clean_param($crit['value'], PARAM_INT);
1171 break;
1173 case 'idnumber':
1174 if (has_capability('moodle/category:manage', $context)) {
1175 $value = clean_param($crit['value'], PARAM_RAW);
1176 } else {
1177 // We must throw an exception.
1178 // Otherwise the dev client would think no idnumber exists.
1179 throw new moodle_exception('criteriaerror',
1180 'webservice', '', null,
1181 'You don\'t have the permissions to search on the "idnumber" field.');
1183 break;
1185 case 'name':
1186 $value = clean_param($crit['value'], PARAM_TEXT);
1187 break;
1189 case 'parent':
1190 $value = clean_param($crit['value'], PARAM_INT);
1191 break;
1193 case 'visible':
1194 if (has_capability('moodle/category:manage', $context)
1195 or has_capability('moodle/category:viewhiddencategories',
1196 context_system::instance())) {
1197 $value = clean_param($crit['value'], PARAM_INT);
1198 } else {
1199 throw new moodle_exception('criteriaerror',
1200 'webservice', '', null,
1201 'You don\'t have the permissions to search on the "visible" field.');
1203 break;
1205 case 'theme':
1206 if (has_capability('moodle/category:manage', $context)) {
1207 $value = clean_param($crit['value'], PARAM_THEME);
1208 } else {
1209 throw new moodle_exception('criteriaerror',
1210 'webservice', '', null,
1211 'You don\'t have the permissions to search on the "theme" field.');
1213 break;
1215 default:
1216 throw new moodle_exception('criteriaerror',
1217 'webservice', '', null,
1218 'You can not search on this criteria: ' . $key);
1221 if (isset($value)) {
1222 $conditions[$key] = $crit['value'];
1223 $wheres[] = $key . " = :" . $key;
1228 if (!empty($wheres)) {
1229 $wheres = implode(" AND ", $wheres);
1231 $categories = $DB->get_records_select('course_categories', $wheres, $conditions);
1233 // Retrieve its sub subcategories (all levels).
1234 if ($categories and !empty($params['addsubcategories'])) {
1235 $newcategories = array();
1237 // Check if we required visible/theme checks.
1238 $additionalselect = '';
1239 $additionalparams = array();
1240 if (isset($conditions['visible'])) {
1241 $additionalselect .= ' AND visible = :visible';
1242 $additionalparams['visible'] = $conditions['visible'];
1244 if (isset($conditions['theme'])) {
1245 $additionalselect .= ' AND theme= :theme';
1246 $additionalparams['theme'] = $conditions['theme'];
1249 foreach ($categories as $category) {
1250 $sqlselect = $DB->sql_like('path', ':path') . $additionalselect;
1251 $sqlparams = array('path' => $category->path.'/%') + $additionalparams; // It will NOT include the specified category.
1252 $subcategories = $DB->get_records_select('course_categories', $sqlselect, $sqlparams);
1253 $newcategories = $newcategories + $subcategories; // Both arrays have integer as keys.
1255 $categories = $categories + $newcategories;
1259 } else {
1260 // Retrieve all categories in the database.
1261 $categories = $DB->get_records('course_categories');
1264 // The not returned categories. key => category id, value => reason of exclusion.
1265 $excludedcats = array();
1267 // The returned categories.
1268 $categoriesinfo = array();
1270 // We need to sort the categories by path.
1271 // The parent cats need to be checked by the algo first.
1272 usort($categories, "core_course_external::compare_categories_by_path");
1274 foreach ($categories as $category) {
1276 // Check if the category is a child of an excluded category, if yes exclude it too (excluded => do not return).
1277 $parents = explode('/', $category->path);
1278 unset($parents[0]); // First key is always empty because path start with / => /1/2/4.
1279 foreach ($parents as $parentid) {
1280 // Note: when the parent exclusion was due to the context,
1281 // the sub category could still be returned.
1282 if (isset($excludedcats[$parentid]) and $excludedcats[$parentid] != 'context') {
1283 $excludedcats[$category->id] = 'parent';
1287 // Check category depth is <= maxdepth (do not check for user who can manage categories).
1288 if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
1289 and !has_capability('moodle/category:manage', $context)) {
1290 $excludedcats[$category->id] = 'depth';
1293 // Check the user can use the category context.
1294 $context = context_coursecat::instance($category->id);
1295 try {
1296 self::validate_context($context);
1297 } catch (Exception $e) {
1298 $excludedcats[$category->id] = 'context';
1300 // If it was the requested category then throw an exception.
1301 if (isset($params['categoryid']) && $category->id == $params['categoryid']) {
1302 $exceptionparam = new stdClass();
1303 $exceptionparam->message = $e->getMessage();
1304 $exceptionparam->catid = $category->id;
1305 throw new moodle_exception('errorcatcontextnotvalid', 'webservice', '', $exceptionparam);
1309 // Return the category information.
1310 if (!isset($excludedcats[$category->id])) {
1312 // Final check to see if the category is visible to the user.
1313 if ($category->visible
1314 or has_capability('moodle/category:viewhiddencategories', context_system::instance())
1315 or has_capability('moodle/category:manage', $context)) {
1317 $categoryinfo = array();
1318 $categoryinfo['id'] = $category->id;
1319 $categoryinfo['name'] = $category->name;
1320 list($categoryinfo['description'], $categoryinfo['descriptionformat']) =
1321 external_format_text($category->description, $category->descriptionformat,
1322 $context->id, 'coursecat', 'description', null);
1323 $categoryinfo['parent'] = $category->parent;
1324 $categoryinfo['sortorder'] = $category->sortorder;
1325 $categoryinfo['coursecount'] = $category->coursecount;
1326 $categoryinfo['depth'] = $category->depth;
1327 $categoryinfo['path'] = $category->path;
1329 // Some fields only returned for admin.
1330 if (has_capability('moodle/category:manage', $context)) {
1331 $categoryinfo['idnumber'] = $category->idnumber;
1332 $categoryinfo['visible'] = $category->visible;
1333 $categoryinfo['visibleold'] = $category->visibleold;
1334 $categoryinfo['timemodified'] = $category->timemodified;
1335 $categoryinfo['theme'] = $category->theme;
1338 $categoriesinfo[] = $categoryinfo;
1339 } else {
1340 $excludedcats[$category->id] = 'visibility';
1345 // Sorting the resulting array so it looks a bit better for the client developer.
1346 usort($categoriesinfo, "core_course_external::compare_categories_by_sortorder");
1348 return $categoriesinfo;
1352 * Sort categories array by path
1353 * private function: only used by get_categories
1355 * @param array $category1
1356 * @param array $category2
1357 * @return int result of strcmp
1358 * @since Moodle 2.3
1360 private static function compare_categories_by_path($category1, $category2) {
1361 return strcmp($category1->path, $category2->path);
1365 * Sort categories array by sortorder
1366 * private function: only used by get_categories
1368 * @param array $category1
1369 * @param array $category2
1370 * @return int result of strcmp
1371 * @since Moodle 2.3
1373 private static function compare_categories_by_sortorder($category1, $category2) {
1374 return strcmp($category1['sortorder'], $category2['sortorder']);
1378 * Returns description of method result value
1380 * @return external_description
1381 * @since Moodle 2.3
1383 public static function get_categories_returns() {
1384 return new external_multiple_structure(
1385 new external_single_structure(
1386 array(
1387 'id' => new external_value(PARAM_INT, 'category id'),
1388 'name' => new external_value(PARAM_TEXT, 'category name'),
1389 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1390 'description' => new external_value(PARAM_RAW, 'category description'),
1391 'descriptionformat' => new external_format_value('description'),
1392 'parent' => new external_value(PARAM_INT, 'parent category id'),
1393 'sortorder' => new external_value(PARAM_INT, 'category sorting order'),
1394 'coursecount' => new external_value(PARAM_INT, 'number of courses in this category'),
1395 'visible' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1396 'visibleold' => new external_value(PARAM_INT, '1: available, 0:not available', VALUE_OPTIONAL),
1397 'timemodified' => new external_value(PARAM_INT, 'timestamp', VALUE_OPTIONAL),
1398 'depth' => new external_value(PARAM_INT, 'category depth'),
1399 'path' => new external_value(PARAM_TEXT, 'category path'),
1400 'theme' => new external_value(PARAM_THEME, 'category theme', VALUE_OPTIONAL),
1401 ), 'List of categories'
1407 * Returns description of method parameters
1409 * @return external_function_parameters
1410 * @since Moodle 2.3
1412 public static function create_categories_parameters() {
1413 return new external_function_parameters(
1414 array(
1415 'categories' => new external_multiple_structure(
1416 new external_single_structure(
1417 array(
1418 'name' => new external_value(PARAM_TEXT, 'new category name'),
1419 'parent' => new external_value(PARAM_INT,
1420 'the parent category id inside which the new category will be created
1421 - set to 0 for a root category',
1422 VALUE_DEFAULT, 0),
1423 'idnumber' => new external_value(PARAM_RAW,
1424 'the new category idnumber', VALUE_OPTIONAL),
1425 'description' => new external_value(PARAM_RAW,
1426 'the new category description', VALUE_OPTIONAL),
1427 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1428 'theme' => new external_value(PARAM_THEME,
1429 'the new category theme. This option must be enabled on moodle',
1430 VALUE_OPTIONAL),
1439 * Create categories
1441 * @param array $categories - see create_categories_parameters() for the array structure
1442 * @return array - see create_categories_returns() for the array structure
1443 * @since Moodle 2.3
1445 public static function create_categories($categories) {
1446 global $CFG, $DB;
1447 require_once($CFG->dirroot . "/course/lib.php");
1449 $params = self::validate_parameters(self::create_categories_parameters(),
1450 array('categories' => $categories));
1452 $transaction = $DB->start_delegated_transaction();
1454 $createdcategories = array();
1455 foreach ($params['categories'] as $category) {
1456 if ($category['parent']) {
1457 if (!$DB->record_exists('course_categories', array('id' => $category['parent']))) {
1458 throw new moodle_exception('unknowcategory');
1460 $context = context_coursecat::instance($category['parent']);
1461 } else {
1462 $context = context_system::instance();
1464 self::validate_context($context);
1465 require_capability('moodle/category:manage', $context);
1467 // Check name.
1468 if (textlib::strlen($category['name'])>255) {
1469 throw new moodle_exception('categorytoolong');
1472 $newcategory = new stdClass();
1473 $newcategory->name = $category['name'];
1474 $newcategory->parent = $category['parent'];
1475 $newcategory->sortorder = 999; // Same as in the course/editcategory.php .
1476 // Format the description.
1477 if (!empty($category['description'])) {
1478 $newcategory->description = $category['description'];
1480 $newcategory->descriptionformat = external_validate_format($category['descriptionformat']);
1481 if (isset($category['theme']) and !empty($CFG->allowcategorythemes)) {
1482 $newcategory->theme = $category['theme'];
1484 // Check id number.
1485 if (!empty($category['idnumber'])) { // Same as in course/editcategory_form.php .
1486 if (textlib::strlen($category['idnumber'])>100) {
1487 throw new moodle_exception('idnumbertoolong');
1489 if ($existing = $DB->get_record('course_categories', array('idnumber' => $category['idnumber']))) {
1490 if ($existing->id) {
1491 throw new moodle_exception('idnumbertaken');
1494 $newcategory->idnumber = $category['idnumber'];
1497 $newcategory = create_course_category($newcategory);
1498 // Populate special fields.
1499 fix_course_sortorder();
1501 $createdcategories[] = array('id' => $newcategory->id, 'name' => $newcategory->name);
1504 $transaction->allow_commit();
1506 return $createdcategories;
1510 * Returns description of method parameters
1512 * @return external_function_parameters
1513 * @since Moodle 2.3
1515 public static function create_categories_returns() {
1516 return new external_multiple_structure(
1517 new external_single_structure(
1518 array(
1519 'id' => new external_value(PARAM_INT, 'new category id'),
1520 'name' => new external_value(PARAM_TEXT, 'new category name'),
1527 * Returns description of method parameters
1529 * @return external_function_parameters
1530 * @since Moodle 2.3
1532 public static function update_categories_parameters() {
1533 return new external_function_parameters(
1534 array(
1535 'categories' => new external_multiple_structure(
1536 new external_single_structure(
1537 array(
1538 'id' => new external_value(PARAM_INT, 'course id'),
1539 'name' => new external_value(PARAM_TEXT, 'category name', VALUE_OPTIONAL),
1540 'idnumber' => new external_value(PARAM_RAW, 'category id number', VALUE_OPTIONAL),
1541 'parent' => new external_value(PARAM_INT, 'parent category id', VALUE_OPTIONAL),
1542 'description' => new external_value(PARAM_RAW, 'category description', VALUE_OPTIONAL),
1543 'descriptionformat' => new external_format_value('description', VALUE_DEFAULT),
1544 'theme' => new external_value(PARAM_THEME,
1545 'the category theme. This option must be enabled on moodle', VALUE_OPTIONAL),
1554 * Update categories
1556 * @param array $categories The list of categories to update
1557 * @return null
1558 * @since Moodle 2.3
1560 public static function update_categories($categories) {
1561 global $CFG, $DB;
1562 require_once($CFG->dirroot . "/course/lib.php");
1564 // Validate parameters.
1565 $params = self::validate_parameters(self::update_categories_parameters(), array('categories' => $categories));
1567 $transaction = $DB->start_delegated_transaction();
1569 foreach ($params['categories'] as $cat) {
1570 if (!$category = $DB->get_record('course_categories', array('id' => $cat['id']))) {
1571 throw new moodle_exception('unknowcategory');
1574 $categorycontext = context_coursecat::instance($cat['id']);
1575 self::validate_context($categorycontext);
1576 require_capability('moodle/category:manage', $categorycontext);
1578 if (!empty($cat['name'])) {
1579 if (textlib::strlen($cat['name'])>255) {
1580 throw new moodle_exception('categorytoolong');
1582 $category->name = $cat['name'];
1584 if (!empty($cat['idnumber'])) {
1585 if (textlib::strlen($cat['idnumber'])>100) {
1586 throw new moodle_exception('idnumbertoolong');
1588 $category->idnumber = $cat['idnumber'];
1590 if (!empty($cat['description'])) {
1591 $category->description = $cat['description'];
1592 $category->descriptionformat = external_validate_format($cat['descriptionformat']);
1594 if (!empty($cat['theme'])) {
1595 $category->theme = $cat['theme'];
1597 if (!empty($cat['parent']) && ($category->parent != $cat['parent'])) {
1598 // First check if parent exists.
1599 if (!$parent_cat = $DB->get_record('course_categories', array('id' => $cat['parent']))) {
1600 throw new moodle_exception('unknowcategory');
1602 // Then check if we have capability.
1603 self::validate_context(get_category_or_system_context((int)$cat['parent']));
1604 require_capability('moodle/category:manage', get_category_or_system_context((int)$cat['parent']));
1605 // Finally move the category.
1606 move_category($category, $parent_cat);
1607 $category->parent = $cat['parent'];
1608 // Get updated path by move_category().
1609 $category->path = $DB->get_field('course_categories', 'path',
1610 array('id' => $category->id));
1612 $DB->update_record('course_categories', $category);
1615 $transaction->allow_commit();
1619 * Returns description of method result value
1621 * @return external_description
1622 * @since Moodle 2.3
1624 public static function update_categories_returns() {
1625 return null;
1629 * Returns description of method parameters
1631 * @return external_function_parameters
1632 * @since Moodle 2.3
1634 public static function delete_categories_parameters() {
1635 return new external_function_parameters(
1636 array(
1637 'categories' => new external_multiple_structure(
1638 new external_single_structure(
1639 array(
1640 'id' => new external_value(PARAM_INT, 'category id to delete'),
1641 'newparent' => new external_value(PARAM_INT,
1642 'the parent category to move the contents to, if specified', VALUE_OPTIONAL),
1643 'recursive' => new external_value(PARAM_BOOL, '1: recursively delete all contents inside this
1644 category, 0 (default): move contents to newparent or current parent category (except if parent is root)', VALUE_DEFAULT, 0)
1653 * Delete categories
1655 * @param array $categories A list of category ids
1656 * @return array
1657 * @since Moodle 2.3
1659 public static function delete_categories($categories) {
1660 global $CFG, $DB;
1661 require_once($CFG->dirroot . "/course/lib.php");
1663 // Validate parameters.
1664 $params = self::validate_parameters(self::delete_categories_parameters(), array('categories' => $categories));
1666 $transaction = $DB->start_delegated_transaction();
1668 foreach ($params['categories'] as $category) {
1669 if (!$deletecat = $DB->get_record('course_categories', array('id' => $category['id']))) {
1670 throw new moodle_exception('unknowcategory');
1672 $context = context_coursecat::instance($deletecat->id);
1673 require_capability('moodle/category:manage', $context);
1674 self::validate_context($context);
1675 self::validate_context(get_category_or_system_context($deletecat->parent));
1677 if ($category['recursive']) {
1678 // If recursive was specified, then we recursively delete the category's contents.
1679 category_delete_full($deletecat, false);
1680 } else {
1681 // In this situation, we don't delete the category's contents, we either move it to newparent or parent.
1682 // If the parent is the root, moving is not supported (because a course must always be inside a category).
1683 // We must move to an existing category.
1684 if (!empty($category['newparent'])) {
1685 if (!$DB->record_exists('course_categories', array('id' => $category['newparent']))) {
1686 throw new moodle_exception('unknowcategory');
1688 $newparent = $category['newparent'];
1689 } else {
1690 $newparent = $deletecat->parent;
1693 // This operation is not allowed. We must move contents to an existing category.
1694 if ($newparent == 0) {
1695 throw new moodle_exception('movecatcontentstoroot');
1698 $parentcontext = get_category_or_system_context($newparent);
1699 require_capability('moodle/category:manage', $parentcontext);
1700 self::validate_context($parentcontext);
1701 category_delete_move($deletecat, $newparent, false);
1705 $transaction->allow_commit();
1709 * Returns description of method parameters
1711 * @return external_function_parameters
1712 * @since Moodle 2.3
1714 public static function delete_categories_returns() {
1715 return null;
1721 * Deprecated course external functions
1723 * @package core_course
1724 * @copyright 2009 Petr Skodak
1725 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1726 * @since Moodle 2.0
1727 * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
1728 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1729 * @see core_course_external
1731 class moodle_course_external extends external_api {
1734 * Returns description of method parameters
1736 * @return external_function_parameters
1737 * @since Moodle 2.0
1738 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1739 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1740 * @see core_course_external::get_courses_parameters()
1742 public static function get_courses_parameters() {
1743 return core_course_external::get_courses_parameters();
1747 * Get courses
1749 * @param array $options
1750 * @return array
1751 * @since Moodle 2.0
1752 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1753 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1754 * @see core_course_external::get_courses()
1756 public static function get_courses($options) {
1757 return core_course_external::get_courses($options);
1761 * Returns description of method result value
1763 * @return external_description
1764 * @since Moodle 2.0
1765 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1766 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1767 * @see core_course_external::get_courses_returns()
1769 public static function get_courses_returns() {
1770 return core_course_external::get_courses_returns();
1774 * Returns description of method parameters
1776 * @return external_function_parameters
1777 * @since Moodle 2.0
1778 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1779 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1780 * @see core_course_external::create_courses_parameters()
1782 public static function create_courses_parameters() {
1783 return core_course_external::create_courses_parameters();
1787 * Create courses
1789 * @param array $courses
1790 * @return array courses (id and shortname only)
1791 * @since Moodle 2.0
1792 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1793 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1794 * @see core_course_external::create_courses()
1796 public static function create_courses($courses) {
1797 return core_course_external::create_courses($courses);
1801 * Returns description of method result value
1803 * @return external_description
1804 * @since Moodle 2.0
1805 * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
1806 * @todo MDL-31194 This will be deleted in Moodle 2.5.
1807 * @see core_course_external::create_courses_returns()
1809 public static function create_courses_returns() {
1810 return core_course_external::create_courses_returns();